diff --git a/ACL/include/ACL/Transport/MessageRequestHandler.h b/ACL/include/ACL/Transport/MessageRequestHandler.h index c2a90e2fb6..f011ad9847 100644 --- a/ACL/include/ACL/Transport/MessageRequestHandler.h +++ b/ACL/include/ACL/Transport/MessageRequestHandler.h @@ -115,7 +115,7 @@ class MessageRequestHandler * Record a stream metric once when a specific threshold of bytes have been read from the stream. * The stream metric name and threshold will be specified in the MessageRequest. */ - void recordStreamMetric(int bytes); + void recordStreamMetric(std::size_t bytes); /** * Record the metric that specifics the start of sending the Message Event to the cloud. @@ -159,7 +159,7 @@ class MessageRequestHandler avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status m_resultStatus; /// The number of bytes that have been read from the stream. - unsigned int m_streamBytesRead; + std::size_t m_streamBytesRead; /// If the stream metric has already been recorded. bool m_recordedStreamMetric; diff --git a/ACL/include/ACL/Transport/MessageRouterFactory.h b/ACL/include/ACL/Transport/MessageRouterFactory.h index 093c46b9ba..dcdfc61404 100644 --- a/ACL/include/ACL/Transport/MessageRouterFactory.h +++ b/ACL/include/ACL/Transport/MessageRouterFactory.h @@ -22,7 +22,6 @@ namespace alexaClientSDK { namespace acl { -using namespace alexaClientSDK::acl; using namespace avsCommon::sdkInterfaces; using namespace avsCommon::avs::attachment; diff --git a/ACL/include/ACL/Transport/MessageRouterFactoryInterface.h b/ACL/include/ACL/Transport/MessageRouterFactoryInterface.h index 7a46a0ede6..a2b3242b6d 100644 --- a/ACL/include/ACL/Transport/MessageRouterFactoryInterface.h +++ b/ACL/include/ACL/Transport/MessageRouterFactoryInterface.h @@ -22,7 +22,6 @@ namespace alexaClientSDK { namespace acl { -using namespace alexaClientSDK::acl; using namespace avsCommon::sdkInterfaces; using namespace avsCommon::avs::attachment; diff --git a/ACL/src/AVSConnectionManager.cpp b/ACL/src/AVSConnectionManager.cpp index c5983a6c8e..75d41af7ec 100644 --- a/ACL/src/AVSConnectionManager.cpp +++ b/ACL/src/AVSConnectionManager.cpp @@ -26,7 +26,7 @@ namespace acl { using namespace alexaClientSDK::avsCommon::sdkInterfaces; /// String to identify log entries originating from this file. -static const std::string TAG("AVSConnectionManager"); +#define TAG "AVSConnectionManager" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/ACL/src/Transport/DownchannelHandler.cpp b/ACL/src/Transport/DownchannelHandler.cpp index f68908c0ba..8699df90d0 100644 --- a/ACL/src/Transport/DownchannelHandler.cpp +++ b/ACL/src/Transport/DownchannelHandler.cpp @@ -43,7 +43,7 @@ static const std::string DOWNCHANNEL_ID_PREFIX = "AVSDownChannel-"; static const std::chrono::seconds ESTABLISH_CONNECTION_TIMEOUT = std::chrono::seconds(60); /// String to identify log entries originating from this file. -static const std::string TAG("DownchannelHandler"); +#define TAG "DownchannelHandler" /// String to identify the metric source prefix for @c DownChannelHandler. static const std::string METRIC_SOURCE_PREFIX = "DOWNCHANNEL_HANDLER-"; diff --git a/ACL/src/Transport/ExchangeHandler.cpp b/ACL/src/Transport/ExchangeHandler.cpp index e47ab179d6..222d71e31a 100644 --- a/ACL/src/Transport/ExchangeHandler.cpp +++ b/ACL/src/Transport/ExchangeHandler.cpp @@ -27,7 +27,7 @@ namespace acl { static const std::string AUTHORIZATION_HEADER = "Authorization: Bearer "; /// String to identify log entries originating from this file. -static const std::string TAG("ExchangeHandler"); +#define TAG "ExchangeHandler" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/ACL/src/Transport/HTTP2Transport.cpp b/ACL/src/Transport/HTTP2Transport.cpp index 5d50ad78e3..bcb0be467f 100644 --- a/ACL/src/Transport/HTTP2Transport.cpp +++ b/ACL/src/Transport/HTTP2Transport.cpp @@ -43,7 +43,7 @@ using namespace avsCommon::utils::metrics; using namespace avsCommon::utils::power; /// String to identify log entries originating from this file. -static const std::string TAG("HTTP2Transport"); +#define TAG "HTTP2Transport" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -288,10 +288,10 @@ HTTP2Transport::HTTP2Transport( m_disconnectReason{ConnectionStatusObserverInterface::ChangedReason::NONE} { m_observers.insert(transportObserver); - m_mainLoopPowerResource = PowerMonitor::getInstance()->createLocalPowerResource(TAG + "_mainLoop"); + m_mainLoopPowerResource = PowerMonitor::getInstance()->createLocalPowerResource(TAG "_mainLoop"); m_requestActivityPowerResource = - PowerMonitor::getInstance()->createLocalPowerResource(TAG + "_requestActivityResource"); + PowerMonitor::getInstance()->createLocalPowerResource(TAG "_requestActivityResource"); if (m_mainLoopPowerResource) { m_mainLoopPowerResource->acquire(); @@ -772,6 +772,8 @@ HTTP2Transport::State HTTP2Transport::handlePostConnecting() { } } + ACSDK_INFO(LX_P("handlePostConnecting").m("completing")); + return sendMessagesAndPings(State::POST_CONNECTING, m_requestQueue); } diff --git a/ACL/src/Transport/HTTP2TransportFactory.cpp b/ACL/src/Transport/HTTP2TransportFactory.cpp index 805536fbdb..e9354cfc39 100644 --- a/ACL/src/Transport/HTTP2TransportFactory.cpp +++ b/ACL/src/Transport/HTTP2TransportFactory.cpp @@ -27,7 +27,7 @@ using namespace avsCommon::sdkInterfaces; using namespace avsCommon::avs::attachment; /// String to identify log entries originating from this file. -static const std::string TAG("HTTP2TransportFactory"); +#define TAG "HTTP2TransportFactory" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/ACL/src/Transport/MessageRequestHandler.cpp b/ACL/src/Transport/MessageRequestHandler.cpp index 4a036005e4..7f0ec41b39 100644 --- a/ACL/src/Transport/MessageRequestHandler.cpp +++ b/ACL/src/Transport/MessageRequestHandler.cpp @@ -32,6 +32,7 @@ namespace alexaClientSDK { namespace acl { +using namespace avsCommon::avs; using namespace avsCommon::avs::attachment; using namespace avsCommon::sdkInterfaces; using namespace avsCommon::utils::http; @@ -66,7 +67,7 @@ static const std::string ATTACHMENT_CONTENT_TYPE = "Content-Type: application/oc static const std::string MESSAGEREQUEST_ID_PREFIX = "AVSEvent-"; /// String to identify log entries originating from this file. -static const std::string TAG("MessageRequestHandler"); +#define TAG "MessageRequestHandler" /// Prefix used to identify metrics published by this module. static const std::string ACL_METRIC_SOURCE_PREFIX = "ACL-"; @@ -92,9 +93,18 @@ static const std::string SEND_COMPLETED = "SEND_COMPLETED"; /// Metric identifier for message send error. static const std::string MESSAGE_SEND_ERROR = "ERROR.MESSAGE_SEND_FAILED"; -// Key value separator for HTTP headers +/// Key value separator for HTTP headers static const std::string HTTP_KEY_VALUE_SEPARATOR = ": "; +/// Event header key for the namespace field. +static const std::string EVENT_HEADER_NAMESPACE = "namespace"; + +/// Event header key for the name field. +static const std::string EVENT_HEADER_NAME = "name"; + +/// Event header missing. +static const std::string EVENT_HEADER_MISSING = "EVENT_HEADER_MISSING"; + /** * Create a LogEntry using this file's TAG and the specified event string. * @@ -131,10 +141,12 @@ static void collectSendDataResultMetric( * * @param metricRecorder The metric recorder object. * @param status The @c MessageRequestObserverInterface::Status of the message. + * @param messageRequest The @c MessageRequest object. */ static void submitMessageSendErrorMetric( const std::shared_ptr& metricRecorder, - MessageRequestObserverInterface::Status status) { + MessageRequestObserverInterface::Status status, + const std::shared_ptr& messageRequest) { if (!metricRecorder) { return; } @@ -155,11 +167,27 @@ static void submitMessageSendErrorMetric( return; } - auto metricEvent = MetricEventBuilder{} - .setActivityName(ACL_METRIC_SOURCE_PREFIX + MESSAGE_SEND_ERROR) - .addDataPoint(DataPointCounterBuilder{}.setName(ss.str()).increment(1).build()) - .build(); - + auto metricEventBuilder = MetricEventBuilder{} + .setActivityName(ACL_METRIC_SOURCE_PREFIX + MESSAGE_SEND_ERROR) + .addDataPoint(DataPointCounterBuilder{}.setName(ss.str()).increment(1).build()); + if (messageRequest) { + auto eventHeaders = messageRequest->retrieveEventHeaders(); + if (!eventHeaders.eventNamespace.empty()) { + metricEventBuilder.addDataPoint( + DataPointStringBuilder{}.setName(EVENT_HEADER_NAMESPACE).setValue(eventHeaders.eventNamespace).build()); + } else { + metricEventBuilder.addDataPoint( + DataPointStringBuilder{}.setName(EVENT_HEADER_NAMESPACE).setValue(EVENT_HEADER_MISSING).build()); + } + if (!eventHeaders.eventName.empty()) { + metricEventBuilder.addDataPoint( + DataPointStringBuilder{}.setName(EVENT_HEADER_NAME).setValue(eventHeaders.eventName).build()); + } else { + metricEventBuilder.addDataPoint( + DataPointStringBuilder{}.setName(EVENT_HEADER_NAME).setValue(EVENT_HEADER_MISSING).build()); + } + } + auto metricEvent = metricEventBuilder.build(); if (!metricEvent) { ACSDK_ERROR(LX("submitErrorMetricFailed").d("reason", "invalid metric event")); return; @@ -168,7 +196,7 @@ static void submitMessageSendErrorMetric( recordMetric(metricRecorder, metricEvent); } -void MessageRequestHandler::recordStreamMetric(int bytes) { +void MessageRequestHandler::recordStreamMetric(std::size_t bytes) { if (m_messageRequest == nullptr) { return; } @@ -506,7 +534,7 @@ void MessageRequestHandler::onResponseFinished(HTTP2ResponseFinishedStatus statu m_messageRequest->sendCompleted(m_resultStatus); - submitMessageSendErrorMetric(m_metricRecorder, m_resultStatus); + submitMessageSendErrorMetric(m_metricRecorder, m_resultStatus, m_messageRequest); } } // namespace acl diff --git a/ACL/src/Transport/MessageRequestQueue.cpp b/ACL/src/Transport/MessageRequestQueue.cpp index 64bc623a9a..ac5adb2b30 100644 --- a/ACL/src/Transport/MessageRequestQueue.cpp +++ b/ACL/src/Transport/MessageRequestQueue.cpp @@ -24,7 +24,7 @@ namespace acl { using namespace avsCommon::avs; /// String to identify log entries originating from this file. -static const std::string TAG("MessageRequestQueue"); +#define TAG "MessageRequestQueue" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/ACL/src/Transport/MessageRouter.cpp b/ACL/src/Transport/MessageRouter.cpp index 752ad20af9..bb8e16ce96 100644 --- a/ACL/src/Transport/MessageRouter.cpp +++ b/ACL/src/Transport/MessageRouter.cpp @@ -31,7 +31,7 @@ using namespace avsCommon::avs::attachment; using namespace avsCommon::avs; /// String to identify log entries originating from this file. -static const std::string TAG("MessageRouter"); +#define TAG "MessageRouter" /// String for logging purpose as the key for the size of m_transports. static constexpr const char* KEY_SIZEOF_TRANSPORTS = "sizeOf m_transports"; @@ -316,7 +316,7 @@ void MessageRouter::notifyObserverOnConnectionStatusChanged( m_serverSideDisconnectNotificationPending = true; m_serverSideDisconnectTimer.start(m_serverSideReconnectGracePeriod, [this]() { ACSDK_DEBUG0(LX("serverSideDisconectTimerPredicate")); - m_executor.submit([this]() { + m_executor.execute([this]() { ACSDK_DEBUG0( LX("serverSideDisconectTimerHandler") .d("m_serverSideDisconnectNotificationPending", m_serverSideDisconnectNotificationPending)); @@ -335,7 +335,7 @@ void MessageRouter::notifyObserverOnConnectionStatusChanged( handleNotifyObserverOnConnectionStatusChanged(status, reason); } }; - m_executor.submit(task); + m_executor.execute(task); } void MessageRouter::handleNotifyObserverOnConnectionStatusChanged( @@ -360,7 +360,7 @@ void MessageRouter::notifyObserverOnReceive(const std::string& contextId, const temp->receive(contextId, message); } }; - m_executor.submit(task); + m_executor.execute(task); } void MessageRouter::createActiveTransportLocked() { @@ -428,7 +428,7 @@ void MessageRouter::safelyResetActiveTransportLocked() { void MessageRouter::safelyReleaseTransport(std::shared_ptr transport) { if (transport) { auto task = [transport]() { transport->shutdown(); }; - m_executor.submit(task); + m_executor.execute(task); } } diff --git a/ACL/src/Transport/MessageRouterFactory.cpp b/ACL/src/Transport/MessageRouterFactory.cpp index b70d903e41..85506d09d9 100644 --- a/ACL/src/Transport/MessageRouterFactory.cpp +++ b/ACL/src/Transport/MessageRouterFactory.cpp @@ -20,7 +20,6 @@ namespace alexaClientSDK { namespace acl { -using namespace alexaClientSDK::acl; using namespace avsCommon::sdkInterfaces; using namespace avsCommon::avs::attachment; diff --git a/ACL/src/Transport/MimeResponseSink.cpp b/ACL/src/Transport/MimeResponseSink.cpp index a9fac90ff7..2bffc4e964 100644 --- a/ACL/src/Transport/MimeResponseSink.cpp +++ b/ACL/src/Transport/MimeResponseSink.cpp @@ -47,7 +47,7 @@ static const std::string MIME_OCTET_STREAM_CONTENT_TYPE = "application/octet-str static const size_t NON_MIME_BODY_MAX_SIZE = 4096; /// String to identify log entries originating from this file. -static const std::string TAG("MimeResponseSink"); +#define TAG "MimeResponseSink" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/ACL/src/Transport/PingHandler.cpp b/ACL/src/Transport/PingHandler.cpp index f6bf8260a4..7589e50647 100644 --- a/ACL/src/Transport/PingHandler.cpp +++ b/ACL/src/Transport/PingHandler.cpp @@ -40,7 +40,7 @@ static const uint8_t PING_PRIORITY = 200; static const std::string PING_ID_PREFIX = "AVSPing-"; /// String to identify log entries originating from this file. -static const std::string TAG("PingHandler"); +#define TAG "PingHandler" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/ACL/src/Transport/PostConnectSequencer.cpp b/ACL/src/Transport/PostConnectSequencer.cpp index aed17e2262..39c0dd813c 100644 --- a/ACL/src/Transport/PostConnectSequencer.cpp +++ b/ACL/src/Transport/PostConnectSequencer.cpp @@ -27,7 +27,7 @@ using namespace avsCommon::utils::error; using namespace avsCommon::utils::power; /// String to identify log entries originating form this file. -static const std::string TAG("PostConnectSequencer"); +#define TAG "PostConnectSequencer" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -50,9 +50,9 @@ std::shared_ptr PostConnectSequencer::create( PostConnectSequencer::PostConnectSequencer(const PostConnectOperationsSet& postConnectOperations) : m_isStopping{false}, m_postConnectOperations{postConnectOperations} { - ACSDK_DEBUG5(LX("init")); + ACSDK_INFO(LX("init")); - m_mainLoopPowerResource = PowerMonitor::getInstance()->createLocalPowerResource(TAG + "_mainLoop"); + m_mainLoopPowerResource = PowerMonitor::getInstance()->createLocalPowerResource(TAG "_mainLoop"); if (m_mainLoopPowerResource) { m_mainLoopPowerResource->acquire(); @@ -60,14 +60,14 @@ PostConnectSequencer::PostConnectSequencer(const PostConnectOperationsSet& postC } PostConnectSequencer::~PostConnectSequencer() { - ACSDK_DEBUG5(LX("destroy")); + ACSDK_INFO(LX("destroy")); stop(); } bool PostConnectSequencer::doPostConnect( std::shared_ptr postConnectSender, std::shared_ptr postConnectObserver) { - ACSDK_DEBUG5(LX("doPostConnect")); + ACSDK_INFO(LX("doPostConnect")); if (!postConnectSender) { ACSDK_ERROR(LX("doPostConnectFailed").d("reason", "nullPostConnectSender")); @@ -94,7 +94,7 @@ bool PostConnectSequencer::doPostConnect( void PostConnectSequencer::mainLoop( std::shared_ptr postConnectSender, std::shared_ptr postConnectObserver) { - ACSDK_DEBUG5(LX("mainLoop")); + ACSDK_INFO(LX("mainLoop")); PowerMonitor::getInstance()->assignThreadPowerResource(m_mainLoopPowerResource); @@ -144,11 +144,11 @@ void PostConnectSequencer::mainLoop( postConnectObserver->onPostConnected(); } - ACSDK_DEBUG5(LX("mainLoopReturning")); + ACSDK_INFO(LX("mainLoopReturning")); } void PostConnectSequencer::onDisconnect() { - ACSDK_DEBUG5(LX("onDisconnect")); + ACSDK_INFO(LX("onDisconnect")); stop(); } @@ -163,7 +163,7 @@ bool PostConnectSequencer::isStopping() { } void PostConnectSequencer::stop() { - ACSDK_DEBUG5(LX("stop")); + ACSDK_INFO(LX("stop")); { std::lock_guard lock{m_mutex}; if (m_isStopping) { diff --git a/ACL/src/Transport/PostConnectSequencerFactory.cpp b/ACL/src/Transport/PostConnectSequencerFactory.cpp index b28d63a251..503619f8a6 100644 --- a/ACL/src/Transport/PostConnectSequencerFactory.cpp +++ b/ACL/src/Transport/PostConnectSequencerFactory.cpp @@ -27,7 +27,7 @@ using namespace avsCommon::sdkInterfaces; using namespace acsdkPostConnectOperationProviderRegistrarInterfaces; /// String to identify log entries originating from this file. -static const std::string TAG("PostConnectSequencerFactory"); +#define TAG "PostConnectSequencerFactory" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/ACL/src/Transport/SynchronizedMessageRequestQueue.cpp b/ACL/src/Transport/SynchronizedMessageRequestQueue.cpp index 701147990a..54860f84c7 100644 --- a/ACL/src/Transport/SynchronizedMessageRequestQueue.cpp +++ b/ACL/src/Transport/SynchronizedMessageRequestQueue.cpp @@ -24,7 +24,7 @@ namespace acl { using namespace avsCommon::avs; /// String to identify log entries originating from this file. -static const std::string TAG("SynchronizedMessageRequestQueue"); +#define TAG "SynchronizedMessageRequestQueue" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/ACL/test/Transport/Common/MockHTTP2Connection.cpp b/ACL/test/Transport/Common/MockHTTP2Connection.cpp index 775cb656c8..b163e42fa8 100644 --- a/ACL/test/Transport/Common/MockHTTP2Connection.cpp +++ b/ACL/test/Transport/Common/MockHTTP2Connection.cpp @@ -55,7 +55,7 @@ std::shared_ptr MockHTTP2Connection::createAndSendRequest if (request->getRequestType() == HTTP2RequestType::POST) { // Parse POST HTTP2 Requests. - std::lock_guard lock(m_postRequestMutex); + std::lock_guard innerLock(m_postRequestMutex); m_postRequestQueue.push_back(request); if (m_postResponseCode != HTTPResponseCode::HTTP_RESPONSE_CODE_UNDEFINED) { request->getSink()->onReceiveResponseCode(responseCodeToInt(m_postResponseCode)); @@ -66,12 +66,12 @@ std::shared_ptr MockHTTP2Connection::createAndSendRequest m_requestPostCv.notify_one(); } else if (m_downchannelURL == request->getUrl()) { // Push downchannel requests to its queue. - std::lock_guard lock(m_downchannelRequestMutex); + std::lock_guard innerLock(m_downchannelRequestMutex); m_downchannelRequestQueue.push_back(request); m_downchannelRequestCv.notify_all(); } else if (m_pingURL == request->getUrl()) { // Push ping requests to its queue. - std::lock_guard lock(m_pingRequestMutex); + std::lock_guard innerLock(m_pingRequestMutex); m_pingRequestQueue.push_back(request); m_pingRequestCv.notify_one(); } diff --git a/ACL/test/Transport/Common/MockMimeResponseSink.cpp b/ACL/test/Transport/Common/MockMimeResponseSink.cpp index fa9dec7451..b2b6aa6f19 100644 --- a/ACL/test/Transport/Common/MockMimeResponseSink.cpp +++ b/ACL/test/Transport/Common/MockMimeResponseSink.cpp @@ -64,7 +64,7 @@ std::vector MockMimeResponseSink::getMimePart(unsigned part) { } unsigned MockMimeResponseSink::getCountOfMimeParts() { - return m_mimeContents.size(); + return static_cast(m_mimeContents.size()); } } // namespace test diff --git a/ACL/test/Transport/HTTP2TransportTest.cpp b/ACL/test/Transport/HTTP2TransportTest.cpp index f8be3dcc6c..f9b7032780 100644 --- a/ACL/test/Transport/HTTP2TransportTest.cpp +++ b/ACL/test/Transport/HTTP2TransportTest.cpp @@ -644,7 +644,7 @@ TEST_F(HTTP2TransportTest, test_pauseSendWhenSDSEmpty) { // Number of chunks the attachment will be divided into const unsigned chunks = 4; // the size of each chunk in bytes, this is the ceiling of (attachment.size / chunks) - unsigned int chunkSize = (attachment.size() + chunks - 1) / chunks; + unsigned int chunkSize = static_cast((attachment.size() + chunks - 1) / chunks); auto writer = attMgr.createWriter(TEST_ATTACHMENT_ID_STRING_ONE, avsCommon::utils::sds::WriterPolicy::BLOCKING); AttachmentWriter::WriteStatus writeStatus = AttachmentWriter::WriteStatus::OK; unsigned int lastChunkSize = (attachment.size() % chunks == 0) ? chunkSize : attachment.size() % chunks; @@ -959,7 +959,7 @@ TEST_F(HTTP2TransportTest, test_onSendCompletedNotification) { // Send a message for each test case defined in the messageResponseMap. std::vector> messageObservers; - unsigned messagesCount = messageResponseMap.size(); // number of test messages to send + unsigned messagesCount = static_cast(messageResponseMap.size()); // number of test messages to send for (unsigned messageNum = 0; messageNum < messagesCount; messageNum++) { std::shared_ptr messageReq = std::make_shared(TEST_MESSAGE, ""); auto messageObserver = std::make_shared(); diff --git a/ACL/test/Transport/MessageRouterTest.cpp b/ACL/test/Transport/MessageRouterTest.cpp index 7cd35bb7d5..8daf3230ac 100644 --- a/ACL/test/Transport/MessageRouterTest.cpp +++ b/ACL/test/Transport/MessageRouterTest.cpp @@ -149,7 +149,7 @@ TEST_F(MessageRouterTest, test_sendMessageDoesNotSendAfterDisconnected) { m_router->sendMessage(messageRequest); } -TEST_F(MessageRouterTest, test_shutdownCalledWithMultipleMessages) { +TEST_F(MessageRouterTest, testTimer_shutdownCalledWithMultipleMessages) { setupStateToConnected(); // wait for the result to propagate by scheduling a task on the client executor diff --git a/ADSL/src/DirectiveProcessor.cpp b/ADSL/src/DirectiveProcessor.cpp index 3ca3dd2c55..80d7e995cc 100644 --- a/ADSL/src/DirectiveProcessor.cpp +++ b/ADSL/src/DirectiveProcessor.cpp @@ -25,7 +25,7 @@ #include "ADSL/DirectiveProcessor.h" /// String to identify log entries originating from this file. -static const std::string TAG("DirectiveProcessor"); +#define TAG "DirectiveProcessor" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -427,7 +427,6 @@ bool DirectiveProcessor::handleQueuedDirectivesLocked(std::unique_lockgetDialogRequestId()); } } - return handleDirectiveCalled; } diff --git a/ADSL/src/DirectiveRouter.cpp b/ADSL/src/DirectiveRouter.cpp index 9cd5f520ff..43cd5a3db0 100644 --- a/ADSL/src/DirectiveRouter.cpp +++ b/ADSL/src/DirectiveRouter.cpp @@ -27,7 +27,7 @@ #include "ADSL/DirectiveRouter.h" /// String to identify log entries originating from this file. -static const std::string TAG("DirectiveRouter"); +#define TAG "DirectiveRouter" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/ADSL/src/DirectiveSequencer.cpp b/ADSL/src/DirectiveSequencer.cpp index 6ce7101540..328e61db0b 100644 --- a/ADSL/src/DirectiveSequencer.cpp +++ b/ADSL/src/DirectiveSequencer.cpp @@ -26,7 +26,7 @@ #include "ADSL/DirectiveSequencer.h" /// String to identify log entries originating from this file. -static const std::string TAG("DirectiveSequencer"); +#define TAG "DirectiveSequencer" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/ADSL/src/MessageInterpreter.cpp b/ADSL/src/MessageInterpreter.cpp index 0cfcf613b9..33bbd3d4a5 100644 --- a/ADSL/src/MessageInterpreter.cpp +++ b/ADSL/src/MessageInterpreter.cpp @@ -39,7 +39,7 @@ static const std::string PARSE_COMPLETE("PARSE_COMPLETE"); static const std::string PARSE_COMPLETE_ACTIVITY_NAME("MESSAGE_INTERPRETER-" + PARSE_COMPLETE); /// String to identify log entries originating from this file. -static const std::string TAG("MessageInterpreter"); +#define TAG "MessageInterpreter" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AFML/src/AudioActivityTracker.cpp b/AFML/src/AudioActivityTracker.cpp index b0ef2fd6ba..bd29030b6a 100644 --- a/AFML/src/AudioActivityTracker.cpp +++ b/AFML/src/AudioActivityTracker.cpp @@ -43,7 +43,7 @@ static const std::string AUDIOACTIVITYTRACKER_CAPABILITY_INTERFACE_NAME = "Audio static const std::string AUDIOACTIVITYTRACKER_CAPABILITY_INTERFACE_VERSION = "1.0"; /// String to identify log entries originating from this file. -static const std::string TAG("AudioActivityTracker"); +#define TAG "AudioActivityTracker" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -113,12 +113,12 @@ void AudioActivityTracker::provideState( const avsCommon::avs::NamespaceAndName& stateProviderName, unsigned int stateRequestToken) { ACSDK_DEBUG5(LX("provideState")); - m_executor.submit([this, stateRequestToken]() { executeProvideState(stateRequestToken); }); + m_executor.execute([this, stateRequestToken]() { executeProvideState(stateRequestToken); }); } void AudioActivityTracker::notifyOfActivityUpdates(const std::vector& channelStates) { ACSDK_DEBUG5(LX("notifyOfActivityUpdates")); - m_executor.submit([this, channelStates]() { executeNotifyOfActivityUpdates(channelStates); }); + m_executor.execute([this, channelStates]() { executeNotifyOfActivityUpdates(channelStates); }); } AudioActivityTracker::AudioActivityTracker( diff --git a/AFML/src/Channel.cpp b/AFML/src/Channel.cpp index 3158ae00ce..8fc5a997ca 100644 --- a/AFML/src/Channel.cpp +++ b/AFML/src/Channel.cpp @@ -25,7 +25,7 @@ using namespace avsCommon::sdkInterfaces; using namespace avsCommon::utils; /// String to identify log entries originating from this file. -static const std::string TAG("Channel"); +#define TAG "Channel" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -49,7 +49,7 @@ Channel::Channel(const std::string& name, const unsigned int priority, bool isVi m_state{name} { /// Non-refcounted. m_powerResource = power::PowerResource::create( - TAG + ":" + name, + TAG ":" + name, power::PowerMonitor::getInstance()->getPowerResourceManager(), PowerResourceManagerInterface::PowerResourceLevel::STANDBY_MED, false); diff --git a/AFML/src/FocusManagementComponent.cpp b/AFML/src/FocusManagementComponent.cpp index f67235aab0..63fac243a4 100644 --- a/AFML/src/FocusManagementComponent.cpp +++ b/AFML/src/FocusManagementComponent.cpp @@ -27,7 +27,7 @@ using AudioFocusAnnotation = avsCommon::sdkInterfaces::AudioFocusAnnotation; using VisualFocusAnnotation = avsCommon::sdkInterfaces::VisualFocusAnnotation; /// String to identify log entries originating from this file. -static const std::string TAG("FocusManagementComponent"); +#define TAG "FocusManagementComponent" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AFML/src/FocusManager.cpp b/AFML/src/FocusManager.cpp index c7c7b6ec5c..555df4d5b0 100644 --- a/AFML/src/FocusManager.cpp +++ b/AFML/src/FocusManager.cpp @@ -29,7 +29,7 @@ using namespace avsCommon::avs; using namespace interruptModel; /// String to identify log entries originating from this file. -static const std::string TAG("FocusManager"); +#define TAG "FocusManager" /// Key for @c FocusManager configurations in configuration node. static const std::string VIRTUAL_CHANNELS_CONFIG_KEY = "virtualChannels"; @@ -77,7 +77,7 @@ bool FocusManager::acquireChannel( return false; } - m_executor.submit( + m_executor.execute( [this, channelToAcquire, channelActivity]() { acquireChannelHelper(channelToAcquire, channelActivity); }); return true; } @@ -97,7 +97,7 @@ bool FocusManager::acquireChannel( return false; } - m_executor.submit( + m_executor.execute( [this, channelToAcquire, channelActivity]() { acquireChannelHelper(channelToAcquire, channelActivity); }); return true; } @@ -117,7 +117,7 @@ std::future FocusManager::releaseChannel( return returnValue; } - m_executor.submit([this, channelToRelease, channelObserver, releaseChannelSuccess, channelName]() { + m_executor.execute([this, channelToRelease, channelObserver, releaseChannelSuccess, channelName]() { releaseChannelHelper(channelToRelease, channelObserver, releaseChannelSuccess, channelName); }); @@ -379,9 +379,9 @@ void FocusManager::stopAllActivitiesHelper(const ChannelsToInterfaceNamesMap& ch // Only release and set entire channel focus to NONE if there are no active Activity remaining. if (!channel->isActive()) { // Lock here to update internal state which stopForegroundActivity may concurrently access. - std::unique_lock lock(m_mutex); + std::unique_lock activeChannelsLock(m_mutex); m_activeChannels.erase(channel); - lock.unlock(); + activeChannelsLock.unlock(); setChannelFocus(channel, FocusState::NONE, MixingBehavior::MUST_STOP); } } diff --git a/AFML/src/VisualActivityTracker.cpp b/AFML/src/VisualActivityTracker.cpp index bf12c73f0c..ffefea6760 100644 --- a/AFML/src/VisualActivityTracker.cpp +++ b/AFML/src/VisualActivityTracker.cpp @@ -40,7 +40,7 @@ static const std::string VISUALACTIVITYTRACKER_CAPABILITY_INTERFACE_NAME = "Visu static const std::string VISUALACTIVITYTRACKER_CAPABILITY_INTERFACE_VERSION = "1.0"; /// String to identify log entries originating from this file. -static const std::string TAG("VisualActivityTracker"); +#define TAG "VisualActivityTracker" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -110,7 +110,7 @@ void VisualActivityTracker::provideState( const avsCommon::avs::NamespaceAndName& stateProviderName, unsigned int stateRequestToken) { ACSDK_DEBUG5(LX("provideState")); - m_executor.submit([this, stateRequestToken]() { executeProvideState(stateRequestToken); }); + m_executor.execute([this, stateRequestToken]() { executeProvideState(stateRequestToken); }); } void VisualActivityTracker::notifyOfActivityUpdates(const std::vector& channels) { @@ -132,7 +132,7 @@ void VisualActivityTracker::notifyOfActivityUpdates(const std::vector +#include #include namespace alexaClientSDK { @@ -27,7 +27,7 @@ namespace avs { * Implementation of CapabilityChangeNotifierInterface */ using CapabilityChangeNotifier = - acsdkNotifier::Notifier; + notifier::Notifier; } // namespace avs } // namespace avsCommon diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/CapabilityChangeNotifierInterface.h b/AVSCommon/AVS/include/AVSCommon/AVS/CapabilityChangeNotifierInterface.h index 3af5c99e69..ac197b669f 100644 --- a/AVSCommon/AVS/include/AVSCommon/AVS/CapabilityChangeNotifierInterface.h +++ b/AVSCommon/AVS/include/AVSCommon/AVS/CapabilityChangeNotifierInterface.h @@ -16,7 +16,7 @@ #ifndef ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_CAPABILITYCHANGENOTIFIERINTERFACE_H_ #define ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_CAPABILITYCHANGENOTIFIERINTERFACE_H_ -#include +#include #include namespace alexaClientSDK { @@ -27,7 +27,7 @@ namespace avs { * Interface for registering to observe capability change. */ using CapabilityChangeNotifierInterface = - acsdkNotifierInterfaces::NotifierInterface; + notifierInterfaces::NotifierInterface; } // namespace avs } // namespace avsCommon diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/CapabilityConfiguration.h b/AVSCommon/AVS/include/AVSCommon/AVS/CapabilityConfiguration.h index 9012952693..03163e2fe3 100644 --- a/AVSCommon/AVS/include/AVSCommon/AVS/CapabilityConfiguration.h +++ b/AVSCommon/AVS/include/AVSCommon/AVS/CapabilityConfiguration.h @@ -28,13 +28,13 @@ namespace avsCommon { namespace avs { /// Key for interface type in the @c CapabilityConfiguration map -static const std::string CAPABILITY_INTERFACE_TYPE_KEY = "type"; +static const auto CAPABILITY_INTERFACE_TYPE_KEY = "type"; /// Key for interface name in the @c CapabilityConfiguration map -static const std::string CAPABILITY_INTERFACE_NAME_KEY = "interface"; +static const auto CAPABILITY_INTERFACE_NAME_KEY = "interface"; /// Key for interface version in the @c CapabilityConfiguration map -static const std::string CAPABILITY_INTERFACE_VERSION_KEY = "version"; +static const auto CAPABILITY_INTERFACE_VERSION_KEY = "version"; /// Key for interface configurations in the @c CapabilityConfiguration map -static const std::string CAPABILITY_INTERFACE_CONFIGURATIONS_KEY = "configurations"; +static const auto CAPABILITY_INTERFACE_CONFIGURATIONS_KEY = "configurations"; /** * Class to encapsulate the capability configuration implemented by a capability agent. diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/MessageRequest.h b/AVSCommon/AVS/include/AVSCommon/AVS/MessageRequest.h index 871dab5d98..6886e5fb5b 100644 --- a/AVSCommon/AVS/include/AVSCommon/AVS/MessageRequest.h +++ b/AVSCommon/AVS/include/AVSCommon/AVS/MessageRequest.h @@ -59,6 +59,28 @@ class MessageRequest { std::shared_ptr reader; }; + /// A struct to hold event namespace and name. + struct EventHeaders { + /** + * Constructor. + * + * @param eventNamespace The namespace of the event. + * @param eventName The name of the event. + */ + EventHeaders(const std::string& eventNamespace, const std::string& eventName) : + eventNamespace{eventNamespace}, + eventName{eventName} { + } + + EventHeaders() = default; + + /// The event namespace. + std::string eventNamespace; + + /// The event name. + std::string eventName; + }; + /** * Function to resolve an editable message request based on the provided resolveKey by updating the MessageRequest. * @param[in,out] req Target editable request message that will be modified in place. @@ -216,6 +238,13 @@ class MessageRequest { */ void removeObserver(std::shared_ptr observer); + /** + * Retrieve MessageRequest event headers (namespace and name). + * + * @return EventHeaders containing the namespace and name. + */ + EventHeaders retrieveEventHeaders() const; + /** * Get additional HTTP headers for this request * diff --git a/AVSCommon/AVS/src/AVSContext.cpp b/AVSCommon/AVS/src/AVSContext.cpp index d3a0a275d8..c80de167e3 100644 --- a/AVSCommon/AVS/src/AVSContext.cpp +++ b/AVSCommon/AVS/src/AVSContext.cpp @@ -39,7 +39,7 @@ static const std::string TIME_OF_SAMPLE_KEY_STRING = "timeOfSample"; static const std::string UNCERTAINTY_KEY_STRING = "uncertaintyInMilliseconds"; /// String to identify log entries originating from this file. -static const std::string TAG("AVSContext"); +#define TAG "AVSContext" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/AVS/src/AVSDirective.cpp b/AVSCommon/AVS/src/AVSDirective.cpp index d9e8c65274..e0559eee83 100644 --- a/AVSCommon/AVS/src/AVSDirective.cpp +++ b/AVSCommon/AVS/src/AVSDirective.cpp @@ -59,7 +59,7 @@ static const std::string JSON_ENDPOINT_ID_KEY = "endpointId"; static const std::string JSON_ENDPOINT_COOKIE_KEY = "cookie"; /// String to identify log entries originating from this file. -static const std::string TAG("AvsDirective"); +#define TAG "AvsDirective" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/AVS/src/AbstractAVSConnectionManager.cpp b/AVSCommon/AVS/src/AbstractAVSConnectionManager.cpp index 4a4ba73e83..617c86455b 100644 --- a/AVSCommon/AVS/src/AbstractAVSConnectionManager.cpp +++ b/AVSCommon/AVS/src/AbstractAVSConnectionManager.cpp @@ -23,7 +23,7 @@ namespace avs { using namespace avsCommon::sdkInterfaces; /// String to identify log entries originating from this file. -static const std::string TAG("AbstractAVSConnectionManager"); +#define TAG "AbstractAVSConnectionManager" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/AVS/src/AlexaClientSDKInit.cpp b/AVSCommon/AVS/src/AlexaClientSDKInit.cpp index 2af6d8c173..f54fb3abfb 100644 --- a/AVSCommon/AVS/src/AlexaClientSDKInit.cpp +++ b/AVSCommon/AVS/src/AlexaClientSDKInit.cpp @@ -30,7 +30,7 @@ namespace avs { namespace initialization { /// String to identify log entries originating from this file. -static const std::string TAG("AlexaClientSdkInit"); +#define TAG "AlexaClientSdkInit" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/AVS/src/Attachment/AttachmentManager.cpp b/AVSCommon/AVS/src/Attachment/AttachmentManager.cpp index 025b13fb51..fca1a7c5dc 100644 --- a/AVSCommon/AVS/src/Attachment/AttachmentManager.cpp +++ b/AVSCommon/AVS/src/Attachment/AttachmentManager.cpp @@ -30,7 +30,7 @@ using namespace alexaClientSDK::avsCommon::utils; using namespace alexaClientSDK::avsCommon::utils::memory; /// String to identify log entries originating from this file. -static const std::string TAG("AttachmentManager"); +#define TAG "AttachmentManager" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/AVS/src/Attachment/AttachmentUtils.cpp b/AVSCommon/AVS/src/Attachment/AttachmentUtils.cpp index 3d837b289f..56f5d5dcc8 100644 --- a/AVSCommon/AVS/src/Attachment/AttachmentUtils.cpp +++ b/AVSCommon/AVS/src/Attachment/AttachmentUtils.cpp @@ -28,7 +28,7 @@ namespace avs { namespace attachment { /// String to identify log entries originating from this file. -static const std::string TAG("AttachmentUtils"); +#define TAG "AttachmentUtils" /// Maximum size of the reader is 4KB. static const std::size_t MAX_READER_SIZE = 4 * 1024; diff --git a/AVSCommon/AVS/src/Attachment/InProcessAttachmentWriter.cpp b/AVSCommon/AVS/src/Attachment/InProcessAttachmentWriter.cpp index 58910e700b..c335433c41 100644 --- a/AVSCommon/AVS/src/Attachment/InProcessAttachmentWriter.cpp +++ b/AVSCommon/AVS/src/Attachment/InProcessAttachmentWriter.cpp @@ -24,7 +24,7 @@ namespace attachment { using namespace alexaClientSDK::avsCommon::utils; /// String to identify log entries originating from this file. -static const std::string TAG("InProcessAttachmentWriter"); +#define TAG "InProcessAttachmentWriter" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/AVS/src/BlockingPolicy.cpp b/AVSCommon/AVS/src/BlockingPolicy.cpp index 97ed6d9619..5082f4a334 100644 --- a/AVSCommon/AVS/src/BlockingPolicy.cpp +++ b/AVSCommon/AVS/src/BlockingPolicy.cpp @@ -26,17 +26,16 @@ namespace avsCommon { namespace avs { using namespace rapidjson; -using namespace avsCommon::avs; using namespace avsCommon::utils::json; /// Flag indicating @c AUDIO medium is used. -static const long MEDIUM_FLAG_AUDIO = 1; +static const unsigned long MEDIUM_FLAG_AUDIO = 1; /// Flag indicating @c VISUAL medium is used. -static const long MEDIUM_FLAG_VISUAL = 2; +static const unsigned long MEDIUM_FLAG_VISUAL = 2; /// String to identify log entries originating from this file. -static const std::string TAG("BlockingPolicy"); +#define TAG "BlockingPolicy" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/AVS/src/CapabilityAgent.cpp b/AVSCommon/AVS/src/CapabilityAgent.cpp index bb5b329aaa..c44c2ec78d 100644 --- a/AVSCommon/AVS/src/CapabilityAgent.cpp +++ b/AVSCommon/AVS/src/CapabilityAgent.cpp @@ -31,7 +31,7 @@ using namespace avsCommon::sdkInterfaces; using namespace avsCommon::utils; /// String to identify log entries originating from this file. -static const std::string TAG("CapabilityAgent"); +#define TAG "CapabilityAgent" /// Maximum number of directives that Can be Queued before we emit warning logs. static const int CAPABILITY_QUEUE_WARN_SIZE = 10; @@ -67,6 +67,10 @@ CapabilityAgent::DirectiveInfo::DirectiveInfo( void CapabilityAgent::preHandleDirective( std::shared_ptr directive, std::unique_ptr result) { + if (!directive) { + ACSDK_ERROR(LX("preHandleDirectiveFailed").d("reason", "nullDirective")); + return; + } std::string messageId = directive->getMessageId(); auto info = getDirectiveInfo(messageId); if (info) { diff --git a/AVSCommon/AVS/src/CapabilityResources.cpp b/AVSCommon/AVS/src/CapabilityResources.cpp index d5247142d2..71bb58fa12 100644 --- a/AVSCommon/AVS/src/CapabilityResources.cpp +++ b/AVSCommon/AVS/src/CapabilityResources.cpp @@ -24,7 +24,7 @@ namespace avsCommon { namespace avs { /// String to identify log entries originating from this file. -static const std::string TAG("CapabilityResources"); +#define TAG "CapabilityResources" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/AVS/src/CapabilitySemantics/ActionsToDirectiveMapping.cpp b/AVSCommon/AVS/src/CapabilitySemantics/ActionsToDirectiveMapping.cpp index 80fcdb5de6..2298a77d4d 100644 --- a/AVSCommon/AVS/src/CapabilitySemantics/ActionsToDirectiveMapping.cpp +++ b/AVSCommon/AVS/src/CapabilitySemantics/ActionsToDirectiveMapping.cpp @@ -46,7 +46,7 @@ static const std::string PAYLOAD_KEY("payload"); static const std::string EMPTY_JSON("{}"); /// String to identify log entries originating from this file. -static const std::string TAG("ActionsToDirectiveMapping"); +#define TAG "ActionsToDirectiveMapping" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/AVS/src/CapabilitySemantics/CapabilitySemantics.cpp b/AVSCommon/AVS/src/CapabilitySemantics/CapabilitySemantics.cpp index 727b7bd5f7..ac3c1efda2 100644 --- a/AVSCommon/AVS/src/CapabilitySemantics/CapabilitySemantics.cpp +++ b/AVSCommon/AVS/src/CapabilitySemantics/CapabilitySemantics.cpp @@ -34,7 +34,7 @@ static const std::string STATE_MAPPINGS_KEY("stateMappings"); static const std::string EMPTY_JSON("{}"); /// String to identify log entries originating from this file. -static const std::string TAG("CapabilitySemantics"); +#define TAG "CapabilitySemantics" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/AVS/src/CapabilitySemantics/StatesToRangeMapping.cpp b/AVSCommon/AVS/src/CapabilitySemantics/StatesToRangeMapping.cpp index 8b43bfe5f2..63ad48014c 100644 --- a/AVSCommon/AVS/src/CapabilitySemantics/StatesToRangeMapping.cpp +++ b/AVSCommon/AVS/src/CapabilitySemantics/StatesToRangeMapping.cpp @@ -50,7 +50,7 @@ static constexpr double UNINITIALIZED_DOUBLE = std::numeric_limits::min( static const std::string EMPTY_JSON("{}"); /// String to identify log entries originating from this file. -static const std::string TAG("StatesToRangeMapping"); +#define TAG "StatesToRangeMapping" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/AVS/src/CapabilitySemantics/StatesToValueMapping.cpp b/AVSCommon/AVS/src/CapabilitySemantics/StatesToValueMapping.cpp index e48cb54111..7a5a207309 100644 --- a/AVSCommon/AVS/src/CapabilitySemantics/StatesToValueMapping.cpp +++ b/AVSCommon/AVS/src/CapabilitySemantics/StatesToValueMapping.cpp @@ -47,7 +47,7 @@ static std::string UNINITIALIZED_STRING = ""; static const std::string EMPTY_JSON("{}"); /// String to identify log entries originating from this file. -static const std::string TAG("StatesToValueMapping"); +#define TAG "StatesToValueMapping" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/AVS/src/ComponentConfiguration.cpp b/AVSCommon/AVS/src/ComponentConfiguration.cpp index 1429ffaf6d..65d22e939b 100644 --- a/AVSCommon/AVS/src/ComponentConfiguration.cpp +++ b/AVSCommon/AVS/src/ComponentConfiguration.cpp @@ -23,7 +23,7 @@ namespace avsCommon { namespace avs { /// String to identify log entries originating from this file. -static const std::string TAG("ComponentConfiguration"); +#define TAG "ComponentConfiguration" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/AVS/src/DialogUXStateAggregator.cpp b/AVSCommon/AVS/src/DialogUXStateAggregator.cpp index f581277b9f..0a97253f80 100644 --- a/AVSCommon/AVS/src/DialogUXStateAggregator.cpp +++ b/AVSCommon/AVS/src/DialogUXStateAggregator.cpp @@ -26,7 +26,7 @@ using namespace avsCommon::utils::metrics; using namespace avsCommon::sdkInterfaces; /// String to identify log entries originating from this file. -static const std::string TAG("DialogUXStateAggregator"); +#define TAG "DialogUXStateAggregator" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -114,7 +114,7 @@ void DialogUXStateAggregator::addObserver(std::shared_ptronDialogUXStateChanged(m_currentState); }); @@ -131,7 +131,7 @@ void DialogUXStateAggregator::removeObserver(std::shared_ptr& audioAnalyzerState) { ACSDK_DEBUG0(LX("onStateChanged").d("SpeechSynthesizerState", state)); m_speechSynthesizerState = state; - m_executor.submit([this, state]() { + m_executor.execute([this, state]() { ACSDK_DEBUG0(LX("onStateChangedLambda").d("SpeechSynthesizerState", state)); switch (state) { case SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING: @@ -222,7 +222,7 @@ void DialogUXStateAggregator::onConnectionStatusChanged( const DialogUXStateAggregator::Status status, const std::vector& engineStatuses) { ACSDK_DEBUG(LX("onConnectionStatusChanged").d("engineAggregatedStatus", status)); - m_executor.submit([this, engineStatuses]() { + m_executor.execute([this, engineStatuses]() { bool isDisconnected = true; for (const auto& engineStatus : engineStatuses) { ACSDK_DEBUG(LX("onConnectionStatusChangedLambda") @@ -244,7 +244,7 @@ void DialogUXStateAggregator::onConnectionStatusChanged( void DialogUXStateAggregator::onRequestProcessingStarted() { ACSDK_DEBUG0(LX("onRequestProcessingStarted")); - m_executor.submit([this]() { + m_executor.execute([this]() { ACSDK_DEBUG0(LX("onRequestProcessingStartedLambda").d("currentState", m_currentState)); // Stop the listening timer m_listeningTimeoutTimer.stop(); @@ -274,7 +274,7 @@ void DialogUXStateAggregator::onRequestProcessingStarted() { void DialogUXStateAggregator::onRequestProcessingCompleted() { ACSDK_DEBUG(LX("onRequestProcessingCompleted")); - m_executor.submit([this]() { + m_executor.execute([this]() { if (DialogUXStateObserverInterface::DialogUXState::LISTENING == m_currentState) { /// It is possible that the cloud sends RPC without sending RPS. In those situations, if we are in /// LISTENING state, switch back to IDLE. @@ -295,7 +295,7 @@ void DialogUXStateAggregator::notifyObserversOfState() { void DialogUXStateAggregator::transitionFromThinkingTimedOut() { ACSDK_DEBUG5(LX("transitionFromThinkingTimedOut")); - m_executor.submit([this]() { + m_executor.execute([this]() { ACSDK_DEBUG5(LX("transitionFromThinkingTimedOutExecutor").d("m_currentState", m_currentState)); if (DialogUXStateObserverInterface::DialogUXState::THINKING == m_currentState) { ACSDK_DEBUG(LX("transitionFromThinkingTimedOut")); @@ -308,7 +308,7 @@ void DialogUXStateAggregator::transitionFromThinkingTimedOut() { void DialogUXStateAggregator::transitionFromListeningTimedOut() { ACSDK_DEBUG5(LX("transitionFromListeningTimedOut")); - m_executor.submit([this]() { + m_executor.execute([this]() { ACSDK_DEBUG5(LX("transitionFromListeningTimedOutExecutor").d("m_currentState", m_currentState)); if (DialogUXStateObserverInterface::DialogUXState::LISTENING == m_currentState) { ACSDK_DEBUG(LX("transitionFromListeningTimedOut")); @@ -321,7 +321,7 @@ void DialogUXStateAggregator::transitionFromListeningTimedOut() { void DialogUXStateAggregator::tryEnterIdleStateOnTimer() { ACSDK_DEBUG5(LX("tryEnterIdleStateOnTimer")); - m_executor.submit([this]() { + m_executor.execute([this]() { ACSDK_DEBUG5(LX("tryEnterIdleStateOnTimerExecutor") .d("m_currentState", m_currentState) .d("m_audioInputProcessorState", m_audioInputProcessorState) diff --git a/AVSCommon/AVS/src/DirectiveRoutingRule.cpp b/AVSCommon/AVS/src/DirectiveRoutingRule.cpp index 0b104c8ab1..1107417b5e 100644 --- a/AVSCommon/AVS/src/DirectiveRoutingRule.cpp +++ b/AVSCommon/AVS/src/DirectiveRoutingRule.cpp @@ -22,7 +22,7 @@ namespace avs { namespace directiveRoutingRule { /// String to identify log entries originating from this file. -static const std::string TAG("DirectiveRoutingRule"); +#define TAG "DirectiveRoutingRule" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/AVS/src/EditableMessageRequest.cpp b/AVSCommon/AVS/src/EditableMessageRequest.cpp index de1fb00b3f..1065870772 100644 --- a/AVSCommon/AVS/src/EditableMessageRequest.cpp +++ b/AVSCommon/AVS/src/EditableMessageRequest.cpp @@ -23,7 +23,7 @@ namespace avs { using namespace avsCommon::sdkInterfaces; /// String to identify log entries originating from this file. -static const std::string TAG("EditableMessageRequest"); +#define TAG "EditableMessageRequest" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/AVS/src/EventBuilder.cpp b/AVSCommon/AVS/src/EventBuilder.cpp index dd5aaed250..4524d7c378 100644 --- a/AVSCommon/AVS/src/EventBuilder.cpp +++ b/AVSCommon/AVS/src/EventBuilder.cpp @@ -31,7 +31,7 @@ using namespace utils; using namespace avs::constants; /// String to identify log entries originating from this file. -static const std::string TAG("EventBuilder"); +#define TAG "EventBuilder" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/AVS/src/ExceptionEncounteredSender.cpp b/AVSCommon/AVS/src/ExceptionEncounteredSender.cpp index 0806bdafa1..e5930aa582 100644 --- a/AVSCommon/AVS/src/ExceptionEncounteredSender.cpp +++ b/AVSCommon/AVS/src/ExceptionEncounteredSender.cpp @@ -32,7 +32,7 @@ using namespace rapidjson; using namespace alexaClientSDK::avsCommon::utils; /// String to identify log entries originating from this file. -static const std::string TAG("ExceptionEncountered"); +#define TAG "ExceptionEncountered" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/AVS/src/Initialization/SDKPrimitivesProvider.cpp b/AVSCommon/AVS/src/Initialization/SDKPrimitivesProvider.cpp index bf0bc33db0..ef769e9966 100644 --- a/AVSCommon/AVS/src/Initialization/SDKPrimitivesProvider.cpp +++ b/AVSCommon/AVS/src/Initialization/SDKPrimitivesProvider.cpp @@ -23,7 +23,7 @@ namespace avs { namespace initialization { /// String to identify log entries originating from this file. -static const std::string TAG("SDKPrimitivesProvider"); +#define TAG "SDKPrimitivesProvider" std::shared_ptr SDKPrimitivesProvider::m_provider; std::mutex SDKPrimitivesProvider::m_mutex; diff --git a/AVSCommon/AVS/src/MessageRequest.cpp b/AVSCommon/AVS/src/MessageRequest.cpp index f1e89005eb..1afd2cc9f4 100644 --- a/AVSCommon/AVS/src/MessageRequest.cpp +++ b/AVSCommon/AVS/src/MessageRequest.cpp @@ -15,16 +15,30 @@ #include "AVSCommon/AVS/MessageRequest.h" #include "AVSCommon/AVS/EditableMessageRequest.h" +#include #include "AVSCommon/Utils/Logger/Logger.h" namespace alexaClientSDK { namespace avsCommon { namespace avs { +using namespace avsCommon::utils::json::jsonUtils; using namespace sdkInterfaces; +/// Event key. +static const std::string EVENT{"event"}; + +/// Header key. +static const std::string EVENT_HEADER{"header"}; + +/// Event header key for the namespace field. +static const std::string EVENT_HEADER_NAMESPACE{"namespace"}; + +/// Event header key for the name field. +static const std::string EVENT_HEADER_NAME{"name"}; + /// String to identify log entries originating from this file. -static const std::string TAG("MessageRequest"); +#define TAG "MessageRequest" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -110,7 +124,7 @@ std::string MessageRequest::getUriPathExtension() const { } int MessageRequest::attachmentReadersCount() const { - return m_readers.size(); + return static_cast(m_readers.size()); } std::string MessageRequest::getStreamMetricName() const { return m_streamMetricName; @@ -180,6 +194,48 @@ void MessageRequest::removeObserver( m_observers.erase(observer); } +MessageRequest::EventHeaders MessageRequest::retrieveEventHeaders() const { + // This could be more efficient if parsed in the constructor. However, if this optimization is done in the future + // EditableMessageRequest will need to also refresh the cache, and thready safety for both classes would need to be + // re-evaluated. + rapidjson::Document document; + auto eventHeaders = MessageRequest::EventHeaders(); + + auto eventExists = parseJSON(this->getJsonContent(), &document); + if (!eventExists) { + ACSDK_ERROR(LX("retrieveEventHeadersFailed").d("reason", "Parsing error")); + return eventHeaders; + } + + auto event = document.FindMember(EVENT); + if (event == document.MemberEnd()) { + ACSDK_ERROR(LX("retrieveEventHeadersFailed").d("reason", "No event found in json")); + return eventHeaders; + } + + auto headers = event->value.FindMember(EVENT_HEADER); + if (headers == event->value.MemberEnd()) { + ACSDK_ERROR(LX("retrieveEventHeadersFailed").d("reason", "No event headers found")); + return eventHeaders; + } + + auto field = headers->value.FindMember(EVENT_HEADER_NAMESPACE); + if (field != headers->value.MemberEnd() && field->value.IsString()) { + eventHeaders.eventNamespace = field->value.GetString(); + } else { + ACSDK_ERROR(LX("retrieveEventHeadersFailed").d("reason", "No event namespace found")); + } + + field = headers->value.FindMember(EVENT_HEADER_NAME); + if (field != headers->value.MemberEnd() && field->value.IsString()) { + eventHeaders.eventName = field->value.GetString(); + } else { + ACSDK_ERROR(LX("retrieveEventHeadersFailed").d("reason", "No event name found")); + } + + return eventHeaders; +} + const std::vector>& MessageRequest::getHeaders() const { return m_headers; } diff --git a/AVSCommon/AVS/src/WaitableMessageRequest.cpp b/AVSCommon/AVS/src/WaitableMessageRequest.cpp index 73ab9ab482..1d57488b6b 100644 --- a/AVSCommon/AVS/src/WaitableMessageRequest.cpp +++ b/AVSCommon/AVS/src/WaitableMessageRequest.cpp @@ -24,7 +24,7 @@ namespace avs { using namespace avsCommon::sdkInterfaces; /// String to identify log entries originating from this file. -static const std::string TAG("WaitableMessageRequest"); +#define TAG "WaitableMessageRequest" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/AVS/test/Attachment/AttachmentReaderTest.cpp b/AVSCommon/AVS/test/Attachment/AttachmentReaderTest.cpp index 59d447e6b4..ecb7d10a72 100644 --- a/AVSCommon/AVS/test/Attachment/AttachmentReaderTest.cpp +++ b/AVSCommon/AVS/test/Attachment/AttachmentReaderTest.cpp @@ -122,7 +122,7 @@ void AttachmentReaderTest::testMultipleReads(bool closeWriterBeforeReading) { std::vector result(TEST_SDS_PARTIAL_READ_AMOUNT_IN_BYTES); auto readStatus = InProcessAttachmentReader::ReadStatus::OK; - int totalBytesRead = 0; + size_t totalBytesRead = 0; bool done = false; int iterations = 0; int iterationsMax = 10; @@ -145,7 +145,7 @@ void AttachmentReaderTest::testMultipleReads(bool closeWriterBeforeReading) { // Not only was all the data read, but the reader remained open. ASSERT_NE(iterations, iterationsMax); ASSERT_EQ(readStatus, terminalStatus); - ASSERT_EQ(totalBytesRead, static_cast(m_testPattern.size())); + ASSERT_EQ(totalBytesRead, m_testPattern.size()); } void AttachmentReaderTest::readAndVerifyResult( diff --git a/AVSCommon/AVS/test/Attachment/AttachmentWriterTest.cpp b/AVSCommon/AVS/test/Attachment/AttachmentWriterTest.cpp index 64655407fa..6c432a6e28 100644 --- a/AVSCommon/AVS/test/Attachment/AttachmentWriterTest.cpp +++ b/AVSCommon/AVS/test/Attachment/AttachmentWriterTest.cpp @@ -89,7 +89,7 @@ void AttachmentWriterTest::testMultipleReads(bool closeWriterBeforeReading) { std::vector result(TEST_SDS_PARTIAL_READ_AMOUNT_IN_BYTES); auto readStatus = InProcessAttachmentReader::ReadStatus::OK; - int totalBytesRead = 0; + size_t totalBytesRead = 0; bool done = false; int iterations = 0; int iterationsMax = 10; @@ -111,7 +111,7 @@ void AttachmentWriterTest::testMultipleReads(bool closeWriterBeforeReading) { ASSERT_NE(iterations, iterationsMax); ASSERT_EQ(readStatus, terminalStatus); - ASSERT_EQ(totalBytesRead, static_cast(m_testPattern.size())); + ASSERT_EQ(totalBytesRead, m_testPattern.size()); } /** diff --git a/AVSCommon/AVS/test/DialogUXStateAggregatorTest.cpp b/AVSCommon/AVS/test/DialogUXStateAggregatorTest.cpp index 267c2ba555..08c0fb274c 100644 --- a/AVSCommon/AVS/test/DialogUXStateAggregatorTest.cpp +++ b/AVSCommon/AVS/test/DialogUXStateAggregatorTest.cpp @@ -25,6 +25,7 @@ namespace test { using namespace avsCommon::avs; using namespace avsCommon::sdkInterfaces; +using namespace avsCommon::utils::mediaPlayer; using namespace std; /// Long time out for observers to wait for the state change callback (we should not reach this). @@ -40,7 +41,7 @@ static const auto SHORT_TIMEOUT = std::chrono::milliseconds(50); static const auto TRANSITION_TIMEOUT = std::chrono::milliseconds(300); /// Dummy value for a media player source id -static const avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId TEST_SOURCE_ID = -1; +static const MediaPlayerInterface::SourceId TEST_SOURCE_ID = static_cast(-1); /// A test observer that mocks out the DialogUXStateObserverInterface##onDialogUXStateChanged() call. class TestObserver : public DialogUXStateObserverInterface { diff --git a/AVSCommon/AVS/test/MessageRequestTest.cpp b/AVSCommon/AVS/test/MessageRequestTest.cpp index 1f5ddd8ced..304fe9f796 100644 --- a/AVSCommon/AVS/test/MessageRequestTest.cpp +++ b/AVSCommon/AVS/test/MessageRequestTest.cpp @@ -22,11 +22,70 @@ using namespace ::testing; namespace alexaClientSDK { - namespace avsCommon { namespace avs { namespace test { +/// Valid test event. +// clang-format off +static const std::string VALID_TEST_EVENT = R"({ + "event": { + "header": { + "namespace": "test_namespace", + "name": "test_name", + "messageId": "test_messageId", + "dialogRequestId": "test_dialogRequestId" + }, + "payload": {} + } +})"; +// clang-format on + +/// Partially valid test event. +// clang-format off +static const std::string PARTIALLY_VALID_TEST_EVENT = R"({ + "event": { + "header": { + "namespace": "test_namespace", + "messageId": "test_messageId", + "dialogRequestId": "test_dialogRequestId" + }, + "payload": {} + } +})"; +// clang-format on + +/// Incorrectly formatted test event. +// clang-format off +static const std::string INCORRECTLY_FORMATTED_TEST_EVENT = R"({ + "event": { + "namespace": "test_namespace", + "messageId": "test_messageId", + "dialogRequestId": "test_dialogRequestId", + "payload": {} + } +})"; +// clang-format on + +/// Invalid test event. +// clang-format off +static const std::string INVALID_TEST_EVENT = R"({ + "event": { + "header": { + "messageId": "test_messageId", + "dialogRequestId": "test_dialogRequestId" + }, + "payload": {} + } +})"; +// clang-format on + +/// Test event namespace header value. +static const std::string TEST_NAMESPACE = "test_namespace"; + +/// Test event name header value. +static const std::string TEST_NAME = "test_name"; + class MockAttachmentReader : public attachment::AttachmentReader { public: MOCK_METHOD4( @@ -87,6 +146,42 @@ TEST_F(MessageRequestTest, test_extraHeaders) { EXPECT_EQ(expected, actual); } +TEST_F(MessageRequestTest, test_eventHeaders) { + auto expectedEventHeaders = MessageRequest::EventHeaders(TEST_NAMESPACE, TEST_NAME); + MessageRequest messageRequest(VALID_TEST_EVENT, true, "", {}); + auto actualEventHeaders = messageRequest.retrieveEventHeaders(); + + EXPECT_EQ(expectedEventHeaders.eventNamespace, actualEventHeaders.eventNamespace); + EXPECT_EQ(expectedEventHeaders.eventName, actualEventHeaders.eventName); +} + +TEST_F(MessageRequestTest, test_partialEventHeaders) { + auto expectedEventHeaders = MessageRequest::EventHeaders(TEST_NAMESPACE, ""); + MessageRequest messageRequest(PARTIALLY_VALID_TEST_EVENT, true, "", {}); + auto actualEventHeaders = messageRequest.retrieveEventHeaders(); + + EXPECT_EQ(expectedEventHeaders.eventNamespace, actualEventHeaders.eventNamespace); + EXPECT_EQ(expectedEventHeaders.eventName, actualEventHeaders.eventName); +} + +TEST_F(MessageRequestTest, test_incorrectlyFormattedEventHeaders) { + auto expectedEventHeaders = MessageRequest::EventHeaders(); + MessageRequest messageRequest(INCORRECTLY_FORMATTED_TEST_EVENT, true, "", {}); + auto actualEventHeaders = messageRequest.retrieveEventHeaders(); + + EXPECT_EQ(expectedEventHeaders.eventNamespace, actualEventHeaders.eventNamespace); + EXPECT_EQ(expectedEventHeaders.eventName, actualEventHeaders.eventName); +} + +TEST_F(MessageRequestTest, test_emptyEventHeaders) { + auto expectedEventHeaders = MessageRequest::EventHeaders(); + MessageRequest messageRequest(INVALID_TEST_EVENT, true, "", {}); + auto actualEventHeaders = messageRequest.retrieveEventHeaders(); + + EXPECT_EQ(expectedEventHeaders.eventNamespace, actualEventHeaders.eventNamespace); + EXPECT_EQ(expectedEventHeaders.eventName, actualEventHeaders.eventName); +} + TEST_F(MessageRequestTest, test_isResolved) { MessageRequest resolvedReq("{}", true, ""); EXPECT_TRUE(resolvedReq.isResolved()); diff --git a/AVSCommon/CMakeLists.txt b/AVSCommon/CMakeLists.txt index 0ff9cb433b..9c5d181fbc 100644 --- a/AVSCommon/CMakeLists.txt +++ b/AVSCommon/CMakeLists.txt @@ -44,7 +44,6 @@ add_library(AVSCommon Utils/src/BluetoothEventBus.cpp Utils/src/Configuration/ConfigurationNode.cpp Utils/src/DeviceInfo.cpp - Utils/src/Executor.cpp Utils/src/FileUtils.cpp Utils/src/FormattedAudioStreamAdapter.cpp Utils/src/JSON/JSONGenerator.cpp @@ -106,6 +105,9 @@ add_library(AVSCommon Utils/src/TaskThread.cpp Utils/src/ThreadPool.cpp Utils/src/Threading/ConditionVariableWrapper.cpp + Utils/src/Threading/ExecutorFactory.cpp + Utils/src/Threading/Executor.cpp + Utils/src/Threading/SharedExecutor.cpp Utils/src/TimePoint.cpp Utils/src/TimeUtils.cpp Utils/src/Timer.cpp @@ -127,6 +129,8 @@ target_include_directories(AVSCommon PUBLIC "${MultipartParser_SOURCE_DIR}" ${CURL_INCLUDE_DIRS}) +target_include_directories(AVSCommon PRIVATE "${AVSCommon_SOURCE_DIR}/Utils/privateInclude") + if (MSVC) target_include_directories(AVSCommon PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/exports") @@ -146,6 +150,7 @@ target_link_libraries(AVSCommon acsdkApplicationAudioPipelineFactoryInterfaces acsdkEqualizerInterfaces acsdkInteractionModelInterfaces + acsdkNotifierInterfaces ) # install target diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AlexaInterfaceMessageSenderInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AlexaInterfaceMessageSenderInterface.h index 01191c80ce..519b8e8e8a 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AlexaInterfaceMessageSenderInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AlexaInterfaceMessageSenderInterface.h @@ -30,7 +30,8 @@ namespace sdkInterfaces { */ class AlexaInterfaceMessageSenderInterface { public: - /// The type of error when calling sendErrorResponseEvent(). + /// The type of error when calling sendErrorResponseEvent() with @c Alexa.ErrorResponse event. + /// https://developer.amazon.com/en-US/docs/alexa/device-apis/alexa-errorresponse.html enum class ErrorResponseType { /// The operation can't be performed because the endpoint is already in operation. ALREADY_IN_OPERATION, @@ -96,6 +97,41 @@ class AlexaInterfaceMessageSenderInterface { VALUE_OUT_OF_RANGE }; + /// The type of video error when calling sendErrorResponseEvent() with @c Alexa.Video.ErrorResponse event. + /// https://developer.amazon.com/en-US/docs/alexa/device-apis/alexa-video-errorresponse.html + enum class AlexaVideoErrorResponseType { + /// Indicates the content does not allow the action requested. For example, if the user tries to delete + /// a recording that is marked as not deletable. + ACTION_NOT_PERMITTED_FOR_CONTENT, + + /// Indicates an additional confirmation must occur before the requested action can be completed. + CONFIRMATION_REQUIRED, + + /// Indicates the record operation failed due to restrictions on the content. + CONTENT_NOT_RECORDABLE, + + /// The user is not subscribed to the content for a channel or other subscription-based content. + NOT_SUBSCRIBED, + + /// Indicates that a recording request failed because the recording already exists. + RECORDING_EXISTS, + + /// Indicates that a recording request failed because the DVR storage is full. + STORAGE_FULL, + + /// Indicates the title specified yielded multiple results, and disambiguation is required to determine + /// the program to record. This value should be used to indicate that the target device will provide a + /// mechanism for disambiguation. For example, this error could indicate that there are multiple airings + /// of a program or that the entity requested for recording has multiple programs associated with it. + TITLE_DISAMBIGUATION_REQUIRED, + + /// Indicates that a recording request failed because of a scheduling conflict with another recording. + TUNER_OCCUPIED, + + /// Indicates an invalid error type + NONE + }; + /** * Destructor. */ @@ -189,6 +225,15 @@ class AlexaInterfaceMessageSenderInterface { const std::string& correlationToken, const int estimatedDeferralInSeconds = 0) = 0; + /** + * Convert @c AlexaVideoErrorResponseType type to its corresponding string. Note that any invalid + * @c AlexaVideoErrorResponseType will return an empty string. + * + * @param responseType the response type to convert. + * @return the corresponding string for video error response type + */ + static std::string alexaVideoErrorResponseToString(AlexaVideoErrorResponseType responseType); + /** * Convert an AlexaResponseType to its corresponding ErrorResponseType. Note that any AlexaResponseType that does * not map to ErrorResponseType will return INTERNAL_ERROR. @@ -199,6 +244,31 @@ class AlexaInterfaceMessageSenderInterface { static ErrorResponseType alexaResponseTypeToErrorType(const avsCommon::avs::AlexaResponseType responseType); }; +inline std::string AlexaInterfaceMessageSenderInterface::alexaVideoErrorResponseToString( + AlexaVideoErrorResponseType responseType) { + switch (responseType) { + case AlexaVideoErrorResponseType::ACTION_NOT_PERMITTED_FOR_CONTENT: + return "ACTION_NOT_PERMITTED_FOR_CONTENT"; + case AlexaVideoErrorResponseType::CONFIRMATION_REQUIRED: + return "CONFIRMATION_REQUIRED"; + case AlexaVideoErrorResponseType::CONTENT_NOT_RECORDABLE: + return "CONTENT_NOT_RECORDABLE"; + case AlexaVideoErrorResponseType::NOT_SUBSCRIBED: + return "NOT_SUBSCRIBED"; + case AlexaVideoErrorResponseType::RECORDING_EXISTS: + return "RECORDING_EXISTS"; + case AlexaVideoErrorResponseType::STORAGE_FULL: + return "STORAGE_FULL"; + case AlexaVideoErrorResponseType::TITLE_DISAMBIGUATION_REQUIRED: + return "TITLE_DISAMBIGUATION_REQUIRED"; + case AlexaVideoErrorResponseType::TUNER_OCCUPIED: + return "TUNER_OCCUPIED"; + case AlexaVideoErrorResponseType::NONE: + return ""; + } + return "UNKNOWN"; +} + inline AlexaInterfaceMessageSenderInterface::ErrorResponseType AlexaInterfaceMessageSenderInterface:: alexaResponseTypeToErrorType(const avsCommon::avs::AlexaResponseType responseType) { switch (responseType) { diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AudioInputProcessorObserverInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AudioInputProcessorObserverInterface.h index b99e176513..5b926f28ab 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AudioInputProcessorObserverInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AudioInputProcessorObserverInterface.h @@ -55,6 +55,14 @@ class AudioInputProcessorObserverInterface { */ virtual void onStateChanged(State state) = 0; + /** + * This function is called when the active @c ASRProfile changes. + * + * @param profile The string representation of the active @c ASRProfile. + * Use the associated getASRProfile to retrieve the @c ASRProfile value. + */ + virtual void onASRProfileChanged(const std::string& profile){}; + /** * This function converts the provided @c State to a string. * diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Bluetooth/BluetoothDeviceInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Bluetooth/BluetoothDeviceInterface.h index 9d6297e9f4..96321071c6 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Bluetooth/BluetoothDeviceInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Bluetooth/BluetoothDeviceInterface.h @@ -209,6 +209,17 @@ class BluetoothDeviceInterface { */ virtual std::future disconnect() = 0; + /** + * Sets the pairing pin for the current pairing attempt. PIN length can range from 4 to 16 + * alphanumeric characters, though most devices will only accept numeric characters in the PIN. + * Expected call flow is: + * pair() -> PIN request callback -> setPairingPin() + * + * @param pin BT pairing pin + * @return Indicates whether pairing pin was set. + */ + virtual bool setPairingPin(const std::string& pin) = 0; + /// @return The Bluetooth Services that this device supports. virtual std::vector> getSupportedServices() = 0; diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Bluetooth/Services/BluetoothServiceInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Bluetooth/Services/BluetoothServiceInterface.h index d5cad86798..fe3db20de6 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Bluetooth/Services/BluetoothServiceInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Bluetooth/Services/BluetoothServiceInterface.h @@ -28,8 +28,8 @@ namespace services { /** * Interface representing a Bluetooth Service. - * More Bluetooth Service information(e.g, UUID, NAME) could be found at - * https://www.bluetooth.com/specifications/assigned-numbers/service-discovery/ + * More Bluetooth Service information(e.g, UUID, NAME) could be found in the service discovery section at + * https://www.bluetooth.com/specifications/assigned-numbers/ */ class BluetoothServiceInterface { public: diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CallStateObserverInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CallStateObserverInterface.h index bea7e4e067..f4a0c43d97 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CallStateObserverInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CallStateObserverInterface.h @@ -59,6 +59,10 @@ class CallStateObserverInterface { std::string inboundCalleeName; /// Textual description of exact call provider type. std::string callProviderType; + /// Call provider image url. + /// This image url may be null. For the case the provider image is not provided by url, + /// the application should have local image to display instead of downloading from this url. + std::string callProviderImageUrl; /// Inbound ringtone url. std::string inboundRingtoneUrl; /// Outbound ringtone url. diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/GUIActivityEvent.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/GUIActivityEvent.h new file mode 100644 index 0000000000..da674c736c --- /dev/null +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/GUIActivityEvent.h @@ -0,0 +1,94 @@ +/* + * 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. + */ + +#ifndef ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_GUIACTIVITYEVENT_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_GUIACTIVITYEVENT_H_ + +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace sdkInterfaces { + +/// Enumeration of activity events used to indicate the state of a GUI +enum class GUIActivityEvent { + /// GUI switched to active state (audio/video started). + ACTIVATED, + + /// GUI become inactive. + DEACTIVATED, + + /// Interrupt event (touch/scroll/etc). + INTERRUPT, + + /// Guard option for unknown received state. + UNKNOWN +}; + +/** + * This function converts the provided string to an @c GUIActivityEvent. + * + * @param string The string to convert to @c GUIActivityEvent. + * @return The @c GUIActivityEvent. + */ +inline GUIActivityEvent guiActivityEventFromString(const std::string& string) { + if ("ACTIVATED" == string) { + return GUIActivityEvent::ACTIVATED; + } else if ("DEACTIVATED" == string) { + return GUIActivityEvent::DEACTIVATED; + } else if ("INTERRUPT" == string) { + return GUIActivityEvent::INTERRUPT; + } else { + return GUIActivityEvent::UNKNOWN; + } +} + +/** + * This function converts a GUIActivityEvent to a string. + * + * @param event the GUIActivityEvent to be converted. + * @return the string representation of the GUIActivityEvent. + */ +inline std::string guiActivityEventToString(GUIActivityEvent event) { + switch (event) { + case GUIActivityEvent::ACTIVATED: + return "ACTIVATED"; + case GUIActivityEvent::DEACTIVATED: + return "DEACTIVATED"; + case GUIActivityEvent::INTERRUPT: + return "INTERRUPT"; + case GUIActivityEvent::UNKNOWN: + return "UNKNOWN"; + } + return "UNKNOWN"; +} + +/** + * Write an @c GUIActivityEvent value to an @c ostream as a string. + * + * @param stream The stream to write the value to. + * @param state The @c GUIActivityEvent value. + * @return The @c ostream that was passed in and written to. + */ +inline std::ostream& operator<<(std::ostream& stream, const GUIActivityEvent& activityEvent) { + return stream << guiActivityEventToString(activityEvent); +} + +} // namespace sdkInterfaces +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_GUIACTIVITYEVENT_H_ \ No newline at end of file diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/GUIActivityEventObserverInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/GUIActivityEventObserverInterface.h new file mode 100644 index 0000000000..a18231952a --- /dev/null +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/GUIActivityEventObserverInterface.h @@ -0,0 +1,44 @@ +/* + * 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. + */ + +#ifndef ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_GUIACTIVITYEVENTOBSERVERINTERFACE_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_GUIACTIVITYEVENTOBSERVERINTERFACE_H_ + +#include "GUIActivityEvent.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace sdkInterfaces { + +/** + * Observer interface for @c GUIActivityEvent + */ +class GUIActivityEventObserverInterface { +public: + virtual ~GUIActivityEventObserverInterface() = default; + + /** + * Observer method to be notified of activity event + * @param source The source of the activity event. + * @param event The activity event. + */ + virtual void onGUIActivityEventReceived(const std::string& source, const GUIActivityEvent& activityEvent) = 0; +}; + +} // namespace sdkInterfaces +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_GUIACTIVITYEVENTOBSERVERINTERFACE_H_ diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/PostConnectOperationInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/PostConnectOperationInterface.h index 9fd4e34536..4a3cc855c2 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/PostConnectOperationInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/PostConnectOperationInterface.h @@ -46,15 +46,16 @@ class PostConnectOperationInterface { /** * Returns the operation priority. The Priority is used to order the sequence of operations in ascending order. * - * @return unsigined int that representing the operation priority. + * @return unsigned int that representing the operation priority. */ virtual unsigned int getOperationPriority() = 0; /** - * Performs the post connect operation. The implementation should ensure that the performOperation returns - * immediately after the abortOperation() method is called. + * Performs the post connect operation. The implementation should ensure that the #performOperation() returns + * immediately after the #abortOperation() method is called. If #abortOperation() is called before + * #performOperation(), the method must immediately return with false result. * - * @note: The performOperation() method is not expected to be called twice. + * @note This method is not expected to be called twice. * * @param messageSender - The @c MessageSenderInterface to send post connect message. * @return True if the post connect operation is successful, else false. @@ -65,7 +66,8 @@ class PostConnectOperationInterface { /** * Aborts an operation that is currently being executed using the performOperation() method. * - * Note: This method will be called from a different thread from where the performOperation() is being called from. + * @note This method will be called from a different thread from where the performOperation() is being called from. + * It is possible, that the method is called before #performOperation() call is made. */ virtual void abortOperation() = 0; }; diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/SpeakerManagerInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/SpeakerManagerInterface.h index 60f097547c..72265233de 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/SpeakerManagerInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/SpeakerManagerInterface.h @@ -76,6 +76,25 @@ class SpeakerManagerInterface { int8_t volume, const NotificationProperties& properties) = 0; + /** + * Handle an external volume/mute state event in the system and update the settings. + * + * A volume could be changed either using SpeakerManager instance or using any other component + * which support volume change. In the case, volume on the device is being updated by some + * other component, this interface could be used to update the speaker settings of the + * associated @c ChannelVolumeInterface. This interface does not modify/change the volume + * or mute. It should be used to update speaker settings within SpeakerManager and notify + * AVS/observers if required of this change. + * + * @param type The type of @c ChannelVolumeInterface to retrieve settings for. + * @param speakerSettings New updated value. Values must be between [0,100] + * @param properties Notification properties that specify how the volume change will be notified. + */ + virtual void onExternalSpeakerSettingsUpdate( + ChannelVolumeInterface::Type type, + const SpeakerInterface::SpeakerSettings& speakerSettings, + const NotificationProperties& properties){}; + /** * Adjusts the volume for ChannelVolumeInterfaces of a certain @c Type with a volume delta. * diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/SpeechInteractionHandlerInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/SpeechInteractionHandlerInterface.h index 830eadc351..d4e85c69eb 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/SpeechInteractionHandlerInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/SpeechInteractionHandlerInterface.h @@ -21,6 +21,7 @@ #include #include +#include #include #include diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/TemplateRuntimeObserverInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/TemplateRuntimeObserverInterface.h deleted file mode 100644 index d3914a2536..0000000000 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/TemplateRuntimeObserverInterface.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_TEMPLATERUNTIMEOBSERVERINTERFACE_H_ -#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_TEMPLATERUNTIMEOBSERVERINTERFACE_H_ - -#include -#include - -#include "AVSCommon/AVS/FocusState.h" -#include - -namespace alexaClientSDK { -namespace avsCommon { -namespace sdkInterfaces { - -/** - * This @c TemplateRuntimeObserverInterface class is used to notify observers when a @c RenderTemplate or - * @c RenderPlayerInfo directive is received. These two directives contains metadata for rendering display - * cards for devices with GUI support. - */ -class TemplateRuntimeObserverInterface { -public: - /** - * The @c AudioPlayerInfo contains information that is useful for rendering a PlayerInfo display card. - * @c AudioPlayerInfo is passed to the observers as a parameter in the @c renderPlayerInfoCard callback. - */ - struct AudioPlayerInfo { - /** - * Default constructor. - */ - AudioPlayerInfo() : - audioPlayerState{avsCommon::avs::PlayerActivity::IDLE}, - offset{std::chrono::milliseconds::zero()} {}; - - /** - * The state of the @c AudioPlayer. This information is useful for implementing the progress bar - * in the display card. It is assumed that the client is responsible for progressing the progress bar - * when the @c AudioPlayer is in PLAYING state. - */ - avsCommon::avs::PlayerActivity audioPlayerState; - - /** - * The offset in millisecond of the media that @c AudioPlayer is handling. This information is - * useful for implementation of the progress bar. - */ - std::chrono::milliseconds offset; - }; - - /** - * Destructor - */ - virtual ~TemplateRuntimeObserverInterface() = default; - - /** - * Used to notify the observer when a RenderTemplate directive is received. Once called, the client should - * render the Template display card based on the metadata provided in the payload in structured JSON format. - * - * @note The payload may contain customer sensitive information and should be used with utmost care. - * Failure to do so may result in exposing or mishandling of customer data. - * - * @param jsonPayload The payload of the RenderTemplate directive in structured JSON format. - * @param focusState The @c FocusState of the channel used by TemplateRuntime interface. - */ - virtual void renderTemplateCard(const std::string& jsonPayload, avsCommon::avs::FocusState focusState) = 0; - - /** - * Used to notify the observer when the client should clear the Template display card. Once the card is cleared, - * the client should call templateCardCleared(). - */ - virtual void clearTemplateCard() = 0; - - /** - * Used to notify the observer when a RenderPlayerInfo directive is received. Once called, the client should - * render the PlayerInfo display card based on the metadata provided in the payload in structured JSON - * format. - * - * @param jsonPayload The payload of the RenderPlayerInfo directive in structured JSON format. - * @param audioPlayerInfo Information on the @c AudioPlayer. - * @param focusState The @c FocusState of the channel used by TemplateRuntime interface. - */ - virtual void renderPlayerInfoCard( - const std::string& jsonPayload, - TemplateRuntimeObserverInterface::AudioPlayerInfo audioPlayerInfo, - avsCommon::avs::FocusState focusState) = 0; - - /** - * Used to notify the observer when the client should clear the PlayerInfo display card. Once the card is cleared, - * the client should call templateCardCleared(). - */ - virtual void clearPlayerInfoCard() = 0; -}; - -} // namespace sdkInterfaces -} // namespace avsCommon -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_TEMPLATERUNTIMEOBSERVERINTERFACE_H_ diff --git a/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/Bluetooth/MockBluetoothDevice.h b/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/Bluetooth/MockBluetoothDevice.h index b9be92e81f..e7b1d0a337 100644 --- a/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/Bluetooth/MockBluetoothDevice.h +++ b/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/Bluetooth/MockBluetoothDevice.h @@ -30,6 +30,10 @@ namespace bluetooth { namespace test { using namespace ::testing; + +static constexpr std::size_t PAIRING_PIN_LENGTH_MIN = 4; +static constexpr std::size_t PAIRING_PIN_LENGTH_MAX = 16; + /** * Mock class that implements BluetoothDeviceInterface. * Please note that MockBluetoothDevice doesn't support sending any @c BluetoothEvent to @c BluetoothEventBus, @@ -47,6 +51,7 @@ class MockBluetoothDevice : public BluetoothDeviceInterface { bool isConnected() override; std::future connect() override; std::future disconnect() override; + bool setPairingPin(const std::string& pin) override; std::vector> getSupportedServices() override; std::shared_ptr getService(std::string uuid) override; utils::bluetooth::MediaStreamingState getStreamingState() override; @@ -132,6 +137,13 @@ inline std::future MockBluetoothDevice::disconnect() { return connectionPromise.get_future(); } +inline bool MockBluetoothDevice::setPairingPin(const std::string& pin) { + if (pin.length() < PAIRING_PIN_LENGTH_MIN || pin.length() > PAIRING_PIN_LENGTH_MAX) { + return false; + } + return true; +} + inline std::vector> MockBluetoothDevice::getSupportedServices() { std::vector> services; for (auto service : m_supportedServices) { diff --git a/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/Endpoints/MockEndpointBuilder.h b/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/Endpoints/MockEndpointBuilder.h index e5db640773..cf5328c1d4 100644 --- a/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/Endpoints/MockEndpointBuilder.h +++ b/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/Endpoints/MockEndpointBuilder.h @@ -32,6 +32,7 @@ class MockEndpointBuilder : public EndpointBuilderInterface { /// @name @c EndpointBuilderInterface methods to be mocked. /// @{ MOCK_METHOD1(withDerivedEndpointId, EndpointBuilderInterface&(const std::string& suffix)); + MOCK_METHOD0(withDeviceRegistration, EndpointBuilderInterface&()); MOCK_METHOD1(withEndpointId, EndpointBuilderInterface&(const EndpointIdentifier& endpointId)); MOCK_METHOD1(withFriendlyName, EndpointBuilderInterface&(const std::string& friendlyName)); MOCK_METHOD1(withDescription, EndpointBuilderInterface&(const std::string& description)); @@ -50,7 +51,61 @@ class MockEndpointBuilder : public EndpointBuilderInterface { withConnections, EndpointBuilderInterface&(const std::vector>& connections)); MOCK_METHOD1(withCookies, EndpointBuilderInterface&(const std::map& cookies)); - MOCK_METHOD0(build, utils::Optional()); + MOCK_METHOD3( + withPowerController, + EndpointBuilderInterface&( + std::shared_ptr powerController, + bool isProactivelyReported, + bool isRetrievable)); + MOCK_METHOD6( + withToggleController, + EndpointBuilderInterface&( + std::shared_ptr toggleController, + const std::string& instance, + const avsCommon::sdkInterfaces::toggleController::ToggleControllerAttributes& toggleControllerAttributes, + bool isProactivelyReported, + bool isRetrievable, + bool isNonControllable)); + MOCK_METHOD6( + withModeController, + EndpointBuilderInterface&( + std::shared_ptr modeController, + const std::string& instance, + const avsCommon::sdkInterfaces::modeController::ModeControllerAttributes& modeControllerAttributes, + bool isProactivelyReported, + bool isRetrievable, + bool isNonControllable)); + MOCK_METHOD6( + withRangeController, + EndpointBuilderInterface&( + std::shared_ptr rangeController, + const std::string& instance, + const avsCommon::sdkInterfaces::rangeController::RangeControllerAttributes& rangeControllerAttributes, + bool isProactivelyReported, + bool isRetrievable, + bool isNonControllable)); + MOCK_METHOD1( + withEndpointCapabilitiesBuilder, + EndpointBuilderInterface&( + const std::shared_ptr&)); + MOCK_METHOD0(build, std::unique_ptr()); + /// @} + /// @name @c EndpointCapabilitiesRegistrarInterface methods to be mocked. + /// @{ + MOCK_METHOD2( + withCapability, + EndpointCapabilitiesRegistrarInterface&( + const avs::CapabilityConfiguration& configuration, + std::shared_ptr directiveHandler)); + MOCK_METHOD2( + withCapability, + EndpointCapabilitiesRegistrarInterface&( + const std::shared_ptr& configurationInterface, + std::shared_ptr directiveHandler)); + MOCK_METHOD1( + withCapabilityConfiguration, + EndpointCapabilitiesRegistrarInterface&( + const std::shared_ptr& configurationInterface)); /// @} }; diff --git a/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockSpeakerManager.h b/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockSpeakerManager.h index bdc1df2421..95bc739d2d 100644 --- a/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockSpeakerManager.h +++ b/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockSpeakerManager.h @@ -34,6 +34,13 @@ class MockSpeakerManager : public SpeakerManagerInterface { int8_t volume, const avsCommon::sdkInterfaces::SpeakerManagerInterface::NotificationProperties& properties)); + MOCK_METHOD3( + onExternalSpeakerSettingsUpdate, + void( + avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, + const avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings& speakerSettings, + const avsCommon::sdkInterfaces::SpeakerManagerInterface::NotificationProperties& properties)); + MOCK_METHOD3( adjustVolume, std::future( diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Configuration/ConfigurationNode.h b/AVSCommon/Utils/include/AVSCommon/Utils/Configuration/ConfigurationNode.h index cf57d30013..73b1d9a5bd 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Configuration/ConfigurationNode.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Configuration/ConfigurationNode.h @@ -194,6 +194,15 @@ class ConfigurationNode { */ ConfigurationNode operator[](const std::string& key) const; + /** + * Get @c ConfigurationNode value for @c key from this @c ConfigurationNode. + * + * @param key The key of the @c ConfigurationNode value to get. + * @return The @c ConfigurationNode value, or an empty node if this @c ConfigurationNode does not have + * a @c ConfigurationNode value for @c key. + */ + ConfigurationNode getChildNode(const char* key) const; + /** * operator bool(). Indicates of the @c ConfigurationNode references a valid object. * diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Error/FinallyGuard.h b/AVSCommon/Utils/include/AVSCommon/Utils/Error/FinallyGuard.h index d65c4076e3..8d4357d9de 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Error/FinallyGuard.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Error/FinallyGuard.h @@ -51,6 +51,13 @@ class FinallyGuard { */ FinallyGuard(const std::function& finallyFunction); + /** + * Constructor. + * + * @param finallyFunction The function to be executed when the object goes out of scope. + */ + FinallyGuard(const std::function&& finallyFunction); + /** * Destructor. Runs @c m_function during destruction. */ @@ -64,6 +71,10 @@ class FinallyGuard { inline FinallyGuard::FinallyGuard(const std::function& finallyFunction) : m_function{finallyFunction} { } +inline FinallyGuard::FinallyGuard(const std::function&& finallyFunction) : + m_function{std::move(finallyFunction)} { +} + inline FinallyGuard::~FinallyGuard() { if (m_function) { m_function(); diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/JSON/JSONGenerator.h b/AVSCommon/Utils/include/AVSCommon/Utils/JSON/JSONGenerator.h index 644a8499c4..8533842e76 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/JSON/JSONGenerator.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/JSON/JSONGenerator.h @@ -210,7 +210,8 @@ class JsonGenerator { template bool JsonGenerator::addStringArray(const std::string& key, const CollectionT& collection) { - if (!checkWriter() || !m_writer.Key(key.c_str(), key.length())) { + auto keyLength = static_cast(key.length()); + if (!checkWriter() || !m_writer.Key(key.c_str(), keyLength)) { return false; } m_writer.StartArray(); @@ -224,7 +225,8 @@ bool JsonGenerator::addStringArray(const std::string& key, const CollectionT& co template bool JsonGenerator::addMembersArray(const std::string& key, const CollectionT& collection) { - if (!checkWriter() || !m_writer.Key(key.c_str(), key.length())) { + auto keyLength = static_cast(key.length()); + if (!checkWriter() || !m_writer.Key(key.c_str(), keyLength)) { return false; } m_writer.StartArray(); @@ -238,7 +240,8 @@ bool JsonGenerator::addMembersArray(const std::string& key, const CollectionT& c template bool JsonGenerator::addCollectionOfStringArray(const std::string& key, const CollectionArrayT& collection) { - if (!checkWriter() || !m_writer.Key(key.c_str(), key.length())) { + auto keyLength = static_cast(key.length()); + if (!checkWriter() || !m_writer.Key(key.c_str(), keyLength)) { return false; } m_writer.StartArray(); diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/CurlMultiHandleWrapper.h b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/CurlMultiHandleWrapper.h index ecaa3632d1..3d8a277213 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/CurlMultiHandleWrapper.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/CurlMultiHandleWrapper.h @@ -80,13 +80,29 @@ class CurlMultiHandleWrapper { CURLMcode perform(int* runningHandles); /** - * Wait for actions to perform on the @c libcurl @c handles added to this @c libcurl @c multi @c handle. + * Poll for actions to perform on the @c libcurl @c handles added to this @c libcurl @c multi @c handle. This can + * waken up by the @c wakeup() call. + * + * @note For Libcurl v7.68.0 or higher, this function will call curl_multi_poll(), this allows the polling thread to + * be woken up by the @c wakeup() call. Otherwise for Libcurl v7.67.0 or lower, it will call curl_multi_wait() and + * the call to @c wakeup() will do nothing. + * * * @param timeout How long to wait for actions to perform. * @param[out] countHandlesUpdated The number of handles for which actions are ready to be performed. * @return @c libcurl code indicating the result of this operation. */ - CURLMcode wait(std::chrono::milliseconds timeout, int* countHandlesUpdated); + CURLMcode poll(std::chrono::milliseconds timeout, int* countHandlesUpdated); + + /** + * This function can be called from any thread to wake up a sleeping @c poll(). + * + * @note For Libcurl v7.68.0 or higher, this function will call curl_multi_wakeup(). Otherwise, this is a no-op + * operation. + * + * @return @c true if the call succeeded, or @c false otherwise. + */ + bool wakeup(); /** * Receive the next messages about the @c libcurl @c handles added to this @c libcurl @c multi @c handle. diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2Connection.h b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2Connection.h index 570025f4b9..b84e090b95 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2Connection.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2Connection.h @@ -69,7 +69,10 @@ class LibcurlHTTP2Connection : public avsCommon::utils::http2::HTTP2ConnectionIn LibcurlHTTP2Connection( const std::shared_ptr& setCurlOptionsCallback = nullptr); -private: + /** + * Alias for c++ ordered map where key is curl handle and value is pointer to LibcurlHTTP2Request. + */ + using ActiveStreamMap = std::map>; /** * Adds a configured stream into this connection. * @param stream Request object with a configured curl handle. @@ -86,10 +89,10 @@ class LibcurlHTTP2Connection : public avsCommon::utils::http2::HTTP2ConnectionIn /** Release a stream. * - * @param stream The stream to release. + * @param[in,out] iterator The iterator to ActiveStreamMap to erase. * @return Whether the operation was successful. */ - bool releaseStream(LibcurlHTTP2Request& stream); + bool releaseStream(ActiveStreamMap::iterator& iterator); /** * Main network loop. Repeatedly call curl_multi_perform in order to transfer data on the incorporated streams. @@ -134,10 +137,10 @@ class LibcurlHTTP2Connection : public avsCommon::utils::http2::HTTP2ConnectionIn /** * Cancel an active stream and report CANCELLED completion status. * - * @param stream The stream to cancel. + * @param[in,out] iterator The iterator to ActiveStreamMap to erase. * @return Whether the operation was successful. */ - bool cancelActiveStream(LibcurlHTTP2Request& stream); + bool cancelActiveStream(ActiveStreamMap::iterator& iterator); /** * Release any active streams and report CANCELLED completion status. @@ -174,8 +177,8 @@ class LibcurlHTTP2Connection : public avsCommon::utils::http2::HTTP2ConnectionIn /// Main thread for this class. std::thread m_networkThread; - /// Represents a CURL multi handle. Intended to only be accessed by the network loop thread. - std::unique_ptr m_multi; + /// Represents a CURL multi handle. Intended to only be accessed by the network loop thread and in @c addStream. + std::shared_ptr m_multi; /// Serializes concurrent access to the m_requestQueue and m_isStopping members. std::mutex m_mutex; @@ -186,7 +189,7 @@ class LibcurlHTTP2Connection : public avsCommon::utils::http2::HTTP2ConnectionIn /// The list of streams that either do not have HTTP response headers, or have outstanding response data. /// Only accessed from the network loop thread. - std::map> m_activeStreams; + ActiveStreamMap m_activeStreams; /// Queue of requests send. Serialized by @c m_mutex. std::deque> m_requestQueue; diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Logger/LogEntry.h b/AVSCommon/Utils/include/AVSCommon/Utils/Logger/LogEntry.h index 93316ca196..7645b03e84 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Logger/LogEntry.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Logger/LogEntry.h @@ -17,6 +17,7 @@ #define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_LOGGER_LOGENTRY_H_ #include +#include #include #include #include @@ -34,24 +35,36 @@ class LogEntry { /** * Constructor. * - * @param source The name of the source of this log entry. - * @param event The name of the event that this log entry describes. + * @param[in] source The name of the source of this log entry. If @a source is nullptr, it is treated as an empty + * string. + * @param[in] event The name of the event that this log entry describes. If @a event is nullptr, it is treated as + * an empty string. + */ + LogEntry(const char* source, const char* event); + + /** + * Constructor. + * + * @param[in] source The name of the source of this log entry. + * @param[in] event The name of the event that this log entry describes. If @a event is nullptr, it is treated as + * an empty string. */ LogEntry(const std::string& source, const char* event); /** * Constructor. * - * @param source The name of the source of this log entry. - * @param event The name of the event that this log entry describes. + * @param[in] source The name of the source of this log entry. + * @param[in] event The name of the event that this log entry describes. */ LogEntry(const std::string& source, const std::string& event); /** * Add a @c key, @c value pair to the metadata of this log entry. * - * @param key The key identifying the value to add to this LogEntry. - * @param value The value to add to this LogEntry. + * @param[in] key The key identifying the value to add to this LogEntry. + * @param[in] value The value to add to this LogEntry. + * * @return This instance to facilitate adding more information to this log entry. */ template @@ -59,17 +72,30 @@ class LogEntry { /** * Add a @c key, @c value pair to the metadata of this log entry. - * @param key The key identifying the value to add to this LogEntry. - * @param value The value to add to this LogEntry. + * + * @param[in] key The key identifying the value to add to this LogEntry. + * @param[in] value The value to add to this LogEntry. If @a value is nullptr, it is treated as an empty string. + * * @return This instance to facilitate adding more information to this log entry. */ LogEntry& d(const char* key, const char* value); + /** + * Add a @c key, @c value pair to the metadata of this log entry. + * + * @param[in] key The key identifying the value to add to this LogEntry. + * @param[in] value The value to add to this LogEntry. If @a value is nullptr, it is treated as an empty string. + * + * @return This instance to facilitate adding more information to this log entry. + */ + LogEntry& d(const char* key, char* value); + /** * Add data (hence the name 'd') in the form of a @c key, @c value pair to the metadata of this log entry. * - * @param key The key identifying the value to add to this LogEntry. - * @param value The value to add to this LogEntry. + * @param[in] key The key identifying the value to add to this LogEntry. + * @param[in] value The value to add to this LogEntry. + * * @return This instance to facilitate adding more information to this log entry. */ LogEntry& d(const char* key, const std::string& value); @@ -77,8 +103,9 @@ class LogEntry { /** * Add data (hence the name 'd') in the form of a @c key, @c value pair to the metadata of this log entry. * - * @param key The key identifying the value to add to this LogEntry. - * @param value The boolean value to add to this LogEntry. + * @param[in] key The key identifying the value to add to this LogEntry. + * @param[in] value The boolean value to add to this LogEntry. + * * @return This instance to facilitate adding more information to this log entry. */ LogEntry& d(const char* key, bool value); @@ -86,23 +113,25 @@ class LogEntry { /** * Add data (hence the name 'd') in the form of a @c key, @c value pair to the metadata of this log entry. * - * @param key The key identifying the value to add to this LogEntry. - * @param value The value to add to this LogEntry. + * @param[in] key The key identifying the value to add to this LogEntry. + * @param[in] value The value to add to this LogEntry. + * * @return This instance to facilitate adding more information to this log entry. */ template - inline LogEntry& d(const char* key, const ValueType& value); + LogEntry& d(const char* key, const ValueType& value); /** * Add sensitive data in the form of a @c key, @c value pair to the metadata of this log entry. * Because the data is 'sensitive' it will only be emitted in DEBUG builds. * - * @param key The key identifying the value to add to this LogEntry. - * @param value The value to add to this LogEntry. + * @param[in] key The key identifying the value to add to this LogEntry. + * @param[in] value The value to add to this LogEntry. + * * @return This instance to facilitate adding more information to this log entry. */ template - inline LogEntry& sensitive(const char* key, const ValueType& value); + LogEntry& sensitive(const char* key, const ValueType& value); /** * Add data in the form of a @c key, @c value pair to the metadata of this log entry. @@ -110,17 +139,20 @@ class LogEntry { * This is done in a distinct method (instead of m or d) to avoid the cost of always checking * against the denylist. * - * @param key The key identifying the value to add to this LogEntry. - * @param value The value to add to this LogEntry, obfuscated if needed. + * @param[in] key The key identifying the value to add to this LogEntry. + * @param[in] value The value to add to this LogEntry, obfuscated if needed. + * * @return This instance to facilitate adding more information to this log entry. */ - inline LogEntry& obfuscatePrivateData(const char* key, const std::string& value); + LogEntry& obfuscatePrivateData(const char* key, const std::string& value); /** * Add an arbitrary message to the end of the text of this LogEntry. Once this has been called no other * additions should be made to this LogEntry. * - * @param message The message to add to the end of the text of this LogEntry. + * @param[in] message The message to add to the end of the text of this LogEntry. If @a message is a nullptr, it is + * treated as an empty string. + * * @return This instance to facilitate passing this instance on. */ LogEntry& m(const char* message); @@ -129,7 +161,8 @@ class LogEntry { * Add an arbitrary message to the end of the text of this LogEntry. Once this has been called no other * additions should be made to this LogEntry. * - * @param message The message to add to the end of the text of this LogEntry. + * @param[in] message The message to add to the end of the text of this LogEntry. + * * @return This instance to facilitate passing this instance on. */ LogEntry& m(const std::string& message); @@ -138,8 +171,10 @@ class LogEntry { * Add pointer (hence the name 'p') in the form of a @c key, address of the object pointed to by the shared_ptr @c * ptr to the metadata of this log entry. * - * @param key The key identifying the value to add to this LogEntry. - * @param ptr The shared_ptr of the object to add to this LogEntry. + * @param[in] key The key identifying the value to add to this LogEntry. If @a key is a nullptr, it is treated as an + * empty string. + * @param[in] ptr The shared_ptr of the object to add to this LogEntry. + * * @return This instance to facilitate adding more information to this log entry. */ template @@ -149,8 +184,10 @@ class LogEntry { * Add pointer (hence the name 'p') in the form of a @c key, address of the raw @c ptr to the metadata of this * log entry. * - * @param key The key identifying the value to add to this LogEntry. - * @param ptr The raw pointer of the object to add to this LogEntry. + * @param[in] key The key identifying the value to add to this LogEntry. If @a key is a nullptr, it is treated as an + * empty string. + * @param[in] ptr The raw pointer of the object to add to this LogEntry. + * * @return This instance to facilitate adding more information to this log entry. */ LogEntry& p(const char* key, const void* ptr); @@ -170,12 +207,6 @@ class LogEntry { /// Add the appropriate prefix for an arbitrary message that is about to be appended to the text of this LogEntry. void prefixMessage(); - /// Return a list of labels we will obfuscate if sent to obfuscatePrivateData - static std::vector getPrivateLabelDenyList() { - static std::vector privateLabelDenyList = {"ssid"}; - return privateLabelDenyList; - } - /** * Append an escaped string to m_stream. * Our metadata and subsequent optional message is of the form: @@ -202,14 +233,18 @@ inline LogEntry& LogEntry::d(const std::string& key, const ValueType& value) { } template -LogEntry& LogEntry::d(const char* key, const ValueType& value) { +inline LogEntry& LogEntry::d(const char* key, const ValueType& value) { prefixKeyValuePair(); m_stream << key << KEY_VALUE_SEPARATOR << value; return *this; } +inline LogEntry& LogEntry::d(const char* key, char* value) { + return d(key, const_cast(value)); +} + template -LogEntry& LogEntry::p(const char* key, const std::shared_ptr& ptr) { +inline LogEntry& LogEntry::p(const char* key, const std::shared_ptr& ptr) { return d(key, ptr.get()); } @@ -217,46 +252,16 @@ LogEntry& LogEntry::p(const char* key, const std::shared_ptr& ptr) { #ifdef ACSDK_EMIT_SENSITIVE_LOGS template -LogEntry& LogEntry::sensitive(const char* key, const ValueType& value) { +inline LogEntry& LogEntry::sensitive(const char* key, const ValueType& value) { return d(key, value); } #else template -LogEntry& LogEntry::sensitive(const char*, const ValueType&) { +inline LogEntry& LogEntry::sensitive(const char*, const ValueType&) { return *this; } #endif -LogEntry& LogEntry::obfuscatePrivateData(const char* key, const std::string& value) { - // if value contains any private label, obfuscate the section after the label - // since it can (but shouldn't) contain multiple, obfuscate from the earliest one found onward - auto firstPosition = value.length(); - - for (auto privateLabel : getPrivateLabelDenyList()) { - auto it = std::search( - value.begin(), - value.end(), - privateLabel.begin(), - privateLabel.end(), - [](char valueChar, char denyListChar) { return std::tolower(valueChar) == std::tolower(denyListChar); }); - if (it != value.end()) { - // capture the least value - auto thisPosition = std::distance(value.begin(), it) + privateLabel.length(); - if (thisPosition < firstPosition) { - firstPosition = thisPosition; - } - } - } - - if (firstPosition <= value.length()) { - // hash everything after the label itself - auto labelPart = value.substr(0, firstPosition); - auto obfuscatedPart = std::to_string(std::hash{}(value.substr(firstPosition))); - return d(key, labelPart + obfuscatedPart); - } - return d(key, value); -} - } // namespace logger } // namespace utils } // namespace avsCommon diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Logger/Logger.h b/AVSCommon/Utils/include/AVSCommon/Utils/Logger/Logger.h index 2f0ab56556..9bedd30626 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Logger/Logger.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Logger/Logger.h @@ -85,7 +85,7 @@ namespace logger { * are passed to the @c LogEntry constructor. Here is an example of the definitions that typically appear at * the start of a .cpp file: * - * static const std::string TAG = "MyClass"; + * #define TAG "MyClass" * #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) * * When an event is to be logged, a wrapper macro named @c ACSDK_ is invoked with an expression that starts @@ -138,14 +138,11 @@ namespace logger { * * add_definitions("-DACSDK_LOG_MODULE=foo") * - * All logs (module specific or not) are output to a @c Sink @c Logger. By default, the @c Sink @c Logger - * is @c ConsoleLogger. This can be overridden by defining @c ACSDK_LOG_SINK. The value of @c ACSDK_LOG_SINK - * specifies a function name of the form: - * - * Logger& getLogger() + * For modules that does not have the @c ACSDK_LOG_MODULE definition, logs will be defaulted to use the + * ConsoleLogger module Logger. * - * That function to used to get the @c Sink @c Logger. When @c ACSDK_LOG_SINK is overridden, it is necessary - * to provide an implementation of that function that returns the desired @c Logger. + * All logs (module specific or not) are output to a @c Sink @c Logger. By default, the @c Sink @c Logger + * is @c ConsoleLogger. The sink logger can be changed by calling initialize in @c LoggerSinkManager. */ class Logger { public: @@ -169,7 +166,7 @@ class Logger { /** * Return true of logs of a specified severity should be emitted by this Logger. * - * @param level The Level to check. + * @param[in] level The Level to check. * @return Returns true if logs of the specified Level should be emitted. */ inline bool shouldLog(Level level) const; @@ -177,8 +174,8 @@ class Logger { /** * Send a log entry to this Logger. * - * @param level The severity Level to associate with this log entry. - * @param entry Object used to build the text of this log entry. + * @param[in] level The severity Level to associate with this log entry. + * @param[in] entry Object used to build the text of this log entry. */ void log(Level level, const LogEntry& entry); @@ -190,8 +187,8 @@ class Logger { * * @note The user code should still ensure that the Logger object itself is valid. * - * @param level The severity Level to associate with this log entry. - * @param entry Object used to build the text of this log entry. + * @param[in] level The severity Level to associate with this log entry. + * @param[in] entry Object used to build the text of this log entry. */ void logAtExit(Level level, const LogEntry& entry); @@ -200,10 +197,10 @@ class Logger { * NOTE: This method must be thread-safe. * NOTE: Delays in returning from this method may hold up calls to Logger::log(). * - * @param level The severity Level of this log line. - * @param time The time that the event to log occurred. - * @param threadMoniker Moniker of the thread that generated the event. - * @param text The text of the entry to log. + * @param[in] level The severity Level of this log line. + * @param[in] time The time that the event to log occurred. + * @param[in] threadMoniker Moniker of the thread that generated the event. + * @param[in] text The text of the entry to log. */ virtual void emit( Level level, @@ -214,16 +211,14 @@ class Logger { /** * Add an observer to this object. * - * @param An observer to this class, which will be notified when - * the logLevel changes. + * @param An observer to this class, which will be notified when the logLevel changes. */ void addLogLevelObserver(LogLevelObserverInterface* observer); /** * Remove an observer to this object. * - * @param An observer to this class that will be removed from the - * notificaiton of logLevel changes. + * @param An observer to this class that will be removed from the notification of logLevel changes. */ void removeLogLevelObserver(LogLevelObserverInterface* observer); @@ -270,27 +265,21 @@ bool Logger::shouldLog(Level level) const { */ #define ACSDK_GET_LOGGER_FUNCTION_NAME(type) ACSDK_CONCATENATE(ACSDK_CONCATENATE(get, type), Logger) -// If @c ACSDK_LOG_SINK was not defined, default to logging to console. -#ifndef ACSDK_LOG_SINK -#define ACSDK_LOG_SINK Console -#endif - -/// Build the getLogger function name for whatever @c Logger logs will be sent to. -#define ACSDK_GET_SINK_LOGGER ACSDK_GET_LOGGER_FUNCTION_NAME(ACSDK_LOG_SINK) - /** * Get the @c Logger that logs should be sent to. * * @return The @c Logger that logs should be sent to. */ -std::shared_ptr ACSDK_GET_SINK_LOGGER(); +std::shared_ptr getConsoleLogger(); } // namespace logger } // namespace utils } // namespace avsCommon } // namespace alexaClientSDK -#ifdef ACSDK_LOG_MODULE +#ifndef ACSDK_LOG_MODULE +#define ACSDK_LOG_MODULE ConsoleLogger +#endif // ACSDK_LOG_MODULE #include "AVSCommon/Utils/Logger/ModuleLogger.h" @@ -320,38 +309,16 @@ inline std::shared_ptr ACSDK_GET_LOGGER_FUNCTION() { } // namespace avsCommon } // namespace alexaClientSDK -#else // ACSDK_LOG_MODULE - -namespace alexaClientSDK { -namespace avsCommon { -namespace utils { -namespace logger { - -/** - * Inline method to get the function that ACSDK_ macros will send logs to. - * In this case @c ACSDK_LOG_MODULE was not defined, so logs are sent to the @c Logger returned by - * @c getLogger(). - */ -inline std::shared_ptr ACSDK_GET_LOGGER_FUNCTION() { - static std::shared_ptr logger = ACSDK_GET_SINK_LOGGER(); - return logger; -} - -} // namespace logger -} // namespace utils -} // namespace avsCommon -} // namespace alexaClientSDK - -#endif - -/// Define log macro if logging is enabled. Else do nothing with params to avoid unused variable error. -#ifdef ACSDK_LOG_ENABLED /** - * Common implementation for sending entries to the log. + * @def ACSDK_LOG + * @brief Common implementation for sending entries to the log. + * + * @note If @c ACSDK_LOG_ENABLED is set to OFF, then logging is disabled. * - * @param level The log level to associate with the log line. - * @param entry The text (or builder of the text) for the log entry. + * @param[in] level The log level to associate with the log line. + * @param[in] entry A constructed @a LogEntry object with the log message text. */ +#ifdef ACSDK_LOG_ENABLED #define ACSDK_LOG(level, entry) \ do { \ auto loggerInstance = alexaClientSDK::avsCommon::utils::logger::ACSDK_GET_LOGGER_FUNCTION(); \ @@ -359,231 +326,172 @@ inline std::shared_ptr ACSDK_GET_LOGGER_FUNCTION() { loggerInstance->log(level, entry); \ } \ } while (false) -#else - /** - * Null implementation for sending entries to the log. - * - * @param level Unused. - * @param entry Unused. - */ +#else // ACSDK_LOG_ENABLED #define ACSDK_LOG(level, entry) \ do { \ - (void)level; \ - (void)entry; \ + (void)sizeof(level); \ + (void)sizeof(entry); \ } while (false) -#endif - -#ifdef ACSDK_DEBUG_LOG_ENABLED +#endif // ACSDK_LOG_ENABLED /** - * Send a DEBUG9 severity log line. + * @def ACSDK_DEBUG_LOG + * @brief Common implementation for sending debug entries to the log. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. - */ -#define ACSDK_DEBUG9(entry) ACSDK_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG9, entry) - -/** - * Send a DEBUG8 severity log line. + * @note If @c ACSDK_DEBUG_LOG_ENABLED is set to OFF, then logging is disabled. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. + * @param[in] level The log level to associate with the log line. + * @param[in] entry A constructed @a LogEntry object with the log message text. */ -#define ACSDK_DEBUG8(entry) ACSDK_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG8, entry) -/** - * Send a DEBUG7 severity log line. - * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. - */ -#define ACSDK_DEBUG7(entry) ACSDK_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG7, entry) +#ifdef ACSDK_DEBUG_LOG_ENABLED +#define ACSDK_DEBUG_LOG(level, entry) ACSDK_LOG((level), (entry)) +#else // ACSDK_DEBUG_LOG_ENABLED +#define ACSDK_DEBUG_LOG(level, entry) \ + do { \ + (void)sizeof(level); \ + (void)sizeof(entry); \ + } while (false) +#endif // ACSDK_DEBUG_LOG_ENABLED /** - * Send a DEBUG6 severity log line. + * Send a DEBUG9 severity log line. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. - */ -#define ACSDK_DEBUG6(entry) ACSDK_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG6, entry) - -/** - * Send a DEBUG5 severity log line. + * @note If @c ACSDK_DEBUG_LOG_ENABLED is set to OFF, then logging is disabled. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. + * @param[in] entry A constructed @a LogEntry object with the log message text. */ -#define ACSDK_DEBUG5(entry) ACSDK_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG5, entry) +#define ACSDK_DEBUG9(entry) ACSDK_DEBUG_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG9, entry) /** - * Send a DEBUG4 severity log line. + * Send a DEBUG8 severity log line. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. - */ -#define ACSDK_DEBUG4(entry) ACSDK_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG4, entry) - -/** - * Send a DEBUG3 severity log line. + * @note If @c ACSDK_DEBUG_LOG_ENABLED is set to OFF, then logging is disabled. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. + * @param[in] entry A constructed @a LogEntry object with the log message text. */ -#define ACSDK_DEBUG3(entry) ACSDK_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG3, entry) +#define ACSDK_DEBUG8(entry) ACSDK_DEBUG_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG8, entry) /** - * Send a DEBUG2 severity log line. + * Send a DEBUG7 severity log line. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. - */ -#define ACSDK_DEBUG2(entry) ACSDK_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG2, entry) - -/** - * Send a DEBUG1 severity log line. + * @note If @c ACSDK_DEBUG_LOG_ENABLED is set to OFF, then logging is disabled. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. + * @param[in] entry A constructed @a LogEntry object with the log message text. */ -#define ACSDK_DEBUG1(entry) ACSDK_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG1, entry) +#define ACSDK_DEBUG7(entry) ACSDK_DEBUG_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG7, entry) /** - * Send a DEBUG0 severity log line. + * Send a DEBUG6 severity log line. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. - */ -#define ACSDK_DEBUG0(entry) ACSDK_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG0, entry) - -/** - * Send a log line at the default debug level (DEBUG0). + * @note If @c ACSDK_DEBUG_LOG_ENABLED is set to OFF, then logging is disabled. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. + * @param[in] entry A constructed @a LogEntry object with the log message text. */ -#define ACSDK_DEBUG(entry) ACSDK_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG0, entry) - -#else // ACSDK_DEBUG_LOG_ENABLED +#define ACSDK_DEBUG6(entry) ACSDK_DEBUG_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG6, entry) /** - * Compile out a DEBUG9 severity log line. + * Send a DEBUG5 severity log line. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. - */ -#define ACSDK_DEBUG9(entry) - -/** - * Compile out a DEBUG8 severity log line. + * @note If @c ACSDK_DEBUG_LOG_ENABLED is set to OFF, then logging is disabled. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. + * @param[in] entry A constructed @a LogEntry object with the log message text. */ -#define ACSDK_DEBUG8(entry) +#define ACSDK_DEBUG5(entry) ACSDK_DEBUG_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG5, entry) /** - * Compile out a DEBUG7 severity log line. + * Send a DEBUG4 severity log line. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. - */ -#define ACSDK_DEBUG7(entry) - -/** - * Compile out a DEBUG6 severity log line. + * @note If @c ACSDK_DEBUG_LOG_ENABLED is set to OFF, then logging is disabled. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. + * @param[in] entry A constructed @a LogEntry object with the log message text. */ -#define ACSDK_DEBUG6(entry) +#define ACSDK_DEBUG4(entry) ACSDK_DEBUG_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG4, entry) /** - * Compile out a DEBUG5 severity log line. + * Send a DEBUG3 severity log line. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. - */ -#define ACSDK_DEBUG5(entry) - -/** - * Compile out a DEBUG4 severity log line. + * @note If @c ACSDK_DEBUG_LOG_ENABLED is set to OFF, then logging is disabled. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. + * @param[in] entry A constructed @a LogEntry object with the log message text. */ -#define ACSDK_DEBUG4(entry) +#define ACSDK_DEBUG3(entry) ACSDK_DEBUG_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG3, entry) /** - * Compile out a DEBUG3 severity log line. + * Send a DEBUG2 severity log line. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. - */ -#define ACSDK_DEBUG3(entry) - -/** - * Compile out a DEBUG2 severity log line. + * @note If @c ACSDK_DEBUG_LOG_ENABLED is set to OFF, then logging is disabled. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. + * @param[in] entry A constructed @a LogEntry object with the log message text. */ -#define ACSDK_DEBUG2(entry) +#define ACSDK_DEBUG2(entry) ACSDK_DEBUG_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG2, entry) /** - * Compile out a DEBUG1 severity log line. + * Send a DEBUG1 severity log line. + * + * @note If @c ACSDK_DEBUG_LOG_ENABLED is set to OFF, then logging is disabled. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. + * @param[in] entry A constructed @a LogEntry object with the log message text. */ -#define ACSDK_DEBUG1(entry) +#define ACSDK_DEBUG1(entry) ACSDK_DEBUG_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG1, entry) /** - * Compile out a DEBUG0 severity log line. + * Send a DEBUG0 severity log line. + * + * @note If @c ACSDK_DEBUG_LOG_ENABLED is set to OFF, then logging is disabled. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. + * @param[in] entry A constructed @a LogEntry object with the log message text. */ -#define ACSDK_DEBUG0(entry) +#define ACSDK_DEBUG0(entry) ACSDK_DEBUG_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG0, entry) /** - * Compile out a DEBUG severity log line. + * Send a log line at the default debug level (DEBUG0). * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. + * @note If @c ACSDK_DEBUG_LOG_ENABLED is set to OFF, then logging is disabled. + * + * @param[in] entry A constructed @a LogEntry object with the log message text. */ -#define ACSDK_DEBUG(entry) - -#endif // ACSDK_DEBUG_LOG_ENABLED +#define ACSDK_DEBUG(entry) ACSDK_DEBUG_LOG(alexaClientSDK::avsCommon::utils::logger::Level::DEBUG0, entry) /** * Send a INFO severity log line. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. + * @note If @c ACSDK_LOG_ENABLED is set to OFF, then logging is disabled. + * + * @param[in] entry A constructed @a LogEntry object with the log message text. */ #define ACSDK_INFO(entry) ACSDK_LOG(alexaClientSDK::avsCommon::utils::logger::Level::INFO, entry) /** * Send a WARN severity log line. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. + * @note If @c ACSDK_LOG_ENABLED is set to OFF, then logging is disabled. + * + * @param[in] entry A constructed @a LogEntry object with the log message text. */ #define ACSDK_WARN(entry) ACSDK_LOG(alexaClientSDK::avsCommon::utils::logger::Level::WARN, entry) /** * Send a ERROR severity log line. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. + * @note If @c ACSDK_LOG_ENABLED is set to OFF, then logging is disabled. + * + * @param[in] entry A constructed @a LogEntry object with the log message text. */ #define ACSDK_ERROR(entry) ACSDK_LOG(alexaClientSDK::avsCommon::utils::logger::Level::ERROR, entry) /** * Send a CRITICAL severity log line. * - * @param loggerArg The Logger to send the line to. - * @param entry The text (or builder of the text) for the log entry. + * @note If @c ACSDK_LOG_ENABLED is set to OFF, then logging is disabled. + * + * @param[in] entry A constructed @a LogEntry object with the log message text. */ #define ACSDK_CRITICAL(entry) ACSDK_LOG(alexaClientSDK::avsCommon::utils::logger::Level::CRITICAL, entry) +#ifndef ACSDK_LOGS_KEEP_FUNC_MACRO +// In older Android releases __func__ is redefined as __PRETTY_FUNCTION__ making log messages difficult to read and +// analyse. We undefine this macro if it is defined. See Logger.cmake for details on how to keep the macro intact. +#undef __func__ +#endif // ACSDK_LOGS_KEEP_FUNC_MACRO + #endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_LOGGER_LOGGER_H_ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Logger/LoggerSinkManager.h b/AVSCommon/Utils/include/AVSCommon/Utils/Logger/LoggerSinkManager.h index 252d03123f..f433e3441e 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Logger/LoggerSinkManager.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Logger/LoggerSinkManager.h @@ -67,8 +67,7 @@ class LoggerSinkManager { * * @param sink The new @c Logger to forward logs to. * - * @note If this function is not called, the default sink logger - * will be the one returned by getLogger(). + * @note If this function is not called, the default sink logger is @c ConsoleLogger. */ void initialize(const std::shared_ptr& sink); diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Logger/ThreadMoniker.h b/AVSCommon/Utils/include/AVSCommon/Utils/Logger/ThreadMoniker.h index ec70c280a6..09b638231a 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Logger/ThreadMoniker.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Logger/ThreadMoniker.h @@ -27,82 +27,64 @@ namespace utils { namespace logger { /** - * Class to provide @c std::this_thread access to unique name for itself. + * Class to provide unique name for execution context. * - * The name ThreadMoniker is used instead of ThreadId to avoid confusion with platform specific thread identifiers - * or the @c std::thread::id values rendered as a string. + * This class provides management for thread-local execution context identifiers. Execution context identifier is a + * hexadecimal string which is managed by facilities like @c Executor and @c Timer. Logging framework adds identifier + * value to all messages to identify which executor or timer is running the task. + * + * @note The same identifier can be reused by different threads, but will have the same context. */ class ThreadMoniker { public: + /// Prefix value for executor monikers. + static constexpr char PREFIX_EXECUTOR = 'e'; + + /// Prefix value for timer monikers. + static constexpr char PREFIX_TIMER = 't'; + /** - * Get the moniker for @c std::this_thread. + * Get the moniker for the caller's thread. * - * @return The moniker for @c std::this_thread. + * Method returns identifier value assigned to the current thread by a call to #setThisThreadMoniker(). If the value + * has not been previously set, a new value is generated by #generateMoniker(), assigned to the current thread, and + * returned. + * + * @return The moniker for the caller's thread. */ - static inline std::string getThisThreadMoniker(); + static std::string getThisThreadMoniker() noexcept; /** * Generate a unique moniker. * + * This method generates a fixed-width moniker string. By default the value is a alpha-numeric string, prepended + * with spaces. If the \a prefix is specified, the value also includes prefix character with colon. + * + * @param prefix Optional prefix for moniker value. If @a prefix if not 0, it is used for generating moniker. + * Some of prefix values are reserved for use by ACSDK: 't' prefix is used by timers, and 'e' prefix is used by + * executors. + * * @return A new unique moniker. */ - static std::string generateMoniker(); + static std::string generateMoniker(char prefix = 0) noexcept; /** - * Set the moniker for @c std::this_thread. This method should be called before @c getThisThreadMoniker() in order - * to take effect. + * Set the moniker for the caller's thread. + * + * This method sets identifier value for the caller's thread. Any subsequent calls to #getThisThreadMoniker() will + * return @a moniker value. * * @param moniker The moniker for @c std::this_thread. */ - static inline void setThisThreadMoniker(const std::string& moniker); + static void setThisThreadMoniker(const std::string& moniker) noexcept; private: /** * Constructor. - * - * @param moniker Optional moniker for this thread. If no moniker is provided, a new moniker will be provided. */ - ThreadMoniker(const std::string& moniker = std::string()); - - /** - * Return the @c ThreadMoniker object for the current thread. - * - * @param moniker Use this moniker to initialize the @c ThreadMoniker if it doesn't exist already. - * @return The moniker for the @c std::this_thread. - */ - static inline const ThreadMoniker& getMonikerObject(const std::string& moniker = std::string()); - - /** - * Return the @c ThreadMoniker object for the current thread for OS that don't support thread local variables. - * - * @param moniker Use this moniker to initialize the @c ThreadMoniker if it doesn't exist already. - * @return The moniker for the @c std::this_thread. - */ - static const ThreadMoniker& getMonikerObjectFromMap(const std::string& moniker = std::string()); - - /// The current thread's moniker. - std::string m_moniker; + ThreadMoniker() = delete; }; -std::string ThreadMoniker::getThisThreadMoniker() { - return getMonikerObject().m_moniker; -} - -void ThreadMoniker::setThisThreadMoniker(const std::string& moniker) { - getMonikerObject(moniker); -} - -const ThreadMoniker& ThreadMoniker::getMonikerObject(const std::string& moniker) { -#ifdef _WIN32 - return getMonikerObjectFromMap(moniker); -#else - /// Per-thread static instance so that @c m_threadMoniker.m_moniker is @c std::this_thread's moniker. - static thread_local ThreadMoniker m_threadMoniker{moniker}; - - return m_threadMoniker; -#endif -} - } // namespace logger } // namespace utils } // namespace avsCommon diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/ErrorTypes.h b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/ErrorTypes.h index 0878ed33ec..b17b083789 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/ErrorTypes.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/ErrorTypes.h @@ -17,6 +17,7 @@ #define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_MEDIAPLAYER_ERRORTYPES_H_ #include +#include namespace alexaClientSDK { namespace avsCommon { diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/MediaDescription.h b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/MediaDescription.h index fb1c8db68e..ec7ef33344 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/MediaDescription.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/MediaDescription.h @@ -69,7 +69,7 @@ inline MediaDescription emptyMediaDescription() { "", Optional(), Optional>(), - {}, + std::unordered_map(), false}; } @@ -81,6 +81,7 @@ inline MediaDescription emptyMediaDescription() { * @return The stream that was passed in and written to. */ inline std::ostream& operator<<(std::ostream& stream, const MediaDescription& mediaDescription) { + stream << "MixingBehavior:"; switch (mediaDescription.mixingBehavior) { case sdkInterfaces::audio::MixingBehavior::BEHAVIOR_PAUSE: stream << "BEHAVIOR_PAUSE"; diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Metrics/MetricRecorderInterface.h b/AVSCommon/Utils/include/AVSCommon/Utils/Metrics/MetricRecorderInterface.h index 6f6cccc33f..b6323fa900 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Metrics/MetricRecorderInterface.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Metrics/MetricRecorderInterface.h @@ -43,10 +43,10 @@ class MetricRecorderInterface { }; /** - * Inline record metric function to handle if ACSDK_ENABLE_METRICS_RECORDING flag is defined or not + * Inline record metric function to handle if ACSDK_ENABLE_METRICS_RECORDING flag is defined or not. * - * @param recorder is the pointer to the MetricRecorderInterface. - * @param metricEvent is the pointer to the MetricEvent. + * @param recorder Optional pointer to MetricRecorderInterface. If this parameter is nullptr, metric is not sent. + * @param metricEvent Pointer to the MetricEvent. */ inline void recordMetric( const std::shared_ptr& recorder, @@ -55,6 +55,9 @@ inline void recordMetric( if (recorder) { recorder->recordMetric(std::move(metricEvent)); } +#else + (void)recorder; + (void)metricEvent; #endif } diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/PlatformDefinitions.h b/AVSCommon/Utils/include/AVSCommon/Utils/PlatformDefinitions.h index e47afd3c1c..e879baa732 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/PlatformDefinitions.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/PlatformDefinitions.h @@ -40,7 +40,79 @@ typedef SSIZE_T ssize_t; #endif #if defined(_MSC_VER) +#ifndef ACSDK_USE_RTTI #define ACSDK_USE_RTTI ON #endif +#endif + +/** + * @macro ACSDK_ALWAYS_INLINE + * + * Compiler-specific macro to disable cost-benefit analysis and always inline a method. + */ + +/** + * @macro ACSDK_NO_INLINE + * + * Compiler-specific macro to disable cost-benefit analysis and never inline a method. + */ + +/** + * @macro ACSDK_HIDDEN + * + * Compiler-specific macro to make symbol not visible outside of binary object. + */ + +/** + * @macro ACSDK_INTERNAL_LINKAGE + * + * Compiler-specific macro to make symbol not visible outside of the scope it is instantiated. + */ + +/** + * @macro ACSDK_HIDE_FROM_ABI + * @brief Compiler-specific macro to exclude symbol from binary exports. + * + * Macro excludes symbols from binary export table. This helps to reduce binary size when building shared libraries. + */ + +#if defined(_MSC_VER) +#define ACSDK_ALWAYS_INLINE __forceinline +#define ACSDK_NO_INLINE __declspec(noinline) +#define ACSDK_HIDDEN +#define ACSDK_INTERNAL_LINKAGE ACSDK_ALWAYS_INLINE +#elif defined(__GNUC__) + +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#define ACSDK_ALWAYS_INLINE inline __attribute__((__always_inline__)) +#define ACSDK_NO_INLINE __attribute__((__noinline__)) +#define ACSDK_HIDDEN __attribute__((__visibility__("hidden"))) + +#if defined(__clang__) +#if __has_attribute(internal_linkage) +#define ACSDK_INTERNAL_LINKAGE __attribute__((internal_linkage)) +#else +#define ACSDK_INTERNAL_LINKAGE __attribute__((__visibility__("hidden"))) +#endif +#else +#define ACSDK_INTERNAL_LINKAGE __attribute__((__visibility__("internal"))) +#endif +#else +#define ACSDK_ALWAYS_INLINE inline +#define ACSDK_NO_INLINE +#define ACSDK_HIDDEN +#define ACSDK_INTERNAL_LINKAGE ACSDK_ALWAYS_INLINE +#endif +#define ACSDK_HIDE_FROM_ABI ACSDK_INTERNAL_LINKAGE + +/** + * @brief Compiler-specific macro for inline-only methods. + * + * Macro excludes symbols from binary export table. This helps to reduce binary size when building shared libraries. + */ +#define ACSDK_INLINE_VISIBILITY ACSDK_HIDE_FROM_ABI #endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_PLATFORMDEFINITIONS_H_ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Power/AggregatedPowerResourceManager.h b/AVSCommon/Utils/include/AVSCommon/Utils/Power/AggregatedPowerResourceManager.h index 069b6b2a12..ffbeb03132 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Power/AggregatedPowerResourceManager.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Power/AggregatedPowerResourceManager.h @@ -61,11 +61,13 @@ class AggregatedPowerResourceManager : public avsCommon::sdkInterfaces::PowerRes /// @name PowerResourceManagerInterface Legacy Methods /// @{ + /**********************************Deprecated Legacy APIs**********************************************/ void acquirePowerResource( const std::string& component, const PowerResourceLevel level = PowerResourceLevel::STANDBY_MED) override; void releasePowerResource(const std::string& component) override; bool isPowerResourceAcquired(const std::string& component) override; + /******************************************************************************************************/ std::shared_ptr create( const std::string& resourceId, bool isRefCounted = true, diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/SDS/BufferLayout.h b/AVSCommon/Utils/include/AVSCommon/Utils/SDS/BufferLayout.h index 67517a5e6c..507ccd0953 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/SDS/BufferLayout.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/SDS/BufferLayout.h @@ -489,6 +489,13 @@ bool SharedDataStream::BufferLayout::init(size_t wordSize, size_t maxReaders, .d("maxReadersLimit", max_field_limit(&Header::maxReaders))); return false; } + if (maxEphemeralReaders > max_field_limit(&Header::maxEphemeralReaders)) { + logger::acsdkError(logger::LogEntry(TAG, "initFailed") + .d("reason", "maxEphermalReadersTooLarge") + .d("maxEphemeralReaders", maxEphemeralReaders) + .d("maxEphemeralReaders", max_field_limit(&Header::maxEphemeralReaders))); + return false; + } // Pre-calculate some pointers and sizes that are frequently accessed. calculateAndCacheConstants(wordSize, maxReaders); @@ -508,9 +515,9 @@ bool SharedDataStream::BufferLayout::init(size_t wordSize, size_t maxReaders, header->magic = MAGIC_NUMBER; header->version = VERSION; header->traitsNameHash = stableHash(T::traitsName); - header->wordSize = wordSize; - header->maxReaders = maxReaders; - header->maxEphemeralReaders = maxEphemeralReaders; + header->wordSize = static_cast(wordSize); + header->maxReaders = static_cast(maxReaders); + header->maxEphemeralReaders = static_cast(maxEphemeralReaders); header->isWriterEnabled = false; header->hasWriterBeenClosed = false; header->writeStartCursor = 0; diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/SDS/Reader.h b/AVSCommon/Utils/include/AVSCommon/Utils/SDS/Reader.h index e7beb43f80..2cbba7304c 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/SDS/Reader.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/SDS/Reader.h @@ -90,7 +90,7 @@ class SharedDataStream::Reader { * @param bufferLayout The @c BufferLayout to use for reading stream data. * @param id The id of the reader, assigned by the @c SharedDataStream. */ - Reader(Policy policy, std::shared_ptr bufferLayout, uint8_t id); + Reader(Policy policy, std::shared_ptr bufferLayout, size_t id); /// This destructor detaches the @c Reader from a @c BufferLayout. ~Reader(); @@ -245,7 +245,7 @@ class SharedDataStream::Reader { * The index in @c BufferLayout::getReaderCursorArray() and @c BufferLayout::getReaderCloseIndexArray() assigned to * this @c Reader. */ - uint8_t m_id; + size_t m_id; /// Pointer to this reader's cursor in BufferLayout::getReaderCursorArray(). AtomicIndex* m_readerCursor; @@ -258,7 +258,7 @@ template const std::string SharedDataStream::Reader::TAG = "SdsReader"; template -SharedDataStream::Reader::Reader(Policy policy, std::shared_ptr bufferLayout, uint8_t id) : +SharedDataStream::Reader::Reader(Policy policy, std::shared_ptr bufferLayout, size_t id) : m_policy{policy}, m_bufferLayout{bufferLayout}, m_id{id}, diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Threading/Executor.h b/AVSCommon/Utils/include/AVSCommon/Utils/Threading/Executor.h index af8e471393..a51e29bcde 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Threading/Executor.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Threading/Executor.h @@ -26,8 +26,9 @@ #include #include -#include "AVSCommon/Utils/Threading/TaskThread.h" -#include "AVSCommon/Utils/Power/PowerResource.h" +#include +#include +#include namespace alexaClientSDK { namespace avsCommon { @@ -35,134 +36,174 @@ namespace utils { namespace threading { /** + * @brief Single-thread executor implementation. + * * An Executor is used to run callable types asynchronously. + * + * This type is a wrapper around ExecutorInterface implementation. */ class Executor { public: + /** + * Constructs an Executor. + */ + Executor() noexcept; + /** * Constructs an Executor. * - * @param delayExit The period of time that this executor will keep its thread running while waiting - * for a new job. We use 1s by default. + * @param unused Unused parameter. + * + * @deprecated This method is kept for backwards compatibility. */ - Executor(const std::chrono::milliseconds& delayExit = std::chrono::milliseconds(1000)); + Executor(const std::chrono::milliseconds& unused) noexcept; /** * Destructs an Executor. */ - ~Executor(); + ~Executor() noexcept; + + /** + * @brief Schedules a function for execution. + * + * Submits a function to be executed on an Executor thread. + * + * @param[in] function Function to execute. Function must not be empty. + * @return True if @a function is accepted for execution, false if @a function is empty or executor is shutdown. + */ + bool execute(std::function&& function) noexcept; + + /** + * @brief Schedules a function for execution. + * + * Submits a function to be executed on an Executor thread. + * + * @param[in] function Function to execute. + * @return True if @a function is accepted for execution, false if @a function is empty or executor is shutdown. + */ + bool execute(const std::function& function) noexcept; /** * Submits a callable type (function, lambda expression, bind expression, or another function object) to be executed * on an Executor thread. The future must be checked for validity before waiting on it. * + * @tparam Task Callable type. + * @tparam Args Argument types. + * * @param task A callable type representing a task. * @param args The arguments to call the task with. - * @returns A @c std::future for the return value of the task. + * @return A @c std::future for the return value of the task. + * + * @note This method is less memory and speed efficient then #execute() and should not be used unless std::future + * result is required. */ template - auto submit(Task task, Args&&... args) -> std::future; + auto submit(Task task, Args&&... args) noexcept -> std::future; /** * Submits a callable type (function, lambda expression, bind expression, or another function object) to the front * of the internal queue to be executed on an Executor thread. The future must be checked for validity before * waiting on it. * + * @tparam Task Callable type. + * @tparam Args Argument types. + * * @param task A callable type representing a task. * @param args The arguments to call the task with. - * @returns A @c std::future for the return value of the task. + * @return A @c std::future for the return value of the task. + * + * @note This method is less memory and speed efficient then #execute() and should not be used unless std::future + * result is required. */ template - auto submitToFront(Task task, Args&&... args) -> std::future; + auto submitToFront(Task task, Args&&... args) noexcept -> std::future; /** * Waits for any previously submitted tasks to complete. */ - void waitForSubmittedTasks(); + void waitForSubmittedTasks() noexcept; /// Clears the executor of outstanding tasks and refuses any additional tasks to be submitted. - void shutdown(); + void shutdown() noexcept; /// Returns whether or not the executor is shutdown. - bool isShutdown(); - -private: - /// The queue type to use for holding tasks. - using Queue = std::deque>; + bool isShutdown() noexcept; /** - * Executes the next job in the queue. + * @brief Provides access to ExecutorInterface reference. * - * @return @c true if there's a next job; @c false if the job queue is empty. + * @return Reference to internal ExecutorInterface. */ - bool runNext(); + operator std::shared_ptr() const noexcept; + +private: + // Friend declaration. + friend class SharedExecutor; /** - * Checks if the job queue is empty and that no job is added in the grace period determined by @c m_timeout. - * - * @return @c true if there's at least one job left in the queue; @c false if the job queue is empty. + * @brief Ordering hint when submitting a new task to executor. */ - bool hasNext(); + enum class QueuePosition { + /// Add task to front of task queue. + Front = 1, + /// Add task to back of task queue. + Back + }; /** - * Returns and removes the task at the front of the queue. If there are no tasks, this call will return an empty - * function. + * @brief Schedules a function for execution. + * + * Submits a function to be executed on an Executor thread. * - * @returns A function that represents a new task. The function will be empty if the queue has no job. + * @param[in] function Function to execute. + * @param[in] queuePosition Position in the queue for the new task. + * @return True on success, false on error: if @a function is empty, @a queuePosition is not valid, or executor is + * in shutdown. */ - std::function pop(); + bool execute(std::function&& function, QueuePosition queuePosition) noexcept; /** * Pushes a task on the the queue. If the queue is shutdown, the task will be dropped, and an invalid * future will be returned. * - * @param front If @c true, push to the front of the queue, else push to the back. + * @param queuePosition Position in the queue for the new task. * @param task A task to push to the front or back of the queue. * @param args The arguments to call the task with. * @returns A @c std::future to access the return value of the task. If the queue is shutdown, the task will be * dropped, and an invalid future will be returned. */ template - auto pushTo(bool front, Task task, Args&&... args) -> std::future; - - /// The queue of tasks - Queue m_queue; + auto pushTo(QueuePosition queuePosition, Task&& task, Args&&... args) noexcept + -> std::future; - /// Flag to indicate if the taskThread already have an executing job. - bool m_threadRunning; - - /// Period that this queue will wait for a new job until it releases the task thread. - std::chrono::milliseconds m_timeout; - - /// A mutex to protect access to the tasks in m_queue. - std::mutex m_queueMutex; - - /// A flag for whether or not the queue is expecting more tasks. - std::atomic_bool m_shutdown; - - /// A @c PowerResource. - std::shared_ptr m_powerResource; - - /// The condition variable used to detect new job or timeout. - std::condition_variable m_delayedCondition; - - /// The id of this instance. - const uint64_t m_id; + /** + * Pushes a function on the the queue. If the queue is shutdown, the function will be dropped, and an invalid + * future will be returned. + * + * @tparam T Return type for @a function and resulting future value type. + * @param queuePosition Position in the queue for the new task. + * @param function A function to push. + * @returns A @c std::future to access the return value of the task. If the queue is shutdown, the task will be + * dropped, and an invalid future will be returned. + */ + template + std::future pushFunction(QueuePosition queuePosition, std::function&& function) noexcept; - /// The thread to execute tasks on. The thread must be declared last to be destructed first. - TaskThread m_taskThread; + /// Internal shared executor reference. + std::shared_ptr m_executor; }; +inline Executor::Executor(const std::chrono::milliseconds&) noexcept : Executor() { +} + template -auto Executor::submit(Task task, Args&&... args) -> std::future { - bool front = false; - return pushTo(front, std::forward(task), std::forward(args)...); +auto Executor::submit(Task task, Args&&... args) noexcept -> std::future { + return pushTo(QueuePosition::Back, std::forward(task), std::forward(args)...); } template -auto Executor::submitToFront(Task task, Args&&... args) -> std::future { - bool front = true; - return pushTo(front, std::forward(task), std::forward(args)...); +auto Executor::submitToFront(Task task, Args&&... args) noexcept -> std::future { + return pushTo(QueuePosition::Front, std::forward(task), std::forward(args)...); } /** @@ -172,14 +213,14 @@ auto Executor::submitToFront(Task task, Args&&... args) -> std::future -inline static void forwardPromise(std::shared_ptr> promise, std::future* future) { +inline static void forwardPromise(std::promise& promise, std::future& future) noexcept { #if __cpp_exceptions || defined(__EXCEPTIONS) try { #endif - promise->set_value(future->get()); + promise.set_value(future.get()); #if __cpp_exceptions || defined(__EXCEPTIONS) } catch (...) { - promise->set_exception(std::current_exception()); + promise.set_exception(std::current_exception()); } #endif } @@ -191,28 +232,32 @@ inline static void forwardPromise(std::shared_ptr> promise, std: * @param future The @c std::future on which to wait before fulfilling @c promise. */ template <> -inline void forwardPromise(std::shared_ptr> promise, std::future* future) { +inline void forwardPromise(std::promise& promise, std::future& future) noexcept { #if __cpp_exceptions || defined(__EXCEPTIONS) try { #endif - future->get(); - promise->set_value(); + future.get(); + promise.set_value(); #if __cpp_exceptions || defined(__EXCEPTIONS) } catch (...) { - promise->set_exception(std::current_exception()); + promise.set_exception(std::current_exception()); } #endif } template -auto Executor::pushTo(bool front, Task task, Args&&... args) -> std::future { +inline auto Executor::pushTo(QueuePosition queuePosition, Task&& task, Args&&... args) noexcept + -> std::future { + using ValueType = decltype(task(args...)); // Remove arguments from the tasks type by binding the arguments to the task. - auto boundTask = std::bind(std::forward(task), std::forward(args)...); + std::function fn{std::bind(std::forward(task), std::forward(args)...)}; + return pushFunction(queuePosition, std::move(fn)); +} +template +std::future Executor::pushFunction(QueuePosition queuePosition, std::function&& function) noexcept { /* - * Create a std::packaged_task with the correct return type. The decltype only returns the return value of the - * boundTask. The following parentheses make it a function call with the boundTask return type. The package task - * will then return a future of the correct type. + * Create a std::packaged_task with the correct return type. * * Note: A std::packaged_task fulfills its future *during* the call to operator(). If the user of a * std::packaged_task hands it off to another thread to execute, and then waits on the future, they will be able to @@ -223,53 +268,69 @@ auto Executor::pushTo(bool front, Task task, Args&&... args) -> std::future; - auto packaged_task = std::make_shared(boundTask); - // Create a promise/future that we will fulfill when we have cleaned up the task. - auto cleanupPromise = std::make_shared>(); - auto cleanupFuture = cleanupPromise->get_future(); + /** + * @brief Structure to carry parameters into lambda through shared pointer. + * @private + */ + struct CallCtx { + /** + * @brief Construct object and assigned function to packaged task. + * + * @param function Function to wrap into packaged task. + */ + inline CallCtx(std::function&& function) : packagedTask{std::move(function)} { + } + + /// Packaged task. + std::packaged_task packagedTask; + /// Promise for result forwarding. + std::promise cleanupPromise; + }; + + auto callCtx = std::make_shared(std::move(function)); // Remove the return type from the task by wrapping it in a lambda with no return value. - auto translated_task = [packaged_task, cleanupPromise]() mutable { + auto translated_task = [callCtx]() mutable { // Execute the task. - packaged_task->operator()(); + callCtx->packagedTask(); // Note the future for the task's result. - auto taskFuture = packaged_task->get_future(); + auto taskFuture = callCtx->packagedTask.get_future(); // Clean up the task. - packaged_task.reset(); + callCtx->packagedTask.reset(); + auto cleanupPromise = std::move(callCtx->cleanupPromise); + // Release parameters. + callCtx.reset(); // Forward the task's result to our cleanup promise/future. - forwardPromise(cleanupPromise, &taskFuture); + forwardPromise(cleanupPromise, taskFuture); }; + // Create a promise/future that we will fulfill when we have cleaned up the task. + auto cleanupFuture = callCtx->cleanupPromise.get_future(); + // Release our local reference to packaged task so that the only remaining reference is inside the lambda. - packaged_task.reset(); - - { - bool restart = false; - std::lock_guard queueLock{m_queueMutex}; - if (!m_shutdown) { - restart = !m_threadRunning; - if (m_powerResource) { - m_powerResource->acquire(); - } - m_queue.emplace(front ? m_queue.begin() : m_queue.end(), std::move(translated_task)); - } else { - using FutureType = decltype(task(args...)); - return std::future(); - } + callCtx.reset(); - if (restart) { - // Restart task thread. - m_taskThread.start(std::bind(&Executor::runNext, this)); - m_threadRunning = true; - } + if (!execute(std::move(translated_task), queuePosition)) { + return std::future(); } - m_delayedCondition.notify_one(); return cleanupFuture; } +/// @name Externalize Executor::pushFunction() for common types. +/// @{ +extern template std::future Executor::pushFunction( + QueuePosition queuePosition, + std::function&& function) noexcept; +extern template std::future Executor::pushFunction( + QueuePosition queuePosition, + std::function&& function) noexcept; +extern template std::future Executor::pushFunction( + QueuePosition queuePosition, + std::function&& function) noexcept; +/// @} + } // namespace threading } // namespace utils } // namespace avsCommon diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Threading/ExecutorFactory.h b/AVSCommon/Utils/include/AVSCommon/Utils/Threading/ExecutorFactory.h new file mode 100644 index 0000000000..be69d72d45 --- /dev/null +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Threading/ExecutorFactory.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_THREADING_EXECUTORFACTORY_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_THREADING_EXECUTORFACTORY_H_ + +#include + +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace threading { + +/** + * @brief Create a single-thread executor. + * + * @return New executor reference or nullptr on error. + */ +std::shared_ptr createSingleThreadExecutor() noexcept; + +} // namespace threading +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_THREADING_EXECUTORFACTORY_H_ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Threading/ExecutorInterface.h b/AVSCommon/Utils/include/AVSCommon/Utils/Threading/ExecutorInterface.h new file mode 100644 index 0000000000..5762b85b73 --- /dev/null +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Threading/ExecutorInterface.h @@ -0,0 +1,78 @@ +/* + * 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. + */ + +#ifndef ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_THREADING_EXECUTORINTERFACE_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_THREADING_EXECUTORINTERFACE_H_ + +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace threading { + +/** + * @brief Interface for asynchronous execution of functions. + * + * This interface enables submission of functions for asynchronous execution. The implementations should use thread pool + * to acquire threads for running functions, and may be single- or multi-threaded. + * + * Executors can be in normal mode, when they accept tasks, and in shutdown mode. In shutdown mode executors do not + * accept new tasks for processing, and any tasks, that haven't started an execution will be dropped. + * + * @code + * auto error = executor->execute([]{ ... }); + * if (error) { + * ACSDK_ERROR(LX(__func__).("executorError", error.message())); + * } + * @endcode + * + * @see Executor + * @see ThreadPool + */ +class ExecutorInterface { +public: + /** + * @brief Destructs an Executor. + * + * This method awaits till all running tasks are completed, and drops all enqueued tasks that haven't started + * execution. + */ + virtual ~ExecutorInterface() noexcept = default; + + /** + * @{ + * @brief Schedules a function for execution. + * + * Submits a function to be executed on an executor thread. + * + * @param[in] function Function to execute. Function must not be empty. + * + * @return Platform-independent error code. If successful, the error value is zero. + * @retval std::errc::invalid_argument If @a function is empty. + * @retval std::errc::operation_not_permitted If executor is shutdown. + */ + virtual std::error_condition execute(std::function&& function) noexcept = 0; + virtual std::error_condition execute(const std::function& function) noexcept = 0; + /// @} +}; + +} // namespace threading +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_THREADING_EXECUTORINTERFACE_H_ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Threading/TaskThread.h b/AVSCommon/Utils/include/AVSCommon/Utils/Threading/TaskThread.h index 3a26dffee5..1a4096cbe7 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Threading/TaskThread.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Threading/TaskThread.h @@ -53,9 +53,12 @@ class TaskThread { * * @param jobRunner Function that should execute jobs. The function should return @c true if there's more tasks * to be executed. + * @param moniker Thread moniker to use when executing @a jobRunner. All logging messages will use provided + * moniker for identifying the execution owner. + * * @return @c true if it succeeds to start the new jobRunner thread; @c false if it fails. */ - bool start(std::function jobRunner); + bool start(std::function jobRunner, const std::string& moniker); private: /** @@ -66,9 +69,6 @@ class TaskThread { */ void run(std::function jobRunner); - /// The monotonic start time of the thread. - std::chrono::steady_clock::time_point m_startTime; - /// Mutex for protecting the worker thread. std::mutex m_mutex; @@ -81,9 +81,6 @@ class TaskThread { /// Flag used to indicate that there is a new job starting. std::atomic_bool m_alreadyStarting; - /// The task thread moniker. - std::string m_moniker; - /// ThreadPool to use for obtaining threads. std::shared_ptr m_threadPool; diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Threading/ThreadPool.h b/AVSCommon/Utils/include/AVSCommon/Utils/Threading/ThreadPool.h index c6ab1f83f2..8b06b86149 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Threading/ThreadPool.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Threading/ThreadPool.h @@ -18,9 +18,6 @@ #include #include -#ifdef THREAD_AFFINITY -#include -#endif #include #include "WorkerThread.h" @@ -57,10 +54,12 @@ class ThreadPool { /** * Obtain a worker thread to operate on. * - * @param optionalMoniker the moniker of the worker desired. - * @return the worker thread obtained. + * This method returns a reference to worker object. If thread pool has worker objects in the pool, it returns one + * of them. If thread pool is empty, a newly constructed object is returned. + * + * @return Worker thread object. */ - std::unique_ptr obtainWorker(std::string optionalMoniker = ""); + std::unique_ptr obtainWorker(); /** * Release a worker @@ -81,7 +80,7 @@ class ThreadPool { * Obtain the current maximum threads for the thread pool. * @return the max threads the thread pool is currently set to hold. */ - uint32_t getMaxThreads(); + size_t getMaxThreads(); /** * Obtain statics for the thread pool @@ -103,10 +102,6 @@ class ThreadPool { static std::shared_ptr getDefaultThreadPool(); private: -#ifdef THREAD_AFFINITY - /// Map of Worker thread monikers to worker thread iterator in the worker queue. - std::map>::iterator> m_workerMap; -#endif /// Queue of worker threads to vend. std::list> m_workerQueue; diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Threading/WorkerThread.h b/AVSCommon/Utils/include/AVSCommon/Utils/Threading/WorkerThread.h index 8ee992a49d..3daaa3b1b8 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Threading/WorkerThread.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Threading/WorkerThread.h @@ -57,10 +57,11 @@ class WorkerThread { void cancel(); /** - * Return the moniker for the worker thread. - * @return the worker thread moniker. + * Return thread id. + * + * @return Thread id for the allocated thread. */ - std::string getMoniker() const; + std::thread::id getThreadId() const; private: /** @@ -68,9 +69,6 @@ class WorkerThread { */ void runInternal(); - /// The thread moniker for the worker thread. - const std::string m_moniker; - /// Flag indicating the thread is stopping. std::atomic m_stop; @@ -88,6 +86,9 @@ class WorkerThread { /// Condition variable for waking the thread. std::condition_variable m_workReady; + + /// Platform-specific thread identifier. + std::thread::id m_threadId; }; } // namespace threading diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Timing/MultiTimer.h b/AVSCommon/Utils/include/AVSCommon/Utils/Timing/MultiTimer.h index 169a80b57b..bc86dedfd9 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Timing/MultiTimer.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Timing/MultiTimer.h @@ -45,17 +45,17 @@ class MultiTimer { * Factory method that creates a shared pointer to a MultiTimer. * @return A new instance of MultiTimer. */ - static std::shared_ptr createMultiTimer(); + static std::shared_ptr createMultiTimer() noexcept; /** * Constructor. */ - MultiTimer(); + MultiTimer() noexcept; /** * Destructor. */ - ~MultiTimer(); + ~MultiTimer() noexcept; /** * Submits a task to be executed after a given delay. @@ -66,14 +66,14 @@ class MultiTimer { * @param task The task to be executed. * @return A unique token that can be used to cancel this task. */ - Token submitTask(const std::chrono::milliseconds& delay, std::function task); + Token submitTask(const std::chrono::milliseconds& delay, std::function task) noexcept; /** * Removes a task from the queue. * * @param token The token used to identify the task to be canceled. */ - void cancelTask(Token token); + void cancelTask(Token token) noexcept; private: /** @@ -81,7 +81,7 @@ class MultiTimer { * * @return @c true if there are more timers scheduled; @c false otherwise. */ - bool executeTimer(); + bool executeTimer() noexcept; /** * Checks if there are any pending tasks within a grace period determined by an internal timeout. @@ -89,11 +89,15 @@ class MultiTimer { * @param lock The lock being held during the check. * @return @c true if there's at least one task left; @c false if there is no pending task. */ - bool hasNextLocked(std::unique_lock& lock); + bool hasNextLocked(std::unique_lock& lock) noexcept; /// Alias for the time point used in this class. using TimePoint = std::chrono::time_point; + /// Moniker for timer tasks. + /// Whenever timer runs a task, it guarantees that task's thread has this value set with @c ThreadMoniker. + const std::string m_timerMoniker; + /// The condition variable used to wait for the next task. std::condition_variable m_waitCondition; diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Timing/Timer.h b/AVSCommon/Utils/include/AVSCommon/Utils/Timing/Timer.h index bad0d3e6d4..c0edac49d8 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Timing/Timer.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Timing/Timer.h @@ -23,10 +23,8 @@ #include #include -#include "AVSCommon/Utils/Error/FinallyGuard.h" -#include "AVSCommon/AVS/Initialization/SDKPrimitivesProvider.h" -#include "AVSCommon/Utils/Logger/LoggerUtils.h" -#include "AVSCommon/Utils/Timing/TimerDelegate.h" +#include +#include namespace alexaClientSDK { namespace avsCommon { @@ -34,6 +32,8 @@ namespace utils { namespace timing { /** + * @brief Timer to schedule task for delayed and periodic execution. + * * A @c Timer is used to schedule a callable type to run in the future. */ class Timer { @@ -42,7 +42,7 @@ class Timer { * Value for @c start()'s @c maxCount parameter which indicates that the @c Timer should continue firing * indefinitely. */ - static const size_t FOREVER = 0; + static constexpr size_t FOREVER = 0; /// Specifies different ways to apply the period of a recurring task. enum class PeriodType { @@ -51,8 +51,8 @@ class Timer { * period type ensures task calls occur on a predictable cadence. * * @note A timer makes one task call at a time, so if a task call takes more than one period to execute, - * the subsequent calls which would have occured while the task was still executing will be skipped, and - * the next call will not occur until the next period-multiple after the original task call completes. + * the subsequent calls which would have occured while the task was still executing will be skipped, and + * the next call will not occur until the next period-multiple after the original task call completes. */ ABSOLUTE, @@ -64,163 +64,188 @@ class Timer { }; /** - * Contructs a @c Timer. + * @brief Constructs a @c Timer. * - * @param timerDelegateFactory A factory to create the @c TimerDelegate. + * @param[in] timerDelegateFactory A factory to create the @c TimerDelegate. By if the parameter is nullptr, + * implementation will use TimerDelegateFactory. */ Timer( - std::shared_ptr timerDelegateFactory = - avs::initialization::SDKPrimitivesProvider::getInstance()->getTimerDelegateFactory()); + const std::shared_ptr& timerDelegateFactory = + avs::initialization::SDKPrimitivesProvider::getInstance()->getTimerDelegateFactory()) noexcept; /** - * Destructs a @c Timer. + * @brief Destructs a @c Timer. */ - ~Timer(); + ~Timer() noexcept; /// Static member function to get FOREVER. - static size_t getForever() { + static inline size_t getForever() noexcept { return FOREVER; } - /// Static member function to get TAG. - static std::string getTag() { - return "Timer"; - } - /** + * @brief Submit a callable type for periodic execution. + * * Submits a callable type (function, lambda expression, bind expression, or another function object) to be * executed after an initial delay, and then called repeatedly on a fixed time schedule. A @c Timer instance * manages only one running @c Timer at a time; calling @c start() on an already-running @c Timer will fail. * Note that the template parameters are typically inferred from the function paramters when calling @c start(). * - * @tparam Rep A type for measuring 'ticks' in a generic @c std::chrono::duration. - * @tparam Period A type for representing the number of ticks per second in a generic @c std::chrono::duration. - * @tparam Task The type of task to execute. - * @tparam Args The argument types for the task to execute. + * @tparam Delay Initial delay type. This can be any specialization of std::chrono::duration convertible to + * std::chrono::nanoseconds. + * @tparam Period Period type. This can be any specialization of std::chrono::duration convertible to + * std::chrono::nanoseconds. + * @tparam Task The type of task to execute. + * @tparam Args The argument types for the task to execute. + * + * @param[in] delay The non-negative time to wait before making the first task call. Negative values will cause + * this function to return false. + * @param[in] period The non-negative time to wait between subsequent task calls. Negative values will cause + * this function to return false. + * @param[in] periodType The type of period to use when making subsequent task calls. + * @param[in] maxCount The desired number of times to call task. Timer::getForever() means to call forever until + * Timer::stop() is called. Note that fewer than @a maxCount calls may occur if @a periodType + * is PeriodType::ABSOLUTE and the task runtime exceeds @a period. + * @param[in] task A callable type representing a task. + * @param[in] args The arguments to call the task with. * - * @param delay The non-negative time to wait before making the first task call. Negative values will cause this - * function to return false. - * @param period The non-negative time to wait between subsequent task calls. Negative values will cause this - * function to return false. - * @param periodType The type of period to use when making subsequent task calls. - * @param maxCount The desired number of times to call task. @c Timer::getForever() means to call forever until - * @c stop() is called. Note that fewer than @c maxCount calls may occur if @c periodType is - * @c PeriodType::ABSOLUTE and the task runtime exceeds @c period. - * @param task A callable type representing a task. - * @param args The arguments to call the task with. * @returns @c true if the timer started, else @c false. */ - template + template bool start( - const std::chrono::duration& delay, - const std::chrono::duration& period, + Delay&& delay, + Period&& period, PeriodType periodType, size_t maxCount, - Task task, - Args&&... args); + Task&& task, + Args&&... args) noexcept; /** + * @brief Submit a callable type for periodic execution. + * * Submits a callable type (function, lambda expression, bind expression, or another function object) to be * executed repeatedly on a fixed time schedule. A @c Timer instance manages only one running @c Timer at a time; * calling @c start() on an already-running @c Timer will fail. Note that the template parameters are typically * inferred from the function paramters when calling @c start(). * - * @tparam Rep A type for measuring 'ticks' in a generic @c std::chrono::duration. - * @tparam Period A type for representing the number of ticks per second in a generic @c std::chrono::duration. - * @tparam Task The type of task to execute. - * @tparam Args The argument types for the task to execute. + * @tparam Period Period and initial delay type. This can be any specialization of std::chrono::duration + * convertible to std::chrono::nanoseconds. + * @tparam Task The type of task to execute. This must be callable with @a args parameters. + * @tparam Args The argument types for the task to execute. + * + * @param[in] period The non-negative time to wait before each @a task call. Negative values will cause this + * function to return false. + * @param[in] periodType The type of period to use when making subsequent task calls. + * @param[in] maxCount The desired number of times to call task. Timer::getForever() means to call forever until + * Timer::stop() is called. Note that fewer than @a maxCount calls may occur if @a periodType + * is PeriodType::ABSOLUTE and the task runtime exceeds @a period. + * @param[in] task A callable type representing a task. + * @param[in] args The arguments to call @a task with. * - * @param period The non-negative time to wait before each @c task call. Negative values will cause this - * function to return false. - * @param periodType The type of period to use when making subsequent task calls. - * @param maxCount The desired number of times to call task. @c Timer::getForever() means to call forever until - * @c stop() is called. Note that fewer than @c maxCount calls may occur if @c periodType is - * @c PeriodType::ABSOLUTE and the task runtime exceeds @c period. - * @param task A callable type representing a task. - * @param args The arguments to call @c task with. * @returns @c true if the timer started, else @c false. */ - template - bool start( - const std::chrono::duration& period, - PeriodType periodType, - size_t maxCount, - Task task, - Args&&... args); + template + bool start(Period&& period, PeriodType periodType, size_t maxCount, Task&& task, Args&&... args) noexcept; /** + * @brief Submit a callable type for single execution with future result. + * * Submits a callable type (function, lambda expression, bind expression, or another function object) to be * executed once, after the specified duration. A @c Timer instance manages only one running @c Timer at a time; * calling @c start() on an already-running @c Timer will fail. Note that the template parameters are typically - * inferred from the function paramters when calling @c start(). + * inferred from the function parameters when calling @c start(). * - * @tparam Rep A type for measuring 'ticks' in a generic @c std::chrono::duration. - * @tparam Period A type for representing the number of ticks per second in a generic @c std::chrono::duration. - * @tparam Task The type of task to execute. - * @tparam Args The argument types for the task to execute. + * @tparam Delay Delay type. This can be any specialization of std::chrono::duration convertible to + * std::chrono::nanoseconds. + * @tparam Task The type of task to execute. + * @tparam Args The argument types for the task to execute. * - * @param delay The non-negative time to wait before calling @c task. Negative values will cause this - * function to return an invalid future. - * @param task A callable type representing a task. - * @param args The arguments to call @c task with. - * @returns A valid @c std::future for the return value of @c task if the timer started, else an invalid - * @c std::future. Note that the promise will be broken if @c stop() is called before @c task is called. + * @param[in] delay The non-negative time to wait before calling @a task. Negative values will cause this function + * to return an invalid future. + * @param[in] task A callable type representing a task. + * @param[in] args The arguments to call @c task with. + * + * @returns A valid @c std::future for the return value of @c task if the timer started, else an invalid @c + * std::future. Note that the promise will be broken if Timer::stop() is called before @a task is called. */ - template - auto start(const std::chrono::duration& delay, Task task, Args&&... args) - -> std::future; + template + auto start(Delay&& delay, Task&& task, Args&&... args) noexcept -> std::future; /** + * @brief Stop the timer. + * * Stops the @c Timer (if running). This will not interrupt an active call to the task, but will prevent any - * subequent calls to the task. If @c stop() is called while the task is executing, this function will block until + * subsequent calls to the task. If @c stop() is called while the task is executing, this function will block until * the task completes. * * @note In the special case that @c stop() is called from inside the task function, @c stop() will still prevent - * any subsequent calls to the task, but will *not* block as described above. + * any subsequent calls to the task, but will *not* block as described above. */ - void stop(); + void stop() noexcept; /** + * @brief Check if timer is active. + * * Reports whether the @c Timer is active. A timer is considered active if it is waiting to start a call to the * task, or if a call to the task is in progress. A timer is only considered inactive if it has not been started, * if all requested/scheduled calls to the task have completed, or after a call to @c stop(). * * @returns @c true if the @c Timer is active, else @c false. */ - bool isActive() const; + bool isActive() const noexcept; private: + /** + * Value for Timer::start()'s @a maxCount parameter which indicates that the @c Timer should fire once. + */ + static constexpr size_t ONCE = 1u; + /** * Atomically activates this @c Timer (by setting @c m_running). * * @returns @c true if the @c Timer was previously inactive, else @c false. */ - bool activate(); + bool activate() noexcept; /** * Waits for the @c Timer @c period, then calls @c task. * - * @tparam Rep A type for measuring 'ticks' in a generic @c std::chrono::duration. - * @tparam Period A type for representing the number of ticks per second in a generic @c std::chrono::duration. - * - * @param delay The non-negative time to wait before making the first @c task call. - * @param period The non-negative time to wait between subsequent @c task calls. - * @param periodType The type of period to use when making subsequent task calls. - * @param maxCount The desired number of times to call task. @c Timer::getForever() means to call forever until - * @c stop() is called. Note that fewer than @c maxCount calls may occur if @c periodType is - * @c PeriodType::ABSOLUTE and the task runtime exceeds @c period. - * @param task A callable type representing a task. + * @param[in] delayNano The non-negative time to wait before making the first @a task call. + * @param[in] periodNano The non-negative time to wait between subsequent @a task calls. + * @param[in] periodType The type of period to use when making subsequent task calls. + * @param[in] maxCount The desired number of times to call task. Timer::getForever() means to call forever until + * Timer::stop() is called. Note that fewer than @a maxCount calls may occur if @a periodType + * is PeriodType::ABSOLUTE and the task runtime exceeds @a period. + * @param[in] task Function object to call. It must be not empty. */ - template - void callTask( - std::chrono::duration delay, - std::chrono::duration period, + bool callTask( + const std::chrono::nanoseconds& delayNano, + const std::chrono::nanoseconds& periodNano, PeriodType periodType, size_t maxCount, - std::function task); + std::function&& task) noexcept; - /// The mutex to protect the @c TimerDelegate. - mutable std::mutex m_mutex; + /** + * @brief Proxy method for convering delay and period to nanoseconds. + * + * @tparam Delay Initial delay type. This can be any specialization of std::chrono::duration convertible to + * std::chrono::nanoseconds. + * @tparam Period Period type. This can be any specialization of std::chrono::duration convertible to + * std::chrono::nanoseconds. + * @param delayNano + * @param periodNano + * @param periodType + * @param maxCount + * @param task + * @return + */ + template + bool adaptTypesAndCallTask( + const Delay& delayNano, + const Period& periodNano, + PeriodType periodType, + size_t maxCount, + std::function&& task) noexcept; /// The @c TimerDelegateInterface which contains timer related logic. std::unique_ptr m_timer; @@ -229,114 +254,88 @@ class Timer { std::thread m_thread; }; -template +template bool Timer::start( - const std::chrono::duration& delay, - const std::chrono::duration& period, + Delay&& delay, + Period&& period, PeriodType periodType, size_t maxCount, - Task task, - Args&&... args) { - if (delay < std::chrono::duration::zero()) { - logger::acsdkError(logger::LogEntry(Timer::getTag(), "startFailed").d("reason", "negativeDelay")); - return false; - } - if (period < std::chrono::duration::zero()) { - logger::acsdkError(logger::LogEntry(Timer::getTag(), "startFailed").d("reason", "negativePeriod")); - return false; - } - - // Don't start if already running. - if (!activate()) { - logger::acsdkError(logger::LogEntry(Timer::getTag(), "startFailed").d("reason", "timerAlreadyActive")); - return false; - } - - // Remove arguments from the task's type by binding the arguments to the task. - using BoundTaskType = decltype(std::bind(std::forward(task), std::forward(args)...)); - auto boundTask = std::make_shared(std::bind(std::forward(task), std::forward(args)...)); - - // Remove the return type from the task by wrapping it in a lambda with no return value. - auto translatedTask = [boundTask]() { boundTask->operator()(); }; + Task&& task, + Args&&... args) noexcept { + // convert arguments to lambda + auto boundTask = std::bind(std::forward(task), std::forward(args)...); // Kick off the new timer thread. - callTask(delay, period, periodType, maxCount, translatedTask); - - return true; + return adaptTypesAndCallTask( + std::forward(delay), std::forward(period), periodType, maxCount, std::move(boundTask)); } -template -bool Timer::start( - const std::chrono::duration& period, - PeriodType periodType, - size_t maxCount, - Task task, - Args&&... args) { - return start(period, period, periodType, maxCount, std::forward(task), std::forward(args)...); +template +bool Timer::start(Period&& period, PeriodType periodType, size_t maxCount, Task&& task, Args&&... args) noexcept { + // convert arguments to lambda + auto boundTask = std::bind(std::forward(task), std::forward(args)...); + + // Kick off the new timer thread. + return adaptTypesAndCallTask( + std::forward(period), std::forward(period), periodType, maxCount, std::move(boundTask)); } -template -auto Timer::start(const std::chrono::duration& delay, Task task, Args&&... args) - -> std::future { - // Don't start if already running. - if (!activate()) { - logger::acsdkError(logger::LogEntry(Timer::getTag(), "startFailed").d("reason", "timerAlreadyActive")); - using FutureType = decltype(task(args...)); - return std::future(); - } +template +auto Timer::start(Delay&& delay, Task&& task, Args&&... args) noexcept -> std::future { + // Task result value type. + using Value = decltype(task(args...)); // Remove arguments from the task's type by binding the arguments to the task. auto boundTask = std::bind(std::forward(task), std::forward(args)...); /* - * Create a std::packaged_task with the correct return type. The decltype only returns the return value of the - * boundTask. The following parentheses make it a function call with the boundTask return type. The package task - * will then return a future of the correct type. + * Create a std::packaged_task with the correct return type. The reference will be passed into execution context + * and will not be shared between threads. */ - using PackagedTaskType = std::packaged_task; - auto packagedTask = std::make_shared(boundTask); + auto packagedTask = std::make_shared>(std::move(boundTask)); + // Get future object reference for function result. + auto packagedTaskFuture = packagedTask->get_future(); // Remove the return type from the task by wrapping it in a lambda with no return value. - auto translatedTask = [packagedTask]() { packagedTask->operator()(); }; + auto translatedTask = std::bind(&std::packaged_task::operator(), std::move(packagedTask)); // Kick off the new timer thread. - static const size_t once = 1; - callTask(delay, delay, PeriodType::ABSOLUTE, once, translatedTask); + if (!adaptTypesAndCallTask( + std::forward(delay), + std::forward(delay), + PeriodType::ABSOLUTE, + ONCE, + std::move(translatedTask))) { + return std::future(); + } - return packagedTask->get_future(); + return packagedTaskFuture; } -template -void Timer::callTask( - std::chrono::duration delay, - std::chrono::duration period, +template +bool Timer::adaptTypesAndCallTask( + const Delay& delay, + const Period& period, PeriodType periodType, size_t maxCount, - std::function task) { - if (!m_timer) { - logger::acsdkError(logger::LogEntry(Timer::getTag(), "callTaskFailed").d("reason", "nullTimerDelegate")); - return; - } - - auto delegatePeriodType = sdkInterfaces::timing::TimerDelegateInterface::PeriodType::ABSOLUTE; - - switch (periodType) { - case PeriodType::ABSOLUTE: - delegatePeriodType = sdkInterfaces::timing::TimerDelegateInterface::PeriodType::ABSOLUTE; - break; - case PeriodType::RELATIVE: - delegatePeriodType = sdkInterfaces::timing::TimerDelegateInterface::PeriodType::RELATIVE; - break; - } - - m_timer->start( - std::chrono::duration_cast(delay), - std::chrono::duration_cast(period), - delegatePeriodType, - maxCount, - task); + std::function&& task) noexcept { + return callTask(delay, period, periodType, maxCount, std::move(task)); } +// Externalize templates for common parameter types. +extern template bool Timer::adaptTypesAndCallTask( + const std::chrono::milliseconds&, + const std::chrono::milliseconds&, + PeriodType, + size_t, + std::function&&) noexcept; +extern template bool Timer::adaptTypesAndCallTask( + const std::chrono::nanoseconds&, + const std::chrono::nanoseconds&, + PeriodType, + size_t, + std::function&&) noexcept; + } // namespace timing } // namespace utils } // namespace avsCommon diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Timing/TimerDelegate.h b/AVSCommon/Utils/include/AVSCommon/Utils/Timing/TimerDelegate.h index 3a98f15b36..5795d42967 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Timing/TimerDelegate.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Timing/TimerDelegate.h @@ -57,13 +57,15 @@ class TimerDelegate : public sdkInterfaces::timing::TimerDelegateInterface { * @param periodType The type of period to use when making subsequent task calls. * @param maxCount The desired number of times to call task. * @param task A callable type representing a task. + * @param moniker Moniker value to use for a new thread. */ void timerLoop( std::chrono::nanoseconds delay, std::chrono::nanoseconds period, PeriodType periodType, size_t maxCount, - std::function task); + std::function task, + std::string moniker); /// Cleanup logic which waits for the thread to join if possible. @c m_callMutex must be held before calling. void cleanupLocked(); diff --git a/shared/acsdkManufactory/include/acsdkManufactory/internal/TypeIndex.h b/AVSCommon/Utils/include/AVSCommon/Utils/TypeIndex.h similarity index 82% rename from shared/acsdkManufactory/include/acsdkManufactory/internal/TypeIndex.h rename to AVSCommon/Utils/include/AVSCommon/Utils/TypeIndex.h index 68efa7cafb..28cfcc110b 100644 --- a/shared/acsdkManufactory/include/acsdkManufactory/internal/TypeIndex.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/TypeIndex.h @@ -13,10 +13,9 @@ * permissions and limitations under the License. */ -#ifndef ACSDKMANUFACTORY_INTERNAL_TYPEINDEX_H_ -#define ACSDKMANUFACTORY_INTERNAL_TYPEINDEX_H_ +#ifndef ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_TYPEINDEX_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_TYPEINDEX_H_ -#include #include #include #include @@ -26,12 +25,9 @@ #include #include -#include "acsdkManufactory/internal/TypeTraitsHelper.h" - namespace alexaClientSDK { -namespace acsdkManufactory { -namespace internal { - +namespace avsCommon { +namespace utils { /** * TypeIndex provide a sortable and hashable identity for C++ types, much like std::type_index, * but with optional use of RTTI. @@ -101,7 +97,7 @@ struct TypeIndex { * * @param value A value that uniquely identifies a type. */ - TypeIndex(Value value); + explicit TypeIndex(Value value); /// The value used to uniquely identify a type. Value m_value; @@ -135,7 +131,8 @@ char TypeIndex::TypeSpecific::m_instance; */ template inline TypeIndex getTypeIndex() { - return TypeIndex{&TypeIndex::TypeSpecific >::m_instance}; + return TypeIndex{&TypeIndex::TypeSpecific< + typename std::remove_cv::type>::type>::m_instance}; } inline std::string TypeIndex::getName() const { @@ -172,7 +169,7 @@ template inline void logTypeIndex(const std::string& name) { avsCommon::utils::logger::acsdkInfo(avsCommon::utils::logger::LogEntry("TypeIndex", __func__) .d("name", name) - .d("TypeIndex", internal::getTypeIndex().getName())); + .d("TypeIndex", getTypeIndex().getName())); } /** @@ -180,22 +177,22 @@ inline void logTypeIndex(const std::string& name) { */ #define ACSDK_LOG_TYPE_INDEX(type) logTypeIndex(#type) -} // namespace internal -} // namespace acsdkManufactory +} // namespace utils +} // namespace avsCommon } // namespace alexaClientSDK namespace std { template <> -struct hash { - std::size_t operator()(alexaClientSDK::acsdkManufactory::internal::TypeIndex typeIndex) const; +struct hash { + std::size_t operator()(alexaClientSDK::avsCommon::utils::TypeIndex typeIndex) const; }; -inline std::size_t hash::operator()( - alexaClientSDK::acsdkManufactory::internal::TypeIndex typeIndex) const { - return hash()(typeIndex.m_value); +inline std::size_t hash::operator()( + alexaClientSDK::avsCommon::utils::TypeIndex typeIndex) const { + return hash()(typeIndex.m_value); } } // namespace std -#endif // ACSDKMANUFACTORY_INTERNAL_TYPEINDEX_H_ \ No newline at end of file +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_TYPEINDEX_H_ diff --git a/AVSCommon/Utils/privateInclude/AVSCommon/Utils/Threading/private/SharedExecutor.h b/AVSCommon/Utils/privateInclude/AVSCommon/Utils/Threading/private/SharedExecutor.h new file mode 100644 index 0000000000..d9472c4329 --- /dev/null +++ b/AVSCommon/Utils/privateInclude/AVSCommon/Utils/Threading/private/SharedExecutor.h @@ -0,0 +1,155 @@ +/* + * 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. + */ + +#ifndef ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_PRIVATEINCLUDE_AVSCOMMON_UTILS_THREADING_PRIVATE_SHAREDEXECUTOR_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_PRIVATEINCLUDE_AVSCOMMON_UTILS_THREADING_PRIVATE_SHAREDEXECUTOR_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace threading { + +/** + * @brief Shared executor implementation. + * + * This implementation is managed by std::shared_ptr. + */ +class SharedExecutor : public virtual ExecutorInterface { +public: + /** + * @brief Constructs an object. + */ + SharedExecutor() noexcept; + + /** + * @brief Destructs an Executor. + * + * This method awaits till all running tasks are completed, and drops all enqueued tasks that haven't started + * execution. + * + * @see #shutdown() + * @see #waitForSubmittedTasks() + */ + ~SharedExecutor() noexcept; + + /// @name Methods from ExecutorInterface. + /// @{ + std::error_condition execute(std::function&& function) noexcept override; + std::error_condition execute(const std::function& function) noexcept override; + /// @} + + /** + * Waits for any previously submitted tasks to complete. + */ + void waitForSubmittedTasks() noexcept; + + /// Clears the executor of outstanding tasks and refuses any additional tasks to be submitted. + void shutdown() noexcept; + + /** + * @brief Returns whether or not the executor is shutdown. + * + * @return True if Executor::shutdown() has been called. + */ + bool isShutdown() noexcept; + +private: + // Friend declaration. + friend class Executor; + + /// Alias for queue position. + using QueuePosition = Executor::QueuePosition; + + /// The queue type to use for holding tasks. + using Queue = std::deque>; + + /** + * Executes the next job in the queue. + * + * @return @c true if there's a next job; @c false if the job queue is empty. + */ + bool runNext() noexcept; + + /** + * Checks if the job queue is empty and that no job is added in the grace period determined by @c m_timeout. + * + * @return @c true if there's at least one job left in the queue; @c false if the job queue is empty. + */ + bool hasNext() noexcept; + + /** + * Returns and removes the task at the front of the queue. If there are no tasks, this call will return an empty + * function. + * + * @returns A function that represents a new task. The function will be empty if the queue has no job. + */ + std::function pop() noexcept; + + /** + * @brief Schedules a function for execution. + * + * Submits a function to be executed on an Executor thread. + * + * @param[in] function Function to execute. + * @param[in] queuePosition Position in the queue for the new task. + * + * @return Portable error code. If successful, value is zero. + */ + std::error_condition execute(std::function&& function, QueuePosition queuePosition) noexcept; + + /// Moniker for executed tasks. + /// Whenever executor runs a task, it guarantees that task's thread has this value set with @c ThreadMoniker. + const std::string m_executorMoniker; + + /// The queue of tasks + Queue m_queue; + + /// Flag to indicate if the taskThread already have an executing job. + bool m_threadRunning; + + /// A mutex to protect access to the tasks in m_queue. + std::mutex m_queueMutex; + + /// A flag for whether or not the queue is expecting more tasks. + std::atomic_bool m_shutdown; + + /// A @c PowerResource. + std::shared_ptr m_powerResource; + + /// The thread to execute tasks on. The thread must be declared last to be destructed first. + TaskThread m_taskThread; +}; + +} // namespace threading +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_PRIVATEINCLUDE_AVSCOMMON_UTILS_THREADING_PRIVATE_SHAREDEXECUTOR_H_ diff --git a/AVSCommon/Utils/src/BluetoothEventBus.cpp b/AVSCommon/Utils/src/BluetoothEventBus.cpp index 3753a2e744..811b259cca 100644 --- a/AVSCommon/Utils/src/BluetoothEventBus.cpp +++ b/AVSCommon/Utils/src/BluetoothEventBus.cpp @@ -23,7 +23,7 @@ namespace utils { namespace bluetooth { /// String to identify log entries originating from this file. -static const std::string TAG{"BluetoothEventBus"}; +#define TAG "BluetoothEventBus" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/Configuration/ConfigurationNode.cpp b/AVSCommon/Utils/src/Configuration/ConfigurationNode.cpp index 59838ecd6c..f474ec255f 100644 --- a/AVSCommon/Utils/src/Configuration/ConfigurationNode.cpp +++ b/AVSCommon/Utils/src/Configuration/ConfigurationNode.cpp @@ -28,7 +28,7 @@ namespace utils { namespace configuration { /// String to identify log entries originating from this file. -static const std::string TAG("ConfigurationNode"); +#define TAG "ConfigurationNode" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -59,6 +59,9 @@ static std::string valueToString(const Value& value) { ACSDK_ERROR(LX("valueToStringFailed").d("reason", "AcceptReturnedFalse")); return ""; } +#else +// This method is not called when debug logging is disabled, but we need it for sizeof() evaluation. +std::string valueToString(const Value& value); #endif // ACSDK_DEBUG_LOG_ENABLED /** @@ -169,10 +172,14 @@ bool ConfigurationNode::getString(const std::string& key, const char** out, cons } ConfigurationNode ConfigurationNode::operator[](const std::string& key) const { - if (!*this) { + return getChildNode(key.c_str()); +} + +ConfigurationNode ConfigurationNode::getChildNode(const char* key) const { + if (!*this || !key) { return ConfigurationNode(); } - auto it = m_object->FindMember(key.c_str()); + auto it = m_object->FindMember(key); if (m_object->MemberEnd() == it || !it->value.IsObject()) { return ConfigurationNode(); } @@ -256,7 +263,7 @@ ConfigurationNode ConfigurationNode::operator[](const std::size_t index) const { return ConfigurationNode(); } const rapidjson::Value& objectRef = *m_object; - return ConfigurationNode(&objectRef[index]); + return ConfigurationNode(&objectRef[static_cast(index)]); } } // namespace configuration diff --git a/AVSCommon/Utils/src/DeviceInfo.cpp b/AVSCommon/Utils/src/DeviceInfo.cpp index 60f052ef03..7c77f5e456 100644 --- a/AVSCommon/Utils/src/DeviceInfo.cpp +++ b/AVSCommon/Utils/src/DeviceInfo.cpp @@ -23,7 +23,7 @@ namespace avsCommon { namespace utils { /// String to identify log entries originating from this file. -static const std::string TAG("DeviceInfo"); +#define TAG "DeviceInfo" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/Executor.cpp b/AVSCommon/Utils/src/Executor.cpp deleted file mode 100644 index ac14bfc82e..0000000000 --- a/AVSCommon/Utils/src/Executor.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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 "AVSCommon/Utils/Memory/Memory.h" -#include "AVSCommon/Utils/Power/PowerMonitor.h" -#include "AVSCommon/Utils/Threading/Executor.h" - -namespace alexaClientSDK { -namespace avsCommon { -namespace utils { -namespace threading { - -/// An id for identifying instances. -static std::atomic g_id{0}; - -Executor::~Executor() { - shutdown(); -} - -Executor::Executor(const std::chrono::milliseconds& delayExit) : - m_threadRunning{false}, - m_timeout{delayExit}, - m_shutdown{false}, - m_id{g_id++} { - m_powerResource = power::PowerMonitor::getInstance()->createLocalPowerResource("Executor:" + std::to_string(m_id)); -} - -void Executor::waitForSubmittedTasks() { - std::unique_lock lock{m_queueMutex}; - if (m_threadRunning) { - // wait for thread to exit. - std::promise flushedPromise; - auto flushedFuture = flushedPromise.get_future(); - m_queue.emplace_back([&flushedPromise]() { flushedPromise.set_value(); }); - - lock.unlock(); - m_delayedCondition.notify_one(); - flushedFuture.wait(); - } -} - -std::function Executor::pop() { - std::lock_guard lock{m_queueMutex}; - if (!m_queue.empty()) { - auto task = std::move(m_queue.front()); - m_queue.pop_front(); - return task; - } - return std::function(); -} - -bool Executor::hasNext() { - std::unique_lock lock{m_queueMutex}; - - m_delayedCondition.wait_for(lock, m_timeout, [this] { return !m_queue.empty() || m_shutdown; }); - m_threadRunning = !m_queue.empty(); - return m_threadRunning; -} - -bool Executor::runNext() { - auto task = pop(); - if (task) { - task(); - } - - if (m_powerResource) { - m_powerResource->release(); - } - - // It is acceptable that we enter LPM before - // the wait. TaskThread will still wait the intended m_timeout relative to the system - // not in LPM. - - return hasNext(); -} - -void Executor::shutdown() { - std::unique_lock lock{m_queueMutex}; - m_queue.clear(); - m_shutdown = true; - lock.unlock(); - waitForSubmittedTasks(); -} - -bool Executor::isShutdown() { - return m_shutdown; -} - -} // namespace threading -} // namespace utils -} // namespace avsCommon -} // namespace alexaClientSDK diff --git a/AVSCommon/Utils/src/FileSystem/FileSystemUtilsLinux.cpp b/AVSCommon/Utils/src/FileSystem/FileSystemUtilsLinux.cpp index 1e53d1dfe4..4f93ed5395 100644 --- a/AVSCommon/Utils/src/FileSystem/FileSystemUtilsLinux.cpp +++ b/AVSCommon/Utils/src/FileSystem/FileSystemUtilsLinux.cpp @@ -37,7 +37,7 @@ namespace utils { namespace filesystem { /// String to identify log entries originating from this file. -static const std::string TAG("FileSystemUtils"); +#define TAG "FileSystemUtils" /// Create a LogEntry using this file's TAG and the specified event std::string. #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) @@ -67,6 +67,7 @@ class UmaskLocker { std::mutex UmaskLocker::s_umaskMutex; +#ifdef ACSDK_LOG_ENABLED static std::string getStrError(int error) { static const size_t BUFFER_SIZE = 255; char buffer[BUFFER_SIZE + 1]{}; @@ -74,6 +75,10 @@ static std::string getStrError(int error) { (void)ignore; // unused since the type can differ depending on the gnu or posix return buffer; } +#else +// Declaref function so it can be used in sizeof() expression when logging is disabled. +std::string getStrError(int); +#endif bool changePermissions(const std::string& path, Permissions perms) { if (chmod(path.c_str(), perms) != 0) { diff --git a/AVSCommon/Utils/src/FileSystem/FileSystemUtilsWindows.cpp b/AVSCommon/Utils/src/FileSystem/FileSystemUtilsWindows.cpp index 2047b834c8..49cc9f12e7 100644 --- a/AVSCommon/Utils/src/FileSystem/FileSystemUtilsWindows.cpp +++ b/AVSCommon/Utils/src/FileSystem/FileSystemUtilsWindows.cpp @@ -32,7 +32,7 @@ namespace utils { namespace filesystem { /// String to identify log entries originating from this file. -static const std::string TAG("FileSystemUtils"); +#define TAG "FileSystemUtils" /// Create a LogEntry using this file's TAG and the specified event std::string. #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) @@ -47,6 +47,7 @@ const Permissions DEFAULT_FILE_PERMISSIONS = OWNER_READ | OWNER_WRITE | GROUP_RE #define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG) #endif +#ifdef ACSDK_LOG_ENABLED static std::string getStrError(int error) { static const size_t BUFFER_SIZE = 255; char buffer[BUFFER_SIZE + 1]{}; @@ -54,6 +55,10 @@ static std::string getStrError(int error) { (void)ignore; // unused since the type can differ depending on the gnu or posix return buffer; } +#else +// Declaref function so it can be used in sizeof() expression when logging is disabled. +std::string getStrError(int); +#endif static std::string getBackslashPath(std::string path) { replace(path.begin(), path.end(), '/', '\\'); @@ -252,7 +257,7 @@ static bool removeDirectory(const std::string& path) { } while (FindNextFileA(handle, &data)); FindClose(handle); - result &= RemoveDirectoryA(path.c_str()); + result &= static_cast(RemoveDirectoryA(path.c_str())); return result; } diff --git a/AVSCommon/Utils/src/FileUtils.cpp b/AVSCommon/Utils/src/FileUtils.cpp index a3aee25376..8fb62965d2 100644 --- a/AVSCommon/Utils/src/FileUtils.cpp +++ b/AVSCommon/Utils/src/FileUtils.cpp @@ -26,7 +26,7 @@ namespace utils { namespace file { /// String to identify log entries originating from this file. -static const std::string TAG("FileUtils"); +#define TAG "FileUtils" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/FormattedAudioStreamAdapter.cpp b/AVSCommon/Utils/src/FormattedAudioStreamAdapter.cpp index 79a60652cf..54c3428e6b 100644 --- a/AVSCommon/Utils/src/FormattedAudioStreamAdapter.cpp +++ b/AVSCommon/Utils/src/FormattedAudioStreamAdapter.cpp @@ -22,7 +22,7 @@ namespace utils { namespace bluetooth { /// String to identify log entries originating from this file. -static const std::string TAG("FormattedAudioStreamAdapter"); +#define TAG "FormattedAudioStreamAdapter" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/HTTP2/HTTP2MimeRequestEncoder.cpp b/AVSCommon/Utils/src/HTTP2/HTTP2MimeRequestEncoder.cpp index 632fd37de3..fa4b77b483 100644 --- a/AVSCommon/Utils/src/HTTP2/HTTP2MimeRequestEncoder.cpp +++ b/AVSCommon/Utils/src/HTTP2/HTTP2MimeRequestEncoder.cpp @@ -25,7 +25,7 @@ #include "AVSCommon/Utils/HTTP2/HTTP2MimeRequestSourceInterface.h" /// String to identify log entries originating from this file. -static const std::string TAG("HTTP2MimeRequestEncoder"); +#define TAG "HTTP2MimeRequestEncoder" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/HTTP2/HTTP2MimeResponseDecoder.cpp b/AVSCommon/Utils/src/HTTP2/HTTP2MimeResponseDecoder.cpp index a6598cb021..a5fe08722f 100644 --- a/AVSCommon/Utils/src/HTTP2/HTTP2MimeResponseDecoder.cpp +++ b/AVSCommon/Utils/src/HTTP2/HTTP2MimeResponseDecoder.cpp @@ -27,7 +27,7 @@ namespace http2 { using namespace avsCommon::utils::http; /// String to identify log entries originating from this file. -static const std::string TAG("HTTP2MimeResponseDecoder"); +#define TAG "HTTP2MimeResponseDecoder" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/ID3Tags/ID3v2Tags.cpp b/AVSCommon/Utils/src/ID3Tags/ID3v2Tags.cpp index 46ee629fac..6a874b7a6c 100644 --- a/AVSCommon/Utils/src/ID3Tags/ID3v2Tags.cpp +++ b/AVSCommon/Utils/src/ID3Tags/ID3v2Tags.cpp @@ -19,7 +19,7 @@ #include "AVSCommon/Utils/Logger/Logger.h" /// String to identify log entries originating from this file. -static const std::string TAG("ID3v2Tags"); +#define TAG "ID3v2Tags" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/JSON/JSONGenerator.cpp b/AVSCommon/Utils/src/JSON/JSONGenerator.cpp index 8a9a2cc5c4..6d0ac1384a 100644 --- a/AVSCommon/Utils/src/JSON/JSONGenerator.cpp +++ b/AVSCommon/Utils/src/JSON/JSONGenerator.cpp @@ -21,7 +21,7 @@ #include "AVSCommon/Utils/JSON/JSONUtils.h" /// String to identify log entries originating from this file. -static const std::string TAG("JSONGenerator"); +#define TAG "JSONGenerator" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -40,7 +40,8 @@ JsonGenerator::JsonGenerator() : m_buffer{}, m_writer{m_buffer} { } bool JsonGenerator::startObject(const std::string& key) { - return checkWriter() && m_writer.Key(key.c_str(), key.length()) && m_writer.StartObject(); + auto keyLength = static_cast(key.length()); + return checkWriter() && m_writer.Key(key.c_str(), keyLength) && m_writer.StartObject(); } bool JsonGenerator::finishObject() { @@ -48,35 +49,43 @@ bool JsonGenerator::finishObject() { } bool JsonGenerator::addMember(const std::string& key, const std::string& value) { - return checkWriter() && m_writer.Key(key.c_str(), key.length()) && m_writer.String(value); + auto keyLength = static_cast(key.length()); + return checkWriter() && m_writer.Key(key.c_str(), keyLength) && m_writer.String(value); } bool JsonGenerator::addMember(const std::string& key, uint64_t value) { - return checkWriter() && m_writer.Key(key.c_str(), key.length()) && m_writer.Uint64(value); + auto keyLength = static_cast(key.length()); + return checkWriter() && m_writer.Key(key.c_str(), keyLength) && m_writer.Uint64(value); } bool JsonGenerator::addMember(const std::string& key, unsigned int value) { - return checkWriter() && m_writer.Key(key.c_str(), key.length()) && m_writer.Uint(value); + auto keyLength = static_cast(key.length()); + return checkWriter() && m_writer.Key(key.c_str(), keyLength) && m_writer.Uint(value); } bool JsonGenerator::addMember(const std::string& key, int64_t value) { - return checkWriter() && m_writer.Key(key.c_str(), key.length()) && m_writer.Int64(value); + auto keyLength = static_cast(key.length()); + return checkWriter() && m_writer.Key(key.c_str(), keyLength) && m_writer.Int64(value); } bool JsonGenerator::addMember(const std::string& key, int value) { - return checkWriter() && m_writer.Key(key.c_str(), key.length()) && m_writer.Int(value); + auto keyLength = static_cast(key.length()); + return checkWriter() && m_writer.Key(key.c_str(), keyLength) && m_writer.Int(value); } bool JsonGenerator::addMember(const std::string& key, bool value) { - return checkWriter() && m_writer.Key(key.c_str(), key.length()) && m_writer.Bool(value); + auto keyLength = static_cast(key.length()); + return checkWriter() && m_writer.Key(key.c_str(), keyLength) && m_writer.Bool(value); } bool JsonGenerator::addMember(const std::string& key, const char* value) { - return value && checkWriter() && m_writer.Key(key.c_str(), key.length()) && m_writer.String(value); + auto keyLength = static_cast(key.length()); + return value && checkWriter() && m_writer.Key(key.c_str(), keyLength) && m_writer.String(value); } bool JsonGenerator::addMember(const std::string& key, double value) { - return checkWriter() && m_writer.Key(key.c_str(), key.length()) && m_writer.Double(value); + auto keyLength = static_cast(key.length()); + return checkWriter() && m_writer.Key(key.c_str(), keyLength) && m_writer.Double(value); } bool JsonGenerator::addRawJsonMember(const std::string& key, const std::string& json, bool validate) { @@ -88,12 +97,14 @@ bool JsonGenerator::addRawJsonMember(const std::string& key, const std::string& return false; } } - return checkWriter() && m_writer.Key(key.c_str(), key.length()) && + auto keyLength = static_cast(key.length()); + return checkWriter() && m_writer.Key(key.c_str(), keyLength) && m_writer.RawValue(json.c_str(), json.length(), rapidjson::kStringType); } bool JsonGenerator::startArray(const std::string& key) { - return checkWriter() && m_writer.Key(key.c_str(), key.length()) && m_writer.StartArray(); + auto keyLength = static_cast(key.length()); + return checkWriter() && m_writer.Key(key.c_str(), keyLength) && m_writer.StartArray(); } bool JsonGenerator::finishArray() { diff --git a/AVSCommon/Utils/src/JSON/JSONUtils.cpp b/AVSCommon/Utils/src/JSON/JSONUtils.cpp index 3cf8edb1d0..327d0bab09 100644 --- a/AVSCommon/Utils/src/JSON/JSONUtils.cpp +++ b/AVSCommon/Utils/src/JSON/JSONUtils.cpp @@ -28,7 +28,7 @@ namespace json { namespace jsonUtils { /// String to identify log entries originating from this file. -static const std::string TAG("JsonUtils"); +#define TAG "JsonUtils" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/LibcurlUtils/CallbackData.cpp b/AVSCommon/Utils/src/LibcurlUtils/CallbackData.cpp index a0eb9d9a7c..7d470b61b4 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/CallbackData.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/CallbackData.cpp @@ -29,7 +29,7 @@ namespace libcurlUtils { using namespace alexaClientSDK::avsCommon::utils; /// String to identify log entries originating from this file. -static const std::string TAG("CallbackData"); +#define TAG "CallbackData" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/LibcurlUtils/CurlEasyHandleWrapper.cpp b/AVSCommon/Utils/src/LibcurlUtils/CurlEasyHandleWrapper.cpp index e72244aedd..b508f8e77b 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/CurlEasyHandleWrapper.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/CurlEasyHandleWrapper.cpp @@ -35,7 +35,7 @@ namespace libcurlUtils { using namespace avsCommon::utils::http; /// String to identify log entries originating from this file. -static const std::string TAG("CurlEasyHandleWrapper"); +#define TAG "CurlEasyHandleWrapper" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/LibcurlUtils/CurlMultiHandleWrapper.cpp b/AVSCommon/Utils/src/LibcurlUtils/CurlMultiHandleWrapper.cpp index 0e73398813..32eb291c76 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/CurlMultiHandleWrapper.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/CurlMultiHandleWrapper.cpp @@ -22,7 +22,7 @@ namespace utils { namespace libcurlUtils { /// String to identify log entries originating from this file. -static const std::string TAG("CurlMultiHandleWrapper"); +#define TAG "CurlMultiHandleWrapper" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -91,14 +91,32 @@ CURLMcode CurlMultiHandleWrapper::perform(int* runningHandles) { return result; } -CURLMcode CurlMultiHandleWrapper::wait(std::chrono::milliseconds timeout, int* countHandlesUpdated) { - auto result = curl_multi_wait(m_handle, NULL, 0, timeout.count(), countHandlesUpdated); +CURLMcode CurlMultiHandleWrapper::poll(std::chrono::milliseconds timeout, int* countHandlesUpdated) { +#if CURL_AT_LEAST_VERSION(7, 68, 0) + auto result = curl_multi_poll(m_handle, nullptr, 0, timeout.count(), countHandlesUpdated); +#else + auto result = curl_multi_wait(m_handle, nullptr, 0, static_cast(timeout.count()), countHandlesUpdated); +#endif if (result != CURLM_OK) { ACSDK_ERROR(LX("curlMultiWaitFailed").d("error", curl_multi_strerror(result))); } return result; } +bool CurlMultiHandleWrapper::wakeup() { + bool returnVal = true; +#if CURL_AT_LEAST_VERSION(7, 68, 0) + auto result = curl_multi_wakeup(m_handle); + if (result != CURLM_OK) { + ACSDK_ERROR(LX("curlMultiWakeupFailed").d("error", curl_multi_strerror(result))); + returnVal = false; + } +#else + // no-op +#endif + return returnVal; +} + CURLMsg* CurlMultiHandleWrapper::infoRead(int* messagesInQueue) { return curl_multi_info_read(m_handle, messagesInQueue); } diff --git a/AVSCommon/Utils/src/LibcurlUtils/HTTPContentFetcherFactory.cpp b/AVSCommon/Utils/src/LibcurlUtils/HTTPContentFetcherFactory.cpp index 0baa0dae9a..bf77fb389a 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/HTTPContentFetcherFactory.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/HTTPContentFetcherFactory.cpp @@ -24,7 +24,7 @@ namespace utils { namespace libcurlUtils { /// String to identify log entries originating from this file. -static const std::string TAG("HTTPContentFetcherFactory"); +#define TAG "HTTPContentFetcherFactory" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/LibcurlUtils/HttpPost.cpp b/AVSCommon/Utils/src/LibcurlUtils/HttpPost.cpp index 42713cb77a..1b2610d982 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/HttpPost.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/HttpPost.cpp @@ -28,7 +28,7 @@ namespace libcurlUtils { using namespace alexaClientSDK::avsCommon::utils; /// String to identify log entries originating from this file. -static const std::string TAG("HttpPost"); +#define TAG "HttpPost" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/LibcurlUtils/HttpPut.cpp b/AVSCommon/Utils/src/LibcurlUtils/HttpPut.cpp index 8690615f36..84d062658e 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/HttpPut.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/HttpPut.cpp @@ -31,7 +31,7 @@ namespace libcurlUtils { using namespace alexaClientSDK::avsCommon::utils; /// String to identify log entries originating from this file. -static const std::string TAG("HttpPut"); +#define TAG "HttpPut" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/LibcurlUtils/LibCurlHttpContentFetcher.cpp b/AVSCommon/Utils/src/LibcurlUtils/LibCurlHttpContentFetcher.cpp index db3955e792..30a370131e 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/LibCurlHttpContentFetcher.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/LibCurlHttpContentFetcher.cpp @@ -31,10 +31,9 @@ namespace libcurlUtils { using namespace avsCommon::sdkInterfaces; using namespace avsCommon::utils::http; -using namespace avsCommon::utils::libcurlUtils; /// String to identify log entries originating from this file. -static const std::string TAG("LibCurlHttpContentFetcher"); +#define TAG "LibCurlHttpContentFetcher" /** * The timeout for a blocking write call to an @c AttachmentWriter. This value may be increased to decrease wakeups but @@ -412,7 +411,7 @@ std::unique_ptr LibCurlHttpContentFetcher::getCon } int numTransfersUpdated = 0; - result = curlMultiHandle->wait(WAIT_FOR_ACTIVITY_TIMEOUT, &numTransfersUpdated); + result = curlMultiHandle->poll(WAIT_FOR_ACTIVITY_TIMEOUT, &numTransfersUpdated); if (result != CURLM_OK) { ACSDK_ERROR(LX("getContentFailed") .d("reason", "multiWaitFailed") @@ -470,7 +469,7 @@ std::unique_ptr LibCurlHttpContentFetcher::getCon } int numTransfersUpdated = 0; - result = curlMultiHandle->wait(WAIT_FOR_ACTIVITY_TIMEOUT, &numTransfersUpdated); + result = curlMultiHandle->poll(WAIT_FOR_ACTIVITY_TIMEOUT, &numTransfersUpdated); if (result != CURLM_OK) { ACSDK_ERROR(LX("getContentFailed") .d("reason", "multiWaitFailed") diff --git a/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2Connection.cpp b/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2Connection.cpp index 2e473873bd..e4702ba5c5 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2Connection.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2Connection.cpp @@ -26,10 +26,9 @@ namespace utils { namespace libcurlUtils { using namespace avsCommon::utils::http2; -using namespace avsCommon::utils::libcurlUtils; /// String to identify log entries originating from this file. -static const std::string TAG("LibcurlHTTP2Connection"); +#define TAG "LibcurlHTTP2Connection" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -167,6 +166,10 @@ void LibcurlHTTP2Connection::setIsStopping() { std::lock_guard lock(m_mutex); m_isStopping = true; m_cv.notify_one(); + auto multiCurlHandle = m_multi; + if (multiCurlHandle) { + multiCurlHandle->wakeup(); + } } std::shared_ptr LibcurlHTTP2Connection::dequeueRequest() { @@ -246,7 +249,7 @@ void LibcurlHTTP2Connection::networkLoop() { } int numTransfersUpdated = 0; - result = m_multi->wait(multiWaitTimeout, &numTransfersUpdated); + result = m_multi->poll(multiWaitTimeout, &numTransfersUpdated); if (result != CURLM_OK) { ACSDK_ERROR( LX_P("networkLoopStopping").d("reason", "multiWaitFailed").d("error", curl_multi_strerror(result))); @@ -329,6 +332,10 @@ bool LibcurlHTTP2Connection::addStream(std::shared_ptr stre } m_requestQueue.push_back(std::move(stream)); m_cv.notify_one(); + auto multiCurlHandle = m_multi; + if (multiCurlHandle) { + multiCurlHandle->wakeup(); + } return true; } @@ -351,7 +358,7 @@ void LibcurlHTTP2Connection::cleanupFinishedStreams() { .d("streamId", it->second->getId()) .d("result", curl_easy_strerror(message->data.result)) .d("CURLcode", message->data.result)); - releaseStream(*(it->second)); + releaseStream(it); } else { ACSDK_ERROR( LX_P("cleanupFinishedStreamError").d("reason", "streamNotFound").d("handle", message->easy_handle)); @@ -363,13 +370,15 @@ void LibcurlHTTP2Connection::cleanupFinishedStreams() { void LibcurlHTTP2Connection::cleanupCancelledAndStalledStreams() { auto it = m_activeStreams.begin(); while (it != m_activeStreams.end()) { - auto stream = (it++)->second; - if (stream->isCancelled()) { - cancelActiveStream(*stream); - } else if (stream->hasProgressTimedOut()) { + auto stream = it->second; + if (stream->hasProgressTimedOut()) { ACSDK_WARN(LX_P("streamProgressTimedOut").d("streamId", stream->getId())); stream->reportCompletion(HTTP2ResponseFinishedStatus::TIMEOUT); - releaseStream(*stream); + releaseStream(it); + } else if (stream->isCancelled()) { + cancelActiveStream(it); + } else { + it++; } } } @@ -395,16 +404,17 @@ void LibcurlHTTP2Connection::unPauseActiveStreams() { } } -bool LibcurlHTTP2Connection::cancelActiveStream(LibcurlHTTP2Request& stream) { - ACSDK_INFO(LX_P("cancelActiveStream").d("streamId", stream.getId())); - stream.reportCompletion(HTTP2ResponseFinishedStatus::CANCELLED); - return releaseStream(stream); +bool LibcurlHTTP2Connection::cancelActiveStream(ActiveStreamMap::iterator& iterator) { + auto stream = iterator->second; + ACSDK_INFO(LX_P("cancelActiveStream").d("streamId", stream->getId())); + stream->reportCompletion(HTTP2ResponseFinishedStatus::CANCELLED); + return releaseStream(iterator); } void LibcurlHTTP2Connection::cancelActiveStreams() { auto it = m_activeStreams.begin(); while (it != m_activeStreams.end()) { - cancelActiveStream(*(it++)->second); + cancelActiveStream(it); } } @@ -427,13 +437,14 @@ void LibcurlHTTP2Connection::cancelAllStreams() { cancelActiveStreams(); } -bool LibcurlHTTP2Connection::releaseStream(LibcurlHTTP2Request& stream) { - auto handle = stream.getCurlHandle(); - ACSDK_DEBUG9(LX_P("releaseStream").d("streamId", stream.getId())); +bool LibcurlHTTP2Connection::releaseStream(ActiveStreamMap::iterator& iterator) { + auto stream = iterator->second; + auto handle = stream->getCurlHandle(); + ACSDK_DEBUG9(LX_P("releaseStream").d("streamId", stream->getId())); auto result = m_multi->removeHandle(handle); - m_activeStreams.erase(handle); + iterator = m_activeStreams.erase(iterator); if (result != CURLM_OK) { - ACSDK_ERROR(LX_P("releaseStreamFailed").d("reason", "removeHandleFailed").d("streamId", stream.getId())); + ACSDK_ERROR(LX_P("releaseStreamFailed").d("reason", "removeHandleFailed").d("streamId", stream->getId())); return false; } return true; diff --git a/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2ConnectionFactory.cpp b/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2ConnectionFactory.cpp index ca262ee74b..9bbd9c26ec 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2ConnectionFactory.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2ConnectionFactory.cpp @@ -24,7 +24,7 @@ namespace utils { namespace libcurlUtils { /// String to identify log entries originating from this file. -static const std::string TAG("LibcurlHTTP2ConnectionFactory"); +#define TAG "LibcurlHTTP2ConnectionFactory" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2Request.cpp b/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2Request.cpp index 5bdafb015e..ebba4d2012 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2Request.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2Request.cpp @@ -28,7 +28,7 @@ using namespace std::chrono; static constexpr long INVALID_RESPONSE_CODE = -1L; /// String to identify log entries originating from this file. -static const std::string TAG("LibcurlHTTP2Request"); +#define TAG "LibcurlHTTP2Request" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/LibcurlUtils/LibcurlUtils.cpp b/AVSCommon/Utils/src/LibcurlUtils/LibcurlUtils.cpp index 3bedd403fd..497d7db9d3 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/LibcurlUtils.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/LibcurlUtils.cpp @@ -28,7 +28,7 @@ namespace utils { namespace libcurlUtils { /// String to identify log entries originating from this file. -static const std::string TAG("LibcurlUtils"); +#define TAG "LibcurlUtils" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/Logger/LogEntry.cpp b/AVSCommon/Utils/src/Logger/LogEntry.cpp index 593d687171..5cde811583 100644 --- a/AVSCommon/Utils/src/Logger/LogEntry.cpp +++ b/AVSCommon/Utils/src/Logger/LogEntry.cpp @@ -24,44 +24,53 @@ namespace utils { namespace logger { /// List of characters we need to escape. -static const char* RESERVED_METADATA_CHARS = R"(\,=:)"; +static constexpr auto RESERVED_METADATA_CHARS = R"(\,=:)"; /// Escape sequence for '%'. -static const std::string ESCAPED_METADATA_ESCAPE = R"(\\)"; +static constexpr auto ESCAPED_METADATA_ESCAPE = R"(\\)"; /// Escape sequence for ','. -static const std::string ESCAPED_PAIR_SEPARATOR = R"(\,)"; +static constexpr auto ESCAPED_PAIR_SEPARATOR = R"(\,)"; /// Escape sequence for ':'. -static const std::string ESCAPED_SECTION_SEPARATOR = R"(\:)"; +static constexpr auto ESCAPED_SECTION_SEPARATOR = R"(\:)"; /// Escape sequence for '='. -static const std::string ESCAPED_KEY_VALUE_SEPARATOR = R"(\=)"; +static constexpr auto ESCAPED_KEY_VALUE_SEPARATOR = R"(\=)"; /// Reserved in metadata sequences for escaping other reserved values. -static const char METADATA_ESCAPE = '\\'; +static constexpr char METADATA_ESCAPE = '\\'; /// Reserved in metadata sequences to separate key,value pairs. -static const char PAIR_SEPARATOR = ','; +static constexpr char PAIR_SEPARATOR = ','; /// Reserved in metadata sequences to separate them from a preceding event and an optional terminal message. -static const char SECTION_SEPARATOR = ':'; +static constexpr char SECTION_SEPARATOR = ':'; /// String for boolean TRUE -static const std::string BOOL_TRUE = "true"; +static constexpr auto BOOL_TRUE = "true"; /// String for boolean FALSE -static const std::string BOOL_FALSE = "false"; +static constexpr auto BOOL_FALSE = "false"; -LogEntry::LogEntry(const std::string& source, const char* event) : m_hasMetadata(false) { - m_stream << source << SECTION_SEPARATOR; +/// Array of strings for obfuscation. +/// @see LogEntry::obfuscatePrivateData() +static constexpr const char* const DENYLIST[] = {"ssid"}; + +LogEntry::LogEntry(const char* source, const char* event) : m_hasMetadata(false) { + if (source) { + m_stream << source; + } + m_stream << SECTION_SEPARATOR; if (event) { m_stream << event; } } -LogEntry::LogEntry(const std::string& source, const std::string& event) : m_hasMetadata(false) { - m_stream << source << SECTION_SEPARATOR << event; +LogEntry::LogEntry(const std::string& source, const char* event) : LogEntry(source.c_str(), event) { +} + +LogEntry::LogEntry(const std::string& source, const std::string& event) : LogEntry(source.c_str(), event.c_str()) { } LogEntry& LogEntry::d(const char* key, const char* value) { @@ -153,6 +162,39 @@ void LogEntry::appendEscapedString(const char* in) { } } +LogEntry& LogEntry::obfuscatePrivateData(const char* key, const std::string& value) { + // if value contains any private label, obfuscate the section after the label + // since it can (but shouldn't) contain multiple, obfuscate from the earliest one found onward + auto firstPosition = value.length(); + + const size_t count = sizeof(DENYLIST) / sizeof(DENYLIST[0]); + for (size_t index = 0; index < count; ++index) { + const auto privateLabel = DENYLIST[index]; + const auto privateLabelLen = std::strlen(privateLabel); + auto it = std::search( + value.begin(), + value.end(), + privateLabel, + privateLabel + privateLabelLen, + [](char valueChar, char denyListChar) { return std::tolower(valueChar) == std::tolower(denyListChar); }); + if (it != value.end()) { + // capture the least value + auto thisPosition = std::distance(value.begin(), it) + privateLabelLen; + if (thisPosition < firstPosition) { + firstPosition = thisPosition; + } + } + } + + if (firstPosition <= value.length()) { + // hash everything after the label itself + auto labelPart = value.substr(0, firstPosition); + auto obfuscatedPart = std::to_string(std::hash{}(value.substr(firstPosition))); + return d(key, labelPart + obfuscatedPart); + } + return d(key, value); +} + } // namespace logger } // namespace utils } // namespace avsCommon diff --git a/AVSCommon/Utils/src/Logger/LogEntryBuffer.cpp b/AVSCommon/Utils/src/Logger/LogEntryBuffer.cpp index 28c442d870..4932741cd2 100644 --- a/AVSCommon/Utils/src/Logger/LogEntryBuffer.cpp +++ b/AVSCommon/Utils/src/Logger/LogEntryBuffer.cpp @@ -50,7 +50,7 @@ LogEntryBuffer::int_type LogEntryBuffer::overflow(int_type ch) { setg(newBase, gptr() + delta, newEnd); m_base = newBase; - *pptr() = ch; + *pptr() = static_cast(ch); pbump(1); return ch; } diff --git a/AVSCommon/Utils/src/Logger/Logger.cpp b/AVSCommon/Utils/src/Logger/Logger.cpp index 77097fd1b9..b5c5c4eb3f 100644 --- a/AVSCommon/Utils/src/Logger/Logger.cpp +++ b/AVSCommon/Utils/src/Logger/Logger.cpp @@ -24,11 +24,13 @@ namespace utils { namespace logger { /// Configuration key for root level "logger" object. -static const std::string CONFIG_KEY_LOGGER = "logger"; +static constexpr auto CONFIG_KEY_LOGGER = "logger"; /// Configuration key for "logLevel" values under "logger" and other per-module objects. -static const std::string CONFIG_KEY_LOG_LEVEL = "logLevel"; +static constexpr auto CONFIG_KEY_LOG_LEVEL = "logLevel"; +/// Predefined thread id for messages that are logged when application exits. +/// @see Logger::logAtExit() static constexpr auto AT_EXIT_THREAD_ID = "0"; Logger::Logger(Level level) : m_level{level} { @@ -42,7 +44,7 @@ void Logger::log(Level level, const LogEntry& entry) { void Logger::init(const configuration::ConfigurationNode configuration) { if (!initLogLevel(configuration)) { - initLogLevel(configuration::ConfigurationNode::getRoot()[CONFIG_KEY_LOGGER]); + initLogLevel(configuration::ConfigurationNode::getRoot()[std::string{CONFIG_KEY_LOGGER}]); } } diff --git a/AVSCommon/Utils/src/Logger/LoggerSinkManager.cpp b/AVSCommon/Utils/src/Logger/LoggerSinkManager.cpp index c4f6767384..7271f35fee 100644 --- a/AVSCommon/Utils/src/Logger/LoggerSinkManager.cpp +++ b/AVSCommon/Utils/src/Logger/LoggerSinkManager.cpp @@ -15,6 +15,7 @@ #include +#include "AVSCommon/Utils/Logger/ConsoleLogger.h" #include "AVSCommon/Utils/Logger/LoggerSinkManager.h" namespace alexaClientSDK { @@ -85,7 +86,7 @@ void LoggerSinkManager::initialize(const std::shared_ptr& sink) { } } -LoggerSinkManager::LoggerSinkManager() : m_sink{ACSDK_GET_SINK_LOGGER()}, m_level(Level::UNKNOWN) { +LoggerSinkManager::LoggerSinkManager() : m_sink{getConsoleLogger()}, m_level(Level::UNKNOWN) { } } // namespace logger diff --git a/AVSCommon/Utils/src/Logger/LoggerUtils.cpp b/AVSCommon/Utils/src/Logger/LoggerUtils.cpp index b887a05468..4dce08379f 100644 --- a/AVSCommon/Utils/src/Logger/LoggerUtils.cpp +++ b/AVSCommon/Utils/src/Logger/LoggerUtils.cpp @@ -13,6 +13,7 @@ * permissions and limitations under the License. */ +#include #include #include #include diff --git a/AVSCommon/Utils/src/Logger/ModuleLogger.cpp b/AVSCommon/Utils/src/Logger/ModuleLogger.cpp index 7b7222a592..92c90c12d0 100644 --- a/AVSCommon/Utils/src/Logger/ModuleLogger.cpp +++ b/AVSCommon/Utils/src/Logger/ModuleLogger.cpp @@ -51,12 +51,10 @@ void ModuleLogger::onSinkChanged(const std::shared_ptr& logger) { } void ModuleLogger::updateLogLevel() { - if (Level::UNKNOWN == m_sinkLogLevel) { + if (Level::UNKNOWN != m_moduleLogLevel) { Logger::setLevel(m_moduleLogLevel); - } else if (Level::UNKNOWN == m_moduleLogLevel) { - Logger::setLevel(m_sinkLogLevel); } else { - Logger::setLevel((m_sinkLogLevel > m_moduleLogLevel) ? m_sinkLogLevel : m_moduleLogLevel); + Logger::setLevel(m_sinkLogLevel); } } diff --git a/AVSCommon/Utils/src/Logger/ThreadMoniker.cpp b/AVSCommon/Utils/src/Logger/ThreadMoniker.cpp index 363ae08bd9..572799d3be 100644 --- a/AVSCommon/Utils/src/Logger/ThreadMoniker.cpp +++ b/AVSCommon/Utils/src/Logger/ThreadMoniker.cpp @@ -13,55 +13,144 @@ * permissions and limitations under the License. */ +#include #include +#include #include -#include #include + +#if defined(_WIN32) || defined(__QNX__) +#include #include #include +#endif -#include "AVSCommon/Utils/Logger/ThreadMoniker.h" +#include namespace alexaClientSDK { namespace avsCommon { namespace utils { namespace logger { -/// Counter to generate (small) unique thread monikers. -static std::atomic g_nextThreadMoniker(1); +/// Space character for moniker formatting. +static constexpr char CHAR_SPACE = ' '; -ThreadMoniker::ThreadMoniker(const std::string& moniker) : m_moniker{moniker.empty() ? generateMoniker() : moniker} { -} +/// Colon character for moniker prefix separation. +static constexpr char CHAR_COLON = ':'; -std::string ThreadMoniker::generateMoniker() { - std::ostringstream stream; - stream << std::setw(3) << std::hex << std::right << g_nextThreadMoniker++; - return stream.str(); -} +/// Size of formatted moniker in characters. +static constexpr size_t MONIKER_SIZE_CHARS = 5u; -const ThreadMoniker& ThreadMoniker::getMonikerObjectFromMap(const std::string& moniker) { - /// Map each thread to a moniker. - static std::unordered_map threadMonikers; - /// Map each moniker to a thread. - static std::unordered_map monikerThreads; - /// Lock used to synchronize access to the local maps. - static std::mutex monikerMutex; +#if defined(_WIN32) || defined(__QNX__) +/// Map each thread to a moniker. +static std::unordered_map monikerMap; +/// Lock used to synchronize access to the local maps. +static std::mutex monikerMutex; - std::lock_guard lock{monikerMutex}; +/** + * Return the moniker value reference for the current thread for OS that don't support thread local variables. + * + * @return The moniker for the @c std::this_thread. + */ +static inline std::string getMonikerValue() noexcept { auto id = std::this_thread::get_id(); - auto entry = threadMonikers.find(id); - if (entry == threadMonikers.end()) { - auto oldEntry = monikerThreads.find(moniker); - if (oldEntry != monikerThreads.end()) { - threadMonikers.erase(oldEntry->second); - } - auto& object = threadMonikers.emplace(std::make_pair(id, ThreadMoniker(moniker))).first->second; - monikerThreads[object.m_moniker] = id; - return object; + std::lock_guard lock{monikerMutex}; + auto entry = monikerMap.find(id); + if (entry == monikerMap.end()) { + entry = monikerMap.emplace(std::make_pair(id, ThreadMoniker::generateMoniker())).first; } return entry->second; } +/** + * @brief Set the moniker value reference for the current thread for OS that doesn't support thread local variables. + * + * @param moniker Moniker value to set. + */ +static inline void setMonikerValue(const std::string& moniker) noexcept { + auto id = std::this_thread::get_id(); + std::lock_guard lock{monikerMutex}; + auto entry = monikerMap.find(id); + if (entry == monikerMap.end()) { + monikerMap.emplace(std::make_pair(id, moniker)); + } else { + entry->second = moniker; + } +} +#else + +/** + * @brief Thread-local moniker value. + * + * Thread local destructors are called before static member destructors. If client code contains any static instance, + * that attempts to use Logging API from destructor, and exit() call is issued, the @a threadMoniker variable is + * released and running threads attempt to access string data through dangling pointers. + * + * To address the issue we use a type with a trivial destructor. + * + * @note This variable is defined only for platforms, that support @a thread_local. + * + * @see [basic.start.term]/p1 from C++ spec. + * @private + */ +static thread_local struct { + // Number of characters in @a value. It can be between 0 and sizeof(value) - 1. + std::size_t len; + // Moniker value. This value is not null-terminated. + char value[16]; +} threadMoniker = {0, {0}}; + +/** + * @brief Changes moniker value for the caller's thread. + * + * @param moniker Value to set. + */ +static inline void setMonikerValue(const std::string& moniker) noexcept { + auto& tm = threadMoniker; + tm.len = std::min(sizeof(tm.value), moniker.length()); + std::memcpy(tm.value, moniker.data(), tm.len); +} + +/** + * @brief Returns reference to this thread's moniker value. + */ +static inline std::string getMonikerValue() noexcept { + auto& tm = threadMoniker; + if (!tm.len) { + setMonikerValue(ThreadMoniker::generateMoniker()); + } + return std::string(tm.value, tm.value + tm.len); +} + +#endif + +std::string ThreadMoniker::generateMoniker(char prefix) noexcept { + /// Counter to generate (small) unique thread monikers. + static std::atomic g_nextThreadMoniker(1); + + std::stringstream ss; + if (prefix) { + ss << prefix << CHAR_COLON; + } + ss << std::hex << g_nextThreadMoniker++; + auto nextMoniker = ss.str(); + + if (nextMoniker.size() < MONIKER_SIZE_CHARS) { + nextMoniker.reserve(MONIKER_SIZE_CHARS); + nextMoniker.insert(nextMoniker.begin(), MONIKER_SIZE_CHARS - nextMoniker.size(), CHAR_SPACE); + } + + return nextMoniker; +} + +std::string ThreadMoniker::getThisThreadMoniker() noexcept { + return getMonikerValue(); +} + +void ThreadMoniker::setThisThreadMoniker(const std::string& moniker) noexcept { + setMonikerValue(moniker); +} + } // namespace logger } // namespace utils } // namespace avsCommon diff --git a/AVSCommon/Utils/src/MacAddressString.cpp b/AVSCommon/Utils/src/MacAddressString.cpp index 98489d5b66..ff592c67f4 100644 --- a/AVSCommon/Utils/src/MacAddressString.cpp +++ b/AVSCommon/Utils/src/MacAddressString.cpp @@ -15,6 +15,7 @@ #include +#include #include #include diff --git a/AVSCommon/Utils/src/MediaPlayer/PlaybackContext.cpp b/AVSCommon/Utils/src/MediaPlayer/PlaybackContext.cpp index 1975d473e3..e6f4fce4c1 100644 --- a/AVSCommon/Utils/src/MediaPlayer/PlaybackContext.cpp +++ b/AVSCommon/Utils/src/MediaPlayer/PlaybackContext.cpp @@ -24,7 +24,7 @@ namespace utils { namespace mediaPlayer { /// String to identify log entries originating from this file. -static const std::string TAG("PlaybackContext"); +#define TAG "PlaybackContext" /** * Create a @c LogEntry using this file's @c TAG and the specified event string. diff --git a/AVSCommon/Utils/src/MediaPlayer/PooledMediaPlayerFactory.cpp b/AVSCommon/Utils/src/MediaPlayer/PooledMediaPlayerFactory.cpp index 435866fb30..9dd503af55 100644 --- a/AVSCommon/Utils/src/MediaPlayer/PooledMediaPlayerFactory.cpp +++ b/AVSCommon/Utils/src/MediaPlayer/PooledMediaPlayerFactory.cpp @@ -23,7 +23,7 @@ namespace mediaPlayer { using namespace avsCommon::utils::mediaPlayer; -static const std::string TAG("PooledMediaPlayerFactory"); +#define TAG "PooledMediaPlayerFactory" #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) diff --git a/AVSCommon/Utils/src/MediaPlayer/PooledMediaResourceProvider.cpp b/AVSCommon/Utils/src/MediaPlayer/PooledMediaResourceProvider.cpp index 06858a3020..9bea774dde 100644 --- a/AVSCommon/Utils/src/MediaPlayer/PooledMediaResourceProvider.cpp +++ b/AVSCommon/Utils/src/MediaPlayer/PooledMediaResourceProvider.cpp @@ -25,7 +25,7 @@ namespace mediaPlayer { using namespace avsCommon::sdkInterfaces; -static const std::string TAG("PooledMediaResourceProvider"); +#define TAG "PooledMediaResourceProvider" #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) diff --git a/AVSCommon/Utils/src/Metrics/DataPointCounterBuilder.cpp b/AVSCommon/Utils/src/Metrics/DataPointCounterBuilder.cpp index fb9d8c1a04..c74a2efafb 100644 --- a/AVSCommon/Utils/src/Metrics/DataPointCounterBuilder.cpp +++ b/AVSCommon/Utils/src/Metrics/DataPointCounterBuilder.cpp @@ -25,7 +25,7 @@ namespace utils { namespace metrics { /// String to identify log entries originating from this file. -static const std::string TAG("DataPointCounterBuilder"); +#define TAG "DataPointCounterBuilder" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/Metrics/DataPointDurationBuilder.cpp b/AVSCommon/Utils/src/Metrics/DataPointDurationBuilder.cpp index 3099243963..ab728f8413 100644 --- a/AVSCommon/Utils/src/Metrics/DataPointDurationBuilder.cpp +++ b/AVSCommon/Utils/src/Metrics/DataPointDurationBuilder.cpp @@ -23,7 +23,7 @@ namespace utils { namespace metrics { /// String to identify log entries originating from this file. -static const std::string TAG("DataPointDurationBuilder"); +#define TAG "DataPointDurationBuilder" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/Metrics/MetricEvent.cpp b/AVSCommon/Utils/src/Metrics/MetricEvent.cpp index c93393dc63..f8993a1ba4 100644 --- a/AVSCommon/Utils/src/Metrics/MetricEvent.cpp +++ b/AVSCommon/Utils/src/Metrics/MetricEvent.cpp @@ -25,7 +25,7 @@ namespace utils { namespace metrics { /// String to identify log entries originating from this file. -static const std::string TAG("MetricEvent"); +#define TAG "MetricEvent" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/Metrics/MetricEventBuilder.cpp b/AVSCommon/Utils/src/Metrics/MetricEventBuilder.cpp index 1b49ebf7f1..b50608877e 100644 --- a/AVSCommon/Utils/src/Metrics/MetricEventBuilder.cpp +++ b/AVSCommon/Utils/src/Metrics/MetricEventBuilder.cpp @@ -22,7 +22,7 @@ namespace utils { namespace metrics { /// String to identify log entries originating from this file. -static const std::string TAG("MetricEventBuilder"); +#define TAG "MetricEventBuilder" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/Metrics/UplData.cpp b/AVSCommon/Utils/src/Metrics/UplData.cpp index 846547e8ca..d1028f0571 100644 --- a/AVSCommon/Utils/src/Metrics/UplData.cpp +++ b/AVSCommon/Utils/src/Metrics/UplData.cpp @@ -23,7 +23,7 @@ namespace utils { namespace metrics { /// String to identify log entries originating from this file. -static const std::string TAG("UplData"); +#define TAG "UplData" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/MultiTimer.cpp b/AVSCommon/Utils/src/MultiTimer.cpp index a5ba08c68b..bf3890ed65 100644 --- a/AVSCommon/Utils/src/MultiTimer.cpp +++ b/AVSCommon/Utils/src/MultiTimer.cpp @@ -15,9 +15,9 @@ #include #include - -#include "AVSCommon/Utils/Logger/Logger.h" -#include "AVSCommon/Utils/Timing/Timer.h" +#include +#include +#include namespace alexaClientSDK { namespace avsCommon { @@ -25,26 +25,31 @@ namespace utils { namespace timing { /// String to identify log entries originating from this file. -static const std::string TAG("MultiTimer"); +#define TAG "MultiTimer" /** * Create a LogEntry using this file's TAG and the specified event string. * - * @param The event string for this @c LogEntry. + * @param event The event string for this @c LogEntry. */ #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) /// Grace period used to avoid restarting the internal thread too often. static const std::chrono::milliseconds GRACE_PERIOD{500}; -std::shared_ptr MultiTimer::createMultiTimer() { +std::shared_ptr MultiTimer::createMultiTimer() noexcept { return std::make_shared(); } -MultiTimer::MultiTimer() : m_isRunning{false}, m_isBeingDestroyed{false}, m_nextToken{0} { +MultiTimer::MultiTimer() noexcept : + m_timerMoniker{utils::logger::ThreadMoniker::generateMoniker(utils::logger::ThreadMoniker::PREFIX_TIMER)}, + m_isRunning{false}, + m_isBeingDestroyed{false}, + m_nextToken{0} { + ACSDK_DEBUG5(LX("init").d("moniker", m_timerMoniker)); } -MultiTimer::~MultiTimer() { +MultiTimer::~MultiTimer() noexcept { std::unique_lock lock{m_waitMutex}; m_timers.clear(); m_tasks.clear(); @@ -57,7 +62,7 @@ MultiTimer::~MultiTimer() { } } -MultiTimer::Token MultiTimer::submitTask(const std::chrono::milliseconds& delay, std::function task) { +MultiTimer::Token MultiTimer::submitTask(const std::chrono::milliseconds& delay, std::function task) noexcept { std::unique_lock lock{m_waitMutex}; auto token = m_nextToken++; @@ -69,7 +74,7 @@ MultiTimer::Token MultiTimer::submitTask(const std::chrono::milliseconds& delay, // Kick-off task execution if needed. if (!m_isRunning) { m_isRunning = true; - m_timerThread.start(std::bind(&MultiTimer::executeTimer, this)); + m_timerThread.start(std::bind(&MultiTimer::executeTimer, this), m_timerMoniker); } else { // Wake up timer thread if the new task is the next to expire. if (m_timers.begin()->second == token) { @@ -80,7 +85,7 @@ MultiTimer::Token MultiTimer::submitTask(const std::chrono::milliseconds& delay, return token; } -void MultiTimer::cancelTask(Token token) { +void MultiTimer::cancelTask(Token token) noexcept { std::unique_lock lock{m_waitMutex}; auto taskIt = m_tasks.find(token); if (taskIt != m_tasks.end()) { @@ -99,7 +104,7 @@ void MultiTimer::cancelTask(Token token) { } } -bool MultiTimer::executeTimer() { +bool MultiTimer::executeTimer() noexcept { std::unique_lock lock{m_waitMutex}; while (!m_timers.empty()) { auto now = std::chrono::steady_clock::now(); @@ -115,17 +120,23 @@ bool MultiTimer::executeTimer() { // Execute task. auto taskIt = m_tasks.find(nextIt->second); if (taskIt != m_tasks.end()) { - auto& task = taskIt->second.second; - task(); + auto task = std::move(taskIt->second.second); m_tasks.erase(taskIt); + m_timers.erase(nextIt); + lock.unlock(); + if (task) { + task(); + } + lock.lock(); + } else { + m_timers.erase(nextIt); } - m_timers.erase(nextIt); } } return hasNextLocked(lock); } -bool MultiTimer::hasNextLocked(std::unique_lock& lock) { +bool MultiTimer::hasNextLocked(std::unique_lock& lock) noexcept { m_waitCondition.wait_for(lock, GRACE_PERIOD, [this] { return (!m_tasks.empty()) || m_isBeingDestroyed; }); m_isRunning = (!m_isBeingDestroyed) && !m_tasks.empty(); return m_isRunning; diff --git a/AVSCommon/Utils/src/Network/InternetConnectionMonitor.cpp b/AVSCommon/Utils/src/Network/InternetConnectionMonitor.cpp index 327cf5528f..94a35adf46 100644 --- a/AVSCommon/Utils/src/Network/InternetConnectionMonitor.cpp +++ b/AVSCommon/Utils/src/Network/InternetConnectionMonitor.cpp @@ -26,7 +26,7 @@ using namespace avsCommon::utils::sds; using namespace utils::timing; /// String to identify log entries originating from this file. -static const std::string TAG("InternetConnectionMonitor"); +#define TAG "InternetConnectionMonitor" /** * Create a @c LogEntry using this file's @c TAG and the specified event string. diff --git a/AVSCommon/Utils/src/Power/AggregatedPowerResourceManager.cpp b/AVSCommon/Utils/src/Power/AggregatedPowerResourceManager.cpp index 0a20750f66..d03484ee47 100644 --- a/AVSCommon/Utils/src/Power/AggregatedPowerResourceManager.cpp +++ b/AVSCommon/Utils/src/Power/AggregatedPowerResourceManager.cpp @@ -26,7 +26,7 @@ namespace power { using namespace avsCommon::sdkInterfaces; /// String to identify log entries originating from this file. -static const std::string TAG("AggregatedPowerResourceManager"); +#define TAG "AggregatedPowerResourceManager" /// Prefix to uniquely identify the resource. static const std::string PREFIX = "ACSDK_"; @@ -89,24 +89,16 @@ AggregatedPowerResourceManager::AggregatedPowerResourceManager( void AggregatedPowerResourceManager::acquirePowerResource( const std::string& component, const PowerResourceLevel level) { - ACSDK_DEBUG9(LX(__func__)); - - std::lock_guard lock(m_mutex); - m_appPowerResourceManager->acquirePowerResource(component, level); + ACSDK_ERROR(LX(__func__).m("API is deprecated.Please see PowerResourceManagerInterface for alternatives")); } void AggregatedPowerResourceManager::releasePowerResource(const std::string& component) { - ACSDK_DEBUG9(LX(__func__)); - - std::lock_guard lock(m_mutex); - m_appPowerResourceManager->releasePowerResource(component); + ACSDK_ERROR(LX(__func__).m("API is deprecated.Please see PowerResourceManagerInterface for alternatives")); }; bool AggregatedPowerResourceManager::isPowerResourceAcquired(const std::string& component) { - ACSDK_DEBUG9(LX(__func__)); - - std::lock_guard lock(m_mutex); - return m_appPowerResourceManager->isPowerResourceAcquired(component); + ACSDK_ERROR(LX(__func__).m("API is deprecated.Please see PowerResourceManagerInterface for alternatives")); + return false; } std::shared_ptr AggregatedPowerResourceManager:: diff --git a/AVSCommon/Utils/src/Power/PowerMonitor.cpp b/AVSCommon/Utils/src/Power/PowerMonitor.cpp index ab5b958765..9b91a9790a 100644 --- a/AVSCommon/Utils/src/Power/PowerMonitor.cpp +++ b/AVSCommon/Utils/src/Power/PowerMonitor.cpp @@ -23,7 +23,7 @@ namespace utils { namespace power { /// String to identify log entries originating from this file. -static const std::string TAG("PowerMonitor"); +#define TAG "PowerMonitor" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/Power/PowerResource.cpp b/AVSCommon/Utils/src/Power/PowerResource.cpp index 2c509c6596..db7b004ac1 100644 --- a/AVSCommon/Utils/src/Power/PowerResource.cpp +++ b/AVSCommon/Utils/src/Power/PowerResource.cpp @@ -24,7 +24,7 @@ namespace power { using namespace avsCommon::sdkInterfaces; /// String to identify log entries originating from this file. -static const std::string TAG("PowerResource"); +#define TAG "PowerResource" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/RequiresShutdown.cpp b/AVSCommon/Utils/src/RequiresShutdown.cpp index 0394d901cd..73ef87a6b9 100644 --- a/AVSCommon/Utils/src/RequiresShutdown.cpp +++ b/AVSCommon/Utils/src/RequiresShutdown.cpp @@ -24,7 +24,7 @@ namespace avsCommon { namespace utils { /// String to identify log entries originating from this file. -static const std::string TAG("RequiresShutdown"); +#define TAG "RequiresShutdown" /** * Create a @c LogEntry using this file's @c TAG and the specified event string. diff --git a/AVSCommon/Utils/src/RetryTimer.cpp b/AVSCommon/Utils/src/RetryTimer.cpp index 7470056c98..fbe08c8f8e 100644 --- a/AVSCommon/Utils/src/RetryTimer.cpp +++ b/AVSCommon/Utils/src/RetryTimer.cpp @@ -47,8 +47,8 @@ RetryTimer::RetryTimer(const std::vector& retryTable, int decreasePercentag std::chrono::milliseconds RetryTimer::calculateTimeToRetry(int retryCount) const { if (retryCount < 0) { retryCount = 0; - } else if ((size_t)retryCount >= m_RetrySize) { - retryCount = m_RetrySize - 1; + } else if (static_cast(retryCount) >= m_RetrySize) { + retryCount = static_cast(m_RetrySize) - 1; } std::mt19937 generator(static_cast(std::time(nullptr))); diff --git a/AVSCommon/Utils/src/Stopwatch.cpp b/AVSCommon/Utils/src/Stopwatch.cpp index 017c04793a..836af0c4aa 100644 --- a/AVSCommon/Utils/src/Stopwatch.cpp +++ b/AVSCommon/Utils/src/Stopwatch.cpp @@ -22,7 +22,7 @@ namespace utils { namespace timing { /// String to identify log entries originating from this file. -static const std::string TAG("Stopwatch"); +#define TAG "Stopwatch" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/Stream/Streambuf.cpp b/AVSCommon/Utils/src/Stream/Streambuf.cpp index 6792a0bc78..1b1c52c6c4 100644 --- a/AVSCommon/Utils/src/Stream/Streambuf.cpp +++ b/AVSCommon/Utils/src/Stream/Streambuf.cpp @@ -12,7 +12,9 @@ * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ +#include +#include #include "AVSCommon/Utils/Stream/Streambuf.h" namespace alexaClientSDK { @@ -20,6 +22,22 @@ namespace avsCommon { namespace utils { namespace stream { +/// String to identify log entries originating from this file. +#define TAG "Streambuf" + +/** + * Create a LogEntry using this file's TAG and the specified event string. + * + * @param event The event string for this @c LogEntry. + */ +#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) + +/// Constant to indicate the MAX stream offset of an integer. +static constexpr std::streamoff MAX_INT = std::numeric_limits::max(); + +/// Constant to indicate a invalid offset. +static constexpr std::streamoff INVALID_OFFSET = std::streamoff(-1); + // There are two casts, as a streambuf uses Type=char. This requires removing the const and removing the unsigned. // setg only is for reading, so this operation is safe, although ugly. Streambuf::Streambuf(const unsigned char* data, size_t length) : @@ -29,22 +47,27 @@ Streambuf::Streambuf(const unsigned char* data, size_t length) : } std::streampos Streambuf::seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which) { + auto errorPos = std::streampos(INVALID_OFFSET); switch (way) { case std::ios_base::beg: setg(m_begin, m_begin + off, m_end); break; case std::ios_base::cur: - gbump(off); + if (off > MAX_INT) { + ACSDK_ERROR(LX("seekoffFailed").d("reason", "offset out of limits").d("off", off).d("limit", MAX_INT)); + return errorPos; + } + gbump(static_cast(off)); break; case std::ios_base::end: setg(m_begin, m_end + off, m_end); break; default: - return std::streampos(std::streamoff(-1)); + return errorPos; } if (!gptr() || gptr() >= egptr() || gptr() < eback()) { - return std::streampos(std::streamoff(-1)); + return errorPos; } return gptr() - eback(); diff --git a/AVSCommon/Utils/src/StringUtils.cpp b/AVSCommon/Utils/src/StringUtils.cpp index f9d853e4f0..fba725e597 100644 --- a/AVSCommon/Utils/src/StringUtils.cpp +++ b/AVSCommon/Utils/src/StringUtils.cpp @@ -31,7 +31,7 @@ namespace string { using namespace avsCommon::utils::logger; /// String to identify log entries originating from this file. -static const std::string TAG("StringUtils"); +#define TAG "StringUtils" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/TaskThread.cpp b/AVSCommon/Utils/src/TaskThread.cpp index af362ac9c8..4c1a8488e1 100644 --- a/AVSCommon/Utils/src/TaskThread.cpp +++ b/AVSCommon/Utils/src/TaskThread.cpp @@ -15,17 +15,17 @@ #include -#include "AVSCommon/Utils/Logger/Logger.h" -#include "AVSCommon/Utils/Logger/ThreadMoniker.h" -#include "AVSCommon/Utils/Threading/TaskThread.h" +#include +#include +#include /// String to identify log entries originating from this file. -static const std::string TAG("TaskThread"); +#define TAG "TaskThread" /** * Create a LogEntry using this file's TAG and the specified event string. * - * @param The event string for this @c LogEntry. + * @param event The event string for this @c LogEntry. */ #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) @@ -62,7 +62,7 @@ TaskThread::~TaskThread() { } } -bool TaskThread::start(std::function jobRunner) { +bool TaskThread::start(std::function jobRunner, const std::string& moniker) { if (!jobRunner) { ACSDK_ERROR(LX("startFailed").d("reason", "invalidFunction")); return false; @@ -74,19 +74,17 @@ bool TaskThread::start(std::function jobRunner) { return false; } - m_startTime = steady_clock::now(); - m_stop = true; std::lock_guard guard(m_mutex); if (m_shuttingDown) { ACSDK_ERROR(LX("startFailed").d("reason", "shuttingDown")); return false; } - m_workerThread = m_threadPool->obtainWorker(m_moniker); + m_workerThread = m_threadPool->obtainWorker(); - m_moniker = m_workerThread->getMoniker(); - m_workerThread->run([this, jobRunner] { - TaskThread::run(jobRunner); + m_workerThread->run([this, jobRunner, moniker] { + utils::logger::ThreadMoniker::setThisThreadMoniker(moniker); + TaskThread::run(std::move(jobRunner)); return false; }); return true; @@ -94,15 +92,14 @@ bool TaskThread::start(std::function jobRunner) { void TaskThread::run(std::function jobRunner) { std::lock_guard guard(m_mutex); - ACSDK_DEBUG9(LX("startThread") - .d("moniker", m_moniker) - .d("duration", duration_cast(steady_clock::now() - m_startTime).count())); + // Reset stop flag and already starting flag. m_stop = false; m_alreadyStarting = false; while (!m_stop && jobRunner()) ; + m_threadPool->releaseWorker(std::move(m_workerThread)); } diff --git a/AVSCommon/Utils/src/ThreadPool.cpp b/AVSCommon/Utils/src/ThreadPool.cpp index bb9abbf6e8..1e4b9c25ea 100644 --- a/AVSCommon/Utils/src/ThreadPool.cpp +++ b/AVSCommon/Utils/src/ThreadPool.cpp @@ -14,20 +14,18 @@ */ #include -#include #include "AVSCommon/Utils/Logger/Logger.h" -#include "AVSCommon/Utils/Logger/ThreadMoniker.h" #include "AVSCommon/Utils/Memory/Memory.h" #include "AVSCommon/Utils/Threading/ThreadPool.h" /// String to identify log entries originating from this file. -static const std::string TAG("ThreadPool"); +#define TAG "ThreadPool" /** * Create a LogEntry using this file's TAG and the specified event string. * - * @param The event string for this @c LogEntry. + * @param event The event string for this @c LogEntry. */ #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) @@ -39,8 +37,6 @@ namespace threading { using namespace std; using namespace logger; -static ThreadPool SINGLETON_THREAD_POOL{}; - ThreadPool::ThreadPool(size_t maxThreads) : m_maxPoolThreads{maxThreads}, m_created{0}, @@ -55,35 +51,18 @@ ThreadPool::~ThreadPool() { m_workerQueue.clear(); } -unique_ptr ThreadPool::obtainWorker(string optionalMoniker) { +unique_ptr ThreadPool::obtainWorker() { std::lock_guard lock(m_queueMutex); - ACSDK_DEBUG9(LX("obtainWorker") - .d("created", m_created) - .d("obtained", m_obtained) - .d("releasedToPool", m_releasedToPool) - .d("releasedFromPool", m_releasedFromPool) - .d("outstanding", m_obtained - (m_releasedToPool + m_releasedFromPool))); + m_obtained++; unique_ptr ret; if (m_workerQueue.empty()) { m_created++; ret = memory::make_unique(); } else { -#ifdef THREAD_AFFINITY - bool containsMoniker = false; - if (!optionalMoniker.empty()) { - containsMoniker = m_workerMap.count(optionalMoniker) > 0; - } - auto workerIterator = containsMoniker ? m_workerMap[optionalMoniker] : m_workerQueue.begin(); -#else auto workerIterator = m_workerQueue.begin(); -#endif ret = std::move(*workerIterator); m_workerQueue.erase(workerIterator); -#ifdef THREAD_AFFINITY - std::string moniker = (*workerIterator)->getMoniker(); - m_workerMap.erase(moniker); -#endif } return ret; @@ -91,7 +70,6 @@ unique_ptr ThreadPool::obtainWorker(string optionalMoniker) { void ThreadPool::releaseWorker(std::unique_ptr workerThread) { std::lock_guard lock(m_queueMutex); - if (m_workerQueue.size() >= m_maxPoolThreads) { // In order to allow this to be called from the thread being released, // we release the first thread in the queue when we want to stop growing. @@ -101,10 +79,6 @@ void ThreadPool::releaseWorker(std::unique_ptr workerThread) { m_releasedToPool++; } m_workerQueue.push_back(std::move(workerThread)); -#ifdef THREAD_AFFINITY - std::string moniker = workerThread->getMoniker(); - m_workerMap[moniker] = --m_workerQueue.end(); -#endif } void ThreadPool::setMaxThreads(size_t maxThreads) { @@ -115,7 +89,7 @@ void ThreadPool::setMaxThreads(size_t maxThreads) { } } -uint32_t ThreadPool::getMaxThreads() { +size_t ThreadPool::getMaxThreads() { std::lock_guard lock(m_queueMutex); return m_maxPoolThreads; } diff --git a/AVSCommon/Utils/src/Threading/ConditionVariableWrapper.cpp b/AVSCommon/Utils/src/Threading/ConditionVariableWrapper.cpp index 547e395c0f..43f6459709 100644 --- a/AVSCommon/Utils/src/Threading/ConditionVariableWrapper.cpp +++ b/AVSCommon/Utils/src/Threading/ConditionVariableWrapper.cpp @@ -26,7 +26,7 @@ namespace threading { using namespace avsCommon::sdkInterfaces; /// String to identify log entries originating from this file. -static const std::string TAG(ConditionVariableWrapper::getTag()); +#define TAG ConditionVariableWrapper::getTag() /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/Threading/Executor.cpp b/AVSCommon/Utils/src/Threading/Executor.cpp new file mode 100644 index 0000000000..9f3aa04d61 --- /dev/null +++ b/AVSCommon/Utils/src/Threading/Executor.cpp @@ -0,0 +1,87 @@ +/* + * 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 + +/// String to identify log entries originating from this file. +#define TAG "ExecutorWrapper" + +/** + * Create a LogEntry using this file's TAG and the specified event string. + * + * @param event The event string for this @c LogEntry. + */ +#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace threading { + +Executor::Executor() noexcept : m_executor{std::make_shared()} { + if (!m_executor) { + ACSDK_ERROR(LX("initError")); + } +} + +Executor::~Executor() noexcept { + m_executor.reset(); +} + +bool Executor::execute(std::function&& function) noexcept { + return execute(std::move(function), QueuePosition::Back); +} + +bool Executor::execute(std::function&& function, QueuePosition queuePosition) noexcept { + if (m_executor) { + auto error = m_executor->execute(std::move(function), queuePosition); + return !error; + } + return false; +} + +bool Executor::execute(const std::function& function) noexcept { + return execute(std::function(function), QueuePosition::Back); +} + +void Executor::waitForSubmittedTasks() noexcept { + if (m_executor) { + m_executor->waitForSubmittedTasks(); + } +} + +void Executor::shutdown() noexcept { + if (m_executor) { + m_executor->shutdown(); + } +} + +bool Executor::isShutdown() noexcept { + if (m_executor) { + return m_executor->isShutdown(); + } + return true; +} + +Executor::operator std::shared_ptr() const noexcept { + return m_executor; +} + +} // namespace threading +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/Utils/src/Threading/ExecutorFactory.cpp b/AVSCommon/Utils/src/Threading/ExecutorFactory.cpp new file mode 100644 index 0000000000..45eeb75e4a --- /dev/null +++ b/AVSCommon/Utils/src/Threading/ExecutorFactory.cpp @@ -0,0 +1,46 @@ +/* + * 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 + +/// String to identify log entries originating from this file. +#define TAG "ExecutorFactory" + +/** + * Create a LogEntry using this file's TAG and the specified event string. + * + * @param event The event string for this @c LogEntry. + */ +#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace threading { + +std::shared_ptr createSingleThreadExecutor() noexcept { + auto res = std::make_shared(); + if (!res) { + ACSDK_ERROR(LX("createExecutorFailed")); + } + return res; +} + +} // namespace threading +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/Utils/src/Threading/SharedExecutor.cpp b/AVSCommon/Utils/src/Threading/SharedExecutor.cpp new file mode 100644 index 0000000000..a80f6fc6fe --- /dev/null +++ b/AVSCommon/Utils/src/Threading/SharedExecutor.cpp @@ -0,0 +1,203 @@ +/* + * 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 + +/// String to identify log entries originating from this file. +#define TAG "Executor" + +/** + * Create a LogEntry using this file's TAG and the specified event string. + * + * @param event The event string for this @c LogEntry. + */ +#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace threading { + +template std::future Executor::pushFunction( + QueuePosition queuePosition, + std::function&& function) noexcept; +template std::future Executor::pushFunction( + QueuePosition queuePosition, + std::function&& function) noexcept; +template std::future Executor::pushFunction( + QueuePosition queuePosition, + std::function&& function) noexcept; + +/// Prefix for power resource owned by @c Executor instance. +/// @private +static constexpr auto POWER_RESOURCE_PREFIX = "Executor:"; + +/** + * Helper method to create power resource id from a moniker. + * + * This method concatenates @a POWER_RESOURCE_PREFIX with @a moniker without trailing spaces. + * + * @param moniker Moniker value to use. + * @return Power resource identifier. + * @private + */ +static std::string createPowerResourceName(const std::string& moniker) noexcept { + std::string::const_iterator it = moniker.begin(); + while (it != moniker.end() && std::isspace(*it)) { + ++it; + } + std::string result; + constexpr auto powerResourcePrefixLen = sizeof(POWER_RESOURCE_PREFIX) - 1; + result.reserve(std::distance(it, moniker.end()) + powerResourcePrefixLen); + result.append(POWER_RESOURCE_PREFIX, powerResourcePrefixLen); + std::copy(it, moniker.end(), back_inserter(result)); + return result; +} + +SharedExecutor::SharedExecutor() noexcept : + m_executorMoniker{utils::logger::ThreadMoniker::generateMoniker(utils::logger::ThreadMoniker::PREFIX_EXECUTOR)}, + m_threadRunning{false}, + m_shutdown{false} { + ACSDK_DEBUG5(LX("created").d("moniker", m_executorMoniker)); + m_powerResource = + power::PowerMonitor::getInstance()->createLocalPowerResource(createPowerResourceName(m_executorMoniker)); +} + +SharedExecutor::~SharedExecutor() noexcept { + shutdown(); + ACSDK_DEBUG5(LX("destroyed").d("moniker", m_executorMoniker)); +} + +std::error_condition SharedExecutor::execute(std::function&& function) noexcept { + return execute(std::move(function), QueuePosition::Back); +} + +std::error_condition SharedExecutor::execute(const std::function& function) noexcept { + // Forward copy of function reference. + return execute(std::function(function), QueuePosition::Back); +} + +void SharedExecutor::waitForSubmittedTasks() noexcept { + std::unique_lock lock{m_queueMutex}; + if (m_threadRunning) { + // wait for thread to exit. + std::promise flushedPromise; + auto flushedFuture = flushedPromise.get_future(); + m_queue.emplace_back([&flushedPromise]() { flushedPromise.set_value(); }); + + lock.unlock(); + flushedFuture.wait(); + } +} + +std::error_condition SharedExecutor::execute(std::function&& function, QueuePosition queuePosition) noexcept { + if (!function) { + ACSDK_ERROR(LX(__func__).d("reason", "emptyFunction")); + return std::errc::invalid_argument; + } + if (QueuePosition::Back != queuePosition && QueuePosition::Front != queuePosition) { + ACSDK_ERROR(LX(__func__).d("reason", "badQueuePosition")); + return std::errc::invalid_argument; + } + + std::lock_guard queueLock{m_queueMutex}; + if (m_shutdown) { + ACSDK_WARN(LX(__func__).d("reason", "shutdownState")); + return std::errc::operation_not_permitted; + } + + if (m_powerResource) { + m_powerResource->acquire(); + } + m_queue.emplace(QueuePosition::Front == queuePosition ? m_queue.begin() : m_queue.end(), std::move(function)); + + if (!m_threadRunning) { + m_threadRunning = true; + // Restart task thread. + m_taskThread.start(std::bind(&SharedExecutor::runNext, this), m_executorMoniker); + } + + return std::error_condition(); +} + +std::function SharedExecutor::pop() noexcept { + std::lock_guard lock{m_queueMutex}; + if (!m_queue.empty()) { + auto task = std::move(m_queue.front()); + m_queue.pop_front(); + return task; + } + return std::function(); +} + +bool SharedExecutor::hasNext() noexcept { + std::unique_lock lock{m_queueMutex}; + m_threadRunning = !m_queue.empty(); + return m_threadRunning; +} + +bool SharedExecutor::runNext() noexcept { + auto task = pop(); + if (task) { +#if __cpp_exceptions || defined(__EXCEPTIONS) + try { +#endif + task(); + // Ensure the task is released after executed. + task = nullptr; + +#if __cpp_exceptions || defined(__EXCEPTIONS) + } catch (const std::exception& ex) { + ACSDK_ERROR(LX(__func__).d("taskException", ex.what())); + } catch (...) { + ACSDK_ERROR(LX(__func__).d("taskException", "other")); + } +#endif + + if (m_powerResource) { + m_powerResource->release(); + } + } + + return hasNext(); +} + +void SharedExecutor::shutdown() noexcept { + Queue releaseQueue; + { + std::lock_guard lock{m_queueMutex}; + m_shutdown = true; + std::swap(releaseQueue, m_queue); + } + releaseQueue.clear(); + waitForSubmittedTasks(); +} + +bool SharedExecutor::isShutdown() noexcept { + return m_shutdown; +} + +} // namespace threading +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/Utils/src/TimePoint.cpp b/AVSCommon/Utils/src/TimePoint.cpp index 5083650c3c..e042977d79 100644 --- a/AVSCommon/Utils/src/TimePoint.cpp +++ b/AVSCommon/Utils/src/TimePoint.cpp @@ -26,7 +26,7 @@ namespace timing { using namespace avsCommon::utils::logger; /// String to identify log entries originating from this file. -static const std::string TAG("TimePoint"); +#define TAG "TimePoint" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/src/TimeUtils.cpp b/AVSCommon/Utils/src/TimeUtils.cpp index 03f470c6ee..4c50d6dc1c 100644 --- a/AVSCommon/Utils/src/TimeUtils.cpp +++ b/AVSCommon/Utils/src/TimeUtils.cpp @@ -33,33 +33,35 @@ using namespace avsCommon::utils::logger; using namespace avsCommon::utils::string; /// String to identify log entries originating from this file. -static const std::string TAG("TimeUtils"); +#define TAG "TimeUtils" /** * Create a LogEntry using this file's TAG and the specified event string. * - * @param The event string for this @c LogEntry. + * @param event The event string for this @c LogEntry. */ #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) /// The length of the year element in an ISO-8601 formatted string. -static const int ENCODED_TIME_STRING_YEAR_STRING_LENGTH = 4; +static const std::size_t ENCODED_TIME_STRING_YEAR_STRING_LENGTH = 4; /// The length of the month element in an ISO-8601 formatted string. -static const int ENCODED_TIME_STRING_MONTH_STRING_LENGTH = 2; +static const std::size_t ENCODED_TIME_STRING_MONTH_STRING_LENGTH = 2; /// The length of the day element in an ISO-8601 formatted string. -static const int ENCODED_TIME_STRING_DAY_STRING_LENGTH = 2; +static const std::size_t ENCODED_TIME_STRING_DAY_STRING_LENGTH = 2; /// The length of the hour element in an ISO-8601 formatted string. -static const int ENCODED_TIME_STRING_HOUR_STRING_LENGTH = 2; +static const std::size_t ENCODED_TIME_STRING_HOUR_STRING_LENGTH = 2; /// The length of the minute element in an ISO-8601 formatted string. -static const int ENCODED_TIME_STRING_MINUTE_STRING_LENGTH = 2; +static const std::size_t ENCODED_TIME_STRING_MINUTE_STRING_LENGTH = 2; /// The length of the second element in an ISO-8601 formatted string. -static const int ENCODED_TIME_STRING_SECOND_STRING_LENGTH = 2; +static const std::size_t ENCODED_TIME_STRING_SECOND_STRING_LENGTH = 2; /// The length of the post-fix element in an ISO-8601 formatted string. -static const int ENCODED_TIME_STRING_POSTFIX_STRING_LENGTH = 4; +static const std::size_t ENCODED_TIME_STRING_POSTFIX_STRING_LENGTH = 4; /// The dash separator used in an ISO-8601 formatted string. static const std::string ENCODED_TIME_STRING_DASH_SEPARATOR_STRING = "-"; /// The 'T' separator used in an ISO-8601 formatted string. static const std::string ENCODED_TIME_STRING_T_SEPARATOR_STRING = "T"; +/// The 'Z' zone designator used in an ISO-8601 formatted string for zero UTC offset. +static const std::string ENCODED_TIME_STRING_Z_DESIGNATOR = "Z"; /// The colon separator used in an ISO-8601 formatted string. static const std::string ENCODED_TIME_STRING_COLON_SEPARATOR_STRING = ":"; /// The plus separator used in an ISO-8601 formatted string. @@ -68,31 +70,36 @@ static const std::string ENCODED_TIME_STRING_PLUS_SEPARATOR_STRING = "+"; /// The offset into an ISO-8601 formatted string where the year begins. static const unsigned long ENCODED_TIME_STRING_YEAR_OFFSET = 0; /// The offset into an ISO-8601 formatted string where the month begins. -static const unsigned long ENCODED_TIME_STRING_MONTH_OFFSET = ENCODED_TIME_STRING_YEAR_OFFSET + - ENCODED_TIME_STRING_YEAR_STRING_LENGTH + - ENCODED_TIME_STRING_DASH_SEPARATOR_STRING.length(); -/// The offset into an ISO-8601 formatted string where the day begins. -static const unsigned long ENCODED_TIME_STRING_DAY_OFFSET = ENCODED_TIME_STRING_MONTH_OFFSET + - ENCODED_TIME_STRING_MONTH_STRING_LENGTH + +static const std::size_t ENCODED_TIME_STRING_MONTH_OFFSET = ENCODED_TIME_STRING_YEAR_OFFSET + + ENCODED_TIME_STRING_YEAR_STRING_LENGTH + ENCODED_TIME_STRING_DASH_SEPARATOR_STRING.length(); +/// The offset into an ISO-8601 formatted string where the day begins. +static const std::size_t ENCODED_TIME_STRING_DAY_OFFSET = ENCODED_TIME_STRING_MONTH_OFFSET + + ENCODED_TIME_STRING_MONTH_STRING_LENGTH + + ENCODED_TIME_STRING_DASH_SEPARATOR_STRING.length(); /// The offset into an ISO-8601 formatted string where the hour begins. -static const unsigned long ENCODED_TIME_STRING_HOUR_OFFSET = ENCODED_TIME_STRING_DAY_OFFSET + - ENCODED_TIME_STRING_DAY_STRING_LENGTH + - ENCODED_TIME_STRING_T_SEPARATOR_STRING.length(); +static const std::size_t ENCODED_TIME_STRING_HOUR_OFFSET = ENCODED_TIME_STRING_DAY_OFFSET + + ENCODED_TIME_STRING_DAY_STRING_LENGTH + + ENCODED_TIME_STRING_T_SEPARATOR_STRING.length(); /// The offset into an ISO-8601 formatted string where the minute begins. -static const unsigned long ENCODED_TIME_STRING_MINUTE_OFFSET = ENCODED_TIME_STRING_HOUR_OFFSET + - ENCODED_TIME_STRING_HOUR_STRING_LENGTH + - ENCODED_TIME_STRING_COLON_SEPARATOR_STRING.length(); +static const std::size_t ENCODED_TIME_STRING_MINUTE_OFFSET = ENCODED_TIME_STRING_HOUR_OFFSET + + ENCODED_TIME_STRING_HOUR_STRING_LENGTH + + ENCODED_TIME_STRING_COLON_SEPARATOR_STRING.length(); /// The offset into an ISO-8601 formatted string where the second begins. -static const unsigned long ENCODED_TIME_STRING_SECOND_OFFSET = ENCODED_TIME_STRING_MINUTE_OFFSET + - ENCODED_TIME_STRING_MINUTE_STRING_LENGTH + - ENCODED_TIME_STRING_COLON_SEPARATOR_STRING.length(); +static const std::size_t ENCODED_TIME_STRING_SECOND_OFFSET = ENCODED_TIME_STRING_MINUTE_OFFSET + + ENCODED_TIME_STRING_MINUTE_STRING_LENGTH + + ENCODED_TIME_STRING_COLON_SEPARATOR_STRING.length(); /// The total expected length of an ISO-8601 formatted string. -static const unsigned long ENCODED_TIME_STRING_EXPECTED_LENGTH = +static const std::size_t ENCODED_TIME_STRING_EXPECTED_LENGTH = ENCODED_TIME_STRING_SECOND_OFFSET + ENCODED_TIME_STRING_SECOND_STRING_LENGTH + ENCODED_TIME_STRING_PLUS_SEPARATOR_STRING.length() + ENCODED_TIME_STRING_POSTFIX_STRING_LENGTH; +/// The total expected length of an ISO-8601 formatted string with UTC time. +static const std::size_t ENCODED_TIME_STRING_EXPECTED_LENGTH_UTC = ENCODED_TIME_STRING_SECOND_OFFSET + + ENCODED_TIME_STRING_SECOND_STRING_LENGTH + + ENCODED_TIME_STRING_Z_DESIGNATOR.length(); + /** * Utility function that wraps localtime conversion to std::time_t. * @@ -178,7 +185,8 @@ bool TimeUtils::convert8601TimeStringToTimeT(const std::string& iso8601TimeStrin std::tm timeInfo; - if (iso8601TimeString.length() != ENCODED_TIME_STRING_EXPECTED_LENGTH) { + if (iso8601TimeString.length() != ENCODED_TIME_STRING_EXPECTED_LENGTH && + iso8601TimeString.length() != ENCODED_TIME_STRING_EXPECTED_LENGTH_UTC) { ACSDK_ERROR( LX("convert8601TimeStringToTimeTFailed").d("unexpected time string length:", iso8601TimeString.length())); return false; diff --git a/AVSCommon/Utils/src/Timer.cpp b/AVSCommon/Utils/src/Timer.cpp index 392bee1f99..13e441064e 100644 --- a/AVSCommon/Utils/src/Timer.cpp +++ b/AVSCommon/Utils/src/Timer.cpp @@ -14,60 +14,121 @@ */ #include - -#include "AVSCommon/Utils/Timing/Timer.h" -#include "AVSCommon/Utils/Timing/TimerDelegateFactory.h" +#include +#include namespace alexaClientSDK { namespace avsCommon { namespace utils { namespace timing { +using namespace sdkInterfaces::timing; + /// String to identify log entries originating from this file. -static const std::string TAG(Timer::getTag()); +#define TAG "Timer" /** * Create a LogEntry using this file's TAG and the specified event string. * - * @param The event string for this @c LogEntry. + * @param event The event string for this @c LogEntry. */ #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) -Timer::Timer(std::shared_ptr timerDelegateFactory) { - if (!timerDelegateFactory) { +// Instantiate templates +template bool Timer::adaptTypesAndCallTask( + const std::chrono::milliseconds&, + const std::chrono::milliseconds&, + PeriodType, + size_t, + std::function&&) noexcept; +template bool Timer::adaptTypesAndCallTask( + const std::chrono::nanoseconds&, + const std::chrono::nanoseconds&, + PeriodType, + size_t, + std::function&&) noexcept; + +Timer::Timer(const std::shared_ptr& timerDelegateFactory) noexcept { + if (timerDelegateFactory) { + m_timer = timerDelegateFactory->getTimerDelegate(); + } else { ACSDK_WARN( LX(__func__).d("reason", "nullTimerDelegateFactory").m("Falling back to default TimerDelegateFactory")); - timerDelegateFactory = std::make_shared(); - if (!timerDelegateFactory) { - ACSDK_ERROR(LX(__func__).d("reason", "nullDefaultTimerDelegateFactory")); - return; - } + m_timer = TimerDelegateFactory{}.getTimerDelegate(); } - m_timer = timerDelegateFactory->getTimerDelegate(); if (!m_timer) { ACSDK_ERROR(LX(__func__).d("reason", "nullTimerDelegate")); } } -Timer::~Timer() { +Timer::~Timer() noexcept { stop(); } -void Timer::stop() { +void Timer::stop() noexcept { if (m_timer) { m_timer->stop(); } } -bool Timer::isActive() const { +bool Timer::isActive() const noexcept { return m_timer && m_timer->isActive(); } -bool Timer::activate() { +bool Timer::activate() noexcept { return m_timer && m_timer->activate(); } +bool Timer::callTask( + const std::chrono::nanoseconds& delayNano, + const std::chrono::nanoseconds& periodNano, + PeriodType periodType, + size_t maxCount, + std::function&& task) noexcept { + if (delayNano < std::chrono::nanoseconds::zero()) { + ACSDK_ERROR(LX(__func__).d("reason", "negativeDelay")); + return false; + } + if (periodNano < std::chrono::nanoseconds::zero()) { + ACSDK_ERROR(LX(__func__).d("reason", "negativePeriod")); + return false; + } + if (!task) { + ACSDK_ERROR(LX(__func__).d("reason", "nullTask")); + return false; + } + + // Don't start if already running. + if (!activate()) { + ACSDK_ERROR(LX(__func__).d("reason", "timerAlreadyActive")); + return false; + } + + if (!m_timer) { + ACSDK_ERROR(LX(__func__).d("reason", "nullTimerDelegate")); + return false; + } + + TimerDelegateInterface::PeriodType delegatePeriodType; + + switch (periodType) { + case PeriodType::ABSOLUTE: + delegatePeriodType = TimerDelegateInterface::PeriodType::ABSOLUTE; + break; + case PeriodType::RELATIVE: + delegatePeriodType = TimerDelegateInterface::PeriodType::RELATIVE; + break; + default: + ACSDK_ERROR(LX(__func__).d("reason", "badPeriodType")); + return false; + } + + m_timer->start(delayNano, periodNano, delegatePeriodType, maxCount, std::move(task)); + + return true; +} + } // namespace timing } // namespace utils } // namespace avsCommon diff --git a/AVSCommon/Utils/src/Timing/TimerDelegate.cpp b/AVSCommon/Utils/src/Timing/TimerDelegate.cpp index 2c818ad6a6..ce5b171066 100644 --- a/AVSCommon/Utils/src/Timing/TimerDelegate.cpp +++ b/AVSCommon/Utils/src/Timing/TimerDelegate.cpp @@ -14,11 +14,12 @@ */ #include -#include "AVSCommon/Utils/Timing/TimerDelegate.h" #include +#include +#include /// String to identify log entries originating from this file. -static const std::string TAG("TimerDelegate"); +#define TAG "TimerDelegate" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -32,6 +33,27 @@ namespace avsCommon { namespace utils { namespace timing { +using utils::logger::ThreadMoniker; + +/** + * @brief Helper method to invoke task and catch exception. + * + * @param[in, out] task Task reference. The reference is cleared before the method returns. + */ +static void safeInvokeTask(std::function& task) { +#if __cpp_exceptions || defined(__EXCEPTIONS) + try { +#endif + task(); +#if __cpp_exceptions || defined(__EXCEPTIONS) + } catch (const std::exception& ex) { + ACSDK_ERROR(LX(__func__).d("taskException", ex.what())); + } catch (...) { + ACSDK_ERROR(LX(__func__).d("taskException", "other")); + } +#endif +} + TimerDelegate::TimerDelegate() : m_running{false}, m_stopping{false} { } @@ -45,10 +67,18 @@ void TimerDelegate::start( PeriodType periodType, size_t maxCount, std::function task) { + auto moniker = ThreadMoniker::generateMoniker(ThreadMoniker::PREFIX_TIMER); + ACSDK_DEBUG5(LX("init").d("moniker", moniker)); + + if (!task) { + ACSDK_ERROR(LX(__func__).d("reason", "nullTask")); + } + std::lock_guard lock(m_callMutex); cleanupLocked(); activateLocked(); - m_thread = std::thread(&TimerDelegate::timerLoop, this, delay, period, periodType, maxCount, task); + m_thread = std::thread( + &TimerDelegate::timerLoop, this, delay, period, periodType, maxCount, std::move(task), std::move(moniker)); } void TimerDelegate::timerLoop( @@ -56,9 +86,13 @@ void TimerDelegate::timerLoop( std::chrono::nanoseconds period, PeriodType periodType, size_t maxCount, - std::function task) { + std::function task, + std::string moniker) { + ThreadMoniker::setThisThreadMoniker(std::move(moniker)); + // Timepoint to measure delay/period against. auto now = std::chrono::steady_clock::now(); + using utils::logger::ThreadMoniker; // Flag indicating whether we've drifted off schedule. bool offSchedule = false; @@ -83,7 +117,7 @@ void TimerDelegate::timerLoop( // Run the task if we're still on schedule. if (!offSchedule) { - task(); + safeInvokeTask(task); } // If the task runtime put us off schedule, skip the next task run. @@ -95,11 +129,15 @@ void TimerDelegate::timerLoop( break; case PeriodType::RELATIVE: - task(); + safeInvokeTask(task); now = std::chrono::steady_clock::now(); break; } } + + // release task reference + task = nullptr; + std::lock_guard lockGuard(m_waitMutex); m_stopping = false; m_running = false; @@ -108,7 +146,7 @@ void TimerDelegate::timerLoop( void TimerDelegate::stop() { std::lock_guard lock(m_callMutex); { - std::lock_guard lock(m_waitMutex); + std::lock_guard stateLock(m_waitMutex); if (m_running) { m_stopping = true; } diff --git a/AVSCommon/Utils/src/UUIDGeneration.cpp b/AVSCommon/Utils/src/UUIDGeneration.cpp index 0579398d0f..39d4d57ffa 100644 --- a/AVSCommon/Utils/src/UUIDGeneration.cpp +++ b/AVSCommon/Utils/src/UUIDGeneration.cpp @@ -34,7 +34,7 @@ namespace utils { namespace uuidGeneration { /// String to identify log entries originating from this file. -static const std::string TAG("UUIDGeneration"); +#define TAG "UUIDGeneration" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -177,13 +177,13 @@ static void addDefaultSeedLocked() { std::random_device rd; uint64_t timeSeed = std::chrono::high_resolution_clock::now().time_since_epoch().count(); if (lastInvokeTime > 0) { - seedsPool.push_front((uint32_t)(timeSeed - lastInvokeTime)); // interval between two invocations + seedsPool.push_front(static_cast(timeSeed - lastInvokeTime)); // interval between two invocations } lastInvokeTime = timeSeed; - seedsPool.push_front((uint32_t)timeSeed); // lower 32bits of current time - seedsPool.push_front(rd()); // random device - seedsPool.push_front( - reinterpret_cast(&timeSeed)); // lower 32bits of memory address of temporary variable + seedsPool.push_front(static_cast(timeSeed)); // lower 32bits of current time + seedsPool.push_front(rd()); // random device + seedsPool.push_front(static_cast( + reinterpret_cast(&timeSeed))); // lower 32bits of memory address of temporary variable if (seedsPool.size() > MAX_SEEDS_POOL_SIZE) { seedsPool.resize(MAX_SEEDS_POOL_SIZE); diff --git a/AVSCommon/Utils/src/WavUtils.cpp b/AVSCommon/Utils/src/WavUtils.cpp index bbe5086075..ca714dc1dc 100644 --- a/AVSCommon/Utils/src/WavUtils.cpp +++ b/AVSCommon/Utils/src/WavUtils.cpp @@ -24,7 +24,7 @@ namespace alexaClientSDK { namespace avsCommon { namespace utils { -static const std::string TAG("WavUtils"); +#define TAG "WavUtils" #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) @@ -233,9 +233,9 @@ bool readWAVFile( // Check if the file size is at least the size of a .wav file header. inputFile.seekg(0, std::ios::end); - int fileLengthInBytes = inputFile.tellg(); + size_t fileLengthInBytes = static_cast(inputFile.tellg()); - const int headerSize = isPCM ? PCM_HEADER_SIZE : NON_PCM_HEADER_SIZE; + const size_t headerSize = isPCM ? PCM_HEADER_SIZE : NON_PCM_HEADER_SIZE; if (fileLengthInBytes <= headerSize) { ACSDK_ERROR(LX("readAudioFileFailed").d("reason", "file size less than RIFF header")); return false; @@ -249,7 +249,7 @@ bool readWAVFile( char* pBuffer = buffer.data(); - if (static_cast(inputFile.gcount()) != static_cast(headerSize)) { + if (static_cast(inputFile.gcount()) != headerSize) { ACSDK_ERROR(LX("readAudioFileFailed").d("reason", "failed reading header")); return false; } @@ -275,7 +275,7 @@ bool readWAVFile( wavHeader.dataSz = readLongFromHeader(pBuffer, DATA_SZ_OFFSET + (isPCM ? 0 : NON_PCM_OFFSET)); // Read the remainder of the wav file (excluding header) into the audioBuffer. - int numSamples = (fileLengthInBytes - headerSize) / sizeof(uint16_t); + size_t numSamples = (fileLengthInBytes - headerSize) / sizeof(uint16_t); audioBuffer->resize(numSamples, 0); inputFile.read(reinterpret_cast(&(audioBuffer->at(0))), numSamples * sizeof(uint16_t)); diff --git a/AVSCommon/Utils/src/WorkerThread.cpp b/AVSCommon/Utils/src/WorkerThread.cpp index e82640b7b7..349f8c5dfe 100644 --- a/AVSCommon/Utils/src/WorkerThread.cpp +++ b/AVSCommon/Utils/src/WorkerThread.cpp @@ -23,10 +23,7 @@ namespace avsCommon { namespace utils { namespace threading { -WorkerThread::WorkerThread() : - m_moniker{alexaClientSDK::avsCommon::utils::logger::ThreadMoniker::generateMoniker()}, - m_stop{false}, - m_cancel{false} { +WorkerThread::WorkerThread() : m_stop{false}, m_cancel{false} { m_thread = std::thread{std::bind(&WorkerThread::runInternal, this)}; } @@ -45,14 +42,14 @@ WorkerThread::~WorkerThread() { } } -std::string WorkerThread::getMoniker() const { - return m_moniker; -} - void WorkerThread::cancel() { m_cancel = true; } +std::thread::id WorkerThread::getThreadId() const { + return m_thread.get_id(); +} + void WorkerThread::run(std::function workFunc) { std::lock_guard lock(m_mutex); m_cancel = false; @@ -61,7 +58,6 @@ void WorkerThread::run(std::function workFunc) { } void WorkerThread::runInternal() { - alexaClientSDK::avsCommon::utils::logger::ThreadMoniker::setThisThreadMoniker(m_moniker); std::unique_lock lock(m_mutex); do { // If run is called before the thread starts, it will notify before we wait, so we guard against that by diff --git a/AVSCommon/Utils/test/AVSCommon/Utils/Power/AggregatedPowerResourceManagerTest.cpp b/AVSCommon/Utils/test/AVSCommon/Utils/Power/AggregatedPowerResourceManagerTest.cpp index 1845154d4e..c84e2b284f 100644 --- a/AVSCommon/Utils/test/AVSCommon/Utils/Power/AggregatedPowerResourceManagerTest.cpp +++ b/AVSCommon/Utils/test/AVSCommon/Utils/Power/AggregatedPowerResourceManagerTest.cpp @@ -301,7 +301,7 @@ TEST_F(AggregatedPowerResourceManagerTest, test_closeCleanup) { * Test ensuring that acquirePowerResource calls the underlying application PowerResourceManagerInterface methods. */ TEST_F(AggregatedPowerResourceManagerTest, test_acquirePowerResourceLegacy) { - EXPECT_CALL(*m_mockAppPowerManager, acquirePowerResource(TEST_ID, TEST_LEVEL)); + EXPECT_CALL(*m_mockAppPowerManager, acquirePowerResource(TEST_ID, TEST_LEVEL)).Times(0); m_powerManager->acquirePowerResource(TEST_ID, TEST_LEVEL); } @@ -309,7 +309,7 @@ TEST_F(AggregatedPowerResourceManagerTest, test_acquirePowerResourceLegacy) { * Test ensuring that releasePowerResource calls the underlying application PowerResourceManagerInterface methods. */ TEST_F(AggregatedPowerResourceManagerTest, test_releasePowerResourceLegacy) { - EXPECT_CALL(*m_mockAppPowerManager, releasePowerResource(TEST_ID)); + EXPECT_CALL(*m_mockAppPowerManager, releasePowerResource(TEST_ID)).Times(0); m_powerManager->releasePowerResource(TEST_ID); } @@ -317,7 +317,7 @@ TEST_F(AggregatedPowerResourceManagerTest, test_releasePowerResourceLegacy) { * Test ensuring that isPowerResourceAcquired calls the underlying application PowerResourceManagerInterface methods. */ TEST_F(AggregatedPowerResourceManagerTest, test_isPowerResourceAcquiredLegacy) { - EXPECT_CALL(*m_mockAppPowerManager, isPowerResourceAcquired(TEST_ID)); + EXPECT_CALL(*m_mockAppPowerManager, isPowerResourceAcquired(TEST_ID)).Times(0); m_powerManager->isPowerResourceAcquired(TEST_ID); } diff --git a/AVSCommon/Utils/test/CMakeLists.txt b/AVSCommon/Utils/test/CMakeLists.txt index 9ed5e4de50..3f86a666a9 100644 --- a/AVSCommon/Utils/test/CMakeLists.txt +++ b/AVSCommon/Utils/test/CMakeLists.txt @@ -1,6 +1,3 @@ add_subdirectory("Common") -set(INCLUDE_PATH - "${AVSCommon_INCLUDE_DIRS}") - -discover_unit_tests("${INCLUDE_PATH}" "AVSCommon;UtilsCommonTestLib;SDKInterfacesTests") +discover_unit_tests("" "AVSCommon;UtilsCommonTestLib;SDKInterfacesTests") diff --git a/AVSCommon/Utils/test/Common/Common.cpp b/AVSCommon/Utils/test/Common/Common.cpp index 91c6360a78..e1471bd049 100644 --- a/AVSCommon/Utils/test/Common/Common.cpp +++ b/AVSCommon/Utils/test/Common/Common.cpp @@ -37,9 +37,9 @@ std::string createRandomAlphabetString(int stringSize) { std::vector vec(stringSize); std::independent_bits_engine engine; std::random_device rd; - engine.seed( + engine.seed(static_cast( rd() + std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()) - .count()); + .count())); std::generate(begin(vec), end(vec), std::ref(engine)); std::vector vec8(stringSize); diff --git a/AVSCommon/Utils/test/Common/TestableMessageObserver.cpp b/AVSCommon/Utils/test/Common/TestableMessageObserver.cpp index 6764d208e2..fe774cc44e 100644 --- a/AVSCommon/Utils/test/Common/TestableMessageObserver.cpp +++ b/AVSCommon/Utils/test/Common/TestableMessageObserver.cpp @@ -24,7 +24,7 @@ namespace utils { using namespace avsCommon::sdkInterfaces; /// String to identify log entries originating from this file. -static const std::string TAG("TestableMessageObserver"); +#define TAG "TestableMessageObserver" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/test/Common/Timing/StopTaskTimer.cpp b/AVSCommon/Utils/test/Common/Timing/StopTaskTimer.cpp index b96a3289cf..32bd2d8fcb 100644 --- a/AVSCommon/Utils/test/Common/Timing/StopTaskTimer.cpp +++ b/AVSCommon/Utils/test/Common/Timing/StopTaskTimer.cpp @@ -28,7 +28,7 @@ using namespace avsCommon::sdkInterfaces::timing; using namespace avsCommon::utils; /// String to identify log entries originating from this file. -static const std::string TAG("StopTaskTimer"); +#define TAG "StopTaskTimer" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -80,7 +80,7 @@ void StopTaskTimer::stop() { if (isActive()) { // Wait until any current executing tasked has finished. // The objective is to force the task to run from the beginning. - std::lock_guard lock(m_taskMutex); + std::lock_guard taskLock(m_taskMutex); m_task(); m_delegate->stop(); } diff --git a/AVSCommon/Utils/test/ExecutorTest.cpp b/AVSCommon/Utils/test/ExecutorTest.cpp index abd87894bf..68a0dc97e1 100644 --- a/AVSCommon/Utils/test/ExecutorTest.cpp +++ b/AVSCommon/Utils/test/ExecutorTest.cpp @@ -13,8 +13,13 @@ * permissions and limitations under the License. */ -#include +#include +#include #include +#include +#include +#include +#include #include "ExecutorTestUtils.h" #include "AVSCommon/Utils/Threading/Executor.h" @@ -26,6 +31,9 @@ namespace utils { namespace threading { namespace test { +/// Maximum timeout for blocking wait when expecting a signal. +static const std::chrono::seconds EXECUTOR_SIGNAL_WAIT_TIMEOUT{30}; + class ExecutorTest : public ::testing::Test { public: Executor executor; @@ -34,24 +42,28 @@ class ExecutorTest : public ::testing::Test { TEST_F(ExecutorTest, testTimer_submitStdFunctionAndVerifyExecution) { std::function function = []() {}; auto future = executor.submit(function); + ASSERT_TRUE(future.valid()); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); ASSERT_EQ(future_status, std::future_status::ready); } TEST_F(ExecutorTest, testTimer_submitStdBindAndVerifyExecution) { auto future = executor.submit(std::bind(exampleFunctionParams, 0)); + ASSERT_TRUE(future.valid()); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); ASSERT_EQ(future_status, std::future_status::ready); } TEST_F(ExecutorTest, testTimer_submitLambdaAndVerifyExecution) { auto future = executor.submit([]() {}); + ASSERT_TRUE(future.valid()); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); ASSERT_EQ(future_status, std::future_status::ready); } TEST_F(ExecutorTest, testTimer_submitFunctionPointerAndVerifyExecution) { auto future = executor.submit(&exampleFunction); + ASSERT_TRUE(future.valid()); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); ASSERT_EQ(future_status, std::future_status::ready); } @@ -59,6 +71,7 @@ TEST_F(ExecutorTest, testTimer_submitFunctionPointerAndVerifyExecution) { TEST_F(ExecutorTest, testTimer_submitFunctorAndVerifyExecution) { ExampleFunctor exampleFunctor; auto future = executor.submit(exampleFunctor); + ASSERT_TRUE(future.valid()); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); ASSERT_EQ(future_status, std::future_status::ready); } @@ -66,6 +79,7 @@ TEST_F(ExecutorTest, testTimer_submitFunctorAndVerifyExecution) { TEST_F(ExecutorTest, testTimer_submitFunctionWithPrimitiveReturnTypeNoArgsAndVerifyExecution) { int value = VALUE; auto future = executor.submit([=]() { return value; }); + ASSERT_TRUE(future.valid()); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); ASSERT_EQ(future_status, std::future_status::ready); ASSERT_EQ(future.get(), value); @@ -74,6 +88,7 @@ TEST_F(ExecutorTest, testTimer_submitFunctionWithPrimitiveReturnTypeNoArgsAndVer TEST_F(ExecutorTest, testTimer_submitFunctionWithObjectReturnTypeNoArgsAndVerifyExecution) { SimpleObject value(VALUE); auto future = executor.submit([=]() { return value; }); + ASSERT_TRUE(future.valid()); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); ASSERT_EQ(future_status, std::future_status::ready); ASSERT_EQ(future.get().getValue(), value.getValue()); @@ -82,6 +97,7 @@ TEST_F(ExecutorTest, testTimer_submitFunctionWithObjectReturnTypeNoArgsAndVerify TEST_F(ExecutorTest, testTimer_submitFunctionWithNoReturnTypePrimitiveArgsAndVerifyExecution) { int value = VALUE; auto future = executor.submit([](int number) {}, value); + ASSERT_TRUE(future.valid()); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); ASSERT_EQ(future_status, std::future_status::ready); } @@ -89,6 +105,7 @@ TEST_F(ExecutorTest, testTimer_submitFunctionWithNoReturnTypePrimitiveArgsAndVer TEST_F(ExecutorTest, testTimer_submitFunctionWithNoReturnTypeObjectArgsAndVerifyExecution) { SimpleObject arg(0); auto future = executor.submit([](SimpleObject object) {}, arg); + ASSERT_TRUE(future.valid()); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); ASSERT_EQ(future_status, std::future_status::ready); } @@ -97,6 +114,7 @@ TEST_F(ExecutorTest, testTimer_submitFunctionWithPrimitiveReturnTypeObjectArgsAn int value = VALUE; SimpleObject arg(0); auto future = executor.submit([=](SimpleObject object) { return value; }, arg); + ASSERT_TRUE(future.valid()); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); ASSERT_EQ(future_status, std::future_status::ready); ASSERT_EQ(future.get(), value); @@ -106,6 +124,7 @@ TEST_F(ExecutorTest, testTimer_submitFunctionWithObjectReturnTypePrimitiveArgsAn int arg = 0; SimpleObject value(VALUE); auto future = executor.submit([=](int primitive) { return value; }, arg); + ASSERT_TRUE(future.valid()); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); ASSERT_EQ(future_status, std::future_status::ready); ASSERT_EQ(future.get().getValue(), value.getValue()); @@ -115,6 +134,7 @@ TEST_F(ExecutorTest, testTimer_submitFunctionWithPrimitiveReturnTypePrimitiveArg int arg = 0; int value = VALUE; auto future = executor.submit([=](int number) { return value; }, arg); + ASSERT_TRUE(future.valid()); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); ASSERT_EQ(future_status, std::future_status::ready); ASSERT_EQ(future.get(), value); @@ -124,6 +144,7 @@ TEST_F(ExecutorTest, testTimer_submitFunctionWithObjectReturnTypeObjectArgsAndVe SimpleObject value(VALUE); SimpleObject arg(0); auto future = executor.submit([=](SimpleObject object) { return value; }, arg); + ASSERT_TRUE(future.valid()); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); ASSERT_EQ(future_status, std::future_status::ready); ASSERT_EQ(future.get().getValue(), value.getValue()); @@ -223,15 +244,14 @@ TEST_F(ExecutorTest, testTimer_futureWaitsForTaskCleanup) { SlowDestructor slowDestructor; // Submit a lambda to execute which captures a parameter by value that is slow to destruct. - executor - .submit([slowDestructor, &cleanedUp] { - // Update the captured copy of slowDestructor so that it will delay destruction and update the cleanedUp - // flag. - slowDestructor.cleanedUp = &cleanedUp; - } - // wait for the promise to be fulfilled. - ) - .wait(); + auto future = executor.submit([slowDestructor, &cleanedUp] { + // Update the captured copy of slowDestructor so that it will delay destruction and update the cleanedUp + // flag. + slowDestructor.cleanedUp = &cleanedUp; + }); + ASSERT_TRUE(future.valid()); + // wait for the promise to be fulfilled. + ASSERT_EQ(std::future_status::ready, future.wait_for(SHORT_TIMEOUT_MS * 2)); ASSERT_TRUE(cleanedUp); } @@ -264,7 +284,7 @@ TEST_F(ExecutorTest, testTimer_shutdown) { EXPECT_TRUE(executor.isShutdown()); // verify that the task has now completed - EXPECT_TRUE(done.valid()); + ASSERT_TRUE(done.valid()); done.get(); // try to submit a new task and verify that it is rejected @@ -294,7 +314,10 @@ TEST_F(ExecutorTest, testTimer_shutdownCancelJob) { auto jobToWaitDrop = [&jobToDropResult, &waitSetUp, &waitJobStart] { waitJobStart.wakeUp(); waitSetUp.wait(SHORT_TIMEOUT_MS); - jobToDropResult.wait_for(SHORT_TIMEOUT_MS); + // prevent unit test crash if jobToDropResult is not valid. It is checked below. + if (jobToDropResult.valid()) { + jobToDropResult.wait_for(SHORT_TIMEOUT_MS); + } }; // 1st job waits for setup to be done then wait for the second job to be cancelled. @@ -302,6 +325,7 @@ TEST_F(ExecutorTest, testTimer_shutdownCancelJob) { // 2nd job that should never run. When cancelled, its return will become available. jobToDropResult = executor.submit(jobToDrop); + ASSERT_TRUE(jobToDropResult.valid()); // Wake up first job and wait for it to start running. const std::chrono::seconds DEFAULT_TIMEOUT{5}; @@ -322,9 +346,9 @@ TEST_F(ExecutorTest, test_forwardPromise) { src.set_value(42); auto future = src.get_future(); - auto dst = std::make_shared>(); - forwardPromise(dst, &future); - EXPECT_EQ(dst->get_future().get(), 42); + std::promise dst; + forwardPromise(dst, future); + EXPECT_EQ(dst.get_future().get(), 42); } // Should forward the void value { @@ -332,9 +356,9 @@ TEST_F(ExecutorTest, test_forwardPromise) { src.set_value(); auto future = src.get_future(); - auto dst = std::make_shared>(); - forwardPromise(dst, &future); - EXPECT_NO_THROW(dst->get_future().get()); + std::promise dst; + forwardPromise(dst, future); + EXPECT_NO_THROW(dst.get_future().get()); } // Should forward the exception { @@ -342,9 +366,9 @@ TEST_F(ExecutorTest, test_forwardPromise) { src.set_exception(std::make_exception_ptr(std::exception())); auto future = src.get_future(); - auto dst = std::make_shared>(); - forwardPromise(dst, &future); - EXPECT_THROW(dst->get_future().get(), std::exception); + std::promise dst; + forwardPromise(dst, future); + EXPECT_THROW(dst.get_future().get(), std::exception); } // Should forward the exception { @@ -352,15 +376,16 @@ TEST_F(ExecutorTest, test_forwardPromise) { src.set_exception(std::make_exception_ptr(std::exception())); auto future = src.get_future(); - auto dst = std::make_shared>(); - forwardPromise(dst, &future); - EXPECT_THROW(dst->get_future().get(), std::exception); + std::promise dst; + forwardPromise(dst, future); + EXPECT_THROW(dst.get_future().get(), std::exception); } } TEST_F(ExecutorTest, test_taskException) { { auto future = executor.submit([] { throw std::exception(); }); + ASSERT_TRUE(future.valid()); EXPECT_THROW(future.get(), std::exception); } { @@ -370,10 +395,133 @@ TEST_F(ExecutorTest, test_taskException) { return param; }, 42); + ASSERT_TRUE(future.valid()); EXPECT_THROW(future.get(), std::runtime_error); } } +/// Verify that empty function is not accepted by executor using movable function. +TEST_F(ExecutorTest, test_executeEmptyMove) { + std::function fn; + ASSERT_FALSE(executor.execute(std::move(fn))); +} + +/// Verify that empty function is not accepted by executor using const reference function. +TEST_F(ExecutorTest, test_executeEmptyRef) { + const std::function fn; + ASSERT_FALSE(executor.execute(fn)); +} + +/// Verify that after task execution, the lambda is released if movable function is used. +TEST_F(ExecutorTest, test_executeLambdaMove) { + std::mutex mutex; + std::condition_variable cond; + volatile bool executed = false; + volatile bool canExecute = false; + volatile bool started = false; + std::error_condition error; + + auto shared = std::make_shared(); + auto weak = std::weak_ptr(shared); + auto lambda = [&, shared] { + std::unique_lock lock{mutex}; + started = true; + cond.notify_all(); + if (cond.wait_for(lock, EXECUTOR_SIGNAL_WAIT_TIMEOUT, [&] { return canExecute; })) { + executed = true; + } else { + error = std::errc::timed_out; + } + (void)&shared; + }; + + // Release strong reference and verify weak one is still valid (hold by lambda). + shared.reset(); + ASSERT_FALSE(shared); + ASSERT_TRUE(weak.lock()); + + // Initiate execution but block executor thread in lambda. + ASSERT_TRUE(executor.execute(std::move(lambda))); + // Ensure lambda execution has started and blocked. If there is a bug in lambda, the executed flag may be set only + // if we give enough time for new thread start. + { + std::unique_lock lock{mutex}; + ASSERT_TRUE(cond.wait_for(lock, EXECUTOR_SIGNAL_WAIT_TIMEOUT, [&] { return started; })); + } + ASSERT_FALSE(executed); + + // Check the reference is still valid. + ASSERT_TRUE(weak.lock()); + + // Allow lambda to complete + { + std::unique_lock lock{mutex}; + canExecute = true; + } + cond.notify_all(); + executor.waitForSubmittedTasks(); + + // Verify the task is completed and shared object is released. + ASSERT_TRUE(executed); + ASSERT_FALSE(weak.lock()); + ASSERT_FALSE(error); +} + +/// Verify that after task execution, the lambda is not released if movable function is not used. +TEST_F(ExecutorTest, test_executeLambdaRef) { + std::mutex mutex; + std::condition_variable cond; + volatile bool executed = false; + volatile bool canExecute = false; + volatile bool started = false; + std::error_condition error; + + auto shared = std::make_shared(); + auto weak = std::weak_ptr(shared); + const auto lambda = [&, shared] { + std::unique_lock lock{mutex}; + started = true; + cond.notify_all(); + if (cond.wait_for(lock, EXECUTOR_SIGNAL_WAIT_TIMEOUT, [&] { return canExecute; })) { + executed = true; + } else { + error = std::errc::timed_out; + } + (void)&shared; + }; + + // Release strong reference and verify weak one is still valid (hold by lambda). + shared.reset(); + ASSERT_FALSE(shared); + ASSERT_TRUE(weak.lock()); + + // Initiate execution but block executor thread in lambda. + ASSERT_TRUE(executor.execute(lambda)); + // Ensure lambda execution has started and blocked. If there is a bug in lambda, the executed flag may be set only + // if we give enough time for new thread start. + { + std::unique_lock lock{mutex}; + ASSERT_TRUE(cond.wait_for(lock, EXECUTOR_SIGNAL_WAIT_TIMEOUT, [&] { return started; })); + } + ASSERT_FALSE(executed); + + // Check the reference is still valid. + ASSERT_TRUE(weak.lock()); + + // Allow lambda to complete + { + std::unique_lock lock{mutex}; + canExecute = true; + } + cond.notify_all(); + executor.waitForSubmittedTasks(); + + // Verify task is completed and we still have shared object through lambda + ASSERT_TRUE(executed); + ASSERT_TRUE(weak.lock()); + ASSERT_FALSE(error); +} + } // namespace test } // namespace threading } // namespace utils diff --git a/AVSCommon/Utils/test/FileSystemUtilsTest.cpp b/AVSCommon/Utils/test/FileSystemUtilsTest.cpp index 1a91f61335..b9c18dcf4a 100644 --- a/AVSCommon/Utils/test/FileSystemUtilsTest.cpp +++ b/AVSCommon/Utils/test/FileSystemUtilsTest.cpp @@ -41,7 +41,7 @@ class FileSystemUtilsTest : public ::testing::Test { createDirectory(WORKING_DIR); ASSERT_TRUE(exists(WORKING_DIR)); -#if defined(__linux__) or defined(__APPLE__) +#if defined(__linux__) || defined(__APPLE__) // on some OS, the temp path is symbolically linked, which can cause issues for prefix tests // to accommodate this, get the realpath of the temp directory char resolved_path[PATH_MAX + 1]; diff --git a/AVSCommon/Utils/test/GmockExtensionTest.cpp b/AVSCommon/Utils/test/GmockExtensionTest.cpp new file mode 100644 index 0000000000..2064840f20 --- /dev/null +++ b/AVSCommon/Utils/test/GmockExtensionTest.cpp @@ -0,0 +1,221 @@ +/* + * 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 + +namespace alexaClientSDK { +namespace test { + +using namespace ::testing; + +/** + * @brief Interface with noexcept and const noexcept methods. + */ +class SomeInterface { +public: + virtual ~SomeInterface() noexcept = default; + virtual int noexceptMethod0() noexcept = 0; + virtual int noexceptMethod1(int) noexcept = 0; + virtual int noexceptMethod2(int, int) noexcept = 0; + virtual int noexceptMethod3(int, int, int) noexcept = 0; + virtual int noexceptMethod4(int, int, int, int) noexcept = 0; + virtual int noexceptMethod5(int, int, int, int, int) noexcept = 0; + virtual int noexceptMethod6(int, int, int, int, int, int) noexcept = 0; + virtual int noexceptMethod7(int, int, int, int, int, int, int) noexcept = 0; + virtual int noexceptMethod8(int, int, int, int, int, int, int, int) noexcept = 0; + virtual int noexceptMethod9(int, int, int, int, int, int, int, int, int) noexcept = 0; + virtual int noexceptMethod10(int, int, int, int, int, int, int, int, int, int) noexcept = 0; + virtual int constNoexceptMethod0() const noexcept = 0; + virtual int constNoexceptMethod1(int) const noexcept = 0; + virtual int constNoexceptMethod2(int, int) const noexcept = 0; + virtual int constNoexceptMethod3(int, int, int) const noexcept = 0; + virtual int constNoexceptMethod4(int, int, int, int) const noexcept = 0; + virtual int constNoexceptMethod5(int, int, int, int, int) const noexcept = 0; + virtual int constNoexceptMethod6(int, int, int, int, int, int) const noexcept = 0; + virtual int constNoexceptMethod7(int, int, int, int, int, int, int) const noexcept = 0; + virtual int constNoexceptMethod8(int, int, int, int, int, int, int, int) const noexcept = 0; + virtual int constNoexceptMethod9(int, int, int, int, int, int, int, int, int) const noexcept = 0; + virtual int constNoexceptMethod10(int, int, int, int, int, int, int, int, int, int) const noexcept = 0; +}; + +/** + * @brief Mock for @c SomeInterface. + */ +class SomeMock : public SomeInterface { +public: + MOCK_NOEXCEPT_METHOD0(noexceptMethod0, int()); + MOCK_NOEXCEPT_METHOD1(noexceptMethod1, int(int)); + MOCK_NOEXCEPT_METHOD2(noexceptMethod2, int(int, int)); + MOCK_NOEXCEPT_METHOD3(noexceptMethod3, int(int, int, int)); + MOCK_NOEXCEPT_METHOD4(noexceptMethod4, int(int, int, int, int)); + MOCK_NOEXCEPT_METHOD5(noexceptMethod5, int(int, int, int, int, int)); + MOCK_NOEXCEPT_METHOD6(noexceptMethod6, int(int, int, int, int, int, int)); + MOCK_NOEXCEPT_METHOD7(noexceptMethod7, int(int, int, int, int, int, int, int)); + MOCK_NOEXCEPT_METHOD8(noexceptMethod8, int(int, int, int, int, int, int, int, int)); + MOCK_NOEXCEPT_METHOD9(noexceptMethod9, int(int, int, int, int, int, int, int, int, int)); + MOCK_NOEXCEPT_METHOD10(noexceptMethod10, int(int, int, int, int, int, int, int, int, int, int)); + + MOCK_CONST_NOEXCEPT_METHOD0(constNoexceptMethod0, int()); + MOCK_CONST_NOEXCEPT_METHOD1(constNoexceptMethod1, int(int)); + MOCK_CONST_NOEXCEPT_METHOD2(constNoexceptMethod2, int(int, int)); + MOCK_CONST_NOEXCEPT_METHOD3(constNoexceptMethod3, int(int, int, int)); + MOCK_CONST_NOEXCEPT_METHOD4(constNoexceptMethod4, int(int, int, int, int)); + MOCK_CONST_NOEXCEPT_METHOD5(constNoexceptMethod5, int(int, int, int, int, int)); + MOCK_CONST_NOEXCEPT_METHOD6(constNoexceptMethod6, int(int, int, int, int, int, int)); + MOCK_CONST_NOEXCEPT_METHOD7(constNoexceptMethod7, int(int, int, int, int, int, int, int)); + MOCK_CONST_NOEXCEPT_METHOD8(constNoexceptMethod8, int(int, int, int, int, int, int, int, int)); + MOCK_CONST_NOEXCEPT_METHOD9(constNoexceptMethod9, int(int, int, int, int, int, int, int, int, int)); + MOCK_CONST_NOEXCEPT_METHOD10(constNoexceptMethod10, int(int, int, int, int, int, int, int, int, int, int)); +}; + +/** + * @brief Test fixture for testing GMock extensions. + */ +class GmockExtensionsTest : public ::testing::Test {}; + +TEST_F(GmockExtensionsTest, test_noexceptMethod0) { + SomeMock mock; + EXPECT_CALL(mock, noexceptMethod0()).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.noexceptMethod0()); +}; + +TEST_F(GmockExtensionsTest, test_noexceptMethod1) { + SomeMock mock; + EXPECT_CALL(mock, noexceptMethod1(1)).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.noexceptMethod1(1)); +}; + +TEST_F(GmockExtensionsTest, test_noexceptMethod2) { + SomeMock mock; + EXPECT_CALL(mock, noexceptMethod2(1, 2)).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.noexceptMethod2(1, 2)); +}; + +TEST_F(GmockExtensionsTest, test_noexceptMethod3) { + SomeMock mock; + EXPECT_CALL(mock, noexceptMethod3(1, 2, 3)).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.noexceptMethod3(1, 2, 3)); +}; + +TEST_F(GmockExtensionsTest, test_noexceptMethod4) { + SomeMock mock; + EXPECT_CALL(mock, noexceptMethod4(1, 2, 3, 4)).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.noexceptMethod4(1, 2, 3, 4)); +}; + +TEST_F(GmockExtensionsTest, test_noexceptMethod5) { + SomeMock mock; + EXPECT_CALL(mock, noexceptMethod5(1, 2, 3, 4, 5)).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.noexceptMethod5(1, 2, 3, 4, 5)); +}; + +TEST_F(GmockExtensionsTest, test_noexceptMethod6) { + SomeMock mock; + EXPECT_CALL(mock, noexceptMethod6(1, 2, 3, 4, 5, 6)).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.noexceptMethod6(1, 2, 3, 4, 5, 6)); +}; + +TEST_F(GmockExtensionsTest, test_noexceptMethod7) { + SomeMock mock; + EXPECT_CALL(mock, noexceptMethod7(1, 2, 3, 4, 5, 6, 7)).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.noexceptMethod7(1, 2, 3, 4, 5, 6, 7)); +}; + +TEST_F(GmockExtensionsTest, test_noexceptMethod8) { + SomeMock mock; + EXPECT_CALL(mock, noexceptMethod8(1, 2, 3, 4, 5, 6, 7, 8)).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.noexceptMethod8(1, 2, 3, 4, 5, 6, 7, 8)); +}; + +TEST_F(GmockExtensionsTest, test_noexceptMethod9) { + SomeMock mock; + EXPECT_CALL(mock, noexceptMethod9(1, 2, 3, 4, 5, 6, 7, 8, 9)).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.noexceptMethod9(1, 2, 3, 4, 5, 6, 7, 8, 9)); +}; + +TEST_F(GmockExtensionsTest, test_noexceptMethod10) { + SomeMock mock; + EXPECT_CALL(mock, noexceptMethod10(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.noexceptMethod10(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); +}; + +TEST_F(GmockExtensionsTest, test_constNoexceptMethod0) { + SomeMock mock; + EXPECT_CALL(mock, constNoexceptMethod0()).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.constNoexceptMethod0()); +}; + +TEST_F(GmockExtensionsTest, test_constNoexceptMethod1) { + SomeMock mock; + EXPECT_CALL(mock, constNoexceptMethod1(1)).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.constNoexceptMethod1(1)); +}; + +TEST_F(GmockExtensionsTest, test_constNoexceptMethod2) { + SomeMock mock; + EXPECT_CALL(mock, constNoexceptMethod2(1, 2)).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.constNoexceptMethod2(1, 2)); +}; + +TEST_F(GmockExtensionsTest, test_constNoexceptMethod3) { + SomeMock mock; + EXPECT_CALL(mock, noexceptMethod3(1, 2, 3)).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.noexceptMethod3(1, 2, 3)); +}; + +TEST_F(GmockExtensionsTest, test_constNoexceptMethod4) { + SomeMock mock; + EXPECT_CALL(mock, constNoexceptMethod4(1, 2, 3, 4)).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.constNoexceptMethod4(1, 2, 3, 4)); +}; + +TEST_F(GmockExtensionsTest, test_constNoexceptMethod5) { + SomeMock mock; + EXPECT_CALL(mock, constNoexceptMethod5(1, 2, 3, 4, 5)).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.constNoexceptMethod5(1, 2, 3, 4, 5)); +}; + +TEST_F(GmockExtensionsTest, test_constNoexceptMethod6) { + SomeMock mock; + EXPECT_CALL(mock, constNoexceptMethod6(1, 2, 3, 4, 5, 6)).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.constNoexceptMethod6(1, 2, 3, 4, 5, 6)); +}; + +TEST_F(GmockExtensionsTest, test_constNoexceptMethod7) { + SomeMock mock; + EXPECT_CALL(mock, constNoexceptMethod7(1, 2, 3, 4, 5, 6, 7)).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.constNoexceptMethod7(1, 2, 3, 4, 5, 6, 7)); +}; + +TEST_F(GmockExtensionsTest, test_constNoexceptMethod8) { + SomeMock mock; + EXPECT_CALL(mock, constNoexceptMethod8(1, 2, 3, 4, 5, 6, 7, 8)).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.constNoexceptMethod8(1, 2, 3, 4, 5, 6, 7, 8)); +}; + +TEST_F(GmockExtensionsTest, test_constNoexceptMethod9) { + SomeMock mock; + EXPECT_CALL(mock, constNoexceptMethod9(1, 2, 3, 4, 5, 6, 7, 8, 9)).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.constNoexceptMethod9(1, 2, 3, 4, 5, 6, 7, 8, 9)); +}; + +TEST_F(GmockExtensionsTest, test_constNoexceptMethod10) { + SomeMock mock; + EXPECT_CALL(mock, constNoexceptMethod10(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)).Times(1).WillOnce(Return(-5)); + ASSERT_EQ(-5, mock.constNoexceptMethod10(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); +}; + +} // namespace test +} // namespace alexaClientSDK diff --git a/AVSCommon/Utils/test/JSONUtilTest.cpp b/AVSCommon/Utils/test/JSONUtilTest.cpp index 562a527218..3e6268bbd7 100644 --- a/AVSCommon/Utils/test/JSONUtilTest.cpp +++ b/AVSCommon/Utils/test/JSONUtilTest.cpp @@ -293,7 +293,7 @@ TEST_F(JSONUtilTest, test_parseJSONInvalidJSON) { */ TEST_F(JSONUtilTest, test_convertToStringValueWithString) { rapidjson::Value expected; - expected.SetString(STRING_VALUE.c_str(), STRING_VALUE.length()); + expected.SetString(STRING_VALUE.c_str(), static_cast(STRING_VALUE.length())); std::string actual; ASSERT_TRUE(convertToValue(expected, &actual)); ASSERT_EQ(expected.GetString(), actual); @@ -326,7 +326,7 @@ TEST_F(JSONUtilTest, test_convertToStringValueWithInvalidValue) { */ TEST_F(JSONUtilTest, test_convertToStringValueWithNullOutputParam) { rapidjson::Value node; - node.SetString(STRING_VALUE.c_str(), STRING_VALUE.length()); + node.SetString(STRING_VALUE.c_str(), static_cast(STRING_VALUE.length())); std::string* value = nullptr; ASSERT_FALSE(convertToValue(node, value)); } diff --git a/AVSCommon/Utils/test/LibCurlHTTP2ConnectionTest.cpp b/AVSCommon/Utils/test/LibCurlHTTP2ConnectionTest.cpp new file mode 100644 index 0000000000..bbf237d84f --- /dev/null +++ b/AVSCommon/Utils/test/LibCurlHTTP2ConnectionTest.cpp @@ -0,0 +1,68 @@ +/* + * 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 "AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2Connection.h" +#include "AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2Request.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace libcurlUtils { +using namespace ::testing; + +/** + * Derived test class for LibCurlHTTP2Connection that is a friend class to the google test class + */ +class LibCurlHTTP2Connection_Test : public LibcurlHTTP2Connection { +public: + LibCurlHTTP2Connection_Test() = default; + friend class GTEST_TEST_CLASS_NAME_(LibCurlHTTP2ConnectionTest, releaseStream_delete_ok); +}; +/** + * Test fixture class for LibCurlHTTP2Connection + */ + +class LibCurlHTTP2ConnectionTest : public ::testing::Test { +protected: + void SetUp() override; + /// The LibCurlHTTP2Connection to test. + std::shared_ptr m_libCurlHTTP2Connection; +}; + +void LibCurlHTTP2ConnectionTest::SetUp() { + m_libCurlHTTP2Connection = std::make_shared(); +} + +TEST_F(LibCurlHTTP2ConnectionTest, releaseStream_delete_ok) { + // Setting IsStopping bool to true because we do not want network loop to process this request + m_libCurlHTTP2Connection->setIsStopping(); + m_libCurlHTTP2Connection->createMultiHandle(); + http2::HTTP2RequestConfig config{http2::HTTP2RequestType::GET, "www.foo.com", "xyz"}; + config.setConnectionTimeout(std::chrono::seconds(60)); + config.setIntermittentTransferExpected(); + auto req = std::make_shared(config, nullptr, config.getId()); + auto handle = req->getCurlHandle(); + m_libCurlHTTP2Connection->m_activeStreams[handle] = req; + ASSERT_TRUE(m_libCurlHTTP2Connection->m_activeStreams.size()); + LibCurlHTTP2Connection_Test::ActiveStreamMap::iterator iter = m_libCurlHTTP2Connection->m_activeStreams.begin(); + m_libCurlHTTP2Connection->releaseStream(iter); + ASSERT_FALSE(m_libCurlHTTP2Connection->m_activeStreams.size()); +} + +} // namespace libcurlUtils +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK \ No newline at end of file diff --git a/AVSCommon/Utils/test/LoggerTest.cpp b/AVSCommon/Utils/test/LoggerTest.cpp index f2442027b0..6460ad9b52 100644 --- a/AVSCommon/Utils/test/LoggerTest.cpp +++ b/AVSCommon/Utils/test/LoggerTest.cpp @@ -191,7 +191,7 @@ void MockLogger::mockEmit( m_lastText = text; } -MockModuleLogger::MockModuleLogger() : ModuleLogger(ACSDK_STRINGIFY(ACSDK_LOG_SINK)) { +MockModuleLogger::MockModuleLogger() : ModuleLogger("ConsoleLogger") { } MockModuleLogger::~MockModuleLogger() { @@ -231,7 +231,7 @@ void LoggerTest::TearDown() { } void LoggerTest::setLevelExpectations(Level level) { - ACSDK_GET_LOGGER_FUNCTION()->setLevel(level); + getLoggerTestLogger()->setLevel(level); switch (level) { case Level::UNKNOWN: @@ -469,16 +469,19 @@ TEST_F(LoggerTest, test_logNoneLevel) { /** * Test observer mechanism in the MockModuleLogger. Expects that when the logLevel changes for the sink, the - * callback of the MockModuleLogger is triggered. Also make sure any changes to sink's logLevel is ignored - * after the MockModuleLogger's logLevel has been set. + * callback of the MockModuleLogger is triggered. Also make sure any changes to MockModuleLogger's logLevel will + * override set log level of the MockModuleLogger and the MockModuleLogger will ignore further changes to the logLevel + * to the sink. */ TEST_F(LoggerTest, test_moduleLoggerObserver) { MockModuleLogger mockModuleLogger; - getLoggerTestLogger()->setLevel(Level::WARN); + getLoggerTestLogger()->setLevel(Level::ERROR); + ASSERT_EQ(mockModuleLogger.getLogLevel(), Level::ERROR); + mockModuleLogger.setLevel(Level::WARN); ASSERT_EQ(mockModuleLogger.getLogLevel(), Level::WARN); - mockModuleLogger.setLevel(Level::CRITICAL); - ASSERT_EQ(mockModuleLogger.getLogLevel(), Level::CRITICAL); getLoggerTestLogger()->setLevel(Level::NONE); + ASSERT_EQ(mockModuleLogger.getLogLevel(), Level::WARN); + mockModuleLogger.setLevel(Level::NONE); ASSERT_EQ(mockModuleLogger.getLogLevel(), Level::NONE); } @@ -490,20 +493,20 @@ TEST_F(LoggerTest, test_multipleModuleLoggerObservers) { MockModuleLogger mockModuleLogger2; MockModuleLogger mockModuleLogger3; - getLoggerTestLogger()->setLevel(Level::WARN); - ASSERT_EQ(mockModuleLogger1.getLogLevel(), Level::WARN); - ASSERT_EQ(mockModuleLogger2.getLogLevel(), Level::WARN); - ASSERT_EQ(mockModuleLogger3.getLogLevel(), Level::WARN); + getLoggerTestLogger()->setLevel(Level::NONE); + ASSERT_EQ(mockModuleLogger1.getLogLevel(), Level::NONE); + ASSERT_EQ(mockModuleLogger2.getLogLevel(), Level::NONE); + ASSERT_EQ(mockModuleLogger3.getLogLevel(), Level::NONE); mockModuleLogger1.setLevel(Level::CRITICAL); ASSERT_EQ(mockModuleLogger1.getLogLevel(), Level::CRITICAL); - ASSERT_EQ(mockModuleLogger2.getLogLevel(), Level::WARN); - ASSERT_EQ(mockModuleLogger3.getLogLevel(), Level::WARN); - - getLoggerTestLogger()->setLevel(Level::NONE); - ASSERT_EQ(mockModuleLogger1.getLogLevel(), Level::NONE); ASSERT_EQ(mockModuleLogger2.getLogLevel(), Level::NONE); ASSERT_EQ(mockModuleLogger3.getLogLevel(), Level::NONE); + + getLoggerTestLogger()->setLevel(Level::WARN); + ASSERT_EQ(mockModuleLogger1.getLogLevel(), Level::CRITICAL); + ASSERT_EQ(mockModuleLogger2.getLogLevel(), Level::WARN); + ASSERT_EQ(mockModuleLogger3.getLogLevel(), Level::WARN); } #ifdef ACSDK_LOG_ENABLED diff --git a/AVSCommon/Utils/test/MIMEParserTest.cpp b/AVSCommon/Utils/test/MIMEParserTest.cpp index 6a61f14af6..a60cddca4f 100644 --- a/AVSCommon/Utils/test/MIMEParserTest.cpp +++ b/AVSCommon/Utils/test/MIMEParserTest.cpp @@ -315,7 +315,7 @@ void runTestForCombination(std::vector& partition, const std::string& paylo void generateCombinationsAndRunTest( std::vector& partitions, size_t pos, - size_t remaining, + int remaining, const std::string& words, const std::string& boundary) { if (remaining == 0) { @@ -351,7 +351,7 @@ TEST_F(MIMEParserTest, test_multipleCombinations) { for (size_t numberOfPartitions = 1; numberOfPartitions <= maxNumberOfPartitions; numberOfPartitions += numberOfPartitionsIncrement) { std::vector partitions(numberOfPartitions); - generateCombinationsAndRunTest(partitions, 0, payload.size(), payload, "WWWoooAAA"); + generateCombinationsAndRunTest(partitions, 0, static_cast(payload.size()), payload, "WWWoooAAA"); } } @@ -379,12 +379,12 @@ TEST_F(MIMEParserTest, test_fixedSizeGroups) { decoder.onReceiveResponseCode(HTTPResponseCode::SUCCESS_OK); std::vector groups; - int nGroups = (parts.size() + groupSize - 1) / groupSize; - for (int i = 0; i < nGroups; i++) { + size_t nGroups = (parts.size() + groupSize - 1) / groupSize; + for (size_t i = 0; i < nGroups; i++) { groups.push_back(""); } - int currentGroup = 0; + size_t currentGroup = 0; for (size_t i = 0; i < parts.size(); ++i) { if (i != 0 && i % groupSize == 0) { currentGroup++; @@ -480,7 +480,7 @@ TEST_F(MIMEParserTest, test_decodingRandomBoundaries) { const std::string CHARACTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'()+_,-.:=?"; std::random_device random_device; std::mt19937 generator(random_device()); - std::uniform_int_distribution<> distribution(0, CHARACTERS.size() - 1); + std::uniform_int_distribution<> distribution(0, static_cast(CHARACTERS.size() - 1)); for (size_t length = 5; length < 50; length++) { std::string quotedRandomBoundary = QUOTE_CHAR; @@ -523,9 +523,9 @@ TEST_F(MIMEParserTest, test_decodingInvalidBoundaries) { "thisstringhasmorethanseventycharacterssoitsinvalid123123123123123123123", "^invalidchar", "\"^invalidchar\""}; - for (auto boundary : invalid_boundaries) { + for (auto testBoundary : invalid_boundaries) { for (auto header : headerAfterBoundary) { - const std::string boundaryHeader{BOUNDARY_HEADER_PREFIX + boundary + header}; + const std::string boundaryHeader{BOUNDARY_HEADER_PREFIX + testBoundary + header}; auto sink = std::make_shared(); HTTP2MimeResponseDecoder decoder{sink}; ASSERT_FALSE(decoder.onReceiveHeaderLine(boundaryHeader)); @@ -538,9 +538,10 @@ TEST_F(MIMEParserTest, test_decodingInvalidBoundaries) { * Expected Result: pass */ TEST_F(MIMEParserTest, test_decodingBoundaryAfteraNonBoundaryHeader) { - std::string boundary = "myboundary"; - const std::vector headers = {"content-type:nana;myprop:abc\r\n", BOUNDARY_HEADER_PREFIX + boundary}; - generatePayloadAndTest(headers, boundary); + std::string testBoundary = "myboundary"; + const std::vector headers = {"content-type:nana;myprop:abc\r\n", + BOUNDARY_HEADER_PREFIX + testBoundary}; + generatePayloadAndTest(headers, testBoundary); } /* @@ -549,10 +550,10 @@ TEST_F(MIMEParserTest, test_decodingBoundaryAfteraNonBoundaryHeader) { */ TEST_F(MIMEParserTest, test_decodingValidBoundariesWithMoreHeaders) { std::vector headerAfterBoundary = {"", ";otherprop=yes", " somethingelse"}; - std::string boundary = "myboundary"; + std::string testBoundary = "myboundary"; for (auto header : headerAfterBoundary) { - const std::string boundaryHeader{boundary + header}; - generatePayloadAndTest(boundaryHeader, boundary); + const std::string boundaryHeader{testBoundary + header}; + generatePayloadAndTest(boundaryHeader, testBoundary); } } @@ -970,8 +971,8 @@ TEST_F(MIMEParserTest, test_aBORT) { sink->m_abort = true; // setup encoder - std::string boundary = createRandomAlphabetString(10); - HTTP2MimeRequestEncoder encoder{boundary, source}; + std::string testBoundary = createRandomAlphabetString(10); + HTTP2MimeRequestEncoder encoder{testBoundary, source}; char buf[LARGE]; // Ensure repeated calls return ABORT ASSERT_TRUE(encoder.onSendData(buf, SMALL).status == HTTP2SendStatus::ABORT); @@ -1041,8 +1042,8 @@ TEST_F(MIMEParserTest, test_variableChunkSizes) { char buf[XLARGE]; // setup encoder - std::string boundary = createRandomAlphabetString(10); - HTTP2MimeRequestEncoder encoder{boundary, source}; + std::string testBoundary = createRandomAlphabetString(10); + HTTP2MimeRequestEncoder encoder{testBoundary, source}; // setup decoder HTTP2MimeResponseDecoder decoder{sink}; @@ -1075,7 +1076,7 @@ TEST_F(MIMEParserTest, test_variableChunkSizes) { index = 0; pauseCount = 0; HTTP2ReceiveDataStatus status{HTTP2ReceiveDataStatus::SUCCESS}; - const std::string boundaryHeader{BOUNDARY_HEADER_PREFIX + boundary}; + const std::string boundaryHeader{BOUNDARY_HEADER_PREFIX + testBoundary}; bool resp = decoder.onReceiveHeaderLine(boundaryHeader); decoder.onReceiveResponseCode( static_cast::type>(HTTPResponseCode::SUCCESS_OK)); diff --git a/AVSCommon/Utils/test/PooledMediaPlayerFactoryTest.cpp b/AVSCommon/Utils/test/PooledMediaPlayerFactoryTest.cpp index e699e0aca8..2405c9b004 100644 --- a/AVSCommon/Utils/test/PooledMediaPlayerFactoryTest.cpp +++ b/AVSCommon/Utils/test/PooledMediaPlayerFactoryTest.cpp @@ -29,7 +29,7 @@ using namespace avsCommon::utils::mediaPlayer; using namespace avsCommon::utils::mediaPlayer::test; /// String to identify log entries originating from this file. -static const std::string TAG("PooledMediaPlayerFactoryTest"); +#define TAG "PooledMediaPlayerFactoryTest" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/AVSCommon/Utils/test/SafeTimeAccessTest.cpp b/AVSCommon/Utils/test/SafeTimeAccessTest.cpp index c6519f0122..f3dc70055b 100644 --- a/AVSCommon/Utils/test/SafeTimeAccessTest.cpp +++ b/AVSCommon/Utils/test/SafeTimeAccessTest.cpp @@ -142,7 +142,7 @@ static void callSafeCTimeFunction( auto safeCTimeAccess = SafeCTimeAccess::instance(); std::vector> internalResults; for (int i = 0; i < 4; ++i) { - for (time_t t = startingSeed; t < LARGE_TIME_VALUE; t = 1.5 * (t + 1)) { + for (time_t t = startingSeed; t < LARGE_TIME_VALUE; t = static_cast(1.5 * (t + 1))) { std::tm result; switch (type) { case TestType::GMTIME: diff --git a/AVSCommon/Utils/test/SharedDataStreamTest.cpp b/AVSCommon/Utils/test/SharedDataStreamTest.cpp index 791098fcff..cf89d8f6b5 100644 --- a/AVSCommon/Utils/test/SharedDataStreamTest.cpp +++ b/AVSCommon/Utils/test/SharedDataStreamTest.cpp @@ -220,7 +220,7 @@ std::future Source::run( for (size_t word = 0; word < blockSizeWords; ++word) { for (size_t byte = 0; byte < wordSize; ++byte) { size_t byteIndex = word * wordSize + byte; - uint8_t byteValue = m_counter >> (byte % wordSize); + uint8_t byteValue = static_cast(m_counter >> (byte % wordSize)); block[byteIndex] = byteValue; } ++m_counter; @@ -314,7 +314,7 @@ std::future Sink::run( for (ssize_t word = 0; word < nWords; ++word) { for (size_t byte = 0; byte < wordSize; ++byte) { size_t byteIndex = word * wordSize + byte; - uint8_t byteValue = m_counter >> (byte % wordSize); + uint8_t byteValue = static_cast(m_counter >> (byte % wordSize)); ASSERT_EQ(block[byteIndex], byteValue); } ++m_counter; @@ -714,7 +714,7 @@ TEST_F(SharedDataStreamTest, test_readerSeek) { Sds::Index writerPos = 0; uint8_t writeBuf[WORDSIZE * WORDCOUNT]; for (size_t i = 0; i < sizeof(writeBuf); ++i) { - writeBuf[i] = i; + writeBuf[i] = static_cast(i); } size_t writeWords = WORDCOUNT / 2; ASSERT_EQ(writer->write(writeBuf, writeWords), static_cast(writeWords)); diff --git a/AVSCommon/Utils/test/TaskThreadTest.cpp b/AVSCommon/Utils/test/TaskThreadTest.cpp index d87c3973b3..6a277e1074 100644 --- a/AVSCommon/Utils/test/TaskThreadTest.cpp +++ b/AVSCommon/Utils/test/TaskThreadTest.cpp @@ -27,6 +27,10 @@ namespace test { /// Timeout used while waiting for synchronization events. const std::chrono::milliseconds MY_WAIT_TIMEOUT{100}; +/// Default thread moniker to use in tests. +const auto THREAD_MONIKER = "1a1"; +/// Another thread moniker to use in tests. +const auto THREAD_MONIKER2 = "1a2"; using namespace logger; @@ -39,7 +43,7 @@ TEST(TaskThreadTest, test_waitForNothing) { TEST(TaskThreadTest, test_startFailsDueToEmptyFunction) { TaskThread taskThread; std::function emptyFunction; - EXPECT_FALSE(taskThread.start(emptyFunction)); + EXPECT_FALSE(taskThread.start(emptyFunction, THREAD_MONIKER)); } /// Test that start will trigger the provided job and thread will exit once the job is done and return @c false. @@ -54,7 +58,7 @@ TEST(TaskThreadTest, test_simpleJob) { { TaskThread taskThread; - EXPECT_TRUE(taskThread.start(simpleJob)); + EXPECT_TRUE(taskThread.start(simpleJob, THREAD_MONIKER)); EXPECT_TRUE(waitEvent.wait(MY_WAIT_TIMEOUT)); } @@ -78,7 +82,7 @@ TEST(TaskThreadTest, test_sequenceJobs) { { TaskThread taskThread; - EXPECT_TRUE(taskThread.start(jobSequence)); + EXPECT_TRUE(taskThread.start(jobSequence, THREAD_MONIKER)); EXPECT_TRUE(waitEvent.wait(MY_WAIT_TIMEOUT)); } @@ -108,10 +112,10 @@ TEST(TaskThreadTest, test_startNewJob) { }; TaskThread taskThread; - EXPECT_TRUE(taskThread.start(increment)); + EXPECT_TRUE(taskThread.start(increment, THREAD_MONIKER)); EXPECT_TRUE(waitEvent.wait(MY_WAIT_TIMEOUT)); - EXPECT_TRUE(taskThread.start(decrement)); + EXPECT_TRUE(taskThread.start(decrement, THREAD_MONIKER)); EXPECT_TRUE(waitEvent2.wait(MY_WAIT_TIMEOUT)); EXPECT_TRUE(taskCounter == 0); } @@ -126,11 +130,11 @@ TEST(TaskThreadTest, testTimer_startFailDueTooManyThreads) { }; TaskThread taskThread; - EXPECT_TRUE(taskThread.start(simpleJob)); + EXPECT_TRUE(taskThread.start(simpleJob, THREAD_MONIKER)); // Wait until first job has started. - waitStart.wait(MY_WAIT_TIMEOUT); - EXPECT_TRUE(taskThread.start([] { return false; })); + EXPECT_TRUE(waitStart.wait(MY_WAIT_TIMEOUT)); + EXPECT_TRUE(taskThread.start([] { return false; }, THREAD_MONIKER)); // Starting a thread again immediately should fail, unless the system is so fast in starting // the thread on the other core that it starts and runs a few instructions before this can @@ -138,7 +142,7 @@ TEST(TaskThreadTest, testTimer_startFailDueTooManyThreads) { int threadStartCount; for (threadStartCount = 0; threadStartCount < 100; threadStartCount++) { // This should fail since the task thread is starting. - if (!taskThread.start([] { return false; })) { + if (!taskThread.start([] { return false; }, THREAD_MONIKER)) { break; } } @@ -147,7 +151,7 @@ TEST(TaskThreadTest, testTimer_startFailDueTooManyThreads) { waitEnqueue.wakeUp(); } -/// Test that threads related to this task thread will always have the same moniker. +/// Test that threads related to this task thread will always have specified moniker. TEST(TaskThreadTest, DISABLED_test_moniker) { WaitEvent waitGetMoniker, waitValidateMoniker; std::string moniker; @@ -157,18 +161,22 @@ TEST(TaskThreadTest, DISABLED_test_moniker) { return false; }; - auto validateMoniker = [&moniker, &waitValidateMoniker] { - EXPECT_EQ(moniker, ThreadMoniker::getThisThreadMoniker()); + std::string moniker2; + auto validateMoniker = [&moniker2, &waitValidateMoniker] { + moniker2 = ThreadMoniker::getThisThreadMoniker(); waitValidateMoniker.wakeUp(); return false; }; TaskThread taskThread; - EXPECT_TRUE(taskThread.start(getMoniker)); - waitGetMoniker.wait(MY_WAIT_TIMEOUT); + EXPECT_TRUE(taskThread.start(getMoniker, THREAD_MONIKER)); + EXPECT_TRUE(waitGetMoniker.wait(MY_WAIT_TIMEOUT)); - EXPECT_TRUE(taskThread.start(validateMoniker)); - waitValidateMoniker.wait(MY_WAIT_TIMEOUT); + EXPECT_TRUE(taskThread.start(validateMoniker, THREAD_MONIKER2)); + EXPECT_TRUE(waitValidateMoniker.wait(MY_WAIT_TIMEOUT)); + + EXPECT_EQ(THREAD_MONIKER, moniker); + EXPECT_EQ(THREAD_MONIKER2, moniker2); } /// Test that threads from different @c TaskThreads will have different monikers. @@ -183,19 +191,23 @@ TEST(TaskThreadTest, test_monikerDifferentObjects) { return false; }; - auto validateMoniker = [&moniker, &waitValidateMoniker] { - EXPECT_NE(moniker, ThreadMoniker::getThisThreadMoniker()); + std::string moniker2; + auto validateMoniker = [&moniker2, &waitValidateMoniker] { + moniker2 = ThreadMoniker::getThisThreadMoniker(); waitValidateMoniker.wakeUp(); return false; }; TaskThread taskThread1; TaskThread taskThread2; - EXPECT_TRUE(taskThread1.start(getMoniker)); - EXPECT_TRUE(taskThread2.start(validateMoniker)); + EXPECT_TRUE(taskThread1.start(getMoniker, THREAD_MONIKER)); + EXPECT_TRUE(taskThread2.start(validateMoniker, THREAD_MONIKER2)); waitThread2Start.wakeUp(); - waitGetMoniker.wait(MY_WAIT_TIMEOUT); - waitValidateMoniker.wait(MY_WAIT_TIMEOUT); + EXPECT_TRUE(waitGetMoniker.wait(MY_WAIT_TIMEOUT)); + EXPECT_TRUE(waitValidateMoniker.wait(MY_WAIT_TIMEOUT)); + + EXPECT_EQ(THREAD_MONIKER, moniker); + EXPECT_EQ(THREAD_MONIKER2, moniker2); } } // namespace test diff --git a/AVSCommon/Utils/test/TimeUtilsTest.cpp b/AVSCommon/Utils/test/TimeUtilsTest.cpp index b05456dcc5..066b2d5831 100644 --- a/AVSCommon/Utils/test/TimeUtilsTest.cpp +++ b/AVSCommon/Utils/test/TimeUtilsTest.cpp @@ -60,6 +60,32 @@ TEST(TimeTest, test_iso8601StringConversion) { ASSERT_EQ(static_cast(sec.count()), unixTime); } +TEST(TimeTest, test_iso8601StringConversionUTC) { + TimeUtils timeUtils; + std::string iso8601Str{"1986-08-10T21:30:00Z"}; + int64_t unixTime; + auto successUnix = timeUtils.convert8601TimeStringToUnix(iso8601Str, &unixTime); + ASSERT_TRUE(successUnix); + + std::chrono::system_clock::time_point utcTimePoint; + auto successUtcTimePoint = timeUtils.convert8601TimeStringToUtcTimePoint(iso8601Str, &utcTimePoint); + ASSERT_TRUE(successUtcTimePoint); + + time_t dateTimeT = std::chrono::system_clock::to_time_t(utcTimePoint); + auto sec = std::chrono::duration_cast(utcTimePoint.time_since_epoch()); + ASSERT_EQ(static_cast(sec.count()), unixTime); + + std::tm dateTm; + auto safeCTimeAccess = SafeCTimeAccess::instance(); + ASSERT_TRUE(safeCTimeAccess->getGmtime(dateTimeT, &dateTm)); + ASSERT_EQ(dateTm.tm_year, 86); + ASSERT_EQ(dateTm.tm_mon, 7); + ASSERT_EQ(dateTm.tm_mday, 10); + ASSERT_EQ(dateTm.tm_hour, 21); + ASSERT_EQ(dateTm.tm_min, 30); + ASSERT_EQ(dateTm.tm_sec, 0); +} + TEST(TimeTest, test_stringConversionError) { TimeUtils timeUtils; std::string dateStr{"1986-8-10T21:30:00+0000"}; diff --git a/AVSCommon/Utils/test/TimerTest.cpp b/AVSCommon/Utils/test/TimerTest.cpp index 09e30d926f..0bf9966db7 100644 --- a/AVSCommon/Utils/test/TimerTest.cpp +++ b/AVSCommon/Utils/test/TimerTest.cpp @@ -18,7 +18,7 @@ #include #include -#include "AVSCommon/Utils/Timing/Timer.h" +#include namespace alexaClientSDK { namespace avsCommon { @@ -111,7 +111,7 @@ class TimerTest : public ::testing::Test { }; void TimerTest::SetUp() { - m_timer = std::shared_ptr(new Timer); + m_timer = std::make_shared(); } void TimerTest::simpleTask(std::chrono::milliseconds duration) { diff --git a/AVSCommon/Utils/test/UUIDGenerationTest.cpp b/AVSCommon/Utils/test/UUIDGenerationTest.cpp index 9d7371d7be..e22e26cb45 100644 --- a/AVSCommon/Utils/test/UUIDGenerationTest.cpp +++ b/AVSCommon/Utils/test/UUIDGenerationTest.cpp @@ -157,7 +157,7 @@ TEST_F(UUIDGenerationTest, test_multipleConcurrentSaltSettings) { } for (auto& future : uuidRequesters) { - unsigned int prevSizeOfSet = uuidsGenerated.size(); + auto prevSizeOfSet = uuidsGenerated.size(); auto uuid = future.get(); uuidsGenerated.insert(uuid); ASSERT_EQ(UUID_LENGTH, uuid.length()); @@ -186,7 +186,7 @@ TEST_F(UUIDGenerationTest, test_multipleRequests) { std::unordered_set uuidsGenerated; for (unsigned int i = 0; i < MAX_UUIDS_TO_GENERATE; ++i) { - unsigned int prevSizeOfSet = uuidsGenerated.size(); + auto prevSizeOfSet = uuidsGenerated.size(); auto uuid = generateUUID(); uuidsGenerated.insert(uuid); ASSERT_EQ(UUID_LENGTH, uuid.length()); @@ -211,7 +211,7 @@ TEST_F(UUIDGenerationTest, test_multipleConcurrentRequests) { } for (auto& future : uuidRequesters) { - unsigned int prevSizeOfSet = uuidsGenerated.size(); + auto prevSizeOfSet = uuidsGenerated.size(); auto uuid = future.get(); uuidsGenerated.insert(uuid); ASSERT_EQ(UUID_LENGTH, uuid.length()); diff --git a/AVSCommon/Utils/test/WorkerThreadTest.cpp b/AVSCommon/Utils/test/WorkerThreadTest.cpp index b7d595b0c5..0d6dd6a1a3 100644 --- a/AVSCommon/Utils/test/WorkerThreadTest.cpp +++ b/AVSCommon/Utils/test/WorkerThreadTest.cpp @@ -35,7 +35,6 @@ TEST(TaskThreadTest, test_runWorkSeveralTimes) { std::atomic_int count{0}; WaitEvent waitEvent; WorkerThread workerThread; - EXPECT_TRUE(!workerThread.getMoniker().empty()); for (int i = 1; i <= 10; i++) { waitEvent.reset(); diff --git a/AVSCommon/Utils/test/acsdk/Test/GmockExtensions.h b/AVSCommon/Utils/test/acsdk/Test/GmockExtensions.h new file mode 100644 index 0000000000..652eb80cd0 --- /dev/null +++ b/AVSCommon/Utils/test/acsdk/Test/GmockExtensions.h @@ -0,0 +1,361 @@ +/* + * 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. + */ + +#ifndef ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_TEST_ACSDK_TEST_GMOCKEXTENSIONS_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_TEST_ACSDK_TEST_GMOCKEXTENSIONS_H_ + +#include + +/** + * @brief Internal macro for constructing method name. + * + * Macro constructs method name in a manner similar to GMOCK_MOCKER_. + * + * @param arity Parameter count. + * @param constness Optional @a const specifier. + * @param Method Method name. + * + * @return Method name. + */ +#define GMOCK_NOEXCEPT_MOCKER_(arity, constness, Method) \ + GTEST_CONCAT_TOKEN_(gmock##constness##noexcept##arity##_##Method##_, __LINE__) + +/** + * @{ + * @brief Internal macro for declaring and defining mock method. + * + * Macro declares and defines mock method with @a noexcept specifier. Otherwise it works similarly to macros + * GMOCK_METHOD0_ ... GMOCK_METHOD10_. + * + * @param tn Number of arguments. + * @param constness Optional @a const specifier. + * @param Method Method name. + */ +#define GMOCK_NOEXCEPT_METHOD0_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method() constness noexcept { \ + GTEST_COMPILE_ASSERT_( \ + (::testing::tuple_size::ArgumentTuple>::value == 0), \ + this_method_does_not_take_0_arguments); \ + GMOCK_NOEXCEPT_MOCKER_(0, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_NOEXCEPT_MOCKER_(0, constness, Method).Invoke(); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& gmock_##Method() constness noexcept { \ + GMOCK_NOEXCEPT_MOCKER_(0, constness, Method).RegisterOwner(this); \ + return GMOCK_NOEXCEPT_MOCKER_(0, constness, Method).With(); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_NOEXCEPT_MOCKER_(0, constness, Method) + +#define GMOCK_NOEXCEPT_METHOD1_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method(GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1) constness noexcept { \ + GTEST_COMPILE_ASSERT_( \ + (::testing::tuple_size::ArgumentTuple>::value == 1), \ + this_method_does_not_take_1_argument); \ + GMOCK_NOEXCEPT_MOCKER_(1, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_NOEXCEPT_MOCKER_(1, constness, Method).Invoke(gmock_a1); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1) constness noexcept { \ + GMOCK_NOEXCEPT_MOCKER_(1, constness, Method).RegisterOwner(this); \ + return GMOCK_NOEXCEPT_MOCKER_(1, constness, Method).With(gmock_a1); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_NOEXCEPT_MOCKER_(1, constness, Method) + +#define GMOCK_NOEXCEPT_METHOD2_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) \ + ct Method(GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2) constness noexcept { \ + GTEST_COMPILE_ASSERT_( \ + (::testing::tuple_size::ArgumentTuple>::value == 2), \ + this_method_does_not_take_2_arguments); \ + GMOCK_NOEXCEPT_MOCKER_(2, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_NOEXCEPT_MOCKER_(2, constness, Method).Invoke(gmock_a1, gmock_a2); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& gmock_##Method( \ + GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2) constness noexcept { \ + GMOCK_NOEXCEPT_MOCKER_(2, constness, Method).RegisterOwner(this); \ + return GMOCK_NOEXCEPT_MOCKER_(2, constness, Method).With(gmock_a1, gmock_a2); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_NOEXCEPT_MOCKER_(2, constness, Method) + +#define GMOCK_NOEXCEPT_METHOD3_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) \ + ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3) constness noexcept { \ + GTEST_COMPILE_ASSERT_( \ + (::testing::tuple_size::ArgumentTuple>::value == 3), \ + this_method_does_not_take_3_arguments); \ + GMOCK_NOEXCEPT_MOCKER_(3, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_NOEXCEPT_MOCKER_(3, constness, Method).Invoke(gmock_a1, gmock_a2, gmock_a3); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& gmock_##Method( \ + GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3) constness noexcept { \ + GMOCK_NOEXCEPT_MOCKER_(3, constness, Method).RegisterOwner(this); \ + return GMOCK_NOEXCEPT_MOCKER_(3, constness, Method).With(gmock_a1, gmock_a2, gmock_a3); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_NOEXCEPT_MOCKER_(3, constness, Method) + +#define GMOCK_NOEXCEPT_METHOD4_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) \ + ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4) constness noexcept { \ + GTEST_COMPILE_ASSERT_( \ + (::testing::tuple_size::ArgumentTuple>::value == 4), \ + this_method_does_not_take_4_arguments); \ + GMOCK_NOEXCEPT_MOCKER_(4, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_NOEXCEPT_MOCKER_(4, constness, Method).Invoke(gmock_a1, gmock_a2, gmock_a3, gmock_a4); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& gmock_##Method( \ + GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4) constness noexcept { \ + GMOCK_NOEXCEPT_MOCKER_(4, constness, Method).RegisterOwner(this); \ + return GMOCK_NOEXCEPT_MOCKER_(4, constness, Method).With(gmock_a1, gmock_a2, gmock_a3, gmock_a4); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_NOEXCEPT_MOCKER_(4, constness, Method) + +#define GMOCK_NOEXCEPT_METHOD5_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) \ + ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5) constness noexcept { \ + GTEST_COMPILE_ASSERT_( \ + (::testing::tuple_size::ArgumentTuple>::value == 5), \ + this_method_does_not_take_5_arguments); \ + GMOCK_NOEXCEPT_MOCKER_(5, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_NOEXCEPT_MOCKER_(5, constness, Method).Invoke(gmock_a1, gmock_a2, gmock_a3, gmock_a4, gmock_a5); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& gmock_##Method( \ + GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5) constness noexcept { \ + GMOCK_NOEXCEPT_MOCKER_(5, constness, Method).RegisterOwner(this); \ + return GMOCK_NOEXCEPT_MOCKER_(5, constness, Method).With(gmock_a1, gmock_a2, gmock_a3, gmock_a4, gmock_a5); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_NOEXCEPT_MOCKER_(5, constness, Method) + +#define GMOCK_NOEXCEPT_METHOD6_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) \ + ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6) constness noexcept { \ + GTEST_COMPILE_ASSERT_( \ + (::testing::tuple_size::ArgumentTuple>::value == 6), \ + this_method_does_not_take_6_arguments); \ + GMOCK_NOEXCEPT_MOCKER_(6, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_NOEXCEPT_MOCKER_(6, constness, Method) \ + .Invoke(gmock_a1, gmock_a2, gmock_a3, gmock_a4, gmock_a5, gmock_a6); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& gmock_##Method( \ + GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6) constness noexcept { \ + GMOCK_NOEXCEPT_MOCKER_(6, constness, Method).RegisterOwner(this); \ + return GMOCK_NOEXCEPT_MOCKER_(6, constness, Method) \ + .With(gmock_a1, gmock_a2, gmock_a3, gmock_a4, gmock_a5, gmock_a6); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_NOEXCEPT_MOCKER_(6, constness, Method) + +#define GMOCK_NOEXCEPT_METHOD7_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) \ + ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7) constness noexcept { \ + GTEST_COMPILE_ASSERT_( \ + (::testing::tuple_size::ArgumentTuple>::value == 7), \ + this_method_does_not_take_7_arguments); \ + GMOCK_NOEXCEPT_MOCKER_(7, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_NOEXCEPT_MOCKER_(7, constness, Method) \ + .Invoke(gmock_a1, gmock_a2, gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& gmock_##Method( \ + GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7) constness noexcept { \ + GMOCK_NOEXCEPT_MOCKER_(7, constness, Method).RegisterOwner(this); \ + return GMOCK_NOEXCEPT_MOCKER_(7, constness, Method) \ + .With(gmock_a1, gmock_a2, gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_NOEXCEPT_MOCKER_(7, constness, Method) + +#define GMOCK_NOEXCEPT_METHOD8_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) \ + ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, \ + GMOCK_ARG_(tn, 8, __VA_ARGS__) gmock_a8) constness noexcept { \ + GTEST_COMPILE_ASSERT_( \ + (::testing::tuple_size::ArgumentTuple>::value == 8), \ + this_method_does_not_take_8_arguments); \ + GMOCK_NOEXCEPT_MOCKER_(8, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_NOEXCEPT_MOCKER_(8, constness, Method) \ + .Invoke(gmock_a1, gmock_a2, gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& gmock_##Method( \ + GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7, \ + GMOCK_MATCHER_(tn, 8, __VA_ARGS__) gmock_a8) constness noexcept { \ + GMOCK_NOEXCEPT_MOCKER_(8, constness, Method).RegisterOwner(this); \ + return GMOCK_NOEXCEPT_MOCKER_(8, constness, Method) \ + .With(gmock_a1, gmock_a2, gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_NOEXCEPT_MOCKER_(8, constness, Method) + +#define GMOCK_NOEXCEPT_METHOD9_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) \ + ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, \ + GMOCK_ARG_(tn, 8, __VA_ARGS__) gmock_a8, \ + GMOCK_ARG_(tn, 9, __VA_ARGS__) gmock_a9) constness noexcept { \ + GTEST_COMPILE_ASSERT_( \ + (::testing::tuple_size::ArgumentTuple>::value == 9), \ + this_method_does_not_take_9_arguments); \ + GMOCK_NOEXCEPT_MOCKER_(9, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_NOEXCEPT_MOCKER_(9, constness, Method) \ + .Invoke(gmock_a1, gmock_a2, gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, gmock_a9); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& gmock_##Method( \ + GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7, \ + GMOCK_MATCHER_(tn, 8, __VA_ARGS__) gmock_a8, \ + GMOCK_MATCHER_(tn, 9, __VA_ARGS__) gmock_a9) constness noexcept { \ + GMOCK_NOEXCEPT_MOCKER_(9, constness, Method).RegisterOwner(this); \ + return GMOCK_NOEXCEPT_MOCKER_(9, constness, Method) \ + .With(gmock_a1, gmock_a2, gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, gmock_a9); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_NOEXCEPT_MOCKER_(9, constness, Method) + +#define GMOCK_NOEXCEPT_METHOD10_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) \ + ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, \ + GMOCK_ARG_(tn, 8, __VA_ARGS__) gmock_a8, \ + GMOCK_ARG_(tn, 9, __VA_ARGS__) gmock_a9, \ + GMOCK_ARG_(tn, 10, __VA_ARGS__) gmock_a10) constness noexcept { \ + GTEST_COMPILE_ASSERT_( \ + (::testing::tuple_size::ArgumentTuple>::value == 10), \ + this_method_does_not_take_10_arguments); \ + GMOCK_NOEXCEPT_MOCKER_(10, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_NOEXCEPT_MOCKER_(10, constness, Method) \ + .Invoke( \ + gmock_a1, gmock_a2, gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, gmock_a9, gmock_a10); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& gmock_##Method( \ + GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7, \ + GMOCK_MATCHER_(tn, 8, __VA_ARGS__) gmock_a8, \ + GMOCK_MATCHER_(tn, 9, __VA_ARGS__) gmock_a9, \ + GMOCK_MATCHER_(tn, 10, __VA_ARGS__) gmock_a10) constness noexcept { \ + GMOCK_NOEXCEPT_MOCKER_(10, constness, Method).RegisterOwner(this); \ + return GMOCK_NOEXCEPT_MOCKER_(10, constness, Method) \ + .With( \ + gmock_a1, gmock_a2, gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, gmock_a9, gmock_a10); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_NOEXCEPT_MOCKER_(10, constness, Method) +/// @} + +/** + * @{ + * @brief Macro to mock method with @a noexcept specifier. + */ +#define MOCK_NOEXCEPT_METHOD0(m, ...) GMOCK_NOEXCEPT_METHOD0_(, , , m, __VA_ARGS__) +#define MOCK_NOEXCEPT_METHOD1(m, ...) GMOCK_NOEXCEPT_METHOD1_(, , , m, __VA_ARGS__) +#define MOCK_NOEXCEPT_METHOD2(m, ...) GMOCK_NOEXCEPT_METHOD2_(, , , m, __VA_ARGS__) +#define MOCK_NOEXCEPT_METHOD3(m, ...) GMOCK_NOEXCEPT_METHOD3_(, , , m, __VA_ARGS__) +#define MOCK_NOEXCEPT_METHOD4(m, ...) GMOCK_NOEXCEPT_METHOD4_(, , , m, __VA_ARGS__) +#define MOCK_NOEXCEPT_METHOD5(m, ...) GMOCK_NOEXCEPT_METHOD5_(, , , m, __VA_ARGS__) +#define MOCK_NOEXCEPT_METHOD6(m, ...) GMOCK_NOEXCEPT_METHOD6_(, , , m, __VA_ARGS__) +#define MOCK_NOEXCEPT_METHOD7(m, ...) GMOCK_NOEXCEPT_METHOD7_(, , , m, __VA_ARGS__) +#define MOCK_NOEXCEPT_METHOD8(m, ...) GMOCK_NOEXCEPT_METHOD8_(, , , m, __VA_ARGS__) +#define MOCK_NOEXCEPT_METHOD9(m, ...) GMOCK_NOEXCEPT_METHOD9_(, , , m, __VA_ARGS__) +#define MOCK_NOEXCEPT_METHOD10(m, ...) GMOCK_NOEXCEPT_METHOD10_(, , , m, __VA_ARGS__) +/// @} + +/** + * @{ + * @brief Macro to mock method with @a const @a noexcept specifier. + */ +#define MOCK_CONST_NOEXCEPT_METHOD0(m, ...) GMOCK_NOEXCEPT_METHOD0_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_NOEXCEPT_METHOD1(m, ...) GMOCK_NOEXCEPT_METHOD1_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_NOEXCEPT_METHOD2(m, ...) GMOCK_NOEXCEPT_METHOD2_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_NOEXCEPT_METHOD3(m, ...) GMOCK_NOEXCEPT_METHOD3_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_NOEXCEPT_METHOD4(m, ...) GMOCK_NOEXCEPT_METHOD4_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_NOEXCEPT_METHOD5(m, ...) GMOCK_NOEXCEPT_METHOD5_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_NOEXCEPT_METHOD6(m, ...) GMOCK_NOEXCEPT_METHOD6_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_NOEXCEPT_METHOD7(m, ...) GMOCK_NOEXCEPT_METHOD7_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_NOEXCEPT_METHOD8(m, ...) GMOCK_NOEXCEPT_METHOD8_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_NOEXCEPT_METHOD9(m, ...) GMOCK_NOEXCEPT_METHOD9_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_NOEXCEPT_METHOD10(m, ...) GMOCK_NOEXCEPT_METHOD10_(, const, , m, __VA_ARGS__) +/// @} + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_TEST_ACSDK_TEST_GMOCKEXTENSIONS_H_ diff --git a/AVSGatewayManager/include/AVSGatewayManager/AVSGatewayManager.h b/AVSGatewayManager/include/AVSGatewayManager/AVSGatewayManager.h index 2c1a60e84d..f895d59676 100644 --- a/AVSGatewayManager/include/AVSGatewayManager/AVSGatewayManager.h +++ b/AVSGatewayManager/include/AVSGatewayManager/AVSGatewayManager.h @@ -57,6 +57,7 @@ class AVSGatewayManager * @param customerDataManager The @c CustomerDataManager object that will track the CustomerDataHandler. * @param configurationRoot The @c ConfigurationNode to get AVS gateway information from the config file. * @param providerRegistrar Object with which to register the new instance as a post connect operation provider. + * @param metricRecorder Optional @c MetricRecorderInterface object for sending metrics. * @return A new instance of the @c AVSGatewayManager. */ static std::shared_ptr createAVSGatewayManagerInterface( @@ -66,7 +67,8 @@ class AVSGatewayManager const std::shared_ptr& configurationRoot, const std::shared_ptr< acsdkPostConnectOperationProviderRegistrarInterfaces::PostConnectOperationProviderRegistrarInterface>& - providerRegistrar); + providerRegistrar, + std::shared_ptr metricRecorder); /** * Creates an instance of the @c AVSGatewayManager. @@ -76,13 +78,15 @@ class AVSGatewayManager * @param customerDataManager The @c CustomerDataManager object that will track the CustomerDataHandler. * @param configurationRoot The @c ConfigurationNode to get AVS gateway information from the config file. * @param authDelegate The @c AuthDelegateInterface to add AuthObservers to take action once Auth state changes + * @param metricRecorder Optional @c MetricRecorderInterface object for sending metrics. * @return A new instance of the @c AVSGatewayManager. */ static std::shared_ptr create( std::shared_ptr avsGatewayManagerStorage, std::shared_ptr customerDataManager, const avsCommon::utils::configuration::ConfigurationNode& configurationRoot, - std::shared_ptr authDelegate = nullptr); + std::shared_ptr authDelegate = nullptr, + std::shared_ptr metricRecorder = nullptr); /// @name AVSGatewayManagerInterface Functions /// @{ @@ -124,12 +128,14 @@ class AVSGatewayManager * * @param avsGatewayManagerStorage The @c AVSGatewayManagerInterface to store avs gateway information. * @param customerDataManager The @c CustomerDataManager object that will track the CustomerDataHandler. + * @param metricRecorder Optional @c MetricRecorderInterface object for sending metrics. * @param defaultGateway The default AVS Gateway URL to use. */ AVSGatewayManager( std::shared_ptr avsGatewayManagerStorage, - const std::shared_ptr& customerDataManager, - const std::shared_ptr& authDelegate, + std::shared_ptr customerDataManager, + std::shared_ptr authDelegate, + std::shared_ptr metricRecorder, const std::string& defaultGateway); /** @@ -153,6 +159,9 @@ class AVSGatewayManager /// The AVS Gateway Assigner. std::shared_ptr m_avsGatewayAssigner; + /// Optional (may be nullptr) interface for recording metrics. + std::shared_ptr m_metricRecorder; + /// The mutex to synchronize access to members. mutable std::mutex m_mutex; diff --git a/AVSGatewayManager/include/AVSGatewayManager/PostConnectVerifyGatewaySender.h b/AVSGatewayManager/include/AVSGatewayManager/PostConnectVerifyGatewaySender.h index ac59d93915..90fa98285a 100644 --- a/AVSGatewayManager/include/AVSGatewayManager/PostConnectVerifyGatewaySender.h +++ b/AVSGatewayManager/include/AVSGatewayManager/PostConnectVerifyGatewaySender.h @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -39,10 +40,17 @@ class PostConnectVerifyGatewaySender * Creates a new instance of @c PostConnectVerifyGatewaySender. * * @param gatewayVerifiedCallback The callback method that should be called on successful gateway verification. + * @param metricRecorder Optional (may be nullptr) reference to metric recorder. * @return a new instance of the @c PostConnectVerifyGatewaySender. */ static std::shared_ptr create( - std::function&)> gatewayVerifiedCallback); + std::function&)> gatewayVerifiedCallback, + std::shared_ptr metricRecorder = nullptr); + + /** + * Destructor for tracking lifecycle. + */ + ~PostConnectVerifyGatewaySender(); /// @name PostConnectOperationInterface Methods /// @{ @@ -76,9 +84,11 @@ class PostConnectVerifyGatewaySender * Constructor. * * @param gatewayVerifiedCallback The callback method that should be called on successful gateway verification. + * @param metricRecorder Optional (may be nullptr) reference to metric recorder. */ explicit PostConnectVerifyGatewaySender( - std::function&)> gatewayVerifiedCallback); + std::function&)> gatewayVerifiedCallback, + std::shared_ptr metricRecorder); /** * The VerifyGateway operation which sends the @c ApiGateway.VerifyGateway event. @@ -103,6 +113,9 @@ class PostConnectVerifyGatewaySender /// The Callback function that will be called after successful response to @c VerifyGateway event. std::function&)> m_gatewayVerifiedCallback; + /// Optional (may be nullptr) interface for metrics. + std::shared_ptr m_metricRecorder; + /// Mutex to synchronize access to @c WaitableMessageRequest. std::mutex m_mutex; diff --git a/AVSGatewayManager/src/AVSGatewayManager.cpp b/AVSGatewayManager/src/AVSGatewayManager.cpp index 47ae25c535..441e58b4c2 100644 --- a/AVSGatewayManager/src/AVSGatewayManager.cpp +++ b/AVSGatewayManager/src/AVSGatewayManager.cpp @@ -28,7 +28,7 @@ using namespace avsCommon::sdkInterfaces; using namespace avsCommon::utils::configuration; /// String to identify log entries originating from this file. -static const std::string TAG("AVSGatewayManager"); +#define TAG "AVSGatewayManager" /** * Create a LogEntry using the file's TAG and the specified event string. @@ -54,7 +54,8 @@ std::shared_ptr AVSGateway const std::shared_ptr& configurationRoot, const std::shared_ptr< acsdkPostConnectOperationProviderRegistrarInterfaces::PostConnectOperationProviderRegistrarInterface>& - providerRegistrar) { + providerRegistrar, + std::shared_ptr metricRecorder) { if (!configurationRoot) { ACSDK_ERROR(LX("createAVSGatewayManagerInterfaceFailed").d("reason", "nullConfigurationRoot")); return nullptr; @@ -67,8 +68,12 @@ std::shared_ptr AVSGateway ACSDK_ERROR(LX("createAVSGatewayManagerInterfaceFailed").d("reason", "nullAuthDelegater")); return nullptr; } - auto gatewayManager = - create(std::move(avsGatewayManagerStorage), customerDataManager, *configurationRoot, authDelegate); + auto gatewayManager = create( + std::move(avsGatewayManagerStorage), + customerDataManager, + *configurationRoot, + authDelegate, + std::move(metricRecorder)); if (!gatewayManager) { ACSDK_ERROR(LX("createAVSGatewayManagerInterfaceFailed").d("reason", "createFailed")); return nullptr; @@ -84,7 +89,8 @@ std::shared_ptr AVSGatewayManager::create( std::shared_ptr avsGatewayManagerStorage, std::shared_ptr customerDataManager, const ConfigurationNode& configurationRoot, - std::shared_ptr authDelegate) { + std::shared_ptr authDelegate, + std::shared_ptr metricRecorder) { ACSDK_DEBUG5(LX(__func__)); if (!avsGatewayManagerStorage) { ACSDK_ERROR(LX("createFailed").d("reason", "nullAvsGatewayManagerStorage")); @@ -102,8 +108,12 @@ std::shared_ptr AVSGatewayManager::create( avsGateway = DEFAULT_AVS_GATEWAY; } - auto avsGatewayManager = std::shared_ptr( - new AVSGatewayManager(avsGatewayManagerStorage, customerDataManager, authDelegate, avsGateway)); + auto avsGatewayManager = std::shared_ptr(new AVSGatewayManager( + std::move(avsGatewayManagerStorage), + std::move(customerDataManager), + std::move(authDelegate), + std::move(metricRecorder), + avsGateway)); if (avsGatewayManager->init()) { return avsGatewayManager; } else { @@ -115,11 +125,13 @@ std::shared_ptr AVSGatewayManager::create( AVSGatewayManager::AVSGatewayManager( std::shared_ptr avsGatewayManagerStorage, - const std::shared_ptr& customerDataManager, - const std::shared_ptr& authDelegate, + std::shared_ptr customerDataManager, + std::shared_ptr authDelegate, + std::shared_ptr metricRecorder, const std::string& defaultAVSGateway) : CustomerDataHandler{std::move(customerDataManager)}, m_avsGatewayStorage{std::move(avsGatewayManagerStorage)}, + m_metricRecorder{std::move(metricRecorder)}, m_authDelegate{std::move(authDelegate)}, m_currentState{defaultAVSGateway, false} { } @@ -137,7 +149,7 @@ std::shared_ptr AVSGatewayManager::createPostConn if (!m_currentState.isVerified) { auto callback = std::bind(&AVSGatewayManager::onGatewayVerified, this, std::placeholders::_1); std::shared_ptr verifyGatewaySender = - PostConnectVerifyGatewaySender::create(callback); + PostConnectVerifyGatewaySender::create(callback, m_metricRecorder); m_currentVerifyGatewaySender = verifyGatewaySender; if (m_authDelegate) { diff --git a/AVSGatewayManager/src/PostConnectVerifyGatewaySender.cpp b/AVSGatewayManager/src/PostConnectVerifyGatewaySender.cpp index b481b9888a..44dae2190e 100644 --- a/AVSGatewayManager/src/PostConnectVerifyGatewaySender.cpp +++ b/AVSGatewayManager/src/PostConnectVerifyGatewaySender.cpp @@ -19,6 +19,8 @@ #include #include +#include +#include #include namespace alexaClientSDK { @@ -26,9 +28,17 @@ namespace avsGatewayManager { using namespace avsCommon::avs; using namespace avsCommon::sdkInterfaces; +using namespace avsCommon::utils::metrics; /// String to identify log entries originating from this file. -static const std::string TAG("PostConnectVerifyGatewaySender"); +#define TAG "PostConnectVerifyGatewaySender" + +/// Activity name for post-connect metric. +/// This name is dependent on TAG value. +#define POST_CONNECT_ACTIVITY_NAME TAG "-sendVerifyGateway" + +/// Prefix for post-connect data point with status value. +#define POST_CONNECT_STATUS_PREFIX "STATUS-" /** * Create a LogEntry using the file's TAG and the specified event string. @@ -60,12 +70,13 @@ static avsCommon::utils::RetryTimer RETRY_TIMER{RETRY_TABLE}; std::shared_ptr PostConnectVerifyGatewaySender::create( std::function& verifyGatewaySender)> - gatewayVerifiedCallback) { + gatewayVerifiedCallback, + std::shared_ptr metricRecorder) { if (!gatewayVerifiedCallback) { ACSDK_ERROR(LX("createFailed").d("reason", "invalid gatewayVerifiedCallback")); } else { return std::shared_ptr( - new PostConnectVerifyGatewaySender(gatewayVerifiedCallback)); + new PostConnectVerifyGatewaySender(gatewayVerifiedCallback, metricRecorder)); } return nullptr; @@ -73,9 +84,16 @@ std::shared_ptr PostConnectVerifyGatewaySender:: PostConnectVerifyGatewaySender::PostConnectVerifyGatewaySender( std::function& verifyGatewaySender)> - gatewayVerifiedCallback) : - m_gatewayVerifiedCallback{gatewayVerifiedCallback}, + gatewayVerifiedCallback, + std::shared_ptr metricRecorder) : + m_gatewayVerifiedCallback{std::move(gatewayVerifiedCallback)}, + m_metricRecorder{std::move(metricRecorder)}, m_isStopping{false} { + ACSDK_INFO(LX("init").p("this", this)); +} + +PostConnectVerifyGatewaySender::~PostConnectVerifyGatewaySender() { + ACSDK_INFO(LX("destroyed").p("this", this)); } unsigned int PostConnectVerifyGatewaySender::getOperationPriority() { @@ -83,7 +101,7 @@ unsigned int PostConnectVerifyGatewaySender::getOperationPriority() { } bool PostConnectVerifyGatewaySender::performOperation(const std::shared_ptr& messageSender) { - ACSDK_DEBUG5(LX(__func__)); + ACSDK_INFO(LX(__func__)); if (!messageSender) { ACSDK_ERROR(LX("performOperationFailed").d("reason", "nullPostConnectSender")); return false; @@ -126,7 +144,7 @@ void PostConnectVerifyGatewaySender::wakeOperation() { } void PostConnectVerifyGatewaySender::abortOperation() { - ACSDK_DEBUG5(LX(__func__)); + ACSDK_INFO(LX(__func__)); std::shared_ptr requestCopy; { std::lock_guard lock{m_mutex}; @@ -157,6 +175,21 @@ PostConnectVerifyGatewaySender::VerifyGatewayReturnCode PostConnectVerifyGateway /// Wait for the response. auto status = m_postConnectRequest->waitForCompletion(); +#ifdef ACSDK_ENABLE_METRICS_RECORDING + if (m_metricRecorder) { + std::stringstream eventNameBuilder; + eventNameBuilder << POST_CONNECT_STATUS_PREFIX << status; + auto metricEvent = + MetricEventBuilder{} + .setActivityName(POST_CONNECT_ACTIVITY_NAME) + .addDataPoint(DataPointCounterBuilder{}.setName(eventNameBuilder.str()).increment(1).build()) + .build(); + if (metricEvent) { + m_metricRecorder->recordMetric(std::move(metricEvent)); + } + } +#endif + switch (status) { /// 200 Response with a set gateway directive. case MessageRequestObserverInterface::Status::SUCCESS: diff --git a/AVSGatewayManager/src/Storage/AVSGatewayManagerStorage.cpp b/AVSGatewayManager/src/Storage/AVSGatewayManagerStorage.cpp index e1331e3527..81b7e758b4 100644 --- a/AVSGatewayManager/src/Storage/AVSGatewayManagerStorage.cpp +++ b/AVSGatewayManager/src/Storage/AVSGatewayManagerStorage.cpp @@ -27,7 +27,7 @@ using namespace avsCommon::sdkInterfaces::storage; using namespace avsCommon::utils::json; /// String to identify log entries originating from this file. -static const std::string TAG("AVSGatewayManagerStorage"); +#define TAG "AVSGatewayManagerStorage" /** * Create a LogEntry using the file's TAG and the specified event string. diff --git a/ApplicationUtilities/AndroidUtilities/include/AndroidUtilities/PlatformSpecificValues.h b/ApplicationUtilities/AndroidUtilities/include/AndroidUtilities/PlatformSpecificValues.h deleted file mode 100644 index 1538ed67bf..0000000000 --- a/ApplicationUtilities/AndroidUtilities/include/AndroidUtilities/PlatformSpecificValues.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_APPLICATIONUTILITIES_ANDROIDUTILITIES_INCLUDE_ANDROIDUTILITIES_PLATFORMSPECIFICVALUES_H_ -#define ALEXA_CLIENT_SDK_APPLICATIONUTILITIES_ANDROIDUTILITIES_INCLUDE_ANDROIDUTILITIES_PLATFORMSPECIFICVALUES_H_ - -#include - -#include "AndroidUtilities/AndroidSLESEngine.h" - -namespace alexaClientSDK { -namespace applicationUtilities { -namespace androidUtilities { - -struct PlatformSpecificValues { - std::shared_ptr openSlEngine; -}; - -} // namespace androidUtilities -} // namespace applicationUtilities -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_APPLICATIONUTILITIES_ANDROIDUTILITIES_INCLUDE_ANDROIDUTILITIES_PLATFORMSPECIFICVALUES_H_ diff --git a/ApplicationUtilities/AndroidUtilities/src/AndroidSLESBufferQueue.cpp b/ApplicationUtilities/AndroidUtilities/src/AndroidSLESBufferQueue.cpp index 4c8b4f4448..eef4e02a77 100644 --- a/ApplicationUtilities/AndroidUtilities/src/AndroidSLESBufferQueue.cpp +++ b/ApplicationUtilities/AndroidUtilities/src/AndroidSLESBufferQueue.cpp @@ -17,7 +17,7 @@ #include /// String to identify log entries originating from this file. -static const std::string TAG{"AndroidSLESBufferQueue"}; +#define TAG "AndroidSLESBufferQueue" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/ApplicationUtilities/AndroidUtilities/src/AndroidSLESEngine.cpp b/ApplicationUtilities/AndroidUtilities/src/AndroidSLESEngine.cpp index 401e2103fa..778f26472c 100644 --- a/ApplicationUtilities/AndroidUtilities/src/AndroidSLESEngine.cpp +++ b/ApplicationUtilities/AndroidUtilities/src/AndroidSLESEngine.cpp @@ -22,7 +22,7 @@ #include /// String to identify log entries originating from this file. -static const std::string TAG{"AndroidSLESEngine"}; +#define TAG "AndroidSLESEngine" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/ApplicationUtilities/AndroidUtilities/src/AndroidSLESMicrophone.cpp b/ApplicationUtilities/AndroidUtilities/src/AndroidSLESMicrophone.cpp index f4a263cd5c..b90d4a7163 100644 --- a/ApplicationUtilities/AndroidUtilities/src/AndroidSLESMicrophone.cpp +++ b/ApplicationUtilities/AndroidUtilities/src/AndroidSLESMicrophone.cpp @@ -19,7 +19,7 @@ #include /// String to identify log entries originating from this file. -static const std::string TAG{"AndroidMicrophone"}; +#define TAG "AndroidMicrophone" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/ApplicationUtilities/AndroidUtilities/src/AndroidSLESObject.cpp b/ApplicationUtilities/AndroidUtilities/src/AndroidSLESObject.cpp index eaa73e4b18..962949c586 100644 --- a/ApplicationUtilities/AndroidUtilities/src/AndroidSLESObject.cpp +++ b/ApplicationUtilities/AndroidUtilities/src/AndroidSLESObject.cpp @@ -20,7 +20,7 @@ namespace applicationUtilities { namespace androidUtilities { /// The tag associated with log entries from this class. -static const std::string TAG{"AndroidSLESObject"}; +#define TAG "AndroidSLESObject" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClient.h b/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClient.h index 2569e2d653..6d4f8cb365 100644 --- a/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClient.h +++ b/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClient.h @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -60,8 +61,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -94,16 +94,17 @@ #include #ifdef ENABLE_PCC -#include -#include +#include +#include #endif #ifdef ENABLE_MCC -#include -#include -#include +#include +#include +#include #endif +#include #include #include #include @@ -115,13 +116,11 @@ #include #include #include -#include -#include -#include +#include +#include #include #include #include -#include #ifdef ENABLE_REVOKE_AUTH #include @@ -138,7 +137,9 @@ namespace defaultClient { * This class serves to instantiate each default component with of the SDK with no specializations to provide an * "out-of-box" component that users may utilize for AVS interaction. */ -class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerInterface { +class DefaultClient + : public avsCommon::sdkInterfaces::SpeechInteractionHandlerInterface + , public sdkClient::FeatureClientInterface { public: using DefaultClientSubsetManufactory = acsdkManufactory::Manufactory< std::shared_ptr, @@ -168,8 +169,6 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::shared_ptr, acsdkManufactory:: Annotated, - acsdkManufactory:: - Annotated, std::shared_ptr, std::shared_ptr, std::shared_ptr, @@ -197,7 +196,7 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::shared_ptr, std::shared_ptr, std::shared_ptr, - std::shared_ptr, + std::shared_ptr, std::shared_ptr>; using DefaultClientManufactory = acsdkManufactory::Manufactory< @@ -228,8 +227,6 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::shared_ptr, acsdkManufactory:: Annotated, - acsdkManufactory:: - Annotated, std::shared_ptr, std::shared_ptr, std::shared_ptr, @@ -257,7 +254,7 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::shared_ptr, std::shared_ptr, std::shared_ptr, - std::shared_ptr, + std::shared_ptr, std::shared_ptr>; /** @@ -293,6 +290,7 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI * @param externalCapabilitiesBuilder Optional object used to build capabilities that are not included in the SDK. * @param firstInteractionAudioProvider Optional object used in the first interaction started from * the alexa voice service + * @param sdkClientRegistry Optional object used when the @c SDKClientBuilder is used to construct DefaultClient * @return A @c std::unique_ptr to a DefaultClient if all went well or @c nullptr otherwise. */ static std::unique_ptr create( @@ -304,12 +302,12 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::shared_ptr> additionalSpeakers, #ifdef ENABLE_PCC std::shared_ptr phoneSpeaker, - std::shared_ptr phoneCaller, + std::shared_ptr phoneCaller, #endif #ifdef ENABLE_MCC std::shared_ptr meetingSpeaker, - std::shared_ptr meetingClient, - std::shared_ptr calendarClient, + std::shared_ptr meetingClient, + std::shared_ptr calendarClient, #endif #ifdef ENABLE_COMMS_AUDIO_PROXY std::shared_ptr commsMediaPlayer, @@ -329,7 +327,8 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::shared_ptr diagnostics = nullptr, const std::shared_ptr& externalCapabilitiesBuilder = nullptr, capabilityAgents::aip::AudioProvider firstInteractionAudioProvider = - capabilityAgents::aip::AudioProvider::null()); + capabilityAgents::aip::AudioProvider::null(), + const std::shared_ptr& sdkClientRegistry = nullptr); /** * Creates and initializes a default AVS SDK client. To connect the client to AVS, users should make a call to @@ -405,6 +404,8 @@ of the @c ExpectSpeech directive's timeout. If provided, this function must rema AudioInputProcessor. * @param firstInteractionAudioProvider Optional object used in the first interaction started from * the alexa voice service + * @param cryptoFactory Optional Encryption facilities factory. + * @param sdkClientRegistry Optional object used when the @c SDKClientBuilder is used to construct DefaultClient * @return A @c std::unique_ptr to a DefaultClient if all went well or @c nullptr otherwise. */ static std::unique_ptr create( @@ -434,12 +435,12 @@ AudioInputProcessor. std::shared_ptr> additionalSpeakers, #ifdef ENABLE_PCC std::shared_ptr phoneSpeaker, - std::shared_ptr phoneCaller, + std::shared_ptr phoneCaller, #endif #ifdef ENABLE_MCC std::shared_ptr meetingSpeaker, - std::shared_ptr meetingClient, - std::shared_ptr calendarClient, + std::shared_ptr meetingClient, + std::shared_ptr calendarClient, #endif #ifdef ENABLE_COMMS_AUDIO_PROXY std::shared_ptr commsMediaPlayer, @@ -482,14 +483,22 @@ AudioInputProcessor. std::shared_ptr diagnostics = nullptr, const std::shared_ptr& externalCapabilitiesBuilder = nullptr, std::shared_ptr channelVolumeFactory = - std::make_shared(), + speakerManager::createChannelVolumeFactory(), bool startAlertSchedulingOnInitialization = true, std::shared_ptr messageRouterFactory = std::make_shared(), const std::shared_ptr& expectSpeechTimeoutHandler = nullptr, capabilityAgents::aip::AudioProvider firstInteractionAudioProvider = - capabilityAgents::aip::AudioProvider::null()); + capabilityAgents::aip::AudioProvider::null(), + const std::shared_ptr& cryptoFactory = nullptr, + const std::shared_ptr& sdkClientRegistry = nullptr); + + /// @c FeatureClientInterface functions + /// @{ + bool configure(const std::shared_ptr& sdkClientRegistry) override; + void doShutdown() override; + /// @} /** * Connects the client to AVS. After this call, users can observe the state of the connection asynchronously by @@ -633,7 +642,7 @@ AudioInputProcessor. * @param observer The observer to add. */ void addTemplateRuntimeObserver( - std::shared_ptr observer); + std::shared_ptr observer); /** * Removes an observer to be notified when a TemplateRuntime directive is received. @@ -641,12 +650,7 @@ AudioInputProcessor. * @param observer The observer to remove. */ void removeTemplateRuntimeObserver( - std::shared_ptr observer); - - /** - * Notify the TemplateRuntime Capability Agent that the display card is cleared from the screen. - */ - void TemplateRuntimeDisplayCardCleared(); + std::shared_ptr observer); /** * Adds an observer to be notified of IndicatorState changes. @@ -1094,7 +1098,29 @@ AudioInputProcessor. */ std::shared_ptr getBluetoothLocal(); + /** + * Stops any ongoing interaction with the SDK by resetting the state of the @c AudioInputProcessor. + * + * This method is intended for use when a device needs to stop the current user interaction with alexa, for example + * when the audio input state needs to be returned to idle as a result of an event such as a back or exit button + * press. Calling this method has no effect on ongoing Alexa speech, audio playback or visual state. + */ + void stopInteraction(); + + /** + * Get a reference to the audio focus manager + * + * @return shared_ptr to the audio @c FocusManagerInterface, callers should perform a nullptr check as the returned + * pointer may be null + */ + std::shared_ptr getAudioFocusManager(); + private: + /** + * Constructor + */ + DefaultClient(); + /** * Initializes the SDK and "glues" all the components together. * @@ -1114,6 +1140,7 @@ AudioInputProcessor. * @param externalCapabilitiesBuilder Object used to build capabilities that are not included in the SDK. * @param firstInteractionAudioProvider Optional object used in the first interaction started from * the alexa voice service + * @param sdkClientRegistry Optional object used when the @c SDKClientBuilder is used to construct DefaultClient * @return Whether the SDK was initialized properly. */ bool initialize( @@ -1125,12 +1152,12 @@ AudioInputProcessor. std::shared_ptr> additionalSpeakers, #ifdef ENABLE_PCC std::shared_ptr phoneSpeaker, - std::shared_ptr phoneCaller, + std::shared_ptr phoneCaller, #endif #ifdef ENABLE_MCC std::shared_ptr meetingSpeaker, - std::shared_ptr meetingClient, - std::shared_ptr calendarClient, + std::shared_ptr meetingClient, + std::shared_ptr calendarClient, #endif #ifdef ENABLE_COMMS_AUDIO_PROXY std::shared_ptr commsMediaPlayer, @@ -1147,7 +1174,8 @@ AudioInputProcessor. std::shared_ptr softwareInfoSenderObserver, std::shared_ptr diagnostics, const std::shared_ptr& externalCapabilitiesBuilder, - capabilityAgents::aip::AudioProvider firstInteractionAudioProvider); + capabilityAgents::aip::AudioProvider firstInteractionAudioProvider, + const std::shared_ptr& sdkClientRegistry = nullptr); /// The directive sequencer. std::shared_ptr m_directiveSequencer; @@ -1155,12 +1183,10 @@ AudioInputProcessor. /// The focus manager for audio channels. std::shared_ptr m_audioFocusManager; - /// The focus manager for visual channels. - std::shared_ptr m_visualFocusManager; - /// The connection manager. std::shared_ptr m_connectionManager; + /// The internet connection monitor. std::shared_ptr m_internetConnectionMonitor; /// The captions manager. @@ -1207,13 +1233,12 @@ AudioInputProcessor. #ifdef ENABLE_PCC /// The phoneCallController capability agent. - std::shared_ptr m_phoneCallControllerCapabilityAgent; + std::shared_ptr m_phoneCallControllerCapabilityAgent; #endif #ifdef ENABLE_MCC /// The MeetingClientController capability agent. - std::shared_ptr - m_meetingClientControllerCapabilityAgent; + std::shared_ptr m_meetingClientControllerCapabilityAgent; #endif /// The call manager capability agent. @@ -1229,7 +1254,7 @@ AudioInputProcessor. std::shared_ptr m_speakerManager; /// The TemplateRuntime capability agent. - std::shared_ptr m_templateRuntime; + std::shared_ptr m_templateRuntime; /// The Equalizer capability agent. std::shared_ptr m_equalizerCapabilityAgent; diff --git a/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClientBuilder.h b/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClientBuilder.h new file mode 100644 index 0000000000..36a7cde41a --- /dev/null +++ b/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClientBuilder.h @@ -0,0 +1,473 @@ +/* + * 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. + */ + +#ifndef ALEXA_CLIENT_SDK_APPLICATIONUTILITIES_DEFAULTCLIENT_INCLUDE_DEFAULTCLIENT_DEFAULTCLIENTBUILDER_H_ +#define ALEXA_CLIENT_SDK_APPLICATIONUTILITIES_DEFAULTCLIENT_INCLUDE_DEFAULTCLIENT_DEFAULTCLIENTBUILDER_H_ + +#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 + +#ifdef ENABLE_PCC +#include +#include +#endif + +#ifdef ENABLE_MCC +#include +#include +#include +#endif + +#include "DefaultClient/EqualizerRuntimeSetup.h" +#include "DefaultClient/ExternalCapabilitiesBuilderInterface.h" + +#include "DefaultClient.h" + +namespace alexaClientSDK { +namespace defaultClient { +/** + * The DefaultClientBuilder is used to construct DefaultClient when used with the ClientBuilder + */ +class DefaultClientBuilder : public sdkClient::FeatureClientBuilderInterface { +public: + /** + * Creates and initializes a default AVS SDK client. To connect the client to AVS, users should make a call to + * connect() after creation. + * + * @param deviceInfo DeviceInfo which reflects the device setup credentials. + * @param customerDataManager CustomerDataManager instance to be used by RegistrationManager and instances of + * all classes extending CustomDataHandler. + * @param externalMusicProviderMediaPlayers The map of to use to play content from each + * external music provider. + * @param externalMusicProviderSpeakers The map of to use to track volume of each + * external music provider media player. + * @param adapterCreationMap The map of to use when creating the adapters for the + * different music providers supported by ExternalMediaPlayer. + * @param speakMediaPlayer The media player to use to play Alexa speech from. + * @param audioMediaPlayerFactory The media player factory to use to generate players for Alexa audio content. + * @param alertsMediaPlayer The media player to use to play alerts from. + * @param notificationsMediaPlayer The media player to play notification indicators. + * @param bluetoothMediaPlayer The media player to play bluetooth content. + * @param ringtoneMediaPlayer The media player to play Comms ringtones. + * @param systemSoundMediaPlayer The media player to play system sounds. + * @param speakSpeaker The speaker to control volume of Alexa speech. + * @param audioSpeakers A list of speakers to control volume of Alexa audio content. + * @param alertsSpeaker The speaker to control volume of alerts. + * @param notificationsSpeaker The speaker to control volume of notifications. + * @param bluetoothSpeaker The speaker to control volume of bluetooth. + * @param ringtoneSpeaker The speaker to control volume of Comms ringtones. + * @param systemSoundSpeaker The speaker to control volume of system sounds. + * @param additionalSpeakers A map of additional speakers to receive volume changes. +#ifdef ENABLE_COMMS_AUDIO_PROXY + * @param commsMediaPlayer The media player to play Comms calling audio. + * @param commsSpeaker The speaker to control volume of Comms calling audio. + * @param sharedDataStream The stream to use which has the audio from microphone. +#endif + * @param equalizerRuntimeSetup Equalizer component runtime setup + * @param audioFactory The audioFactory is a component that provides unique audio streams. + * @param authDelegate The component that provides the client with valid LWA authorization. + * @param alertStorage The storage interface that will be used to store alerts. + * @param messageStorage The storage interface that will be used to store certified sender messages. + * @param notificationsStorage The storage interface that will be used to store notification indicators. + * @param deviceSettingStorage The storage interface that will be used to store device settings. + * @param bluetoothStorage The storage interface that will be used to store bluetooth data. + * @param miscStorage The storage interface that will be used to store key / value pairs. + * @param alexaDialogStateObservers Observers that can be used to be notified of Alexa dialog related UX state + * changes. + * @param connectionObservers Observers that can be used to be notified of connection status changes. + * @param isGuiSupported Whether the device supports GUI. + * @param capabilitiesDelegate The component that provides the client with the ability to send messages to the + * Capabilities API. + * @param contextManager The @c ContextManager which will provide the context for various components. + * @param transportFactory The object passed in here will be used whenever a new transport object + * for AVS communication is needed. + * @param avsGatewayManager The @c AVSGatewayManager instance used to create the ApiGateway CA. + * @param localeAssetsManager The device locale assets manager. + * @param enabledConnectionRules The set of @c BluetoothDeviceConnectionRuleInterface instances used to + * create the Bluetooth CA. + * @param systemTimezone Optional object used to set the system timezone. + * @param firmwareVersion The firmware version to report to @c AVS or @c INVALID_FIRMWARE_VERSION. + * @param sendSoftwareInfoOnConnected Whether to send SoftwareInfo upon connecting to @c AVS. + * @param softwareInfoSenderObserver Object to receive notifications about sending SoftwareInfo. + * @param bluetoothDeviceManager The @c BluetoothDeviceManager instance used to create the Bluetooth CA. + * @param metricRecorder The metric recorder object used to capture metrics. + * @param powerResourceManager Object to manage power resource. + * @param diagnostics Diagnostics interface which provides suite of APIs for diagnostic insight into SDK. + * @param externalCapabilitiesBuilder Optional object used to build capabilities that are not included in the SDK. + * @param channelVolumeFactory Optional object used to build @c ChannelVolumeInterface in the SDK. + * @param startAlertSchedulingOnInitialization Whether to start scheduling alerts after client initialization. If + * this is set to false, no alert scheduling will occur until onSystemClockSynchronized is called. + * @param messageRouterFactory Object used to instantiate @c MessageRouter in the SDK. + * @param expectSpeechTimeoutHandler An optional object that applications may provide to specify external handling +of the @c ExpectSpeech directive's timeout. If provided, this function must remain valid for the lifetime of the @c +AudioInputProcessor. + * @param firstInteractionAudioProvider Optional object used in the first interaction started from + * the alexa voice service + * @param cryptoFactory Optional Encryption facilities factory. + * @return A @c std::unique_ptr to a DefaultClient if all went well or @c nullptr otherwise. + */ + static std::unique_ptr create( + std::shared_ptr deviceInfo, + std::shared_ptr customerDataManager, + std::unordered_map> + externalMusicProviderMediaPlayers, + std::unordered_map> + externalMusicProviderSpeakers, + acsdkExternalMediaPlayer::ExternalMediaPlayer::AdapterCreationMap adapterCreationMap, + std::shared_ptr speakMediaPlayer, + std::unique_ptr audioMediaPlayerFactory, + std::shared_ptr alertsMediaPlayer, + std::shared_ptr notificationsMediaPlayer, + std::shared_ptr bluetoothMediaPlayer, + std::shared_ptr ringtoneMediaPlayer, + std::shared_ptr systemSoundMediaPlayer, + std::shared_ptr speakSpeaker, + std::vector> audioSpeakers, + std::shared_ptr alertsSpeaker, + std::shared_ptr notificationsSpeaker, + std::shared_ptr bluetoothSpeaker, + std::shared_ptr ringtoneSpeaker, + std::shared_ptr systemSoundSpeaker, + std::multimap< + avsCommon::sdkInterfaces::ChannelVolumeInterface::Type, + std::shared_ptr> additionalSpeakers, +#ifdef ENABLE_PCC + std::shared_ptr phoneSpeaker, + std::shared_ptr phoneCaller, +#endif +#ifdef ENABLE_MCC + std::shared_ptr meetingSpeaker, + std::shared_ptr meetingClient, + std::shared_ptr calendarClient, +#endif +#ifdef ENABLE_COMMS_AUDIO_PROXY + std::shared_ptr commsMediaPlayer, + std::shared_ptr commsSpeaker, + std::shared_ptr sharedDataStream, +#endif + std::shared_ptr equalizerRuntimeSetup, + std::shared_ptr audioFactory, + std::shared_ptr authDelegate, + std::shared_ptr alertStorage, + std::shared_ptr messageStorage, + std::shared_ptr notificationsStorage, + std::shared_ptr deviceSettingStorage, + std::shared_ptr bluetoothStorage, + std::shared_ptr miscStorage, + std::unordered_set> + alexaDialogStateObservers, + std::unordered_set> + connectionObservers, + std::shared_ptr internetConnectionMonitor, + bool isGuiSupported, + std::shared_ptr capabilitiesDelegate, + std::shared_ptr contextManager, + std::shared_ptr transportFactory, + std::shared_ptr avsGatewayManager, + std::shared_ptr localeAssetsManager, + std::unordered_set> + enabledConnectionRules = std::unordered_set< + std::shared_ptr>(), + std::shared_ptr systemTimezone = nullptr, + avsCommon::sdkInterfaces::softwareInfo::FirmwareVersion firmwareVersion = + avsCommon::sdkInterfaces::softwareInfo::INVALID_FIRMWARE_VERSION, + bool sendSoftwareInfoOnConnected = false, + std::shared_ptr softwareInfoSenderObserver = + nullptr, + std::unique_ptr bluetoothDeviceManager = + nullptr, + std::shared_ptr metricRecorder = nullptr, + std::shared_ptr powerResourceManager = nullptr, + std::shared_ptr diagnostics = nullptr, + std::shared_ptr externalCapabilitiesBuilder = nullptr, + std::shared_ptr channelVolumeFactory = + speakerManager::createChannelVolumeFactory(), + bool startAlertSchedulingOnInitialization = true, + std::shared_ptr messageRouterFactory = + std::make_shared(), + std::shared_ptr expectSpeechTimeoutHandler = + nullptr, + capabilityAgents::aip::AudioProvider firstInteractionAudioProvider = + capabilityAgents::aip::AudioProvider::null(), + std::shared_ptr cryptoFactory = nullptr); + + /** + * Function used by SDKClientBuilder to construct an instance of DefaultClient + * @param sdkClientRegistry The @c SDKClientRegistry object + * @return An instance of the @c DefaultClient, or nullptr if creation failed + */ + std::shared_ptr construct(const std::shared_ptr& sdkClientRegistry); + + /// @c FeatureClientBuilderInterface functions + /// @{ + std::string name() override; + /// @} + +private: + /// Constructor + DefaultClientBuilder(); + + /// Flag indicating whether the @c construct method has previously been called on this instance + bool m_constructed; + + /// DeviceInfo which reflects the device setup credentials. + std::shared_ptr m_deviceInfo; + + /// CustomerDataManager instance to be used by RegistrationManager and instances of all classes extending + /// CustomDataHandler. + std::shared_ptr m_customerDataManager; + + /// The map of to use to play content from each external music provider. + std:: + unordered_map> + m_externalMusicProviderMediaPlayers; + + /// The map of to use to track volume of each external music provider media player. + std::unordered_map> + m_externalMusicProviderSpeakers; + + /// The map of to use when creating the adapters for the different music providers + /// supported by ExternalMediaPlayer. + alexaClientSDK::acsdkExternalMediaPlayer::ExternalMediaPlayer::AdapterCreationMap m_adapterCreationMap; + + /// The media player to use to play Alexa speech from. + std::shared_ptr m_speakMediaPlayer; + + /// The media player factory to use to generate players for Alexa audio content. + std::unique_ptr + m_audioMediaPlayerFactory; + + /// The media player to use to play alerts from. + std::shared_ptr m_alertsMediaPlayer; + + /// The media player to play notification indicators. + std::shared_ptr m_notificationsMediaPlayer; + + /// The media player to play bluetooth content. + std::shared_ptr m_bluetoothMediaPlayer; + + /// The media player to play Comms ringtones. + std::shared_ptr m_ringtoneMediaPlayer; + + /// The media player to play system sounds. + std::shared_ptr m_systemSoundMediaPlayer; + + /// The speaker to control volume of Alexa speech. + std::shared_ptr m_speakSpeaker; + + /// A list of speakers to control volume of Alexa audio content. + std::vector> m_audioSpeakers; + + /// The speaker to control volume of alerts. + std::shared_ptr m_alertsSpeaker; + + /// The speaker to control volume of notifications. + std::shared_ptr m_notificationsSpeaker; + + /// The speaker to control volume of bluetooth. + std::shared_ptr m_bluetoothSpeaker; + + /// The speaker to control volume of Comms ringtones. + std::shared_ptr m_ringtoneSpeaker; + + /// The speaker to control volume of system sounds. + std::shared_ptr m_systemSoundSpeaker; + + /// A map of additional speakers to receive volume changes. + std::multimap< + alexaClientSDK::avsCommon::sdkInterfaces::ChannelVolumeInterface::Type, + std::shared_ptr> + m_additionalSpeakers; + + /// Equalizer component runtime setup + std::shared_ptr m_equalizerRuntimeSetup; + + /// The audioFactory is a component that provides unique audio streams. + std::shared_ptr m_audioFactory; + + /// The component that provides the client with valid LWA authorization. + std::shared_ptr m_authDelegate; + + /// The storage interface that will be used to store alerts. + std::shared_ptr m_alertStorage; + + /// The storage interface that will be used to store certified sender messages. + std::shared_ptr m_messageStorage; + + /// The storage interface that will be used to store notification indicators. + std::shared_ptr m_notificationsStorage; + + /// The storage interface that will be used to store device settings. + std::shared_ptr m_deviceSettingStorage; + + /// The storage interface that will be used to store bluetooth data. + std::shared_ptr m_bluetoothStorage; + + /// The storage interface that will be used to store key / value pairs. + std::shared_ptr m_miscStorage; + + /// Observers that can be used to be notified of Alexa dialog related UX state changes. + std::unordered_set> + m_alexaDialogStateObservers; + + /// Observers that can be used to be notified of connection status changes. + std::unordered_set> + m_connectionObservers; + + /// The interface for monitoring and reporting internet connection status + std::shared_ptr + m_internetConnectionMonitor; + + /// Whether the device supports GUI. + bool m_isGuiSupported; + + /// The component that provides the client with the ability to send messages to the Capabilities API. + std::shared_ptr m_capabilitiesDelegate; + + /// The @c ContextManager which will provide the context for various components. + std::shared_ptr m_contextManager; + + /// The object passed in here will be used whenever a new transport object for AVS communication is needed. + std::shared_ptr m_transportFactory; + + /// The @c AVSGatewayManager instance used to create the ApiGateway CA. + std::shared_ptr m_avsGatewayManager; + + /// The device locale assets manager. + std::shared_ptr m_localeAssetsManager; + + /// The set of @c BluetoothDeviceConnectionRuleInterface instances used to create the Bluetooth CA. + std::unordered_set< + std::shared_ptr> + m_enabledConnectionRules; + + /// Optional object used to set the system timezone. + std::shared_ptr m_systemTimezone; + + /// The firmware version to report to @c AVS or @c INVALID_FIRMWARE_VERSION. + alexaClientSDK::avsCommon::sdkInterfaces::softwareInfo::FirmwareVersion m_firmwareVersion; + + /// Whether to send SoftwareInfo upon connecting to @c AVS. + bool m_sendSoftwareInfoOnConnected; + + /// Object to receive notifications about sending SoftwareInfo. + std::shared_ptr + m_softwareInfoSenderObserver; + + /// The @c BluetoothDeviceManager instance used to create the Bluetooth CA. + std::unique_ptr + m_bluetoothDeviceManager; + + /// The metric recorder object used to capture metrics. + std::shared_ptr m_metricRecorder; + + /// Object to manage power resource. + std::shared_ptr m_powerResourceManager; + + /// Diagnostics interface which provides suite of APIs for diagnostic insight into SDK. + std::shared_ptr m_diagnostics; + + /// Optional object used to build capabilities that are not included in the SDK. + std::shared_ptr m_externalCapabilitiesBuilder; + + /// Optional object used to build @c ChannelVolumeInterface in the SDK. + std::shared_ptr m_channelVolumeFactory; + + /// Whether to start scheduling alerts after client initialization. If this is set to false, no alert scheduling + /// will occur until onSystemClockSynchronized is called. + bool m_startAlertSchedulingOnInitialization; + + /// Object used to instantiate @c MessageRouter in the SDK. + std::shared_ptr m_messageRouterFactory; + + /// An optional object that applications may provide to specify external handling of the @c ExpectSpeech directive's + /// timeout. If provided, this function must remain valid for the lifetime of the @c AudioInputProcessor. + std::shared_ptr + m_expectSpeechTimeoutHandler; + + /// Optional object used in the first interaction started from the alexa voice service + alexaClientSDK::capabilityAgents::aip::AudioProvider m_firstInteractionAudioProvider = + alexaClientSDK::capabilityAgents::aip::AudioProvider::null(); + + /// Optional Encryption facilities factory. + std::shared_ptr m_cryptoFactory; + +#ifdef ENABLE_COMMS_AUDIO_PROXY + /// The media player to play Comms calling audio. + std::shared_ptr m_commsMediaPlayer; + + /// The speaker to control volume of Comms calling audio. + std::shared_ptr m_commsSpeaker; + + /// The stream to use which has the audio from microphone. + std::shared_ptr m_sharedDataStream; +#endif + +#ifdef ENABLE_PCC + /// The speaker to control volume of phone audio. + std::shared_ptr m_phoneSpeaker; + + /// The calling functions available on a calling device + std::shared_ptr m_phoneCaller; +#endif + +#ifdef ENABLE_MCC + /// The speaker to control volume of meeting audio. + std::shared_ptr m_meetingSpeaker; + + /// The meeting functions available on a meeting device + std::shared_ptr m_meetingClient; + + /// The calendar functions available on a calendar device + std::shared_ptr m_calendarClient; +#endif +}; +} // namespace defaultClient +} // namespace alexaClientSDK +#endif // ALEXA_CLIENT_SDK_APPLICATIONUTILITIES_DEFAULTCLIENT_INCLUDE_DEFAULTCLIENT_DEFAULTCLIENTBUILDER_H_ diff --git a/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClientComponent.h b/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClientComponent.h index 0673c3c6c6..cbf15164d2 100644 --- a/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClientComponent.h +++ b/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClientComponent.h @@ -18,6 +18,7 @@ #include +#include #include #include #include @@ -34,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -57,7 +59,6 @@ #include #include #include -#include #include #include #include @@ -68,7 +69,6 @@ #include #include #include -#include #include "DefaultClient/EqualizerRuntimeSetup.h" #include "DefaultClient/StubApplicationAudioPipelineFactory.h" @@ -108,8 +108,6 @@ using DefaultClientComponent = acsdkManufactory::Component< std::shared_ptr, acsdkManufactory:: Annotated, - acsdkManufactory:: - Annotated, std::shared_ptr, std::shared_ptr, std::shared_ptr, @@ -139,8 +137,9 @@ using DefaultClientComponent = acsdkManufactory::Component< std::shared_ptr, std::shared_ptr, std::shared_ptr, - std::shared_ptr, - std::shared_ptr>; + std::shared_ptr, + std::shared_ptr, + std::shared_ptr>; /** * Get the manufactory @c Component for (legacy) @c DefaultClient initialization. @@ -179,7 +178,8 @@ DefaultClientComponent getComponent( const std::shared_ptr& bluetoothStorage, const std::shared_ptr& bluetoothConnectionRulesProvider, - const std::shared_ptr& notificationsStorage); + const std::shared_ptr& notificationsStorage, + const std::shared_ptr& cryptoFactory); } // namespace defaultClient } // namespace alexaClientSDK diff --git a/ApplicationUtilities/DefaultClient/include/DefaultClient/ExternalCapabilitiesBuilderInterface.h b/ApplicationUtilities/DefaultClient/include/DefaultClient/ExternalCapabilitiesBuilderInterface.h index 615a48c9dd..1eb9a849a6 100644 --- a/ApplicationUtilities/DefaultClient/include/DefaultClient/ExternalCapabilitiesBuilderInterface.h +++ b/ApplicationUtilities/DefaultClient/include/DefaultClient/ExternalCapabilitiesBuilderInterface.h @@ -38,15 +38,15 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include -#include +#include namespace alexaClientSDK { namespace defaultClient { @@ -78,17 +78,6 @@ class ExternalCapabilitiesBuilderInterface { */ virtual ~ExternalCapabilitiesBuilderInterface() = default; - /** - * This method sets the focus manager responsible for visual interactions. - * - * This method will only get called if GUI support has been enabled. - * - * @param visualFocusManager The focus manager object. - * @return A reference to this builder to allow nested function calls. - */ - virtual ExternalCapabilitiesBuilderInterface& withVisualFocusManager( - std::shared_ptr visualFocusManager) = 0; - /** * This method sets the storage using for setting. * @@ -107,7 +96,7 @@ class ExternalCapabilitiesBuilderInterface { * @param templateRuntime The TemplateRuntime object. */ virtual ExternalCapabilitiesBuilderInterface& withTemplateRunTime( - std::shared_ptr templateRuntime) = 0; + std::shared_ptr templateRuntime) = 0; /** * Get the CallManager reference. @@ -124,6 +113,15 @@ class ExternalCapabilitiesBuilderInterface { virtual ExternalCapabilitiesBuilderInterface& withInternetConnectionMonitor( std::shared_ptr internetConnectionMonitor) = 0; + /** + * This method sets the Alexa Interface Message Sender to send Alexa Interface response events. + * + * @param alexaMessageSender The AlexaInterfaceMessageSenderInterface object. + */ + virtual ExternalCapabilitiesBuilderInterface& withAlexaInterfaceMessageSender( + std::shared_ptr + alexaMessageSender) = 0; + /** * This method sets the DialogUXStateAggregator for CallManager. * @@ -161,6 +159,7 @@ class ExternalCapabilitiesBuilderInterface { * @param softwareComponentReporter Object to report adapters' versions. * @param playbackRouter Object to route local playback control command. * @param endpointRegistrationManager Object to manage endpoints. + * @param metricRecorder Object to manage AVS SDK metric reporting. * @return A list with all capabilities as well as objects that require explicit shutdown. Shutdown will be * performed in the reverse order of occurrence. */ @@ -192,7 +191,8 @@ class ExternalCapabilitiesBuilderInterface { std::shared_ptr softwareComponentReporter, std::shared_ptr playbackRouter, std::shared_ptr - endpointRegistrationManager) = 0; + endpointRegistrationManager, + std::shared_ptr metricRecorder) = 0; }; } // namespace defaultClient diff --git a/ApplicationUtilities/DefaultClient/src/CMakeLists.txt b/ApplicationUtilities/DefaultClient/src/CMakeLists.txt index 3ff7530960..27f8d654dd 100644 --- a/ApplicationUtilities/DefaultClient/src/CMakeLists.txt +++ b/ApplicationUtilities/DefaultClient/src/CMakeLists.txt @@ -4,6 +4,7 @@ add_definitions("-DACSDK_LOG_MODULE=defaultClient") add_library(DefaultClient ConnectionRetryTrigger.cpp DefaultClient.cpp + DefaultClientBuilder.cpp DefaultClientComponent.cpp EqualizerRuntimeSetup.cpp StubApplicationAudioPipelineFactory.cpp @@ -15,8 +16,7 @@ target_include_directories(DefaultClient PUBLIC "${Endpoints_SOURCE_DIR}/include") if(BLUETOOTH_BLUEZ) - target_link_libraries(DefaultClient - BluetoothImplementationsBlueZ) + target_link_libraries(DefaultClient BluetoothImplementationsBlueZ) endif() target_link_libraries(DefaultClient @@ -47,31 +47,34 @@ target_link_libraries(DefaultClient RegistrationManager SDKComponent SoftwareComponentReporter - SpeakerManager SpeechSynthesizer SystemSoundPlayer - TemplateRuntime acsdkAlerts acsdkAlertsInterfaces + acsdkAudioEncoderInterfaces acsdkAudioPlayer acsdkBluetooth acsdkBluetoothInterfaces acsdkEqualizer acsdkExternalMediaPlayer acsdkInteractionModel + acsdkSDKClient acsdkStartupManagerInterfaces acsdkSystemClockMonitorInterfaces acsdkDeviceSetup acsdkDeviceSetupInterfaces acsdkAuthorization - acsdkAuthorizationInterfaces) + acsdkAuthorizationInterfaces + acsdkSpeakerManagerComponent + acsdkTemplateRuntime + acsdkTemplateRuntimeInterfaces) if (CAPTIONS) target_link_libraries(DefaultClient CaptionsLib) endif() if (PCC) - target_link_libraries(DefaultClient PhoneCallController) + target_link_libraries(DefaultClient acsdkPhoneCallController) endif() if (MC) @@ -82,7 +85,7 @@ if (MC) endif() if (MCC) - target_link_libraries(DefaultClient MeetingClientController) + target_link_libraries(DefaultClient acsdkMeetingClientController) endif() if (METRICS) @@ -90,9 +93,5 @@ if (METRICS) target_link_libraries(DefaultClient MetricRecorder) endif() -if (OPUS) - target_link_libraries(DefaultClient OpusEncoderContext) -endif() - # install target asdk_install() diff --git a/ApplicationUtilities/DefaultClient/src/ConnectionRetryTrigger.cpp b/ApplicationUtilities/DefaultClient/src/ConnectionRetryTrigger.cpp index 6c2e7fa383..ef9f3852fc 100644 --- a/ApplicationUtilities/DefaultClient/src/ConnectionRetryTrigger.cpp +++ b/ApplicationUtilities/DefaultClient/src/ConnectionRetryTrigger.cpp @@ -23,7 +23,7 @@ namespace defaultClient { using namespace alexaClientSDK::avsCommon::sdkInterfaces; /// String to identify log entries originating from this file. -static const std::string TAG("ConnectionRetryTrigger"); +#define TAG "ConnectionRetryTrigger" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/ApplicationUtilities/DefaultClient/src/DefaultClient.cpp b/ApplicationUtilities/DefaultClient/src/DefaultClient.cpp index e377a10f05..36f3562997 100644 --- a/ApplicationUtilities/DefaultClient/src/DefaultClient.cpp +++ b/ApplicationUtilities/DefaultClient/src/DefaultClient.cpp @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include #include @@ -38,17 +40,6 @@ #include #include -#ifdef ENABLE_PCC -#include -#include -#endif - -#ifdef ENABLE_MCC -#include -#include -#include -#endif - #include "DefaultClient/DefaultClient.h" #include "DefaultClient/DefaultClientComponent.h" #include "DefaultClient/StubApplicationAudioPipelineFactory.h" @@ -67,7 +58,7 @@ using namespace alexaClientSDK::avsCommon::sdkInterfaces; using namespace alexaClientSDK::avsCommon::utils; /// String to identify log entries originating from this file. -static const std::string TAG("DefaultClient"); +#define TAG "DefaultClient" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -85,12 +76,12 @@ std::unique_ptr DefaultClient::create( std::shared_ptr> additionalSpeakers, #ifdef ENABLE_PCC std::shared_ptr phoneSpeaker, - std::shared_ptr phoneCaller, + std::shared_ptr phoneCaller, #endif #ifdef ENABLE_MCC std::shared_ptr meetingSpeaker, - std::shared_ptr meetingClient, - std::shared_ptr calendarClient, + std::shared_ptr meetingClient, + std::shared_ptr calendarClient, #endif #ifdef ENABLE_COMMS_AUDIO_PROXY std::shared_ptr commsMediaPlayer, @@ -107,7 +98,8 @@ std::unique_ptr DefaultClient::create( std::shared_ptr softwareInfoSenderObserver, std::shared_ptr diagnostics, const std::shared_ptr& externalCapabilitiesBuilder, - capabilityAgents::aip::AudioProvider firstInteractionAudioProvider) { + capabilityAgents::aip::AudioProvider firstInteractionAudioProvider, + const std::shared_ptr& sdkClientRegistry) { std::unique_ptr defaultClient(new DefaultClient()); if (!defaultClient->initialize( manufactory, @@ -136,7 +128,12 @@ std::unique_ptr DefaultClient::create( softwareInfoSenderObserver, diagnostics, externalCapabilitiesBuilder, - firstInteractionAudioProvider)) { + firstInteractionAudioProvider, + sdkClientRegistry)) { + auto shutdownManager = defaultClient->getShutdownManager(); + if (shutdownManager) { + shutdownManager->shutdown(); + } return nullptr; } return defaultClient; @@ -169,12 +166,12 @@ std::unique_ptr DefaultClient::create( std::shared_ptr> additionalSpeakers, #ifdef ENABLE_PCC std::shared_ptr phoneSpeaker, - std::shared_ptr phoneCaller, + std::shared_ptr phoneCaller, #endif #ifdef ENABLE_MCC std::shared_ptr meetingSpeaker, - std::shared_ptr meetingClient, - std::shared_ptr calendarClient, + std::shared_ptr meetingClient, + std::shared_ptr calendarClient, #endif #ifdef ENABLE_COMMS_AUDIO_PROXY std::shared_ptr commsMediaPlayer, @@ -216,7 +213,9 @@ std::unique_ptr DefaultClient::create( bool startAlertSchedulingOnInitialization, std::shared_ptr messageRouterFactory, const std::shared_ptr& expectSpeechTimeoutHandler, - capabilityAgents::aip::AudioProvider firstInteractionAudioProvider) { + capabilityAgents::aip::AudioProvider firstInteractionAudioProvider, + const std::shared_ptr& cryptoFactory, + const std::shared_ptr& sdkClientRegistry) { if (!equalizerRuntimeSetup) { equalizerRuntimeSetup = std::make_shared(false); @@ -304,7 +303,8 @@ std::unique_ptr DefaultClient::create( std::move(bluetoothDeviceManager), std::move(bluetoothStorage), bluetoothConnectionRulesProvider, - std::move(notificationsStorage)); + std::move(notificationsStorage), + cryptoFactory); auto manufactory = DefaultClientManufactory::create(component); auto speakerManager = manufactory->get>(); @@ -350,7 +350,8 @@ std::unique_ptr DefaultClient::create( softwareInfoSenderObserver, diagnostics, externalCapabilitiesBuilder, - firstInteractionAudioProvider); + firstInteractionAudioProvider, + sdkClientRegistry); } bool DefaultClient::initialize( @@ -362,12 +363,12 @@ bool DefaultClient::initialize( std::shared_ptr> additionalSpeakers, #ifdef ENABLE_PCC std::shared_ptr phoneSpeaker, - std::shared_ptr phoneCaller, + std::shared_ptr phoneCaller, #endif #ifdef ENABLE_MCC std::shared_ptr meetingSpeaker, - std::shared_ptr meetingClient, - std::shared_ptr calendarClient, + std::shared_ptr meetingClient, + std::shared_ptr calendarClient, #endif #ifdef ENABLE_COMMS_AUDIO_PROXY std::shared_ptr commsMediaPlayer, @@ -384,7 +385,15 @@ bool DefaultClient::initialize( std::shared_ptr softwareInfoSenderObserver, std::shared_ptr diagnostics, const std::shared_ptr& externalCapabilitiesBuilder, - capabilityAgents::aip::AudioProvider firstInteractionAudioProvider) { + capabilityAgents::aip::AudioProvider firstInteractionAudioProvider, + const std::shared_ptr& sdkClientRegistry) { + + m_shutdownManager = manufactory->get>(); + if (!m_shutdownManager) { + ACSDK_ERROR(LX("initializeFailed").m("Failed to get ShutdownManager!")); + return false; + } + if (!ringtoneMediaPlayer) { ACSDK_ERROR(LX("initializeFailed").d("reason", "nullRingtoneMediaPlayer")); return false; @@ -475,13 +484,13 @@ bool DefaultClient::initialize( m_internetConnectionMonitor = manufactory->get>(); if (!m_internetConnectionMonitor) { - ACSDK_ERROR(LX("initializeFailed").d("reason", "nullConnectionManager")); + ACSDK_ERROR(LX("initializeFailed").d("reason", "nullInternetConnectionMonitor")); return false; } m_connectionManager = manufactory->get>(); if (!m_connectionManager) { - ACSDK_ERROR(LX("initializeFailed").d("reason", "nullDefaultEndpointBuilder")); + ACSDK_ERROR(LX("initializeFailed").d("reason", "nullConnectionManager")); return false; } @@ -522,6 +531,10 @@ bool DefaultClient::initialize( ACSDK_ERROR(LX("initializeFailed").d("reason", "nullAlexaMessageSender")); return false; } + if (externalCapabilitiesBuilder) { + ACSDK_INFO(LX(__FUNCTION__).m("Supply m_alexaMessageSender to externalCapabilitiesBuilder")); + externalCapabilitiesBuilder->withAlexaInterfaceMessageSender(m_alexaMessageSender); + } m_speakerManager = manufactory->get>(); if (!m_speakerManager) { @@ -569,12 +582,6 @@ bool DefaultClient::initialize( return false; } - m_shutdownManager = manufactory->get>(); - if (!m_shutdownManager) { - ACSDK_ERROR(LX("initializeFailed").m("Failed to get ShutdownManager!")); - return false; - } - m_certifiedSender = manufactory->get>(); if (!m_certifiedSender) { ACSDK_ERROR(LX("initializeFailed").d("reason", "nullCertifiedSender")); @@ -740,7 +747,7 @@ bool DefaultClient::initialize( speechConfirmationSetting, capabilityChangeNotifier, wakeWordsSetting, - manufactory->get>(), + manufactory->get>(), firstInteractionAudioProvider, powerResourceManager, metricRecorder, @@ -832,7 +839,7 @@ bool DefaultClient::initialize( * that implements the * PhoneCallController interface of AVS */ - m_phoneCallControllerCapabilityAgent = capabilityAgents::phoneCallController::PhoneCallController::create( + m_phoneCallControllerCapabilityAgent = phoneCallController::PhoneCallController::create( m_contextManager, m_connectionManager, phoneCaller, phoneSpeaker, m_audioFocusManager, m_exceptionSender); if (!m_phoneCallControllerCapabilityAgent) { ACSDK_ERROR(LX("initializeFailed").d("reason", "unableToCreatePhoneCallControllerCapabilityAgent")); @@ -844,23 +851,20 @@ bool DefaultClient::initialize( * Creating the MeetingClientController - This component is the Capability Agent that implements the * MeetingClientController interface of AVS */ - m_meetingClientControllerCapabilityAgent = - capabilityAgents::meetingClientController::MeetingClientController::create( - m_contextManager, - m_connectionManager, - meetingClient, - calendarClient, - m_speakerManager, - m_audioFocusManager, - m_exceptionSender); + m_meetingClientControllerCapabilityAgent = meetingClientController::MeetingClientController::create( + m_contextManager, + m_connectionManager, + meetingClient, + calendarClient, + m_speakerManager, + m_audioFocusManager, + m_exceptionSender); if (!m_meetingClientControllerCapabilityAgent) { ACSDK_ERROR(LX("initializeFailed").d("reason", "unableToCreateMeetingClientControllerCapabilityAgent")); } #endif if (isGuiSupported) { - m_visualFocusManager = manufactory->get< - acsdkManufactory::Annotated>(); auto renderPlayerInfoCardsProviderRegistrar = manufactory ->get>(); @@ -874,13 +878,14 @@ bool DefaultClient::initialize( * Capability Agent that implements the * TemplateRuntime interface of AVS. */ - m_templateRuntime = capabilityAgents::templateRuntime::TemplateRuntime::createTemplateRuntime( - renderPlayerInfoCardsProviderRegistrar, m_visualFocusManager, m_exceptionSender); - if (!m_templateRuntime) { + auto templateRuntimeData = templateRuntime::TemplateRuntimeFactory::create( + renderPlayerInfoCardsProviderRegistrar, m_exceptionSender, m_defaultEndpointBuilder); + if (!templateRuntimeData.hasValue()) { ACSDK_ERROR(LX("initializeFailed").d("reason", "unableToCreateTemplateRuntimeCapabilityAgent")); return false; } - m_dialogUXStateAggregator->addObserver(m_templateRuntime); + m_templateRuntime = templateRuntimeData.value().templateRuntime; + m_shutdownObjects.push_back(templateRuntimeData.value().requiresShutdown); if (externalCapabilitiesBuilder) { externalCapabilitiesBuilder->withTemplateRunTime(m_templateRuntime); } @@ -1059,10 +1064,6 @@ bool DefaultClient::initialize( } #endif - if (isGuiSupported) { - m_defaultEndpointBuilder->withCapability(m_templateRuntime, m_templateRuntime); - } - if (m_equalizerCapabilityAgent) { m_defaultEndpointBuilder->withCapability(m_equalizerCapabilityAgent, m_equalizerCapabilityAgent); } @@ -1111,7 +1112,8 @@ bool DefaultClient::initialize( powerResourceManager, m_softwareReporterCapabilityAgent, m_playbackRouter, - m_endpointRegistrationManager); + m_endpointRegistrationManager, + metricRecorder); for (auto& capability : externalCapabilities.first) { if (capability.configuration.hasValue()) { m_defaultEndpointBuilder->withCapability(capability.configuration.value(), capability.directiveHandler); @@ -1150,6 +1152,29 @@ bool DefaultClient::initialize( } m_defaultEndpointBuilder->withCapabilityConfiguration(m_softwareReporterCapabilityAgent); + + if (sdkClientRegistry) { + sdkClientRegistry->registerComponent(m_defaultEndpointBuilder); + sdkClientRegistry->registerComponent(m_connectionManager); + sdkClientRegistry->registerComponent(m_exceptionSender); + sdkClientRegistry->registerComponent(m_certifiedSender); + sdkClientRegistry->registerComponent(m_audioFocusManager); + sdkClientRegistry->registerComponent(customerDataManager); + sdkClientRegistry->registerComponent(reportStateHandler); + sdkClientRegistry->registerComponent(m_audioInputProcessor); + sdkClientRegistry->registerComponent(m_speakerManager); + sdkClientRegistry->registerComponent(m_directiveSequencer); + sdkClientRegistry->registerComponent(userInactivityMonitor); + sdkClientRegistry->registerComponent(m_contextManager); + sdkClientRegistry->registerComponent(m_avsGatewayManager); + sdkClientRegistry->registerComponent(audioFactory); + sdkClientRegistry->registerComponent(powerResourceManager); + sdkClientRegistry->registerComponent(m_softwareReporterCapabilityAgent); + sdkClientRegistry->registerComponent(m_playbackRouter); + sdkClientRegistry->registerComponent(m_endpointRegistrationManager); + sdkClientRegistry->registerComponent(metricRecorder); + sdkClientRegistry->registerComponent(m_alexaMessageSender); + } return true; } @@ -1265,7 +1290,7 @@ void DefaultClient::removeAudioPlayerObserver( } void DefaultClient::addTemplateRuntimeObserver( - std::shared_ptr observer) { + std::shared_ptr observer) { if (!m_templateRuntime) { ACSDK_ERROR(LX("addTemplateRuntimeObserverFailed").d("reason", "guiNotSupported")); return; @@ -1274,7 +1299,7 @@ void DefaultClient::addTemplateRuntimeObserver( } void DefaultClient::removeTemplateRuntimeObserver( - std::shared_ptr observer) { + std::shared_ptr observer) { if (!m_templateRuntime) { ACSDK_ERROR(LX("removeTemplateRuntimeObserverFailed").d("reason", "guiNotSupported")); return; @@ -1282,14 +1307,6 @@ void DefaultClient::removeTemplateRuntimeObserver( m_templateRuntime->removeObserver(observer); } -void DefaultClient::TemplateRuntimeDisplayCardCleared() { - if (!m_templateRuntime) { - ACSDK_ERROR(LX("TemplateRuntimeDisplayCardClearedFailed").d("reason", "guiNotSupported")); - return; - } - m_templateRuntime->displayCardCleared(); -} - void DefaultClient::addNotificationsObserver( std::shared_ptr observer) { m_notificationsNotifier->addObserver(observer); @@ -1690,6 +1707,31 @@ std::shared_ptr DefaultClient } DefaultClient::~DefaultClient() { + shutdown(); +} + +bool DefaultClient::setEncodingAudioFormat(AudioFormat::Encoding encoding) { + return m_audioInputProcessor->setEncodingAudioFormat(encoding); +} + +capabilityAgents::aip::AudioInputProcessor::EncodingFormatResponse DefaultClient::requestEncodingAudioFormats( + const capabilityAgents::aip::AudioInputProcessor::EncodingFormatRequest& encodings) { + return m_audioInputProcessor->requestEncodingAudioFormats(encodings); +} + +bool DefaultClient::configure(const std::shared_ptr& sdkClientRegistry) { + return true; +} + +DefaultClient::DefaultClient() : FeatureClientInterface(TAG) { +} + +void DefaultClient::doShutdown() { + if (m_shutdownManager) { + m_shutdownManager->shutdown(); + m_shutdownManager.reset(); + } + while (!m_shutdownObjects.empty()) { if (m_shutdownObjects.back()) { m_shutdownObjects.back()->shutdown(); @@ -1701,10 +1743,6 @@ DefaultClient::~DefaultClient() { ACSDK_DEBUG5(LX("EndpointRegistrationManagerShutdown")); m_endpointRegistrationManager->shutdown(); } - if (m_templateRuntime) { - ACSDK_DEBUG5(LX("TemplateRuntimeShutdown")); - m_templateRuntime->shutdown(); - } if (m_audioInputProcessor) { ACSDK_DEBUG5(LX("AIPShutdown")); removeInternetConnectionObserver(m_audioInputProcessor); @@ -1774,13 +1812,15 @@ DefaultClient::~DefaultClient() { } } -bool DefaultClient::setEncodingAudioFormat(AudioFormat::Encoding encoding) { - return m_audioInputProcessor->setEncodingAudioFormat(encoding); +void DefaultClient::stopInteraction() { + if (m_audioInputProcessor) { + m_audioInputProcessor->resetState(); + } } -capabilityAgents::aip::AudioInputProcessor::EncodingFormatResponse DefaultClient::requestEncodingAudioFormats( - const capabilityAgents::aip::AudioInputProcessor::EncodingFormatRequest& encodings) { - return m_audioInputProcessor->requestEncodingAudioFormats(encodings); +std::shared_ptr DefaultClient::getAudioFocusManager() { + return m_audioFocusManager; } + } // namespace defaultClient } // namespace alexaClientSDK diff --git a/ApplicationUtilities/DefaultClient/src/DefaultClientBuilder.cpp b/ApplicationUtilities/DefaultClient/src/DefaultClientBuilder.cpp new file mode 100644 index 0000000000..ba1e5d7326 --- /dev/null +++ b/ApplicationUtilities/DefaultClient/src/DefaultClientBuilder.cpp @@ -0,0 +1,271 @@ +/* + * 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 "DefaultClient/DefaultClientBuilder.h" + +namespace alexaClientSDK { +namespace defaultClient { +/// String to identify log entries originating from this file. +#define TAG "DefaultClientBuilder" + +/** + * 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) + +// Friendly name for this builder component +static const char DEFAULT_CLIENT_BUILDER_NAME[] = "DefaultClientBuilder"; + +std::unique_ptr DefaultClientBuilder::create( + std::shared_ptr deviceInfo, + std::shared_ptr customerDataManager, + std::unordered_map> + externalMusicProviderMediaPlayers, + std::unordered_map> + externalMusicProviderSpeakers, + acsdkExternalMediaPlayer::ExternalMediaPlayer::AdapterCreationMap adapterCreationMap, + std::shared_ptr speakMediaPlayer, + std::unique_ptr audioMediaPlayerFactory, + std::shared_ptr alertsMediaPlayer, + std::shared_ptr notificationsMediaPlayer, + std::shared_ptr bluetoothMediaPlayer, + std::shared_ptr ringtoneMediaPlayer, + std::shared_ptr systemSoundMediaPlayer, + std::shared_ptr speakSpeaker, + std::vector> audioSpeakers, + std::shared_ptr alertsSpeaker, + std::shared_ptr notificationsSpeaker, + std::shared_ptr bluetoothSpeaker, + std::shared_ptr ringtoneSpeaker, + std::shared_ptr systemSoundSpeaker, + std::multimap< + avsCommon::sdkInterfaces::ChannelVolumeInterface::Type, + std::shared_ptr> additionalSpeakers, +#ifdef ENABLE_PCC + std::shared_ptr phoneSpeaker, + std::shared_ptr phoneCaller, +#endif +#ifdef ENABLE_MCC + std::shared_ptr meetingSpeaker, + std::shared_ptr meetingClient, + std::shared_ptr calendarClient, +#endif +#ifdef ENABLE_COMMS_AUDIO_PROXY + std::shared_ptr commsMediaPlayer, + std::shared_ptr commsSpeaker, + std::shared_ptr sharedDataStream, +#endif + std::shared_ptr equalizerRuntimeSetup, + std::shared_ptr audioFactory, + std::shared_ptr authDelegate, + std::shared_ptr alertStorage, + std::shared_ptr messageStorage, + std::shared_ptr notificationsStorage, + std::shared_ptr deviceSettingStorage, + std::shared_ptr bluetoothStorage, + std::shared_ptr miscStorage, + std::unordered_set> + alexaDialogStateObservers, + std::unordered_set> + connectionObservers, + std::shared_ptr internetConnectionMonitor, + bool isGuiSupported, + std::shared_ptr capabilitiesDelegate, + std::shared_ptr contextManager, + std::shared_ptr transportFactory, + std::shared_ptr avsGatewayManager, + std::shared_ptr localeAssetsManager, + std::unordered_set> + enabledConnectionRules, + std::shared_ptr systemTimezone, + avsCommon::sdkInterfaces::softwareInfo::FirmwareVersion firmwareVersion, + bool sendSoftwareInfoOnConnected, + std::shared_ptr softwareInfoSenderObserver, + std::unique_ptr bluetoothDeviceManager, + std::shared_ptr metricRecorder, + std::shared_ptr powerResourceManager, + std::shared_ptr diagnostics, + std::shared_ptr externalCapabilitiesBuilder, + std::shared_ptr channelVolumeFactory, + bool startAlertSchedulingOnInitialization, + std::shared_ptr messageRouterFactory, + std::shared_ptr expectSpeechTimeoutHandler, + capabilityAgents::aip::AudioProvider firstInteractionAudioProvider, + std::shared_ptr cryptoFactory) { + auto builder = new DefaultClientBuilder(); + + builder->m_deviceInfo = std::move(deviceInfo); + builder->m_customerDataManager = std::move(customerDataManager); + builder->m_externalMusicProviderMediaPlayers = std::move(externalMusicProviderMediaPlayers); + builder->m_externalMusicProviderSpeakers = std::move(externalMusicProviderSpeakers); + builder->m_adapterCreationMap = std::move(adapterCreationMap); + builder->m_speakMediaPlayer = std::move(speakMediaPlayer); + builder->m_audioMediaPlayerFactory = std::move(audioMediaPlayerFactory); + builder->m_alertsMediaPlayer = std::move(alertsMediaPlayer); + builder->m_notificationsMediaPlayer = std::move(notificationsMediaPlayer); + builder->m_bluetoothMediaPlayer = std::move(bluetoothMediaPlayer); + builder->m_ringtoneMediaPlayer = std::move(ringtoneMediaPlayer); + builder->m_systemSoundMediaPlayer = std::move(systemSoundMediaPlayer); + builder->m_speakSpeaker = std::move(speakSpeaker); + builder->m_audioSpeakers = std::move(audioSpeakers); + builder->m_alertsSpeaker = std::move(alertsSpeaker); + builder->m_notificationsSpeaker = std::move(notificationsSpeaker); + builder->m_bluetoothSpeaker = std::move(bluetoothSpeaker); + builder->m_ringtoneSpeaker = std::move(ringtoneSpeaker); + builder->m_systemSoundSpeaker = std::move(systemSoundSpeaker); + builder->m_additionalSpeakers = std::move(additionalSpeakers); +#ifdef ENABLE_PCC + builder->m_phoneSpeaker = std::move(phoneSpeaker); + builder->m_phoneCaller = std::move(phoneCaller); +#endif +#ifdef ENABLE_MCC + builder->m_meetingSpeaker = std::move(meetingSpeaker); + builder->m_meetingClient = std::move(meetingClient); + builder->m_calendarClient = std::move(calendarClient); +#endif +#ifdef ENABLE_COMMS_AUDIO_PROXY + builder->m_commsMediaPlayer = std::move(commsMediaPlayer); + builder->m_commsSpeaker = std::move(commsSpeaker); + builder->m_sharedDataStream = std::move(sharedDataStream); +#endif + builder->m_equalizerRuntimeSetup = std::move(equalizerRuntimeSetup); + builder->m_audioFactory = std::move(audioFactory); + builder->m_authDelegate = std::move(authDelegate); + builder->m_alertStorage = std::move(alertStorage); + builder->m_messageStorage = std::move(messageStorage); + builder->m_notificationsStorage = std::move(notificationsStorage); + builder->m_deviceSettingStorage = std::move(deviceSettingStorage); + builder->m_bluetoothStorage = std::move(bluetoothStorage); + builder->m_miscStorage = std::move(miscStorage); + builder->m_alexaDialogStateObservers = std::move(alexaDialogStateObservers); + builder->m_connectionObservers = std::move(connectionObservers); + builder->m_internetConnectionMonitor = std::move(internetConnectionMonitor); + builder->m_isGuiSupported = isGuiSupported; + builder->m_capabilitiesDelegate = std::move(capabilitiesDelegate); + builder->m_contextManager = std::move(contextManager); + builder->m_transportFactory = std::move(transportFactory); + builder->m_avsGatewayManager = std::move(avsGatewayManager); + builder->m_localeAssetsManager = std::move(localeAssetsManager); + builder->m_enabledConnectionRules = std::move(enabledConnectionRules); + builder->m_systemTimezone = std::move(systemTimezone); + builder->m_firmwareVersion = std::move(firmwareVersion); + builder->m_sendSoftwareInfoOnConnected = sendSoftwareInfoOnConnected; + builder->m_softwareInfoSenderObserver = std::move(softwareInfoSenderObserver); + builder->m_bluetoothDeviceManager = std::move(bluetoothDeviceManager); + builder->m_metricRecorder = std::move(metricRecorder); + builder->m_powerResourceManager = std::move(powerResourceManager); + builder->m_diagnostics = std::move(diagnostics); + builder->m_externalCapabilitiesBuilder = std::move(externalCapabilitiesBuilder); + builder->m_channelVolumeFactory = std::move(channelVolumeFactory); + builder->m_startAlertSchedulingOnInitialization = startAlertSchedulingOnInitialization; + builder->m_messageRouterFactory = std::move(messageRouterFactory); + builder->m_expectSpeechTimeoutHandler = std::move(expectSpeechTimeoutHandler); + builder->m_firstInteractionAudioProvider = std::move(firstInteractionAudioProvider); + builder->m_cryptoFactory = std::move(cryptoFactory); + + return std::unique_ptr(builder); +} + +DefaultClientBuilder::DefaultClientBuilder() : m_constructed(false) { +} + +std::shared_ptr DefaultClientBuilder::construct( + const std::shared_ptr& sdkClientRegistry) { + if (m_constructed) { + ACSDK_ERROR(LX("constructFailed").d("reason", "Repeated call to construct()")); + return nullptr; + } + + m_constructed = true; + + return DefaultClient::create( + std::move(m_deviceInfo), + std::move(m_customerDataManager), + std::move(m_externalMusicProviderMediaPlayers), + std::move(m_externalMusicProviderSpeakers), + std::move(m_adapterCreationMap), + std::move(m_speakMediaPlayer), + std::move(m_audioMediaPlayerFactory), + std::move(m_alertsMediaPlayer), + std::move(m_notificationsMediaPlayer), + std::move(m_bluetoothMediaPlayer), + std::move(m_ringtoneMediaPlayer), + std::move(m_systemSoundMediaPlayer), + std::move(m_speakSpeaker), + std::move(m_audioSpeakers), + std::move(m_alertsSpeaker), + std::move(m_notificationsSpeaker), + std::move(m_bluetoothSpeaker), + std::move(m_ringtoneSpeaker), + std::move(m_systemSoundSpeaker), + std::move(m_additionalSpeakers), +#ifdef ENABLE_PCC + std::move(m_phoneSpeaker), + std::move(m_phoneCaller), +#endif +#ifdef ENABLE_MCC + std::move(m_meetingSpeaker), + std::move(m_meetingClient), + std::move(m_calendarClient), +#endif +#ifdef ENABLE_COMMS_AUDIO_PROXY + std::move(m_commsMediaPlayer), + std::move(m_commsSpeaker), + std::move(m_sharedDataStream), +#endif + std::move(m_equalizerRuntimeSetup), + std::move(m_audioFactory), + std::move(m_authDelegate), + std::move(m_alertStorage), + std::move(m_messageStorage), + std::move(m_notificationsStorage), + std::move(m_deviceSettingStorage), + std::move(m_bluetoothStorage), + std::move(m_miscStorage), + std::move(m_alexaDialogStateObservers), + std::move(m_connectionObservers), + std::move(m_internetConnectionMonitor), + m_isGuiSupported, + std::move(m_capabilitiesDelegate), + std::move(m_contextManager), + std::move(m_transportFactory), + std::move(m_avsGatewayManager), + std::move(m_localeAssetsManager), + std::move(m_enabledConnectionRules), + std::move(m_systemTimezone), + std::move(m_firmwareVersion), + m_sendSoftwareInfoOnConnected, + std::move(m_softwareInfoSenderObserver), + std::move(m_bluetoothDeviceManager), + std::move(m_metricRecorder), + std::move(m_powerResourceManager), + std::move(m_diagnostics), + std::move(m_externalCapabilitiesBuilder), + std::move(m_channelVolumeFactory), + m_startAlertSchedulingOnInitialization, + std::move(m_messageRouterFactory), + std::move(m_expectSpeechTimeoutHandler), + std::move(m_firstInteractionAudioProvider), + std::move(m_cryptoFactory), + sdkClientRegistry); +} + +std::string DefaultClientBuilder::name() { + return DEFAULT_CLIENT_BUILDER_NAME; +} +} // namespace defaultClient +} // namespace alexaClientSDK diff --git a/ApplicationUtilities/DefaultClient/src/DefaultClientComponent.cpp b/ApplicationUtilities/DefaultClient/src/DefaultClientComponent.cpp index 98bab7f423..3191573541 100644 --- a/ApplicationUtilities/DefaultClient/src/DefaultClientComponent.cpp +++ b/ApplicationUtilities/DefaultClient/src/DefaultClientComponent.cpp @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include #include @@ -51,12 +51,11 @@ #include #include #include -#include -#include +#include #include #include #include -#include +#include #include #include "DefaultClient/DefaultClientComponent.h" @@ -83,7 +82,7 @@ using namespace capabilityAgents::alexa; using namespace capabilityAgents::system; /// String to identify log entries originating from this file. -static const std::string TAG("DefaultClientComponent"); +#define TAG "DefaultClientComponent" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -222,7 +221,8 @@ DefaultClientComponent getComponent( const std::shared_ptr& bluetoothStorage, const std::shared_ptr& bluetoothConnectionRulesProvider, - const std::shared_ptr& notificationsStorage) { + const std::shared_ptr& notificationsStorage, + const std::shared_ptr& cryptoFactory) { std::shared_ptr bluetoothEventBus; if (bluetoothDeviceManager) { bluetoothEventBus = bluetoothDeviceManager->getEventBus(); @@ -259,6 +259,7 @@ DefaultClientComponent getComponent( .addInstance(bluetoothEventBus) .addInstance(bluetoothStorage) .addInstance(notificationsStorage) + .addInstance(cryptoFactory) .addRetainedFactory(getCreateApplicationAudioPipelineFactory(stubAudioPipelineFactory)) .addRetainedFactory(getCreateDeviceSettingStorageInterface(deviceSettingStorage)) @@ -292,7 +293,7 @@ DefaultClientComponent getComponent( createCapabilitiesDelegateStorageInterface) /// Optional, horizontal components. - .addComponent(acsdkSpeechEncoder::getComponent()) + .addComponent(audioEncoderComponent::getComponent()) .addComponent(captions::getComponent()) /// Capability Agents. Some CAs are still instantiated in DefaultClient.cpp. @@ -308,10 +309,9 @@ DefaultClientComponent getComponent( #endif .addComponent(acsdkNotifications::getComponent()) .addComponent(capabilityAgents::playbackController::getComponent()) - .addComponent(capabilityAgents::speakerManager::getComponent()) + .addComponent(speakerManagerComponent::getSpeakerManagerComponent()) .addComponent(capabilityAgents::system::getComponent()) - .addRetainedFactory(capabilityAgents::templateRuntime::RenderPlayerInfoCardsProviderRegistrar:: - createRenderPlayerInfoCardsProviderRegistrarInterface) + .addRetainedFactory(templateRuntime::createRenderPlayerInfoCardsProviderRegistrarInterface) .addComponent(acsdkDeviceSetup::getComponent()); } diff --git a/ApplicationUtilities/DefaultClient/src/EqualizerRuntimeSetup.cpp b/ApplicationUtilities/DefaultClient/src/EqualizerRuntimeSetup.cpp index 310df8c159..50e20c247e 100644 --- a/ApplicationUtilities/DefaultClient/src/EqualizerRuntimeSetup.cpp +++ b/ApplicationUtilities/DefaultClient/src/EqualizerRuntimeSetup.cpp @@ -24,7 +24,7 @@ namespace defaultClient { using namespace alexaClientSDK::acsdkEqualizerInterfaces; /// String to identify log entries originating from this file. -static const std::string TAG("EqualizerRuntimeSetup"); +#define TAG "EqualizerRuntimeSetup" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/ApplicationUtilities/DefaultClient/src/StubApplicationAudioPipelineFactory.cpp b/ApplicationUtilities/DefaultClient/src/StubApplicationAudioPipelineFactory.cpp index f06f9a927e..4b6fb5ea41 100644 --- a/ApplicationUtilities/DefaultClient/src/StubApplicationAudioPipelineFactory.cpp +++ b/ApplicationUtilities/DefaultClient/src/StubApplicationAudioPipelineFactory.cpp @@ -23,7 +23,7 @@ using namespace avsCommon::sdkInterfaces; using namespace avsCommon::utils::mediaPlayer; /// String to identify log entries originating from this file. -static const std::string TAG("StubApplicationAudioPipelineFactory"); +#define TAG "StubApplicationAudioPipelineFactory" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/ApplicationUtilities/Resources/Audio/include/Audio/Data/create_header.bash b/ApplicationUtilities/Resources/Audio/include/Audio/Data/create_header.bash index ad1c51c01c..8eb362c20c 100644 --- a/ApplicationUtilities/Resources/Audio/include/Audio/Data/create_header.bash +++ b/ApplicationUtilities/Resources/Audio/include/Audio/Data/create_header.bash @@ -1,5 +1,18 @@ #!/bin/bash +# 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. + # This is used to create a header file that contains binary data from a file that can be used in this library. The # files are downloaded from https://developer.amazon.com/docs/alexa/alexa-voice-service/ux-design-overview.html#sounds. diff --git a/ApplicationUtilities/Resources/Audio/src/CMakeLists.txt b/ApplicationUtilities/Resources/Audio/src/CMakeLists.txt index 2b66b0c3c4..01a09a658b 100644 --- a/ApplicationUtilities/Resources/Audio/src/CMakeLists.txt +++ b/ApplicationUtilities/Resources/Audio/src/CMakeLists.txt @@ -8,8 +8,7 @@ add_library(AudioResources SystemSoundAudioFactory.cpp) target_include_directories(AudioResources PUBLIC - "${AudioResources_SOURCE_DIR}/include" - "${AVSCommon_SOURCE_DIR}/Utils/include") + "${AudioResources_SOURCE_DIR}/include") target_link_libraries(AudioResources AVSCommon) diff --git a/ApplicationUtilities/SDKComponent/src/SDKComponent.cpp b/ApplicationUtilities/SDKComponent/src/SDKComponent.cpp index 3cb1e4355d..becaa40f59 100644 --- a/ApplicationUtilities/SDKComponent/src/SDKComponent.cpp +++ b/ApplicationUtilities/SDKComponent/src/SDKComponent.cpp @@ -29,7 +29,7 @@ using namespace avsCommon::utils::logger; static const std::string SDK_COMPONENT_NAME = "com.amazon.alexa.deviceSDK"; /// String to identify log entries originating from this file. -static const std::string TAG("SDKComponent"); +#define TAG "SDKComponent" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/ApplicationUtilities/SystemSoundPlayer/src/SystemSoundPlayer.cpp b/ApplicationUtilities/SystemSoundPlayer/src/SystemSoundPlayer.cpp index 21478a5b0e..bbf19dd492 100644 --- a/ApplicationUtilities/SystemSoundPlayer/src/SystemSoundPlayer.cpp +++ b/ApplicationUtilities/SystemSoundPlayer/src/SystemSoundPlayer.cpp @@ -24,7 +24,7 @@ using namespace avsCommon::utils::logger; using MediaPlayerState = avsCommon::utils::mediaPlayer::MediaPlayerState; /// String to identify log entries originating from this file. -static const std::string TAG("SystemSoundPlayer"); +#define TAG "SystemSoundPlayer" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/BluetoothImplementations/BlueZ/include/BlueZ/BlueZBluetoothDevice.h b/BluetoothImplementations/BlueZ/include/BlueZ/BlueZBluetoothDevice.h index 29f43a53e4..92dac78e21 100644 --- a/BluetoothImplementations/BlueZ/include/BlueZ/BlueZBluetoothDevice.h +++ b/BluetoothImplementations/BlueZ/include/BlueZ/BlueZBluetoothDevice.h @@ -80,6 +80,8 @@ class BlueZBluetoothDevice std::future connect() override; std::future disconnect() override; + bool setPairingPin(const std::string& pin) override; + std::vector> getSupportedServices() override; std::shared_ptr getService( diff --git a/BluetoothImplementations/BlueZ/src/BlueZA2DPSink.cpp b/BluetoothImplementations/BlueZ/src/BlueZA2DPSink.cpp index fc35a085f1..cc10f0a83a 100644 --- a/BluetoothImplementations/BlueZ/src/BlueZA2DPSink.cpp +++ b/BluetoothImplementations/BlueZ/src/BlueZA2DPSink.cpp @@ -26,7 +26,7 @@ using namespace avsCommon::utils; using namespace avsCommon::sdkInterfaces::bluetooth::services; /// String to identify log entries originating from this file. -static const std::string TAG{"BlueZA2DPSink"}; +#define TAG "BlueZA2DPSink" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/BluetoothImplementations/BlueZ/src/BlueZA2DPSource.cpp b/BluetoothImplementations/BlueZ/src/BlueZA2DPSource.cpp index 28f8393922..528d5f942c 100644 --- a/BluetoothImplementations/BlueZ/src/BlueZA2DPSource.cpp +++ b/BluetoothImplementations/BlueZ/src/BlueZA2DPSource.cpp @@ -27,7 +27,7 @@ using namespace avsCommon::utils; using namespace avsCommon::sdkInterfaces::bluetooth::services; /// String to identify log entries originating from this file. -static const std::string TAG{"BlueZA2DPSource"}; +#define TAG "BlueZA2DPSource" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/BluetoothImplementations/BlueZ/src/BlueZAVRCPController.cpp b/BluetoothImplementations/BlueZ/src/BlueZAVRCPController.cpp index e4afdc8e75..3d72cf5c67 100644 --- a/BluetoothImplementations/BlueZ/src/BlueZAVRCPController.cpp +++ b/BluetoothImplementations/BlueZ/src/BlueZAVRCPController.cpp @@ -24,7 +24,7 @@ using namespace avsCommon::utils; using namespace avsCommon::sdkInterfaces::bluetooth::services; /// String to identify log entries originating from this file. -static const std::string TAG{"BlueZAVRCPController"}; +#define TAG "BlueZAVRCPController" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/BluetoothImplementations/BlueZ/src/BlueZAVRCPTarget.cpp b/BluetoothImplementations/BlueZ/src/BlueZAVRCPTarget.cpp index 50351806d5..1b9e0894e1 100644 --- a/BluetoothImplementations/BlueZ/src/BlueZAVRCPTarget.cpp +++ b/BluetoothImplementations/BlueZ/src/BlueZAVRCPTarget.cpp @@ -24,7 +24,7 @@ using namespace avsCommon::utils; using namespace avsCommon::sdkInterfaces::bluetooth::services; /// String to identify log entries originating from this file. -static const std::string TAG{"BlueZAVRCPTarget"}; +#define TAG "BlueZAVRCPTarget" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/BluetoothImplementations/BlueZ/src/BlueZBluetoothDevice.cpp b/BluetoothImplementations/BlueZ/src/BlueZBluetoothDevice.cpp index 662cb4e189..a1c9f41749 100644 --- a/BluetoothImplementations/BlueZ/src/BlueZBluetoothDevice.cpp +++ b/BluetoothImplementations/BlueZ/src/BlueZBluetoothDevice.cpp @@ -37,7 +37,7 @@ using namespace avsCommon::sdkInterfaces::bluetooth; using namespace avsCommon::sdkInterfaces::bluetooth::services; /// String to identify log entries originating from this file. -static const std::string TAG{"BlueZBluetoothDevice"}; +#define TAG "BlueZBluetoothDevice" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -503,6 +503,11 @@ bool BlueZBluetoothDevice::executeDisconnect() { return true; } +bool BlueZBluetoothDevice::setPairingPin(const std::string& pin) { + // This feature is not supported currently. + return false; +} + std::vector> BlueZBluetoothDevice::getSupportedServices() { ACSDK_DEBUG5(LX(__func__)); @@ -750,7 +755,7 @@ void BlueZBluetoothDevice::onPropertyChanged(const GVariantMapReader& changesMap initializeServices(uuids); } - m_executor.submit([this, pairedChanged, paired, connectedChanged, connected, aliasChanged, aliasStr] { + m_executor.execute([this, pairedChanged, paired, connectedChanged, connected, aliasChanged, aliasStr] { if (aliasChanged) { ACSDK_DEBUG5(LX("nameChanged").d("oldName", m_friendlyName).d("newName", aliasStr)); m_friendlyName = aliasStr; diff --git a/BluetoothImplementations/BlueZ/src/BlueZBluetoothDeviceManager.cpp b/BluetoothImplementations/BlueZ/src/BlueZBluetoothDeviceManager.cpp index e10d5a7eef..49cc5863d7 100644 --- a/BluetoothImplementations/BlueZ/src/BlueZBluetoothDeviceManager.cpp +++ b/BluetoothImplementations/BlueZ/src/BlueZBluetoothDeviceManager.cpp @@ -25,7 +25,7 @@ using namespace avsCommon::sdkInterfaces::bluetooth; using namespace avsCommon::utils::bluetooth; /// String to identify log entries originating from this file. -static const std::string TAG{"BlueZBluetoothDeviceManager"}; +#define TAG "BlueZBluetoothDeviceManager" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/BluetoothImplementations/BlueZ/src/BlueZDeviceManager.cpp b/BluetoothImplementations/BlueZ/src/BlueZDeviceManager.cpp index ff225cd6b6..670f9b2339 100644 --- a/BluetoothImplementations/BlueZ/src/BlueZDeviceManager.cpp +++ b/BluetoothImplementations/BlueZ/src/BlueZDeviceManager.cpp @@ -79,7 +79,7 @@ static const std::string STATE_IDLE = "idle"; static const std::string STATE_ACTIVE = "active"; /// String to identify log entries originating from this file. -static const std::string TAG{"BlueZDeviceManager"}; +#define TAG "BlueZDeviceManager" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/BluetoothImplementations/BlueZ/src/BlueZHFP.cpp b/BluetoothImplementations/BlueZ/src/BlueZHFP.cpp index f3d1fabdb1..45e7d9bfef 100644 --- a/BluetoothImplementations/BlueZ/src/BlueZHFP.cpp +++ b/BluetoothImplementations/BlueZ/src/BlueZHFP.cpp @@ -26,7 +26,7 @@ using namespace avsCommon::utils; using namespace avsCommon::sdkInterfaces::bluetooth::services; /// String to identify log entries originating from this file. -static const std::string TAG{"BlueZHFP"}; +#define TAG "BlueZHFP" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/BluetoothImplementations/BlueZ/src/BlueZHID.cpp b/BluetoothImplementations/BlueZ/src/BlueZHID.cpp index 27d8d0e385..ab3d08ab13 100644 --- a/BluetoothImplementations/BlueZ/src/BlueZHID.cpp +++ b/BluetoothImplementations/BlueZ/src/BlueZHID.cpp @@ -26,7 +26,7 @@ using namespace avsCommon::utils; using namespace avsCommon::sdkInterfaces::bluetooth::services; /// String to identify log entries originating from this file. -static const std::string TAG{"BlueZHID"}; +#define TAG "BlueZHID" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/BluetoothImplementations/BlueZ/src/BlueZHostController.cpp b/BluetoothImplementations/BlueZ/src/BlueZHostController.cpp index 2ea1d28502..314cd1b34d 100644 --- a/BluetoothImplementations/BlueZ/src/BlueZHostController.cpp +++ b/BluetoothImplementations/BlueZ/src/BlueZHostController.cpp @@ -25,7 +25,7 @@ namespace bluetoothImplementations { namespace blueZ { /// String to identify log entries originating from this file. -static const std::string TAG{"BlueZHostController"}; +#define TAG "BlueZHostController" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/BluetoothImplementations/BlueZ/src/BlueZSPP.cpp b/BluetoothImplementations/BlueZ/src/BlueZSPP.cpp index b1173cf6f9..16686a42bc 100644 --- a/BluetoothImplementations/BlueZ/src/BlueZSPP.cpp +++ b/BluetoothImplementations/BlueZ/src/BlueZSPP.cpp @@ -26,7 +26,7 @@ using namespace avsCommon::utils; using namespace avsCommon::sdkInterfaces::bluetooth::services; /// String to identify log entries originating from this file. -static const std::string TAG{"BlueZSPP"}; +#define TAG "BlueZSPP" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/BluetoothImplementations/BlueZ/src/DBusConnection.cpp b/BluetoothImplementations/BlueZ/src/DBusConnection.cpp index 24f57a6d15..063bb8347a 100644 --- a/BluetoothImplementations/BlueZ/src/DBusConnection.cpp +++ b/BluetoothImplementations/BlueZ/src/DBusConnection.cpp @@ -23,7 +23,7 @@ namespace bluetoothImplementations { namespace blueZ { /// String to identify log entries originating from this file. -static const std::string TAG{"DBusConnection"}; +#define TAG "DBusConnection" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/BluetoothImplementations/BlueZ/src/DBusObjectBase.cpp b/BluetoothImplementations/BlueZ/src/DBusObjectBase.cpp index 496ce7909f..f8ee17343a 100644 --- a/BluetoothImplementations/BlueZ/src/DBusObjectBase.cpp +++ b/BluetoothImplementations/BlueZ/src/DBusObjectBase.cpp @@ -23,7 +23,7 @@ namespace bluetoothImplementations { namespace blueZ { /// String to identify log entries originating from this file. -static const std::string TAG{"DBusObjectBase"}; +#define TAG "DBusObjectBase" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/BluetoothImplementations/BlueZ/src/DBusPropertiesProxy.cpp b/BluetoothImplementations/BlueZ/src/DBusPropertiesProxy.cpp index ed3de477e8..12bb2b3f86 100644 --- a/BluetoothImplementations/BlueZ/src/DBusPropertiesProxy.cpp +++ b/BluetoothImplementations/BlueZ/src/DBusPropertiesProxy.cpp @@ -24,7 +24,7 @@ namespace bluetoothImplementations { namespace blueZ { /// String to identify log entries originating from this file. -static const std::string TAG{"DBusPropertiesProxy"}; +#define TAG "DBusPropertiesProxy" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/BluetoothImplementations/BlueZ/src/DBusProxy.cpp b/BluetoothImplementations/BlueZ/src/DBusProxy.cpp index c813add37c..59d8c2a052 100644 --- a/BluetoothImplementations/BlueZ/src/DBusProxy.cpp +++ b/BluetoothImplementations/BlueZ/src/DBusProxy.cpp @@ -25,7 +25,7 @@ namespace bluetoothImplementations { namespace blueZ { /// String to identify log entries originating from this file. -static const std::string TAG{"BlueZUtils"}; +#define TAG "BlueZUtils" static const int PROXY_DEFAULT_TIMEOUT = -1; diff --git a/BluetoothImplementations/BlueZ/src/GVariantMapReader.cpp b/BluetoothImplementations/BlueZ/src/GVariantMapReader.cpp index 2f141b5000..830e790282 100644 --- a/BluetoothImplementations/BlueZ/src/GVariantMapReader.cpp +++ b/BluetoothImplementations/BlueZ/src/GVariantMapReader.cpp @@ -22,7 +22,7 @@ namespace bluetoothImplementations { namespace blueZ { /// String to identify log entries originating from this file. -static const std::string TAG{"GVariantMapReader"}; +#define TAG "GVariantMapReader" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/BluetoothImplementations/BlueZ/src/GVariantTupleReader.cpp b/BluetoothImplementations/BlueZ/src/GVariantTupleReader.cpp index da5a9a3b3e..66cd7a1bb1 100644 --- a/BluetoothImplementations/BlueZ/src/GVariantTupleReader.cpp +++ b/BluetoothImplementations/BlueZ/src/GVariantTupleReader.cpp @@ -22,7 +22,7 @@ namespace bluetoothImplementations { namespace blueZ { /// String to identify log entries originating from this file. -static const std::string TAG{"GVariantTupleReader"}; +#define TAG "GVariantTupleReader" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/BluetoothImplementations/BlueZ/src/MPRISPlayer.cpp b/BluetoothImplementations/BlueZ/src/MPRISPlayer.cpp index 944fe5290c..0caa549e8d 100644 --- a/BluetoothImplementations/BlueZ/src/MPRISPlayer.cpp +++ b/BluetoothImplementations/BlueZ/src/MPRISPlayer.cpp @@ -30,7 +30,7 @@ using namespace avsCommon::sdkInterfaces::bluetooth::services; using namespace avsCommon::utils::bluetooth; /// String to identify log entries originating from this file. -static const std::string TAG{"MPRISPlayer"}; +#define TAG "MPRISPlayer" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/BluetoothImplementations/BlueZ/src/MediaEndpoint.cpp b/BluetoothImplementations/BlueZ/src/MediaEndpoint.cpp index dff806e84e..bdbf127699 100644 --- a/BluetoothImplementations/BlueZ/src/MediaEndpoint.cpp +++ b/BluetoothImplementations/BlueZ/src/MediaEndpoint.cpp @@ -96,7 +96,7 @@ constexpr size_t MAX_SANE_CODE_SIZE = MAX_SANE_FRAME_LENGTH * 32; constexpr size_t MIN_SANE_CODE_SIZE = 1; // Standard SDK per module logging constants -static const std::string TAG{"MediaEndpoint"}; +#define TAG "MediaEndpoint" #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) /** diff --git a/BluetoothImplementations/BlueZ/src/PairingAgent.cpp b/BluetoothImplementations/BlueZ/src/PairingAgent.cpp index 662811b8de..56f1fb6f2d 100644 --- a/BluetoothImplementations/BlueZ/src/PairingAgent.cpp +++ b/BluetoothImplementations/BlueZ/src/PairingAgent.cpp @@ -27,7 +27,7 @@ namespace bluetoothImplementations { namespace blueZ { /// String to identify log entries originating from this file. -static const std::string TAG{"PairingAgent"}; +#define TAG "PairingAgent" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/BluetoothImplementations/BlueZ/src/PulseAudioBluetoothInitializer.cpp b/BluetoothImplementations/BlueZ/src/PulseAudioBluetoothInitializer.cpp index 1812af5e8b..5474ecde60 100644 --- a/BluetoothImplementations/BlueZ/src/PulseAudioBluetoothInitializer.cpp +++ b/BluetoothImplementations/BlueZ/src/PulseAudioBluetoothInitializer.cpp @@ -21,7 +21,7 @@ #include /// String to identify log entries originating from this file. -static const std::string TAG{"PulseAudioBluetoothInitializer"}; +#define TAG "PulseAudioBluetoothInitializer" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -414,7 +414,7 @@ void PulseAudioBluetoothInitializer::onEventFired(const BluetoothEvent& event) { return; } - m_executor.submit([this] { + m_executor.execute([this] { if (!m_paLoopStarted) { m_paLoopStarted = true; run(); diff --git a/CHANGELOG.md b/CHANGELOG.md index 1418530279..a37bf9c745 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ ## ChangeLog +### Version 3.0.0 - September 30 2022 +Feature enhancements, updates, and resolved issues from all releases are available on the [Amazon developer portal](https://developer.amazon.com/docs/alexa/avs-device-sdk/release-notes.html) + +### Version 1.26.0 - November 15 2021 +Feature enhancements, updates, and resolved issues from all releases are available on the [Amazon developer portal](https://developer.amazon.com/en-US/docs/alexa/avs-device-sdk-1-2x/release-notes.html) + ### Version 1.25.0 - August 23 2021 Feature enhancements, updates, and resolved issues from all releases are available on the [Amazon developer portal](https://developer.amazon.com/docs/alexa/avs-device-sdk/release-notes.html) diff --git a/CMakeLists.txt b/CMakeLists.txt index a49a5c5b6a..327a7fa005 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) # Set project information -project(AlexaClientSDK VERSION 1.26.0 LANGUAGES CXX) +project(AlexaClientSDK VERSION 3.0.0 LANGUAGES CXX) set(PROJECT_BRIEF "A cross-platform, modular SDK for interacting with the Alexa Voice Service") # This variable should be used by extension packages to include cmake files from this project. @@ -27,6 +27,9 @@ configure_file ( "${PROJECT_BINARY_DIR}/AVSCommon/Utils/include/AVSCommon/Utils/SDKConfig.h" ) +# Create Integration directory in output +file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/Integration) + # Add utils under ThirdParty first so extensions can also utilize them add_subdirectory("ThirdParty") @@ -56,16 +59,12 @@ endif() add_subdirectory("InterruptModel") add_subdirectory("PlaylistParser") add_subdirectory("CapabilityAgents") -if (NOT MSVC) - add_subdirectory("Integration") -endif() -add_subdirectory("SampleApp") add_subdirectory("ApplicationUtilities") add_subdirectory("MediaPlayer") add_subdirectory("shared") -add_subdirectory("SpeechEncoder") add_subdirectory("Storage") add_subdirectory("SynchronizeStateSender") +add_subdirectory("SampleApplications") add_subdirectory("doc") # Create .pc pkg-config file diff --git a/CapabilitiesDelegate/include/CapabilitiesDelegate/CapabilitiesDelegate.h b/CapabilitiesDelegate/include/CapabilitiesDelegate/CapabilitiesDelegate.h index 8a7106f886..dfe0182ec6 100644 --- a/CapabilitiesDelegate/include/CapabilitiesDelegate/CapabilitiesDelegate.h +++ b/CapabilitiesDelegate/include/CapabilitiesDelegate/CapabilitiesDelegate.h @@ -60,6 +60,15 @@ struct InProcessEndpointsToConfigMapStruct { /** * CapabilitiesDelegate provides an implementation of the CapabilitiesDelegateInterface. It allows clients to register * capabilities implemented by agents and publish them so that Alexa is aware of the device's capabilities. + * + * While updating capabilities for an endpoint, the device will also send the cached capabilities of all endpoints that + * share the same registration information. Such endpoints are referred to as deduplicated endpoints. + * + * @note: The following restrictions apply to deduplicated endpoints: + * 1. We can only have one set of deduplicated endpoints, and this set will include the default endpoint. + * 2. All capabilities of the deduplicated endpoints will need to fit into one discovery event. + * 3. Deleting a deduplicated endpoint is not permitted. + * */ class CapabilitiesDelegate : public avsCommon::sdkInterfaces::CapabilitiesDelegateInterface @@ -78,6 +87,7 @@ class CapabilitiesDelegate * @param providerRegistrar Object with which to register the new instance as a post connect operation provider. * @param shutdownNotifier The object to register with to be notified when it is time to shut down. * @param alexaEventProcessedNotifier The object to register with to be notified of AlexaEventProcessed directives. + * @param metricRecorder Optional reference to metric recorder. * @return If successful, returns a new CapabilitiesDelegate, otherwise @c nullptr. */ static std::shared_ptr createCapabilitiesDelegateInterface( @@ -89,7 +99,8 @@ class CapabilitiesDelegate providerRegistrar, const std::shared_ptr& shutdownNotifier, const std::shared_ptr& - alexaEventProcessedNotifier); + alexaEventProcessedNotifier, + const std::shared_ptr& metricRecorder = nullptr); /** * Create an CapabilitiesDelegate. @@ -103,7 +114,8 @@ class CapabilitiesDelegate static std::shared_ptr create( const std::shared_ptr& authDelegate, const std::shared_ptr& storage, - const std::shared_ptr& customerDataManager); + const std::shared_ptr& customerDataManager, + const std::shared_ptr& metricRecorder = nullptr); /// @name CapabilitiesDelegateInterface method overrides. /// @{ @@ -181,7 +193,8 @@ class CapabilitiesDelegate CapabilitiesDelegate( const std::shared_ptr& authDelegate, const std::shared_ptr& storage, - const std::shared_ptr& customerDataManager); + const std::shared_ptr& customerDataManager, + const std::shared_ptr& metricsRecorder); /** * Perform initialization after construction but before returning the @@ -285,6 +298,26 @@ class CapabilitiesDelegate */ void moveInFlightEndpointsToRegisteredEndpoints(); + /** + * Invoke the DiscoveryEventSender to send endpoints. + * @param addOrUpdateEndpointsToSend map of endpoints to be added or updated. + * @param deleteEndpointsToSend map of endpoints to be deleted. + * @return false if the DiscoveryEventSender was not successfully created, true otherwise. + */ + bool executeSendDiscoveryEvents( + const std::unordered_map& addOrUpdateEndpointsToSend, + const std::unordered_map& deleteEndpointsToSend); + + /** + * Determine whether an endpoint is deduplicated. + * @param endpointId Identifier of the endpoint. + * @return true if the endpoint is deduplicated, false otherwise. + */ + bool isEndpointDeduplicated(const avsCommon::sdkInterfaces::endpoints::EndpointIdentifier& endpointId); + + /// Optional (may be nullptr) interface for metrics. + std::shared_ptr m_metricRecorder; + /// Mutex used to serialize access to Capabilities state and Capabilities state observers. std::mutex m_observerMutex; @@ -323,6 +356,13 @@ class CapabilitiesDelegate /// A map of endpointId to configuration for currently registered endpoints. std::unordered_map m_endpoints; + /// A map of endpointId to Registration for currently pending/in-flight/registered endpoints. + /// @note these endpoint(s) are the device maintaining the HTTP/2 connection. + std::unordered_map< + avsCommon::sdkInterfaces::endpoints::EndpointIdentifier, + avsCommon::utils::Optional> + m_endpointRegistrations; + /// The mutex to serialize operations related to @c m_currentDiscoveryEventSender. std::mutex m_currentDiscoveryEventSenderMutex; diff --git a/CapabilitiesDelegate/include/CapabilitiesDelegate/DiscoveryEventSender.h b/CapabilitiesDelegate/include/CapabilitiesDelegate/DiscoveryEventSender.h index d936567f31..4c4a44ac48 100644 --- a/CapabilitiesDelegate/include/CapabilitiesDelegate/DiscoveryEventSender.h +++ b/CapabilitiesDelegate/include/CapabilitiesDelegate/DiscoveryEventSender.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -50,14 +51,18 @@ class DiscoveryEventSender * @param deleteReportEndpoints The map of endpoints for which the @c Discovery.DeleteReport event will be sent. * @param authDelegate The auth delegate instance to request the auth token from to be sent in the @c Discovery * events. - * @param waitForEventProcessed Indicate if sender should wait for the EventProcessed directive. + * @param waitForEventProcessed Indicate if sender should wait for the EventProcessed directive. Default is true. + * @param metricRecorder Optional (may be nullptr) reference to metric recorder. + * @param postConnect Indicate if sender is operating in post-connect operation context. Default is false. * @return a new instance of the @c DiscoveryEventSender. */ static std::shared_ptr create( const std::unordered_map& addOrUpdateReportEndpoints, const std::unordered_map& deleteReportEndpoints, const std::shared_ptr& authDelegate, - const bool waitForEventProcessed = true); + bool waitForEventProcessed = true, + const std::shared_ptr& metricRecorder = nullptr, + bool postConnect = false); /** * Destructor. @@ -92,12 +97,16 @@ class DiscoveryEventSender * @param authDelegate The auth delegate instance to request the auth token from to be sent in the @c Discovery * events. * @param waitForEventProcessed Indicate if sender should wait for the EventProcessed directive. + * @param metricRecorder Optional (may be nullptr) reference to metric recorder. + * @param postConnect Indicate if sender is operating in post-connect operation context. */ DiscoveryEventSender( const std::unordered_map& addOrUpdateReportEndpoints, const std::unordered_map& deleteReportEndpoints, const std::shared_ptr& authDelegate, - const bool waitForEventProcessed); + bool waitForEventProcessed, + const std::shared_ptr& metricRecorder, + bool postConnect); /** * Sends the discovery event while taking into account retries. @@ -193,6 +202,9 @@ class DiscoveryEventSender /// Auth delegate used to get the access token std::shared_ptr m_authDelegate; + /// Optional (may be nullptr) interface for metrics. + std::shared_ptr m_metricRecorder; + /// The authDelegate's auth status. AuthObserverInterface::State m_currentAuthState; @@ -235,6 +247,9 @@ class DiscoveryEventSender /// Flag indicating if the event should wait for the EventProcessed directive. const bool m_waitForEventProcessed; + + /// Flag indicating if the object is used for post-connect operations. + const bool m_postConnect; }; } // namespace capabilitiesDelegate diff --git a/CapabilitiesDelegate/include/CapabilitiesDelegate/Utils/DiscoveryUtils.h b/CapabilitiesDelegate/include/CapabilitiesDelegate/Utils/DiscoveryUtils.h index 86d27fecaf..9409115be3 100644 --- a/CapabilitiesDelegate/include/CapabilitiesDelegate/Utils/DiscoveryUtils.h +++ b/CapabilitiesDelegate/include/CapabilitiesDelegate/Utils/DiscoveryUtils.h @@ -110,6 +110,18 @@ std::string getDeleteReportEventJson( const std::vector& endpointConfigurations, const std::string& authToken); +/** + * Get the maximum number of supported endpoints. + * @return The maximum number of endpoints. + */ +size_t getMaxEndpoints(); + +/** + * Get the maximum number of capabilities per endpoint. + * @return The maximum number of capabilities an endpoint can have. + */ +size_t getMaxCapabilitiesPerEndpoint(); + } // namespace utils } // namespace capabilitiesDelegate } // namespace alexaClientSDK diff --git a/CapabilitiesDelegate/src/CapabilitiesDelegate.cpp b/CapabilitiesDelegate/src/CapabilitiesDelegate.cpp index 57ba3563b2..942102894b 100644 --- a/CapabilitiesDelegate/src/CapabilitiesDelegate.cpp +++ b/CapabilitiesDelegate/src/CapabilitiesDelegate.cpp @@ -14,6 +14,7 @@ */ #include +#include #include #include @@ -31,15 +32,16 @@ using namespace avsCommon::sdkInterfaces; using namespace avsCommon::sdkInterfaces::endpoints; using namespace avsCommon::utils; using namespace avsCommon::utils::configuration; +using namespace avsCommon::utils::metrics; using namespace capabilitiesDelegate::utils; /// String to identify log entries originating from this file. -static const std::string TAG("CapabilitiesDelegate"); +#define TAG "CapabilitiesDelegate" /** * Create a LogEntry using this file's TAG and the specified event string. * - * @param The event string for this @c LogEntry. + * @param event The event string for this @c LogEntry. */ #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) @@ -71,7 +73,8 @@ std::shared_ptr CapabilitiesDelegate::createCapab providerRegistrar, const std::shared_ptr& shutdownNotifier, const std::shared_ptr& - alexaEventProcessedNotifier) { + alexaEventProcessedNotifier, + const std::shared_ptr& metricRecorder) { if (!providerRegistrar) { ACSDK_ERROR(LX("createCapabilitiesDelegateInterfaceFailed").d("reason", "nullProviderRegistrar")); return nullptr; @@ -82,7 +85,7 @@ std::shared_ptr CapabilitiesDelegate::createCapab return nullptr; } - auto capabilitiesDelegate = create(authDelegate, std::move(storage), customerDataManager); + auto capabilitiesDelegate = create(authDelegate, std::move(storage), customerDataManager, metricRecorder); if (!capabilitiesDelegate) { ACSDK_ERROR(LX("createCapabilitiesDelegateInterfaceFailed").d("reason", "createFailed")); return nullptr; @@ -100,7 +103,8 @@ std::shared_ptr CapabilitiesDelegate::createCapab std::shared_ptr CapabilitiesDelegate::create( const std::shared_ptr& authDelegate, const std::shared_ptr& capabilitiesDelegateStorage, - const std::shared_ptr& customerDataManager) { + const std::shared_ptr& customerDataManager, + const std::shared_ptr& metricRecorder) { if (!authDelegate) { ACSDK_ERROR(LX("createFailed").d("reason", "nullAuthDelegate")); } else if (!capabilitiesDelegateStorage) { @@ -109,7 +113,7 @@ std::shared_ptr CapabilitiesDelegate::create( ACSDK_ERROR(LX("createFailed").d("reason", "nullCustomerDataManager")); } else { std::shared_ptr instance( - new CapabilitiesDelegate(authDelegate, capabilitiesDelegateStorage, customerDataManager)); + new CapabilitiesDelegate(authDelegate, capabilitiesDelegateStorage, customerDataManager, nullptr)); if (!(instance->init())) { ACSDK_ERROR(LX("createFailed").d("reason", "CapabilitiesDelegateInitFailed")); return nullptr; @@ -124,9 +128,11 @@ std::shared_ptr CapabilitiesDelegate::create( CapabilitiesDelegate::CapabilitiesDelegate( const std::shared_ptr& authDelegate, const std::shared_ptr& capabilitiesDelegateStorage, - const std::shared_ptr& customerDataManager) : + const std::shared_ptr& customerDataManager, + const std::shared_ptr& metricRecorder) : RequiresShutdown{"CapabilitiesDelegate"}, CustomerDataHandler{customerDataManager}, + m_metricRecorder{metricRecorder}, m_capabilitiesState{CapabilitiesDelegateObserverInterface::State::UNINITIALIZED}, m_capabilitiesError{CapabilitiesDelegateObserverInterface::Error::UNINITIALIZED}, m_authDelegate{authDelegate}, @@ -248,6 +254,12 @@ void CapabilitiesDelegate::invalidateCapabilities() { } } +/// Check if the endpoint is present in the map of endpoint registrations. +bool CapabilitiesDelegate::isEndpointDeduplicated(const EndpointIdentifier& endpointId) { + ACSDK_DEBUG5(LX(__func__)); + return m_endpointRegistrations.find(endpointId) != m_endpointRegistrations.end(); +} + bool CapabilitiesDelegate::addOrUpdateEndpoint( const AVSDiscoveryEndpointAttributes& endpointAttributes, const std::vector& capabilities) { @@ -272,7 +284,7 @@ bool CapabilitiesDelegate::addOrUpdateEndpoint( } } - std::string endpointId = endpointAttributes.endpointId; + const EndpointIdentifier& endpointId = endpointAttributes.endpointId; { std::lock_guard lock{m_endpointsMutex}; @@ -285,6 +297,17 @@ bool CapabilitiesDelegate::addOrUpdateEndpoint( return false; } + // Check that this endpoint's registration is not changing. + // If the endpoint is already present in m_endpointRegistrations, confirm that the current registration is not + // empty, and is the same as the previous one. + if (m_endpointRegistrations.find(endpointId) != m_endpointRegistrations.end() && + m_endpointRegistrations[endpointId] != endpointAttributes.registration) { + ACSDK_ERROR(LX("addOrUpdateEndpointFailed") + .d("reason", "Endpoint registration changed") + .sensitive("endpointId", endpointId)); + return false; + } + // Add endpoint to pending list. it = m_addOrUpdateEndpoints.pending.find(endpointId); if (m_addOrUpdateEndpoints.pending.end() != it) { @@ -294,19 +317,121 @@ bool CapabilitiesDelegate::addOrUpdateEndpoint( return false; } + if (capabilities.size() > getMaxCapabilitiesPerEndpoint()) { + ACSDK_ERROR(LX("addOrUpdateEndpointFailed") + .d("reason", "Maximum number of capabilities reached") + .d("size", capabilities.size())); + return false; + } + std::string endpointConfigJson = getEndpointConfigJson(endpointAttributes, capabilities); // Check for endpoint configuration. if (endpointConfigJson.size() > MAX_ENDPOINTS_SIZE_IN_PAYLOAD) { - ACSDK_ERROR(LX("addOrUpdateEndpointFailed").d("reason", "endpointConfiguration too big")); + ACSDK_ERROR(LX("addOrUpdateEndpointFailed") + .d("reason", "endpointConfiguration too big") + .d("size", endpointConfigJson.size())); return false; } + if (endpointAttributes.registration.hasValue()) { + if (!m_endpointRegistrations.empty()) { + // check that the registration of endpointId matches the others by comparing it to the first one. + // Note that if endpointId is already present in m_endpointRegistrations, it has already been confirmed + // earlier that its registration is unchanged. + auto registration = m_endpointRegistrations.begin()->second; + if (endpointAttributes.registration != registration) { + ACSDK_ERROR(LX("addOrUpdateEndpointFailed") + .d("reason", "Endpoint registration does not match existing deduplicated endpoints") + .sensitive("endpointId", endpointId)); + return false; + } + } + // If this endpoint needs to be added to m_endpointRegistrations check whether doing so will + // exceed the maximum number of endpoints. + if (m_endpointRegistrations.find(endpointId) == m_endpointRegistrations.end() && + m_endpointRegistrations.size() + 1 > getMaxEndpoints()) { + ACSDK_ERROR(LX("addOrUpdateEndpointFailed") + .d("reason", "Max number of deduplicated endpoints reached") + .sensitive("endpointId", endpointId)); + return false; + } + + m_endpointRegistrations[endpointId] = endpointAttributes.registration; + + // Check if any of the deduplicated endpoints are pending deletion or in-flight for deletion. If they are, + // fail this addOrUpdate. This should never happen. + for (auto& iter : m_endpointRegistrations) { + if ((m_deleteEndpoints.pending.find(iter.first) != m_deleteEndpoints.pending.end()) || + (m_deleteEndpoints.inFlight.find(iter.first) != m_deleteEndpoints.inFlight.end())) { + ACSDK_ERROR(LX("addOrUpdateEndpointFailed") + .d("reason", "Deduplicated endpoint already pending delete") + .sensitive("endpointId", endpointId)); + return false; + } + } + + // Check that all de-duplicated endpoints can fit into one discovery message by adding the sizes of their + // configurations. + // First add the size of the current endpoint configuration. + size_t totalConfigurationSize = endpointConfigJson.size(); + for (auto& iter : m_endpointRegistrations) { + if (iter.first == endpointId) { + // skip the current endpoint, since we have already added its configuration size. + continue; + } + // Check pending before in-flight to ensure that the most recent configuration is used, wherever + // possible. + if (m_addOrUpdateEndpoints.pending.find(iter.first) != m_addOrUpdateEndpoints.pending.end()) { + totalConfigurationSize += m_addOrUpdateEndpoints.pending[iter.first].size(); + } else if (m_addOrUpdateEndpoints.inFlight.find(iter.first) != m_addOrUpdateEndpoints.inFlight.end()) { + totalConfigurationSize += m_addOrUpdateEndpoints.inFlight[iter.first].size(); + } else { + totalConfigurationSize += m_endpoints[iter.first].size(); + } + } + + if (totalConfigurationSize > MAX_ENDPOINTS_SIZE_IN_PAYLOAD) { + ACSDK_ERROR( + LX("addOrUpdateEndpointFailed") + .d("reason", "Deduplicated endpoint configurations too big to fit into one discovery event")); + return false; + } + + // All deduplicated endpoints can fit into one discovery message. + // Add the de-duplicated endpoints to the list of pending addOrUpdate endpoints, if they are not already + // present. If any of them are in flight, use that configuration as it is the most recent. + for (auto& iter : m_endpointRegistrations) { + auto dedupedEndpointId = iter.first; + if (dedupedEndpointId == endpointId) { + // skip adding the endpoint here, as it will be added after. + continue; + } + if (m_addOrUpdateEndpoints.pending.find(dedupedEndpointId) != m_addOrUpdateEndpoints.pending.end()) { + // already pending update. Nothing to do. + } else if ( + m_addOrUpdateEndpoints.inFlight.find(dedupedEndpointId) != m_addOrUpdateEndpoints.inFlight.end()) { + // update is in flight. + m_addOrUpdateEndpoints.pending.insert( + std::make_pair(dedupedEndpointId, m_addOrUpdateEndpoints.inFlight[dedupedEndpointId])); + ACSDK_DEBUG0(LX("addOrUpdateEndpoint") + .m("Adding inflight deduplicated Endpoint") + .sensitive("endpointId", dedupedEndpointId)); + } else { + // add de-duped endpoint. + ACSDK_DEBUG0(LX("addOrUpdateEndpoint") + .m("Adding registered deduplicated Endpoint") + .sensitive("endpointId", dedupedEndpointId)); + m_addOrUpdateEndpoints.pending.insert( + std::make_pair(dedupedEndpointId, m_endpoints[dedupedEndpointId])); + } + } + } m_addOrUpdateEndpoints.pending.insert(std::make_pair(endpointId, endpointConfigJson)); } if (!m_currentDiscoveryEventSender) { - m_executor.submit([this] { executeSendPendingEndpoints(); }); + m_executor.execute([this] { executeSendPendingEndpoints(); }); } return true; @@ -336,7 +461,7 @@ bool CapabilitiesDelegate::deleteEndpoint( } } - std::string endpointId = endpointAttributes.endpointId; + const EndpointIdentifier& endpointId = endpointAttributes.endpointId; { std::lock_guard lock{m_endpointsMutex}; @@ -358,6 +483,23 @@ bool CapabilitiesDelegate::deleteEndpoint( return false; } + // Check that this endpoint's registration is not changing + if (m_endpointRegistrations.find(endpointId) != m_endpointRegistrations.end() && + m_endpointRegistrations[endpointId] != endpointAttributes.registration) { + ACSDK_ERROR(LX("deleteEndpointFailed") + .d("reason", "Endpoint registration changed") + .sensitive("endpointId", endpointId)); + return false; + } + + // Disable deleting de-duped endpoints. + if (isEndpointDeduplicated(endpointId)) { + ACSDK_ERROR(LX("deleteEndpointFailed") + .d("reason", "Cannot delete a de-duplicated endpoint") + .sensitive("endpointId", endpointId)); + return false; + } + // Add endpoint to pending list. it = m_deleteEndpoints.pending.find(endpointId); if (m_deleteEndpoints.pending.end() != it) { @@ -371,7 +513,7 @@ bool CapabilitiesDelegate::deleteEndpoint( } if (!m_currentDiscoveryEventSender) { - m_executor.submit([this] { executeSendPendingEndpoints(); }); + m_executor.execute([this] { executeSendPendingEndpoints(); }); } return true; @@ -395,46 +537,100 @@ void CapabilitiesDelegate::executeSendPendingEndpoints() { return; } - if (m_addOrUpdateEndpoints.pending.empty() && m_deleteEndpoints.pending.empty()) { - ACSDK_DEBUG5(LX(__func__).d("Skipped", "No endpoints to register or delete")); - return; - } - - std::unordered_map addOrUpdateEndpointsToSend; + std::vector> addOrUpdateEndpointsToSendVector; std::unordered_map deleteEndpointsToSend; { std::lock_guard lock{m_endpointsMutex}; - // Move pending endpoints to in-flight, since we are going to send them. - m_addOrUpdateEndpoints.inFlight = m_addOrUpdateEndpoints.pending; - addOrUpdateEndpointsToSend = m_addOrUpdateEndpoints.inFlight; - m_addOrUpdateEndpoints.pending.clear(); + if (m_addOrUpdateEndpoints.pending.empty() && m_deleteEndpoints.pending.empty()) { + ACSDK_DEBUG5(LX(__func__).d("Skipped", "No endpoints to register or delete")); + return; + } - m_deleteEndpoints.inFlight = m_deleteEndpoints.pending; - deleteEndpointsToSend = m_deleteEndpoints.inFlight; - m_deleteEndpoints.pending.clear(); + // check if the addOrUpdate Discovery event will need to be split. + size_t totalAddOrUpdateConfigSize = 0; + for (auto& iter : m_addOrUpdateEndpoints.pending) { + totalAddOrUpdateConfigSize += iter.second.size(); + } + + if (totalAddOrUpdateConfigSize <= MAX_ENDPOINTS_SIZE_IN_PAYLOAD) { + ACSDK_DEBUG5(LX("ExecuteSendPendingEndpoints").d("reason", "No need to split discovery message")); + // No split necessary. + m_addOrUpdateEndpoints.inFlight = m_addOrUpdateEndpoints.pending; + addOrUpdateEndpointsToSendVector.push_back(m_addOrUpdateEndpoints.inFlight); + m_addOrUpdateEndpoints.pending.clear(); + + m_deleteEndpoints.inFlight = m_deleteEndpoints.pending; + deleteEndpointsToSend = m_deleteEndpoints.inFlight; + m_deleteEndpoints.pending.clear(); + } else { + // Discovery event will need to be split. + ACSDK_DEBUG5(LX("ExecuteSendPendingEndpoints").d("reason", "Need to split discovery message")); + // first send all endpoints that do not have registration information, i.e. are not de-duplicated. Send the + // endpoints to be deleted as well + std::unordered_map addOrUpdateEndpointsToSend; + for (auto iter = m_addOrUpdateEndpoints.pending.begin(); iter != m_addOrUpdateEndpoints.pending.end();) { + if (!isEndpointDeduplicated(iter->first)) { + addOrUpdateEndpointsToSend.insert(*iter); + m_addOrUpdateEndpoints.inFlight.insert(*iter); + iter = m_addOrUpdateEndpoints.pending.erase(iter); + } else { + ++iter; + } + } + // No non-deduplicated endpoints are pending add or update. + if (!addOrUpdateEndpointsToSend.empty()) { + addOrUpdateEndpointsToSendVector.push_back(addOrUpdateEndpointsToSend); + } + + m_deleteEndpoints.inFlight = m_deleteEndpoints.pending; + deleteEndpointsToSend = m_deleteEndpoints.inFlight; + m_deleteEndpoints.pending.clear(); + + addOrUpdateEndpointsToSend.clear(); + deleteEndpointsToSend.clear(); + + // Send remaining deduplicated endpoints in a separate discovery event, if any. + if (!m_addOrUpdateEndpoints.pending.empty()) { + m_addOrUpdateEndpoints.inFlight = m_addOrUpdateEndpoints.pending; + addOrUpdateEndpointsToSend = m_addOrUpdateEndpoints.inFlight; + m_addOrUpdateEndpoints.pending.clear(); + } + addOrUpdateEndpointsToSendVector.push_back(addOrUpdateEndpointsToSend); + } + } + + for (auto& addOrUpdateEndpointsToSend : addOrUpdateEndpointsToSendVector) { + if (!executeSendDiscoveryEvents(addOrUpdateEndpointsToSend, deleteEndpointsToSend)) { + return; + } + deleteEndpointsToSend.clear(); } +} +bool CapabilitiesDelegate::executeSendDiscoveryEvents( + const std::unordered_map& addOrUpdateEndpointsToSend, + const std::unordered_map& deleteEndpointsToSend) { ACSDK_DEBUG5(LX(__func__) .d("num endpoints to add", addOrUpdateEndpointsToSend.size()) .d("num endpoints to delete", deleteEndpointsToSend.size())); - auto discoveryEventSender = - DiscoveryEventSender::create(addOrUpdateEndpointsToSend, deleteEndpointsToSend, m_authDelegate); + auto discoveryEventSender = DiscoveryEventSender::create( + addOrUpdateEndpointsToSend, deleteEndpointsToSend, m_authDelegate, true, m_metricRecorder); if (!discoveryEventSender) { - ACSDK_ERROR(LX("failedExecuteSendPendingEndpoints").d("reason", "failed to create DiscoveryEventSender")); + ACSDK_ERROR(LX("failedExecuteSendDiscoveryEvents").d("reason", "failed to create DiscoveryEventSender")); moveInFlightEndpointsToPending(); setCapabilitiesState( CapabilitiesDelegateObserverInterface::State::FATAL_ERROR, CapabilitiesDelegateObserverInterface::Error::UNKNOWN_ERROR, getEndpointIdentifiers(addOrUpdateEndpointsToSend), getEndpointIdentifiers(deleteEndpointsToSend)); - return; + return false; } - setDiscoveryEventSender(discoveryEventSender); discoveryEventSender->sendDiscoveryEvents(m_messageSender); + return true; } void CapabilitiesDelegate::onAlexaEventProcessedReceived(const std::string& eventCorrelationToken) { @@ -520,11 +716,18 @@ std::shared_ptr CapabilitiesDelegate::createPostC /// even though no Discovery event was sent. This logic is not required for pending delete endpoints, since /// calls to CapabilitiesDelegate.deleteEndpoint fail when the endpoint is not already cached with /// CapabilitiesDelegate. - if (addOrUpdateEndpointsToSend.empty() && !originalPendingAddOrUpdateEndpoints.empty()) { + auto unchangedPendingAddOrUpdateEndpoints = originalPendingAddOrUpdateEndpoints; + for (const auto& endpoint : addOrUpdateEndpointsToSend) { + auto endpointIt = unchangedPendingAddOrUpdateEndpoints.find(endpoint.first); + if (endpointIt != unchangedPendingAddOrUpdateEndpoints.end()) { + unchangedPendingAddOrUpdateEndpoints.erase(endpointIt); + } + } + if (!unchangedPendingAddOrUpdateEndpoints.empty()) { setCapabilitiesState( CapabilitiesDelegateObserverInterface::State::SUCCESS, CapabilitiesDelegateObserverInterface::Error::SUCCESS, - getEndpointIdentifiers(originalPendingAddOrUpdateEndpoints), + getEndpointIdentifiers(unchangedPendingAddOrUpdateEndpoints), std::vector{}); } @@ -538,8 +741,8 @@ std::shared_ptr CapabilitiesDelegate::createPostC .d("num endpoints to add", addOrUpdateEndpointsToSend.size()) .d("num endpoints to delete", deleteEndpointsToSend.size())); - std::shared_ptr newEventSender = - DiscoveryEventSender::create(addOrUpdateEndpointsToSend, deleteEndpointsToSend, m_authDelegate); + std::shared_ptr newEventSender = DiscoveryEventSender::create( + addOrUpdateEndpointsToSend, deleteEndpointsToSend, m_authDelegate, true, m_metricRecorder, true); if (!newEventSender) { ACSDK_ERROR(LX("createPostConnectOperationFailed").m("Could not create DiscoveryEventSender.")); return nullptr; @@ -562,9 +765,13 @@ void CapabilitiesDelegate::onDiscoveryCompleted( const std::unordered_map& deleteReportEndpoints) { ACSDK_DEBUG5(LX(__func__)); - if (m_addOrUpdateEndpoints.inFlight != addOrUpdateReportEndpoints || - m_deleteEndpoints.inFlight != deleteReportEndpoints) { - ACSDK_WARN(LX(__func__).m("Cached in-flight endpoints do not match endpoints registered to AVS")); + { + std::lock_guard lock{m_endpointsMutex}; + + if (m_addOrUpdateEndpoints.inFlight != addOrUpdateReportEndpoints || + m_deleteEndpoints.inFlight != deleteReportEndpoints) { + ACSDK_WARN(LX(__func__).m("Cached in-flight endpoints do not match endpoints registered to AVS")); + } } auto addOrUpdateReportEndpointIdentifiers = getEndpointIdentifiers(addOrUpdateReportEndpoints); @@ -589,7 +796,7 @@ void CapabilitiesDelegate::onDiscoveryCompleted( addOrUpdateReportEndpointIdentifiers, deleteReportEndpointIdentifiers); - m_executor.submit([this] { executeSendPendingEndpoints(); }); + m_executor.execute([this] { executeSendPendingEndpoints(); }); } void CapabilitiesDelegate::onDiscoveryFailure(MessageRequestObserverInterface::Status status) { @@ -601,13 +808,20 @@ void CapabilitiesDelegate::onDiscoveryFailure(MessageRequestObserverInterface::S } ACSDK_ERROR(LX(__func__).d("reason", status)); - auto addOrUpdateReportEndpointIdentifiers = getEndpointIdentifiers(m_addOrUpdateEndpoints.inFlight); - auto deleteReportEndpointIdentifiers = getEndpointIdentifiers(m_deleteEndpoints.inFlight); - - switch (status) { - case MessageRequestObserverInterface::Status::INVALID_AUTH: + std::vector addOrUpdateReportEndpointIdentifiers; + std::vector deleteReportEndpointIdentifiers; + { + std::lock_guard lock{m_endpointsMutex}; + addOrUpdateReportEndpointIdentifiers = getEndpointIdentifiers(m_addOrUpdateEndpoints.inFlight); + deleteReportEndpointIdentifiers = getEndpointIdentifiers(m_deleteEndpoints.inFlight); + if (status == MessageRequestObserverInterface::Status::INVALID_AUTH || + status == MessageRequestObserverInterface::Status::BAD_REQUEST) { m_addOrUpdateEndpoints.inFlight.clear(); m_deleteEndpoints.inFlight.clear(); + } + } + switch (status) { + case MessageRequestObserverInterface::Status::INVALID_AUTH: resetCurrentDiscoveryEventSender(); setCapabilitiesState( @@ -617,8 +831,6 @@ void CapabilitiesDelegate::onDiscoveryFailure(MessageRequestObserverInterface::S deleteReportEndpointIdentifiers); break; case MessageRequestObserverInterface::Status::BAD_REQUEST: - m_addOrUpdateEndpoints.inFlight.clear(); - m_deleteEndpoints.inFlight.clear(); resetCurrentDiscoveryEventSender(); setCapabilitiesState( @@ -735,7 +947,7 @@ void CapabilitiesDelegate::onConnectionStatusChanged( if (ConnectionStatusObserverInterface::Status::CONNECTED == status) { /// If newly connected, send Discovery events for any endpoints that may have been added or deleted /// during the post-connect stage. - m_executor.submit([this] { executeSendPendingEndpoints(); }); + m_executor.execute([this] { executeSendPendingEndpoints(); }); } } @@ -759,10 +971,16 @@ void CapabilitiesDelegate::addStaleEndpointsToPendingDeleteLocked( ACSDK_ERROR(LX("findEndpointsToDeleteLockedFailed").d("reason", "invalidStoredEndpointConfig")); return; } - + // Do not allow a deduplicated endpoint to be deleted. for (auto& it : *storedEndpointConfig) { if (m_endpoints.end() == m_endpoints.find(it.first) && m_addOrUpdateEndpoints.pending.end() == m_addOrUpdateEndpoints.pending.find(it.first)) { + if (isEndpointDeduplicated(it.first)) { + ACSDK_ERROR(LX(__func__) + .d("reason", "deduplicated endpoint included in deleteReport") + .sensitive("endpointId", it.first)); + return; + } ACSDK_DEBUG9(LX(__func__).d("step", "endpoint included in deleteReport").sensitive("endpointId", it.first)); m_deleteEndpoints.pending.insert({it.first, getDeleteReportEndpointConfigJson(it.first)}); } diff --git a/CapabilitiesDelegate/src/DiscoveryEventSender.cpp b/CapabilitiesDelegate/src/DiscoveryEventSender.cpp index 11106eaef1..3d46f161ed 100644 --- a/CapabilitiesDelegate/src/DiscoveryEventSender.cpp +++ b/CapabilitiesDelegate/src/DiscoveryEventSender.cpp @@ -17,6 +17,8 @@ #include "CapabilitiesDelegate/Utils/DiscoveryUtils.h" #include +#include +#include #include namespace alexaClientSDK { @@ -24,10 +26,20 @@ namespace capabilitiesDelegate { using namespace avsCommon::avs; using namespace avsCommon::sdkInterfaces; +using namespace avsCommon::utils::metrics; using namespace capabilitiesDelegate::utils; /// String to identify log entries originating from this file. -static const std::string TAG("DiscoveryEventSender"); +#define TAG "DiscoveryEventSender" + +/// Activity name for post-connect metrics after sending a report. +#define POST_CONNECT_ADD_OR_UPDATE_REPORT_ACTIVITY "PostConnect" TAG "-sendAddOrUpdateReport" + +/// Activity name for post-connect metrics after sending a report. +#define POST_CONNECT_DELETE_REPORT_ACTIVITY "PostConnect" TAG "-sendDeleteReport" + +/// Prefix for post-connect data point with status value. +#define POST_CONNECT_STATUS_PREFIX "STATUS-" /** * Create a LogEntry using the file's TAG and the specified event string. @@ -61,14 +73,21 @@ std::shared_ptr DiscoveryEventSender::create( const std::unordered_map& addOrUpdateReportEndpoints, const std::unordered_map& deleteReportEndpoints, const std::shared_ptr& authDelegate, - const bool waitForEventProcessed) { + bool waitForEventProcessed, + const std::shared_ptr& metricRecorder, + bool postConnect) { if (addOrUpdateReportEndpoints.empty() && deleteReportEndpoints.empty()) { ACSDK_ERROR(LX("createFailed").d("reason", "endpoint map empty")); } else if (!authDelegate) { ACSDK_ERROR(LX("createFailed").d("reason", "invalid auth delegate")); } else { auto instance = std::shared_ptr(new DiscoveryEventSender( - addOrUpdateReportEndpoints, deleteReportEndpoints, authDelegate, waitForEventProcessed)); + addOrUpdateReportEndpoints, + deleteReportEndpoints, + authDelegate, + waitForEventProcessed, + metricRecorder, + postConnect)); return instance; } @@ -79,14 +98,18 @@ DiscoveryEventSender::DiscoveryEventSender( const std::unordered_map& addOrUpdateReportEndpoints, const std::unordered_map& deleteReportEndpoints, const std::shared_ptr& authDelegate, - const bool waitForEventProcessed) : + bool waitForEventProcessed, + const std::shared_ptr& metricRecorder, + bool postConnect) : m_addOrUpdateReportEndpoints{addOrUpdateReportEndpoints}, m_deleteReportEndpoints{deleteReportEndpoints}, m_authDelegate{authDelegate}, + m_metricRecorder{metricRecorder}, m_currentAuthState{AuthObserverInterface::State::UNINITIALIZED}, m_isStopping{false}, m_isSendDiscoveryEventsInvoked{false}, - m_waitForEventProcessed{waitForEventProcessed} { + m_waitForEventProcessed{waitForEventProcessed}, + m_postConnect{postConnect} { } DiscoveryEventSender::~DiscoveryEventSender() { @@ -188,6 +211,7 @@ MessageRequestObserverInterface::Status DiscoveryEventSender::sendDiscoveryEvent const std::shared_ptr& messageSender, const std::string& eventString, bool waitForEventProcessed) { + ACSDK_INFO(LX(__func__)); ACSDK_DEBUG5(LX(__func__).sensitive("discoveryEvent", eventString)); std::unique_lock lock{m_mutex}; m_eventProcessedWaitEvent.reset(); @@ -237,6 +261,27 @@ bool DiscoveryEventSender::sendDiscoveryEventWithRetries( } auto status = sendDiscoveryEvent(messageSender, eventString, isAddOrUpdateReportEvent); + +#ifdef ACSDK_ENABLE_METRICS_RECORDING + if (m_postConnect && m_metricRecorder) { + const auto activityName = isAddOrUpdateReportEvent ? POST_CONNECT_ADD_OR_UPDATE_REPORT_ACTIVITY + : POST_CONNECT_DELETE_REPORT_ACTIVITY; + std::stringstream eventNameBuilder; + eventNameBuilder << POST_CONNECT_STATUS_PREFIX << status; + auto metricEvent = + MetricEventBuilder{} + .setActivityName(activityName) + .addDataPoint(DataPointCounterBuilder{}.setName(eventNameBuilder.str()).increment(1).build()) + .build(); + if (metricEvent) { + m_metricRecorder->recordMetric(std::move(metricEvent)); + } + } +#else + (void)m_postConnect; + (void)m_metricRecorder; +#endif + switch (status) { case MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED: /// Successful response, proceed to send next event if available. @@ -265,12 +310,12 @@ bool DiscoveryEventSender::sendDiscoveryEvents( const std::vector& endpointConfigurations, const std::shared_ptr& messageSender, bool isAddOrUpdateReportEvent) { - int currentEventSize = 0; + size_t currentEventSize = 0; std::vector currentEndpointConfigurationsBuffer; auto currentEndpointConfigIt = endpointConfigurations.begin(); while (currentEndpointConfigIt != endpointConfigurations.end()) { - int currentEndpointConfigSize = currentEndpointConfigIt->size(); + size_t currentEndpointConfigSize = currentEndpointConfigIt->size(); bool sendEvent = false; @@ -309,7 +354,7 @@ bool DiscoveryEventSender::sendDiscoveryEvents( bool DiscoveryEventSender::sendAddOrUpdateReportEvents( const std::shared_ptr& messageSender) { - ACSDK_DEBUG5(LX(__func__).d("num endpoints", m_addOrUpdateReportEndpoints.size())); + ACSDK_INFO(LX(__func__).d("num endpoints", m_addOrUpdateReportEndpoints.size())); if (m_addOrUpdateReportEndpoints.empty()) { ACSDK_DEBUG5(LX(__func__).m("endpoints list empty")); @@ -380,7 +425,7 @@ void DiscoveryEventSender::removeDiscoveryStatusObserver( } void DiscoveryEventSender::reportDiscoveryStatus(MessageRequestObserverInterface::Status status) { - ACSDK_DEBUG5(LX(__func__)); + ACSDK_INFO(LX(__func__).d("status", status)); std::shared_ptr observer; { diff --git a/CapabilitiesDelegate/src/PostConnectCapabilitiesPublisher.cpp b/CapabilitiesDelegate/src/PostConnectCapabilitiesPublisher.cpp index 85eeb62e58..e86ebb76d4 100644 --- a/CapabilitiesDelegate/src/PostConnectCapabilitiesPublisher.cpp +++ b/CapabilitiesDelegate/src/PostConnectCapabilitiesPublisher.cpp @@ -30,7 +30,7 @@ using namespace avsCommon::sdkInterfaces; using namespace capabilitiesDelegate::utils; /// String to identify log entries originating from this file. -static const std::string TAG("PostConnectCapabilitiesPublisher"); +#define TAG "PostConnectCapabilitiesPublisher" /** * Create a LogEntry using the file's TAG and the specified event string. @@ -56,11 +56,13 @@ PostConnectCapabilitiesPublisher::PostConnectCapabilitiesPublisher( std::shared_ptr discoveryEventSender) : m_isPerformOperationInvoked{false}, m_discoveryEventSender{std::move(discoveryEventSender)} { + ACSDK_INFO(LX("init").p("this", this)); } PostConnectCapabilitiesPublisher::~PostConnectCapabilitiesPublisher() { ACSDK_DEBUG5(LX(__func__)); m_discoveryEventSender->stop(); + ACSDK_INFO(LX("destroyed").p("this", this)); } unsigned int PostConnectCapabilitiesPublisher::getOperationPriority() { @@ -68,7 +70,7 @@ unsigned int PostConnectCapabilitiesPublisher::getOperationPriority() { } bool PostConnectCapabilitiesPublisher::performOperation(const std::shared_ptr& messageSender) { - ACSDK_DEBUG5(LX(__func__)); + ACSDK_INFO(LX(__func__)); if (!messageSender) { ACSDK_ERROR(LX("performOperationFailed").d("reason", "nullPostConnectSender")); return false; @@ -92,7 +94,7 @@ bool PostConnectCapabilitiesPublisher::performOperation(const std::shared_ptrstop(); } diff --git a/CapabilitiesDelegate/src/Storage/SQLiteCapabilitiesDelegateStorage.cpp b/CapabilitiesDelegate/src/Storage/SQLiteCapabilitiesDelegateStorage.cpp index 0fb734095d..9b18a94afc 100644 --- a/CapabilitiesDelegate/src/Storage/SQLiteCapabilitiesDelegateStorage.cpp +++ b/CapabilitiesDelegate/src/Storage/SQLiteCapabilitiesDelegateStorage.cpp @@ -24,7 +24,7 @@ namespace storage { using namespace avsCommon::utils::configuration; /// String to identify log entries originating from this file. -static const std::string TAG("SQLiteCapabilitiesDelegateStorage"); +#define TAG "SQLiteCapabilitiesDelegateStorage" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/CapabilitiesDelegate/src/Utils/DiscoveryUtils.cpp b/CapabilitiesDelegate/src/Utils/DiscoveryUtils.cpp index 62c7dcdb3b..6fa4a4a786 100644 --- a/CapabilitiesDelegate/src/Utils/DiscoveryUtils.cpp +++ b/CapabilitiesDelegate/src/Utils/DiscoveryUtils.cpp @@ -25,7 +25,7 @@ #include /// String to identify log entries originating from this file. -static const std::string TAG("DiscoveryUtils"); +#define TAG "DiscoveryUtils" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -113,6 +113,14 @@ static const std::string SCOPE_TYPE_BEARER_TOKEN = "BearerToken"; /// Scope Token key static const std::string SCOPE_TOKEN_KEY = "token"; +/// Maximum number of endpoints for a user. +/// See here for more information: https://developer.amazon.com/en-US/docs/alexa/device-apis/alexa-discovery.html#limits +static constexpr size_t MAX_ENDPOINTS = 300; + +/// Maximum capabilities per endpoint. +/// See here for more information: https://developer.amazon.com/en-US/docs/alexa/device-apis/alexa-discovery.html#limits +static constexpr size_t MAX_CAPABILITIES_PER_ENDPOINT = 100; + /** * Helper struct to build json objects */ @@ -414,6 +422,14 @@ std::string getDeleteReportEventJson( return buildJsonEventString(header, Optional(), payloadGenerator.toString()); } +size_t getMaxEndpoints() { + return MAX_ENDPOINTS; +} + +size_t getMaxCapabilitiesPerEndpoint() { + return MAX_CAPABILITIES_PER_ENDPOINT; +} + } // namespace utils } // namespace capabilitiesDelegate } // namespace alexaClientSDK diff --git a/CapabilitiesDelegate/test/CapabilitiesDelegateTest.cpp b/CapabilitiesDelegate/test/CapabilitiesDelegateTest.cpp index f170dba2fd..d8abc4caf9 100644 --- a/CapabilitiesDelegate/test/CapabilitiesDelegateTest.cpp +++ b/CapabilitiesDelegate/test/CapabilitiesDelegateTest.cpp @@ -57,6 +57,22 @@ static const std::string EVENT_CORRELATION_TOKEN_KEY = "eventCorrelationToken"; /// Constant representing the timeout for test events. /// @note Use a large enough value that should not fail even in slower systems. static const std::chrono::seconds MY_WAIT_TIMEOUT{5}; +/// A Test client Id. +static const std::string TEST_CLIENT_ID = "TEST_CLIENT_ID"; +/// A Test product Id. +static const std::string TEST_PRODUCT_ID = "TEST_PRODUCT_ID"; +/// A Test serial number. +static const std::string TEST_SERIAL_NUMBER = "TEST_SERIAL_NUMBER"; +/// A Test product Id. +static const std::string TEST_REGISTRATION_KEY = "TEST_REGISTRATION_KEY"; +/// A Test product Id Key. +static const std::string TEST_PRODUCT_ID_KEY = "TEST_PRODUCT_ID_KEY"; +/// A Test manufacturer name. +static const std::string TEST_MANUFACTURER_NAME = "TEST_MANUFACTURER_NAME"; +/// A Test description. +static const std::string TEST_DESCRIPTION = "TEST_DESCRIPTION"; +/// A Test display category. +static const std::string TEST_DISPLAY_CATEGORY = "TEST_DISPLAY_CATEGORY"; /** * Structure to store event data from a Discovery event JSON. @@ -79,13 +95,29 @@ AVSDiscoveryEndpointAttributes createEndpointAttributes(const std::string endpoi AVSDiscoveryEndpointAttributes attributes; attributes.endpointId = endpointId; - attributes.description = "TEST_DESCRIPTION"; - attributes.manufacturerName = "TEST_MANUFACTURER_NAME"; - attributes.displayCategories = {"TEST_DISPLAY_CATEGORY"}; + attributes.description = TEST_DESCRIPTION; + attributes.manufacturerName = TEST_MANUFACTURER_NAME; + attributes.displayCategories = {TEST_DISPLAY_CATEGORY}; return attributes; } +/** + * Create a test @c AVSDiscoveryEndpointAttributes::Registration object. + * @param productId Optional product Id to use in these attributes. + * @param serialNumber Optional device serial number to use in these attributes. + * @param registrationKey Optional registration key to use in these attributes. + * @param productIdKey Optional product Id Key to use in these attributes. + * @return a @c AVSDiscoveryEndpointAttributes::Registration structure. + */ +AVSDiscoveryEndpointAttributes::Registration createEndpointRegistration( + const std::string productID = TEST_PRODUCT_ID, + const std::string serialNumber = TEST_SERIAL_NUMBER, + const std::string registrationKey = TEST_REGISTRATION_KEY, + const std::string productIdKey = TEST_PRODUCT_ID_KEY) { + return AVSDiscoveryEndpointAttributes::Registration(productID, serialNumber, registrationKey, productIdKey); +} + /** * Creates a test @c CapabilityConfiguration. * @return a @c CapabilityConfiguration structure. @@ -124,6 +156,12 @@ class CapabilitiesDelegateTest : public Test { /// Helper that validates dynamically adding an endpoint. Used for testing dynamic delete. void addEndpoint(AVSDiscoveryEndpointAttributes attributes, CapabilityConfiguration configuration); + /// Helper that validates dynamically adding an endpoint. Used for testing dynamic delete. + void addEndpointToCapabilitiesDelegate( + std::shared_ptr, + AVSDiscoveryEndpointAttributes attributes, + CapabilityConfiguration configuration); + /* * Gets the event correlation token string. * @param request The message request to parse. @@ -162,6 +200,7 @@ void CapabilitiesDelegateTest::SetUp() { /// Expect calls to storage. EXPECT_CALL(*m_mockCapabilitiesStorage, open()).WillOnce(Return(true)); m_capabilitiesDelegate = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager); + ASSERT_NE(m_capabilitiesDelegate, nullptr); /// Add a new observer and it receives notifications of the current capabilities state. @@ -281,6 +320,7 @@ TEST_F(CapabilitiesDelegateTest, test_init) { EXPECT_CALL(*m_mockCapabilitiesStorage, createDatabase()).WillOnce(Return(true)); instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager); + ASSERT_NE(instance, nullptr); instance->shutdown(); } @@ -656,8 +696,8 @@ TEST_F(CapabilitiesDelegateTest, test_createPostConnectOperationWithDifferentEnd })); auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager); - instance->addOrUpdateEndpoint(endpointAttributes, {capabilityConfig}); + instance->addOrUpdateEndpoint(endpointAttributes, {capabilityConfig}); /// Endpoint config is different from the endpoint config created with the test endpoint attributes so a /// post connect operation is created. auto publisher = instance->createPostConnectOperation(); @@ -1032,6 +1072,69 @@ TEST_F( instance->shutdown(); } +/** + * Tests if the createPostConnectOperation() creates a new @c PostConnectCapabilitiesPublisher when registered + * endpoint configurations are same as the ones in storage, but there is one additional stored endpoint that is + * not registered (and needs to be deleted). + * Tests CapabilitiesObservers are notified of added endpoints even though they were not published in an event. + * Tests if CapabilitiesDelegate returns a non-null post connect operation, since there is a stale endpoint to delete. + */ +TEST_F( + CapabilitiesDelegateTest, + test_createPostConnectOperationWithNewEndpointAndPendingEndpointsWithSameEndpointConfigs) { + auto unchangedEndpointAttributes = createEndpointAttributes("endpointId"); + auto unchangedEndpointConfiguration = createCapabilityConfiguration(); + std::vector unchangedCapabilityConfigs = {unchangedEndpointConfiguration}; + + auto newEndpointAttributes = createEndpointAttributes("newEndpointId"); + auto newEndpointConfiguration = createCapabilityConfiguration(); + std::vector newCapabilityConfigs = {newEndpointConfiguration}; + + std::string unchangedEndpointConfig = + utils::getEndpointConfigJson(unchangedEndpointAttributes, unchangedCapabilityConfigs); + std::string newEndpointConfig = utils::getEndpointConfigJson(newEndpointAttributes, newCapabilityConfigs); + EXPECT_CALL(*m_mockCapabilitiesStorage, open()).Times(1).WillOnce(Return(true)); + EXPECT_CALL(*m_mockCapabilitiesStorage, load(_)) + .Times(1) + .WillOnce( + Invoke([unchangedEndpointAttributes, unchangedEndpointConfig, newEndpointAttributes, newEndpointConfig]( + std::unordered_map* storedEndpoints) { + storedEndpoints->insert({unchangedEndpointAttributes.endpointId, unchangedEndpointConfig}); + return true; + })); + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([](CapabilitiesDelegateObserverInterface::State newState, + CapabilitiesDelegateObserverInterface::Error newError, + std::vector addOrUpdateReportEndpointIdentifiers, + std::vector deleteReportEndpointIdentifiers) { + EXPECT_EQ(newState, CapabilitiesDelegateObserverInterface::State::UNINITIALIZED); + EXPECT_EQ(newError, CapabilitiesDelegateObserverInterface::Error::UNINITIALIZED); + EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector{}); + EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector{}); + })); + + auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager); + instance->addCapabilitiesObserver(m_mockCapabilitiesDelegateObserver); + instance->addOrUpdateEndpoint(unchangedEndpointAttributes, unchangedCapabilityConfigs); + instance->addOrUpdateEndpoint(newEndpointAttributes, newCapabilityConfigs); + + /// Observer callback should only contain the pending endpoint to add (since that is already registered), + /// but not the stale endpoint to delete (since that still needs to be sent to AVS). + EXPECT_CALL( + *m_mockCapabilitiesDelegateObserver, + onCapabilitiesStateChange( + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, + std::vector{unchangedEndpointAttributes.endpointId}, + std::vector{})); + + auto publisher = instance->createPostConnectOperation(); + ASSERT_NE(publisher, nullptr); + + // Clean-up. + instance->shutdown(); +} + /** * Tests if before the stale endpoint is deleted and the stale endpoint is added, that the first * createPostConnectOperation will create a deleteReport for the stale endpoint, but the second @@ -1534,6 +1637,537 @@ TEST_F(CapabilitiesDelegateTest, test_duplicateEndpointInPendingAddOrUpdateAndDe ASSERT_FALSE(m_capabilitiesDelegate->addOrUpdateEndpoint(deleteEndpointAttributes, {capabilityConfig})); } +/** + * Test endpoint registrations. + * Confirm that changing the registration of an endpoint results in a failure. + * Also confirm that adding an endpoint with a different registration results in a failure. + */ +TEST_F(CapabilitiesDelegateTest, test_registration) { + const std::string endpointID1{"TEST_ENDPOINT_ID_1"}; + const std::string endpointID2{"TEST_ENDPOINT_ID_2"}; + const std::string endpointID3{"TEST_ENDPOINT_ID_3"}; + + /// Configure endpointId1 attributes with a non-empty registration. + auto endpointAttributes1 = createEndpointAttributes(endpointID1); + endpointAttributes1.registration = createEndpointRegistration(); + + auto capabilityConfig = createCapabilityConfiguration(); + + ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(endpointAttributes1, {capabilityConfig})); + + /// Try endpointId1 with empty registration. + auto updatedEndpointAttributes1 = createEndpointAttributes(endpointID1); + ASSERT_FALSE(m_capabilitiesDelegate->addOrUpdateEndpoint(updatedEndpointAttributes1, {capabilityConfig})); + + /// Configure endpointId2 attributes with a non-empty registration. + auto endpointAttributes2 = createEndpointAttributes(endpointID2); + endpointAttributes2.registration = createEndpointRegistration(); + ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(endpointAttributes2, {capabilityConfig})); + + /// Try endpointId2 with non-empty registration. + auto updatedEndpointAttributes2 = createEndpointAttributes(endpointID2); + updatedEndpointAttributes2.registration = createEndpointRegistration("UPDATED_PRODUCT_ID"); + ASSERT_FALSE(m_capabilitiesDelegate->addOrUpdateEndpoint(updatedEndpointAttributes2, {capabilityConfig})); + + /// Try endpointId3 with different and non-empty registration. + auto updatedEndpointAttributes3 = createEndpointAttributes(endpointID3); + updatedEndpointAttributes3.registration = createEndpointRegistration("UPDATED_PRODUCT_ID"); + ASSERT_FALSE(m_capabilitiesDelegate->addOrUpdateEndpoint(updatedEndpointAttributes3, {capabilityConfig})); +} + +/** + * Test adding 3 endpoints that share registration information (i.e. they are de-duplicated in the cloud). + * Verify that other endpoints are sent in the discovery message whenever an endpoint is added. + * Finally, update the first endpoint that was sent and confirm that all endpoints are sent in the discovery message. + */ +TEST_F(CapabilitiesDelegateTest, test_addAndUpdateOfDeduplicatedEndpoints) { + m_capabilitiesDelegate->onConnectionStatusChanged( + ConnectionStatusObserverInterface::Status::CONNECTED, + ConnectionStatusObserverInterface::ChangedReason::SUCCESS); + + WaitEvent e; + validateAuthDelegate(); + + const std::string endpointID1{"TEST_ENDPOINT_ID_1"}; + const std::string endpointID2{"TEST_ENDPOINT_ID_2"}; + const std::string endpointID3{"TEST_ENDPOINT_ID_3"}; + + /// Set-up. + auto endpointAttributes1 = createEndpointAttributes(endpointID1); + endpointAttributes1.registration = createEndpointRegistration(); + + auto endpointAttributes2 = createEndpointAttributes(endpointID2); + endpointAttributes2.registration = createEndpointRegistration(); + + auto endpointAttributes3 = createEndpointAttributes(endpointID3); + endpointAttributes3.registration = createEndpointRegistration(); + + auto capabilityConfig = createCapabilityConfiguration(); + + /// endpointId1 is being registered first. The Discovery message should only contain it. + /// Expect calls to MessageSender. + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)) + .Times(Exactly(1)) + .WillOnce(Invoke([this](std::shared_ptr request) { + std::string eventCorrelationTokenString; + getEventCorrelationTokenString(request, eventCorrelationTokenString); + + request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED); + m_capabilitiesDelegate->onAlexaEventProcessedReceived(eventCorrelationTokenString); + })); + /// Expect call to storage. + EXPECT_CALL(*m_mockCapabilitiesStorage, store(_)).WillOnce(Return(true)); + EXPECT_CALL(*m_mockCapabilitiesStorage, erase((std::unordered_map{}))) + .WillOnce(Return(true)); + + /// Expect callback to CapabilitiesObserver. + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([&](CapabilitiesDelegateObserverInterface::State state, + CapabilitiesDelegateObserverInterface::Error error, + std::vector addedEndpoints, + std::vector deletedEndpoints) { + EXPECT_EQ(state, CapabilitiesDelegateObserverInterface::State::SUCCESS); + EXPECT_EQ(error, CapabilitiesDelegateObserverInterface::Error::SUCCESS); + EXPECT_EQ(addedEndpoints, std::vector{endpointID1}); + EXPECT_EQ(deletedEndpoints, (std::vector{})); + + e.wakeUp(); + })); + ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(endpointAttributes1, {capabilityConfig})); + ASSERT_TRUE(e.wait(MY_WAIT_TIMEOUT)); + e.reset(); + + /// endpointId1 has already been registered. Confirm that it is added when endpointId2 is added. + /// Expect calls to MessageSender. + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)) + .Times(Exactly(1)) + .WillOnce(Invoke([this](std::shared_ptr request) { + std::string eventCorrelationTokenString; + getEventCorrelationTokenString(request, eventCorrelationTokenString); + + request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED); + m_capabilitiesDelegate->onAlexaEventProcessedReceived(eventCorrelationTokenString); + })); + /// Expect call to storage. + EXPECT_CALL(*m_mockCapabilitiesStorage, store(_)).WillOnce(Return(true)); + EXPECT_CALL(*m_mockCapabilitiesStorage, erase((std::unordered_map{}))) + .WillOnce(Return(true)); + + /// Expect callback to CapabilitiesObserver. + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([&](CapabilitiesDelegateObserverInterface::State state, + CapabilitiesDelegateObserverInterface::Error error, + std::vector addedEndpoints, + std::vector deletedEndpoints) { + std::vector expectedAddedEndpoints{endpointID1, endpointID2}; + std::sort(expectedAddedEndpoints.begin(), expectedAddedEndpoints.end()); + std::sort(addedEndpoints.begin(), addedEndpoints.end()); + EXPECT_EQ(state, CapabilitiesDelegateObserverInterface::State::SUCCESS); + EXPECT_EQ(error, CapabilitiesDelegateObserverInterface::Error::SUCCESS); + EXPECT_EQ(addedEndpoints, expectedAddedEndpoints); + EXPECT_EQ(deletedEndpoints, (std::vector{})); + + e.wakeUp(); + })); + + ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(endpointAttributes2, {capabilityConfig})); + ASSERT_TRUE(e.wait(MY_WAIT_TIMEOUT)); + e.reset(); + + /// endpointId1 and endpointId2 have already been registered. Confirm that they are added when endpointId3 is added. + /// Expect calls to MessageSender. + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)) + .Times(Exactly(1)) + .WillOnce(Invoke([this](std::shared_ptr request) { + std::string eventCorrelationTokenString; + getEventCorrelationTokenString(request, eventCorrelationTokenString); + + request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED); + m_capabilitiesDelegate->onAlexaEventProcessedReceived(eventCorrelationTokenString); + })); + /// Expect call to storage. + EXPECT_CALL(*m_mockCapabilitiesStorage, store(_)).WillOnce(Return(true)); + EXPECT_CALL(*m_mockCapabilitiesStorage, erase((std::unordered_map{}))) + .WillOnce(Return(true)); + + /// Expect callback to CapabilitiesObserver. + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([&](CapabilitiesDelegateObserverInterface::State state, + CapabilitiesDelegateObserverInterface::Error error, + std::vector addedEndpoints, + std::vector deletedEndpoints) { + std::vector expectedAddedEndpoints{endpointID1, endpointID2, endpointID3}; + std::sort(expectedAddedEndpoints.begin(), expectedAddedEndpoints.end()); + std::sort(addedEndpoints.begin(), addedEndpoints.end()); + EXPECT_EQ(state, CapabilitiesDelegateObserverInterface::State::SUCCESS); + EXPECT_EQ(error, CapabilitiesDelegateObserverInterface::Error::SUCCESS); + EXPECT_EQ(addedEndpoints, expectedAddedEndpoints); + EXPECT_EQ(deletedEndpoints, (std::vector{})); + + e.wakeUp(); + })); + + ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(endpointAttributes3, {capabilityConfig})); + ASSERT_TRUE(e.wait(MY_WAIT_TIMEOUT)); + e.reset(); + + /// Update the configuration + std::string additionalAttribute(256 * 10, 'X'); + std::map additionalAttributes; + additionalAttributes["test"] = "{\"test\":\"" + additionalAttribute + "\"}"; + auto updatedCapabilityConfig = createCapabilityConfiguration(additionalAttributes); + + auto updatedEndpointAttributes1 = createEndpointAttributes(endpointID1); + updatedEndpointAttributes1.registration = createEndpointRegistration(); + + /// endpointId1, endpointId2 and endpointId3 have already been registered. Confirm that they are added when + /// endpointId1 is updated. + /// Expect calls to MessageSender. + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)) + .Times(Exactly(1)) + .WillOnce(Invoke([this](std::shared_ptr request) { + std::string eventCorrelationTokenString; + getEventCorrelationTokenString(request, eventCorrelationTokenString); + + request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED); + m_capabilitiesDelegate->onAlexaEventProcessedReceived(eventCorrelationTokenString); + })); + /// Expect call to storage. + EXPECT_CALL(*m_mockCapabilitiesStorage, store(_)).WillOnce(Return(true)); + EXPECT_CALL(*m_mockCapabilitiesStorage, erase((std::unordered_map{}))) + .WillOnce(Return(true)); + + /// Expect callback to CapabilitiesObserver. + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([&](CapabilitiesDelegateObserverInterface::State state, + CapabilitiesDelegateObserverInterface::Error error, + std::vector addedEndpoints, + std::vector deletedEndpoints) { + std::vector expectedAddedEndpoints{endpointID1, endpointID2, endpointID3}; + std::sort(expectedAddedEndpoints.begin(), expectedAddedEndpoints.end()); + std::sort(addedEndpoints.begin(), addedEndpoints.end()); + EXPECT_EQ(state, CapabilitiesDelegateObserverInterface::State::SUCCESS); + EXPECT_EQ(error, CapabilitiesDelegateObserverInterface::Error::SUCCESS); + EXPECT_EQ(addedEndpoints, expectedAddedEndpoints); + EXPECT_EQ(deletedEndpoints, (std::vector{})); + + e.wakeUp(); + })); + + ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(updatedEndpointAttributes1, {updatedCapabilityConfig})); + ASSERT_TRUE(e.wait(MY_WAIT_TIMEOUT)); +} + +/** + * Test that deleting a de-duplicated endpoint fails. + */ +TEST_F(CapabilitiesDelegateTest, test_deduplictedDeletionFailure) { + m_capabilitiesDelegate->onConnectionStatusChanged( + ConnectionStatusObserverInterface::Status::CONNECTED, + ConnectionStatusObserverInterface::ChangedReason::SUCCESS); + + WaitEvent e; + validateAuthDelegate(); + + const std::string endpointID1{"TEST_ENDPOINT_ID_1"}; + const std::string endpointID2{"TEST_ENDPOINT_ID_2"}; + + auto endpointAttributes1 = createEndpointAttributes(endpointID1); + endpointAttributes1.registration = createEndpointRegistration(); + + auto capabilityConfig = createCapabilityConfiguration(); + auto capabilityConfigJson = utils::getEndpointConfigJson(endpointAttributes1, {capabilityConfig}); + + /// endpointId1 is being registered first. The Discovery message should only contain it. + /// Expect calls to MessageSender. + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)) + .Times(Exactly(1)) + .WillOnce(Invoke([this](std::shared_ptr request) { + std::string eventCorrelationTokenString; + getEventCorrelationTokenString(request, eventCorrelationTokenString); + + request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED); + m_capabilitiesDelegate->onAlexaEventProcessedReceived(eventCorrelationTokenString); + })); + /// Expect call to storage. + EXPECT_CALL(*m_mockCapabilitiesStorage, store(_)).WillOnce(Return(true)); + EXPECT_CALL(*m_mockCapabilitiesStorage, erase((std::unordered_map{}))) + .WillOnce(Return(true)); + + /// Expect callback to CapabilitiesObserver. + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([&](CapabilitiesDelegateObserverInterface::State state, + CapabilitiesDelegateObserverInterface::Error error, + std::vector addedEndpoints, + std::vector deletedEndpoints) { + EXPECT_EQ(state, CapabilitiesDelegateObserverInterface::State::SUCCESS); + EXPECT_EQ(error, CapabilitiesDelegateObserverInterface::Error::SUCCESS); + EXPECT_EQ(addedEndpoints, std::vector{endpointID1}); + EXPECT_EQ(deletedEndpoints, (std::vector{})); + + e.wakeUp(); + })); + ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(endpointAttributes1, {capabilityConfig})); + ASSERT_TRUE(e.wait(MY_WAIT_TIMEOUT)); + e.reset(); + + /// Deleting endpoint 1 should fail. + ASSERT_FALSE(m_capabilitiesDelegate->deleteEndpoint(endpointAttributes1, {capabilityConfig})); +} + +/** + * Test adding two pairs of 3 endpoints that share registration information (i.e. they are de-duplicated in the cloud). + * For this test, endpointid3 and endpointId6 have large configurations, preventing them from being sent in the same + * message. + * Verify that the endpoints are split into two messages, each containing 3 endpoints. + */ +TEST_F(CapabilitiesDelegateTest, test_SplitMessagePendingDeduplicatedEndpoints) { + m_capabilitiesDelegate->onConnectionStatusChanged( + ConnectionStatusObserverInterface::Status::DISCONNECTED, + ConnectionStatusObserverInterface::ChangedReason::SERVER_SIDE_DISCONNECT); + + WaitEvent e; + validateAuthDelegate(); + + const std::string endpointID1{"TEST_ENDPOINT_ID_1"}; + const std::string endpointID2{"TEST_ENDPOINT_ID_2"}; + const std::string endpointID3{"TEST_ENDPOINT_ID_3"}; + + /// Set-up the first set of deduplicated endpoints. + auto endpointAttributes1 = createEndpointAttributes(endpointID1); + endpointAttributes1.registration = createEndpointRegistration(); + + auto endpointAttributes2 = createEndpointAttributes(endpointID2); + endpointAttributes2.registration = createEndpointRegistration(); + + auto endpointAttributes3 = createEndpointAttributes(endpointID3); + endpointAttributes3.registration = createEndpointRegistration(); + + const std::string endpointID4{"TEST_ENDPOINT_ID_4"}; + const std::string endpointID5{"TEST_ENDPOINT_ID_5"}; + const std::string endpointID6{"TEST_ENDPOINT_ID_6"}; + + /// Set-up the second set of endpoints. + auto endpointAttributes4 = createEndpointAttributes(endpointID4); + + auto endpointAttributes5 = createEndpointAttributes(endpointID5); + + auto endpointAttributes6 = createEndpointAttributes(endpointID6); + + /// Create a large capability configuration for endpoint3 and endpoint6. + std::string additionalAttribute(240 * 1024, 'X'); + std::map additionalAttributes; + additionalAttributes["test"] = "{\"test\":\"" + additionalAttribute + "\"}"; + auto largeCapabilityConfig = createCapabilityConfiguration(additionalAttributes); + + /// Create a default capability configuration for the other endpoints. + auto capabilityConfig = createCapabilityConfiguration(); + + ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(endpointAttributes1, {capabilityConfig})); + + ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(endpointAttributes2, {capabilityConfig})); + + ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(endpointAttributes3, {largeCapabilityConfig})); + + ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(endpointAttributes4, {capabilityConfig})); + + ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(endpointAttributes5, {capabilityConfig})); + + ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(endpointAttributes6, {largeCapabilityConfig})); + + /// Upon connect, expect two messages to be sent. One with endpoints 1,2 and 3, and the other with endpoints 4, 5 + /// and 6. Expect calls to MessageSender. + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)) + .Times(Exactly(2)) + .WillRepeatedly(Invoke([this](std::shared_ptr request) { + std::string eventCorrelationTokenString; + getEventCorrelationTokenString(request, eventCorrelationTokenString); + + request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED); + m_capabilitiesDelegate->onAlexaEventProcessedReceived(eventCorrelationTokenString); + })); + /// Expect call to storage. + EXPECT_CALL(*m_mockCapabilitiesStorage, store(_)).WillRepeatedly(Return(true)); + EXPECT_CALL(*m_mockCapabilitiesStorage, erase((std::unordered_map{}))) + .WillRepeatedly(Return(true)); + + /// Expect callback to CapabilitiesObserver. + std::vector> expectedAddedEndpoints{{endpointID1, endpointID2, endpointID3}, + {endpointID4, endpointID5, endpointID6}}; + int other{0}; + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .Times(Exactly(2)) + .WillOnce(Invoke([&](CapabilitiesDelegateObserverInterface::State state, + CapabilitiesDelegateObserverInterface::Error error, + std::vector addedEndpoints, + std::vector deletedEndpoints) { + std::sort(addedEndpoints.begin(), addedEndpoints.end()); + EXPECT_EQ(state, CapabilitiesDelegateObserverInterface::State::SUCCESS); + EXPECT_EQ(error, CapabilitiesDelegateObserverInterface::Error::SUCCESS); + if (addedEndpoints[0] == endpointID1) { + other = 1; + EXPECT_EQ(addedEndpoints, expectedAddedEndpoints[0]); + } else { + EXPECT_EQ(addedEndpoints, expectedAddedEndpoints[1]); + } + EXPECT_EQ(deletedEndpoints, (std::vector{})); + })) + .WillOnce(Invoke([&](CapabilitiesDelegateObserverInterface::State state, + CapabilitiesDelegateObserverInterface::Error error, + std::vector addedEndpoints, + std::vector deletedEndpoints) { + std::sort(addedEndpoints.begin(), addedEndpoints.end()); + EXPECT_EQ(state, CapabilitiesDelegateObserverInterface::State::SUCCESS); + EXPECT_EQ(error, CapabilitiesDelegateObserverInterface::Error::SUCCESS); + EXPECT_EQ(addedEndpoints, expectedAddedEndpoints[other]); + EXPECT_EQ(deletedEndpoints, (std::vector{})); + e.wakeUp(); + })); + + m_capabilitiesDelegate->onConnectionStatusChanged( + ConnectionStatusObserverInterface::Status::CONNECTED, + ConnectionStatusObserverInterface::ChangedReason::SUCCESS); + + ASSERT_TRUE(e.wait(MY_WAIT_TIMEOUT)); +} + +/** + * Test limits of the Alexa.Discovery interface. + * Specifically test : + * 1. Adding more than 300 deduplicated endpoints triggers a failure. + * 2. Having more than 100 capabilities per deduplicated endpoint triggers a failure. + * This is because a single discovery message cannot contain more than 300 endpoints, per the limits imposed here: + * https://developer.amazon.com/en-US/docs/alexa/device-apis/alexa-discovery.html#limits + */ +TEST_F(CapabilitiesDelegateTest, test_endpointLimits) { + // Create deduplicated endpoints + for (size_t i = 0; i < getMaxEndpoints(); ++i) { + const std::string testEndpointID = "TEST_ENDPOINT_ID_" + std::to_string(i); + + auto endpointAttributes = createEndpointAttributes(testEndpointID); + endpointAttributes.registration = createEndpointRegistration(); + auto capabilityConfig = createCapabilityConfiguration(); + + ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(endpointAttributes, {capabilityConfig})); + } + const std::string lastEndpointID = "TEST_ENDPOINT_ID_LAST"; + auto lastEndpointAttributes = createEndpointAttributes(lastEndpointID); + lastEndpointAttributes.registration = createEndpointRegistration(); + auto lastCapabilityConfig = createCapabilityConfiguration(); + + ASSERT_FALSE(m_capabilitiesDelegate->addOrUpdateEndpoint(lastEndpointAttributes, {lastCapabilityConfig})); + + const std::string endpointID = "TEST_ENDPOINT_ID"; + + /// Add a deduplicated endpoint with more than MAX_CAPABILITIES_PER_ENDPOINT capabilities. + auto endpointAttributes = createEndpointAttributes(endpointID); + endpointAttributes.registration = createEndpointRegistration(); + std::vector largeConfig; + for (size_t i = 0; i <= getMaxCapabilitiesPerEndpoint(); ++i) { + largeConfig.push_back(createCapabilityConfiguration()); + } + + ASSERT_FALSE(m_capabilitiesDelegate->addOrUpdateEndpoint(endpointAttributes, largeConfig)); +} + +/** + * Test updating a deduplicated endpoint when it is in flight. + */ +TEST_F(CapabilitiesDelegateTest, test_updateDeduplicatedEndpointWhenInflight) { + m_capabilitiesDelegate->onConnectionStatusChanged( + ConnectionStatusObserverInterface::Status::CONNECTED, + ConnectionStatusObserverInterface::ChangedReason::SUCCESS); + WaitEvent e; + validateAuthDelegate(); + + const std::string endpointID{"TEST_ENDPOINT_ID"}; + + /// Set-up. + auto endpointAttributes = createEndpointAttributes(endpointID); + endpointAttributes.registration = createEndpointRegistration(); + + auto capabilityConfig = createCapabilityConfiguration(); + + addEndpoint(endpointAttributes, {capabilityConfig}); + + /// Add a DiscoveryEventSender to simulate Discovery event in-flight. + auto discoveryEventSender = std::make_shared>(); + EXPECT_CALL(*discoveryEventSender, addDiscoveryStatusObserver(_)) + .WillOnce(Invoke([this](const std::shared_ptr& observer) { + EXPECT_EQ(observer, m_capabilitiesDelegate); + })); + EXPECT_CALL(*discoveryEventSender, removeDiscoveryStatusObserver(_)) + .WillOnce(Invoke([this](const std::shared_ptr& observer) { + EXPECT_EQ(observer, m_capabilitiesDelegate); + })); + EXPECT_CALL(*discoveryEventSender, stop()); + m_capabilitiesDelegate->setDiscoveryEventSender(discoveryEventSender); + + /// Create an updated capability configuration for the update to endpoint1. + std::string additionalAttribute(2 * 1024, 'X'); + std::map additionalAttributes; + additionalAttributes["test"] = "{\"test\":\"" + additionalAttribute + "\"}"; + auto updatedCapabilityConfig = createCapabilityConfiguration(additionalAttributes); + + /// Expect no callback to CapabilitiesObserver, since the update remains in pending. + EXPECT_CALL( + *m_mockCapabilitiesDelegateObserver, + onCapabilitiesStateChange(_, _, std::vector{endpointID}, std::vector{})) + .Times(0); + + /// endpointId1 is inflight. Should be able to add it again to the pending update set. + ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(endpointAttributes, {updatedCapabilityConfig})); +} + +/** + * Test adding a deduplicated endpoint when another one is in flight. + * Verify that the first endpoint is also sent in the discovery message when the second endpoint is added. + */ +TEST_F(CapabilitiesDelegateTest, test_addDeduplicatedEndpointWhenOtherInflight) { + m_capabilitiesDelegate->onConnectionStatusChanged( + ConnectionStatusObserverInterface::Status::CONNECTED, + ConnectionStatusObserverInterface::ChangedReason::SUCCESS); + + WaitEvent e; + validateAuthDelegate(); + + const std::string endpointID1{"TEST_ENDPOINT_ID_1"}; + const std::string endpointID2{"TEST_ENDPOINT_ID_2"}; + + /// Set-up. + auto endpointAttributes1 = createEndpointAttributes(endpointID1); + endpointAttributes1.registration = createEndpointRegistration(); + + auto endpointAttributes2 = createEndpointAttributes(endpointID2); + endpointAttributes2.registration = createEndpointRegistration(); + + auto capabilityConfig = createCapabilityConfiguration(); + + addEndpoint(endpointAttributes1, {capabilityConfig}); + + /// Add a DiscoveryEventSender to simulate Discovery event in-flight. + auto discoveryEventSender = std::make_shared>(); + EXPECT_CALL(*discoveryEventSender, addDiscoveryStatusObserver(_)) + .WillOnce(Invoke([this](const std::shared_ptr& observer) { + EXPECT_EQ(observer, m_capabilitiesDelegate); + })); + EXPECT_CALL(*discoveryEventSender, removeDiscoveryStatusObserver(_)) + .WillOnce(Invoke([this](const std::shared_ptr& observer) { + EXPECT_EQ(observer, m_capabilitiesDelegate); + })); + EXPECT_CALL(*discoveryEventSender, stop()); + m_capabilitiesDelegate->setDiscoveryEventSender(discoveryEventSender); + + /// Expect no callback to CapabilitiesObserver, since the add of endpoint2 remains in pending. + EXPECT_CALL( + *m_mockCapabilitiesDelegateObserver, + onCapabilitiesStateChange(_, _, std::vector{endpointID1, endpointID2}, std::vector{})) + .Times(0); + + /// Endpoint1 is in flight. Confirm that Endpoint2 is added to the pending endpoints. + ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(endpointAttributes2, {capabilityConfig})); +} + } // namespace test } // namespace capabilitiesDelegate } // namespace alexaClientSDK diff --git a/CapabilityAgents/AIP/include/AIP/ASRProfile.h b/CapabilityAgents/AIP/include/AIP/ASRProfile.h index 1e4bf8774c..e69fcace61 100644 --- a/CapabilityAgents/AIP/include/AIP/ASRProfile.h +++ b/CapabilityAgents/AIP/include/AIP/ASRProfile.h @@ -34,7 +34,9 @@ enum class ASRProfile { /// Cloud determines end of user speech (0 to 5 ft). NEAR_FIELD, /// Cloud determines end of user speech (0 to 20+ ft). - FAR_FIELD + FAR_FIELD, + /// Indicates that ASR profile is undefined. + UNDEFINED }; /** @@ -55,6 +57,9 @@ inline std::ostream& operator<<(std::ostream& stream, ASRProfile profile) { case ASRProfile::FAR_FIELD: stream << "FAR_FIELD"; break; + case ASRProfile::UNDEFINED: + stream << ""; + break; } return stream; } @@ -73,10 +78,31 @@ inline std::string asrProfileToString(ASRProfile profile) { return "NEAR_FIELD"; case ASRProfile::FAR_FIELD: return "FAR_FIELD"; + case ASRProfile::UNDEFINED: + return ""; } return ""; } +/** + * This function reverse maps the provided string to corresponding ASRProfile Implementation as specified by + * asrProfileToString + * @param input string to convert to corresponding ASRProfile + * @return @c ASRProfile that corresponds to the input string. In case of error + * the API returns ASRProfile::UNDEFINED + */ +inline ASRProfile getASRProfile(const std::string& input) { + ASRProfile profile = ASRProfile::UNDEFINED; + if (asrProfileToString(ASRProfile::CLOSE_TALK) == input) { + profile = ASRProfile::CLOSE_TALK; + } else if (asrProfileToString(ASRProfile::NEAR_FIELD) == input) { + profile = ASRProfile::NEAR_FIELD; + } else if (asrProfileToString(ASRProfile::FAR_FIELD) == input) { + profile = ASRProfile::FAR_FIELD; + } + return profile; +} + } // namespace aip } // namespace capabilityAgents } // namespace alexaClientSDK diff --git a/CapabilityAgents/AIP/include/AIP/AudioInputProcessor.h b/CapabilityAgents/AIP/include/AIP/AudioInputProcessor.h index e01cda8854..e4bf67566b 100644 --- a/CapabilityAgents/AIP/include/AIP/AudioInputProcessor.h +++ b/CapabilityAgents/AIP/include/AIP/AudioInputProcessor.h @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -50,13 +51,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include "AudioProvider.h" #include "Initiator.h" @@ -149,7 +150,7 @@ class AudioInputProcessor * configurations change. * @param wakeWordsSetting The setting that represents the enabled wake words. This parameter is required if this * device supports wake words. - * @param speechEncoder The Speech Encoder used to encode audio inputs. This parameter is optional and + * @param audioEncoder The Audio Encoder used to encode audio inputs. This parameter is optional and * defaults to nullptr, which disable the encoding feature. * @param defaultAudioProvider A default @c avsCommon::AudioProvider to use for ExpectSpeech if the previous * provider is not readable (@c avsCommon::AudioProvider::alwaysReadable). This parameter is optional and @@ -175,7 +176,7 @@ class AudioInputProcessor std::shared_ptr speechConfirmation, const std::shared_ptr& capabilityChangeNotifier, std::shared_ptr wakeWordsSetting = nullptr, - std::shared_ptr speechEncoder = nullptr, + std::shared_ptr audioEncoder = nullptr, AudioProvider defaultAudioProvider = AudioProvider::null(), std::shared_ptr powerResourceManager = nullptr, std::shared_ptr metricRecorder = nullptr, @@ -396,7 +397,7 @@ class AudioInputProcessor * @param exceptionEncounteredSender The object to use for sending ExceptionEncountered messages. * @param userInactivityMonitor The object to use for resetting user inactivity. * @param systemSoundPlayer The instance of the system sound player. - * @param speechEncoder The Speech Encoder used to encode audio inputs. This parameter is optional and + * @param audioEncoder The Audio Encoder used to encode audio inputs. This parameter is optional and * will disable the encoding feature if set to nullptr. * @param defaultAudioProvider A default @c avsCommon::AudioProvider to use for ExpectSpeech if the previous * provider is not readable (@c AudioProvider::alwaysReadable). This parameter is optional, and ignored if set @@ -427,7 +428,7 @@ class AudioInputProcessor std::shared_ptr userInactivityMonitor, std::shared_ptr systemSoundPlayer, const std::shared_ptr& assetsManager, - std::shared_ptr speechEncoder, + std::shared_ptr audioEncoder, AudioProvider defaultAudioProvider, std::shared_ptr wakeWordConfirmation, std::shared_ptr speechConfirmation, @@ -766,7 +767,7 @@ class AudioInputProcessor avsCommon::utils::timing::Timer m_expectingSpeechTimer; /// The Speech Encoder to encode input stream. - const std::shared_ptr m_encoder; + const std::shared_ptr m_encoder; /** * @name Executor Thread Variables @@ -957,6 +958,11 @@ class AudioInputProcessor */ std::string m_uploadMetricName; + /** + * The Duration Builder used to calculate the duration of the fetch context call. + */ + avsCommon::utils::metrics::DataPointDurationBuilder m_fetchContextTimeMetricData; + /// A @c PowerResourceId used for wakelock logic. std::shared_ptr m_powerResourceId; diff --git a/CapabilityAgents/AIP/src/AudioInputProcessor.cpp b/CapabilityAgents/AIP/src/AudioInputProcessor.cpp index d0944a00c8..c3003b58ca 100644 --- a/CapabilityAgents/AIP/src/AudioInputProcessor.cpp +++ b/CapabilityAgents/AIP/src/AudioInputProcessor.cpp @@ -36,7 +36,6 @@ #include #include #include -#include #include #include "AIP/AudioInputProcessor.h" @@ -84,7 +83,7 @@ static const std::string CAPABILITY_INTERFACE_VALUES_KEY = "values"; static const std::string CAPABILITY_INTERFACE_DEFAULT_LOCALE = "DEFAULT"; /// String to identify log entries originating from this file. -static const std::string TAG("AudioInputProcessor"); +#define TAG "AudioInputProcessor" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -199,6 +198,8 @@ static const std::string WAKEWORD_DETECTION_SEGMENT_UPLOADED_PCM = "AIP_WAKEWORD /// Recognize EVENT is built for AIP metric source static const std::string RECOGNIZE_START_SEND_MESSAGE = "RECOGNIZE_EVENT_IS_BUILT"; +static const std::string RECOGNIZE_START_SEND_MESSAGE_ACTIVITY_NAME = + METRIC_ACTIVITY_NAME_PREFIX_AIP + RECOGNIZE_START_SEND_MESSAGE; /// Wakeword Activity Name for AIP metric source static const std::string START_OF_STREAM_TIMESTAMP = "START_OF_STREAM_TIMESTAMP"; @@ -217,6 +218,11 @@ static const std::string ACQUIRE_POWER_RESOURCE_ACTIVITY = METRIC_ACTIVITY_NAME_ static const std::string RELEASE_POWER_RESOURCE = "RELEASE_POWER_RESOURCE"; static const std::string RELEASE_POWER_RESOURCE_ACTIVITY = METRIC_ACTIVITY_NAME_PREFIX_AIP + RELEASE_POWER_RESOURCE; +/// The timestamp for the start of execute on context available +static const std::string FETCH_CONTEXT_DURATION = "FETCH_CONTEXT_DURATION"; +static const std::string FETCH_CONTEXT_DURATION_ACTIVITY_NAME = + METRIC_ACTIVITY_NAME_PREFIX_AIP + FETCH_CONTEXT_DURATION; + /// End of Speech Offset Received Activity Name for AIP metric source static const std::string END_OF_SPEECH_OFFSET_RECEIVED = "END_OF_SPEECH_OFFSET"; static const std::string END_OF_SPEECH_OFFSET_RECEIVED_ACTIVITY_NAME = @@ -260,6 +266,9 @@ static const int WAKEWORD_DETECTION_SEGMENT_SIZE_BYTES_OPUS = 5209; /// Threshold number of bytes for PCM Encoded Wakeword detection static const int WAKEWORD_DETECTION_SEGMENT_SIZE_BYTES_PCM = 40480; +/// Cloud resolve key constant. Used to determine the encoding sent to the cloud. +static const std::string CLOUD_RESOLVE_KEY = "CLOUD"; + /** * Helper function to get string values of encoding audio format, which are used in Recognize event. * @param encoding Target encoding format @@ -410,7 +419,7 @@ std::shared_ptr AudioInputProcessor::create( std::shared_ptr speechConfirmation, const std::shared_ptr& capabilityChangeNotifier, std::shared_ptr wakeWordsSetting, - std::shared_ptr speechEncoder, + std::shared_ptr audioEncoder, AudioProvider defaultAudioProvider, std::shared_ptr powerResourceManager, std::shared_ptr metricRecorder, @@ -470,7 +479,7 @@ std::shared_ptr AudioInputProcessor::create( userInactivityMonitor, systemSoundPlayer, assetsManager, - speechEncoder, + audioEncoder, defaultAudioProvider, wakeWordConfirmation, speechConfirmation, @@ -508,7 +517,7 @@ void AudioInputProcessor::addObserver(std::shared_ptr observe ACSDK_ERROR(LX("addObserverFailed").d("reason", "nullObserver")); return; } - m_executor.submit([this, observer]() { m_observers.insert(observer); }); + m_executor.execute([this, observer]() { m_observers.insert(observer); }); } void AudioInputProcessor::removeObserver(std::shared_ptr observer) { @@ -579,11 +588,13 @@ std::future AudioInputProcessor::resetState() { } void AudioInputProcessor::onContextAvailable(const std::string& jsonContext) { - m_executor.submit([this, jsonContext]() { executeOnContextAvailable(jsonContext); }); + ACSDK_DEBUG0(LX(__func__)); + m_executor.execute([this, jsonContext]() { executeOnContextAvailable(jsonContext); }); } void AudioInputProcessor::onContextFailure(const ContextRequestError error) { - m_executor.submit([this, error]() { executeOnContextFailure(error); }); + ACSDK_DEBUG0(LX(__func__)); + m_executor.execute([this, error]() { executeOnContextFailure(error); }); } void AudioInputProcessor::handleDirectiveImmediately(std::shared_ptr directive) { @@ -650,11 +661,11 @@ void AudioInputProcessor::onDeregistered() { void AudioInputProcessor::onFocusChanged(avsCommon::avs::FocusState newFocus, avsCommon::avs::MixingBehavior behavior) { ACSDK_DEBUG9(LX("onFocusChanged").d("newFocus", newFocus).d("MixingBehavior", behavior)); - m_executor.submit([this, newFocus]() { executeOnFocusChanged(newFocus); }); + m_executor.execute([this, newFocus]() { executeOnFocusChanged(newFocus); }); } void AudioInputProcessor::onDialogUXStateChanged(DialogUXStateObserverInterface::DialogUXState newState) { - m_executor.submit([this, newState]() { executeOnDialogUXStateChanged(newState); }); + m_executor.execute([this, newState]() { executeOnDialogUXStateChanged(newState); }); } AudioInputProcessor::AudioInputProcessor( @@ -666,7 +677,7 @@ AudioInputProcessor::AudioInputProcessor( std::shared_ptr userInactivityMonitor, std::shared_ptr systemSoundPlayer, const std::shared_ptr& assetsManager, - std::shared_ptr speechEncoder, + std::shared_ptr audioEncoder, AudioProvider defaultAudioProvider, std::shared_ptr wakeWordConfirmation, std::shared_ptr speechConfirmation, @@ -684,7 +695,7 @@ AudioInputProcessor::AudioInputProcessor( m_contextManager{contextManager}, m_focusManager{focusManager}, m_userInactivityMonitor{userInactivityMonitor}, - m_encoder{speechEncoder}, + m_encoder{audioEncoder}, m_defaultAudioProvider{defaultAudioProvider}, m_lastAudioProvider{AudioProvider::null()}, m_state{ObserverInterface::State::IDLE}, @@ -709,6 +720,7 @@ AudioInputProcessor::AudioInputProcessor( m_messageRequestResolver{nullptr}, m_encodingAudioFormats{{DEFAULT_RESOLVE_KEY, AudioFormat::Encoding::LPCM}} { m_capabilityConfigurations.insert(capabilitiesConfiguration); + m_fetchContextTimeMetricData.setName(FETCH_CONTEXT_DURATION); if (m_powerResourceManager) { m_powerResourceId = m_powerResourceManager->create( @@ -819,7 +831,7 @@ bool resolveMessageRequest( rapidjson::Value formatValue; auto encodingFormat = encodingFormats.at(resolveKey); auto formatString = encodingFormatToString(encodingFormat); - formatValue.SetString(formatString.c_str(), formatString.length()); + formatValue.SetString(formatString.c_str(), static_cast(formatString.length())); if (payload->value.FindMember(FORMAT_KEY) != payload->value.MemberEnd()) { ACSDK_WARN(LX("Format already exists in Json payload. Replace it with").d("format", formatString)); @@ -868,7 +880,7 @@ std::future AudioInputProcessor::expectSpeechTimedOut() { void AudioInputProcessor::handleStopCaptureDirective(std::shared_ptr info) { m_stopCaptureReceivedTime = steady_clock::now(); - m_executor.submit([this, info]() { + m_executor.execute([this, info]() { bool stopImmediately = true; executeStopCapture(stopImmediately, info); }); @@ -895,7 +907,7 @@ void AudioInputProcessor::handleExpectSpeechDirective(std::shared_ptr info) { @@ -1105,7 +1117,7 @@ bool AudioInputProcessor::executeRecognize( } if (settings::WakeWordConfirmationSettingType::TONE == m_wakeWordConfirmation->get()) { - m_executor.submit( + m_executor.execute( [this]() { m_systemSoundPlayer->playTone(SystemSoundPlayerInterface::Tone::WAKEWORD_NOTIFICATION); }); } @@ -1141,10 +1153,12 @@ bool AudioInputProcessor::executeRecognize( avsCommon::avs::AudioInputStream::Index encodingOffset = 0; AudioInputStream::Reader::Reference encodingReference = AudioInputStream::Reader::Reference::ABSOLUTE; - // Set up the speech encoder + // Set up the audio encoder + std::shared_ptr encodedStream; if (m_usingEncoder) { ACSDK_DEBUG(LX("encodingAudio").d("format", avsEncodingFormat)); - if (!m_encoder->startEncoding(provider.stream, provider.format, offset, reference)) { + encodedStream = m_encoder->startEncoding(provider.stream, provider.format, offset, reference); + if (!encodedStream) { ACSDK_ERROR(LX("executeRecognizeFailed").d("reason", "Failed to start encoder")); return false; } @@ -1179,7 +1193,7 @@ bool AudioInputProcessor::executeRecognize( std::shared_ptr audioReader = attachment::DefaultAttachmentReader::create( sds::ReaderPolicy::NONBLOCKING, - isLPCMEncodingAudioFormat ? provider.stream : m_encoder->getEncodedStream(), + isLPCMEncodingAudioFormat ? provider.stream : encodedStream, isLPCMEncodingAudioFormat ? offset : encodingOffset, isLPCMEncodingAudioFormat ? reference : encodingReference); if (!audioReader) { @@ -1193,14 +1207,24 @@ bool AudioInputProcessor::executeRecognize( ACSDK_INFO(LX("Create audio attachment reader success") .d("resolveKey", resolveKey) .d("format", encodingFormatToString(it.second))); + + if (resolveKey == CLOUD_RESOLVE_KEY) { + if (isLPCMEncodingAudioFormat) { + m_audioBytesForMetricThreshold = WAKEWORD_DETECTION_SEGMENT_SIZE_BYTES_PCM; + m_uploadMetricName = WAKEWORD_DETECTION_SEGMENT_UPLOADED_PCM; + } else { + m_audioBytesForMetricThreshold = WAKEWORD_DETECTION_SEGMENT_SIZE_BYTES_OPUS; + m_uploadMetricName = WAKEWORD_DETECTION_SEGMENT_UPLOADED_OPUS; + } + } } if (!multiStreamsRequestedLocked()) { m_messageRequestResolver = nullptr; // Set up format for single audio stream request - if (m_usingEncoder && m_encoder->getContext()) { - avsEncodingFormat = m_encoder->getContext()->getAVSFormatName(); + if (m_usingEncoder) { + avsEncodingFormat = m_encoder->getAVSFormatName(); } payloadGenerator.addMember(FORMAT_KEY, avsEncodingFormat); @@ -1243,6 +1267,11 @@ bool AudioInputProcessor::executeRecognize( setState(ObserverInterface::State::RECOGNIZING); + // Notify observers of current ASRProfile + for (auto observer : m_observers) { + observer->onASRProfileChanged(asrProfileToString(provider.profile)); + } + // Note that we're preparing to send a Recognize event. m_preparingToSend = true; @@ -1251,6 +1280,7 @@ bool AudioInputProcessor::executeRecognize( m_streamIsClosedInRecognizingState = false; // Start assembling the context; we'll service the callback after assembling our Recognize event. + m_fetchContextTimeMetricData.startDurationTimer(); m_contextManager->getContextWithoutReportableStateProperties(shared_from_this()); // Stop the ExpectSpeech timer so we don't get a timeout. @@ -1266,6 +1296,9 @@ bool AudioInputProcessor::executeRecognize( m_recognizeRequest.reset(); // Handle metrics for this event. + if (m_usingEncoder) { + avsEncodingFormat = m_encoder->getAVSFormatName(); + } submitMetric( m_metricRecorder, MetricEventBuilder{} @@ -1282,6 +1315,7 @@ bool AudioInputProcessor::executeRecognize( .setName("RESOURCE_TYPE_ID") .setValue(std::to_string(m_resourceFlags.to_ulong())) .build()) + .addDataPoint(DataPointStringBuilder{}.setName("ENCODING_FORMAT").setValue(avsEncodingFormat).build()) .addDataPoint(DataPointCounterBuilder{}.setName(START_OF_UTTERANCE).increment(1).build()), m_preCachedDialogRequestId); @@ -1312,9 +1346,16 @@ bool AudioInputProcessor::executeRecognize( .addDataPoint( DataPointDurationBuilder{duration_cast(startOfStreamTimestamp.time_since_epoch())} .setName(START_OF_STREAM_TIMESTAMP) - .build()) + .build()), + m_preCachedDialogRequestId); + + submitMetric( + m_metricRecorder, + MetricEventBuilder{} + .setActivityName(RECOGNIZE_START_SEND_MESSAGE_ACTIVITY_NAME) .addDataPoint(DataPointCounterBuilder{}.setName(RECOGNIZE_START_SEND_MESSAGE).increment(1).build()), m_preCachedDialogRequestId); + ACSDK_DEBUG(LX(__func__).d("WW_DURATION(ms)", duration.count())); } @@ -1330,6 +1371,13 @@ bool AudioInputProcessor::executeRecognize( void AudioInputProcessor::executeOnContextAvailable(const std::string& jsonContext) { ACSDK_DEBUG(LX("executeOnContextAvailable").sensitive("jsonContext", jsonContext)); + /// Submit execute context start metric + submitMetric( + m_metricRecorder, + MetricEventBuilder{} + .setActivityName(FETCH_CONTEXT_DURATION_ACTIVITY_NAME) + .addDataPoint(m_fetchContextTimeMetricData.stopDurationTimer().build()), + m_preCachedDialogRequestId); // Should already be RECOGNIZING if we get here. if (m_state != ObserverInterface::State::RECOGNIZING) { ACSDK_ERROR( @@ -1470,7 +1518,7 @@ bool AudioInputProcessor::executeStopCapture(bool stopImmediately, std::shared_p std::function stopCapture = [=] { ACSDK_DEBUG(LX("stopCapture").d("stopImmediately", stopImmediately)); if (m_usingEncoder) { - // If SpeechEncoder is enabled, let it finish it so the stream will be closed automatically. + // If Audio Encoder is enabled, let it finish it so the stream will be closed automatically. m_encoder->stopEncoding(stopImmediately); m_usingEncoder = false; } @@ -1631,7 +1679,7 @@ void AudioInputProcessor::setState(ObserverInterface::State state) { // Reset the user inactivity if transitioning to or from `RECOGNIZING` state. if (ObserverInterface::State::RECOGNIZING == m_state || ObserverInterface::State::RECOGNIZING == state) { - m_executor.submit([this]() { m_userInactivityMonitor->onUserActive(); }); + m_executor.execute([this]() { m_userInactivityMonitor->onUserActive(); }); } auto currentDialogRequestId = @@ -1699,7 +1747,7 @@ void AudioInputProcessor::onExceptionReceived(const std::string& exceptionMessag void AudioInputProcessor::onSendCompleted(MessageRequestObserverInterface::Status status) { ACSDK_DEBUG(LX("onSendCompleted").d("status", status)); - m_executor.submit([this, status]() { + m_executor.execute([this, status]() { if (MessageRequestObserverInterface::Status::SUCCESS == status && ObserverInterface::State::RECOGNIZING == m_state) { // This is to take care of the edge case where the event stream is closed before a stop capture is received. @@ -1715,7 +1763,7 @@ std::unordered_set> Aud } void AudioInputProcessor::onLocaleAssetsChanged() { - m_executor.submit([this]() { executeOnLocaleAssetsChanged(); }); + m_executor.execute([this]() { executeOnLocaleAssetsChanged(); }); } void AudioInputProcessor::executeOnLocaleAssetsChanged() { @@ -1787,7 +1835,7 @@ bool AudioInputProcessor::handleSetWakeWordConfirmation(std::shared_ptrsetAvsChange(value); }; - m_executor.submit(executeChange); + m_executor.execute(executeChange); if (info->result) { info->result->setCompleted(); @@ -1810,7 +1858,7 @@ bool AudioInputProcessor::handleSetSpeechConfirmation(std::shared_ptr> value; @@ -1823,7 +1871,7 @@ bool AudioInputProcessor::handleSetSpeechConfirmation(std::shared_ptrsetAvsChange(value); }; - m_executor.submit(executeChange); + m_executor.execute(executeChange); if (info->result) { info->result->setCompleted(); @@ -1852,7 +1900,7 @@ bool AudioInputProcessor::handleSetWakeWords(std::shared_ptr info return false; } - m_executor.submit([this, wakeWords, info]() { m_wakeWordsSetting->setAvsChange(wakeWords); }); + m_executor.execute([this, wakeWords, info]() { m_wakeWordsSetting->setAvsChange(wakeWords); }); if (info->result) { info->result->setCompleted(); @@ -1902,7 +1950,7 @@ void AudioInputProcessor::managePowerResource(ObserverInterface::State newState) void AudioInputProcessor::onConnectionStatusChanged(bool connected) { if (!connected) { - m_executor.submit([this]() { return executeDisconnected(); }); + m_executor.execute([this]() { return executeDisconnected(); }); } } @@ -1914,8 +1962,7 @@ void AudioInputProcessor::executeDisconnected() { } bool AudioInputProcessor::setEncodingAudioFormat(AudioFormat::Encoding encoding) { - if (encoding == AudioFormat::Encoding::LPCM || - (m_encoder && m_encoder->getContext() && encoding == m_encoder->getContext()->getAudioFormat().encoding)) { + if (encoding == AudioFormat::Encoding::LPCM || (m_encoder && encoding == m_encoder->getEncoding())) { std::lock_guard lock(m_encodingFormatMutex); m_encodingAudioFormats.clear(); // Only one format is configured, and AIP will send resolved RequestMessage, and this resolveKey is simply a @@ -1927,10 +1974,10 @@ bool AudioInputProcessor::setEncodingAudioFormat(AudioFormat::Encoding encoding) } bool AudioInputProcessor::initialize() { - if (m_encoder && m_encoder->getContext()) { + if (m_encoder) { std::lock_guard lock(m_encodingFormatMutex); m_encodingAudioFormats.clear(); - m_encodingAudioFormats.emplace(DEFAULT_RESOLVE_KEY, m_encoder->getContext()->getAudioFormat().encoding); + m_encodingAudioFormats.emplace(DEFAULT_RESOLVE_KEY, m_encoder->getEncoding()); } m_assetsManager->addLocaleAssetsObserver(shared_from_this()); return true; @@ -1986,21 +2033,17 @@ void AudioInputProcessor::closeAttachmentReaders(attachment::AttachmentReader::C } bool AudioInputProcessor::isEncodingFormatSupported(avsCommon::utils::AudioFormat::Encoding encodingFormat) const { - if (encodingFormat == AudioFormat::Encoding::LPCM || - (m_encoder && m_encoder->getContext() && - encodingFormat == m_encoder->getContext()->getAudioFormat().encoding)) { + if (encodingFormat == AudioFormat::Encoding::LPCM || (m_encoder && encodingFormat == m_encoder->getEncoding())) { return true; } return false; } bool AudioInputProcessor::isUsingEncoderLocked() const { - for (auto it = m_encodingAudioFormats.begin(); it != m_encodingAudioFormats.end(); it++) { - if (it->second != AudioFormat::Encoding::LPCM) { - return true; - } - } - return false; + return std::any_of( + m_encodingAudioFormats.begin(), + m_encodingAudioFormats.end(), + [](EncodingFormatResponse::const_reference entry) { return entry.second != AudioFormat::Encoding::LPCM; }); } bool AudioInputProcessor::multiStreamsRequestedLocked() const { diff --git a/CapabilityAgents/AIP/src/CMakeLists.txt b/CapabilityAgents/AIP/src/CMakeLists.txt index 2c2eed5328..244db6c5fb 100644 --- a/CapabilityAgents/AIP/src/CMakeLists.txt +++ b/CapabilityAgents/AIP/src/CMakeLists.txt @@ -8,7 +8,6 @@ target_include_directories(AIP PUBLIC "${AFML_SOURCE_DIR}/include" "${AVSCommon_INCLUDE_DIRS}" "${DeviceSettings_INCLUDE_DIRS}" - "${SpeechEncoder_SOURCE_DIR}/include" "${SystemSoundPlayer_INCLUDE_DIRS}") target_link_libraries(AIP @@ -16,7 +15,7 @@ target_link_libraries(AIP ADSL AFML DeviceSettings - SpeechEncoder + acsdkAudioEncoderInterfaces SystemSoundPlayer acsdkNotifier) diff --git a/CapabilityAgents/AIP/test/AudioInputProcessorTest.cpp b/CapabilityAgents/AIP/test/AudioInputProcessorTest.cpp index c1b553b7b5..a588977906 100644 --- a/CapabilityAgents/AIP/test/AudioInputProcessorTest.cpp +++ b/CapabilityAgents/AIP/test/AudioInputProcessorTest.cpp @@ -27,6 +27,8 @@ #include #include +#include +#include #include #include #include @@ -111,6 +113,9 @@ static const avsCommon::avs::NamespaceAndName DIRECTIVES[] = {STOP_CAPTURE, /// The SpeechRecognizer context state signature. static const avsCommon::avs::NamespaceAndName RECOGNIZER_STATE{NAMESPACE, "RecognizerState"}; +/// Byte array for audio sample data passing. +using Bytes = audioEncoderInterfaces::BlockAudioEncoderInterface::Bytes; + /// Sample rate for audio input stream. static const unsigned int SAMPLE_RATE_HZ = 16000; @@ -632,7 +637,7 @@ void RecognizeEvent::verifyMessage( auto bytesRead = namedReader->reader->read( samples.data() + samplesRead, (samples.size() - samplesRead) * SDS_WORDSIZE, &status); if (avsCommon::avs::attachment::AttachmentReader::ReadStatus::OK_WOULDBLOCK == status) { - std::this_thread::yield(); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; } EXPECT_EQ(status, avsCommon::avs::attachment::AttachmentReader::ReadStatus::OK); @@ -683,8 +688,8 @@ class MockExpectSpeechTimeoutHandler : public avsCommon::sdkInterfaces::ExpectSp bool(std::chrono::milliseconds timeout, const std::function()>& expectSpeechTimedOut)); }; -/// Mock class that implements the SpeechEncoderContext. -class MockEncoderContext : public speechencoder::EncoderContext { +/// Mock class that implements the @c BlockAudioEncoderInterface. +class MockBlockAudioEncoder : public audioEncoderInterfaces::BlockAudioEncoderInterface { public: MOCK_METHOD1(init, bool(alexaClientSDK::avsCommon::utils::AudioFormat inputFormat)); MOCK_METHOD0(getInputFrameSize, size_t()); @@ -692,8 +697,9 @@ class MockEncoderContext : public speechencoder::EncoderContext { MOCK_METHOD0(requiresFullyRead, bool()); MOCK_METHOD0(getAudioFormat, alexaClientSDK::avsCommon::utils::AudioFormat()); MOCK_METHOD0(getAVSFormatName, std::string()); - MOCK_METHOD0(start, bool()); - MOCK_METHOD3(processSamples, ssize_t(void* samples, size_t numberOfWords, uint8_t* buffer)); + MOCK_METHOD1(start, bool(Bytes&)); + MOCK_METHOD3(processSamples, bool(Bytes::const_iterator, Bytes::const_iterator, Bytes&)); + MOCK_METHOD1(flush, bool(Bytes&)); MOCK_METHOD0(close, void()); }; @@ -1018,10 +1024,10 @@ class AudioInputProcessorTest : public ::testing::Test { std::string m_dialogRequestId; /// The audio encoder object - std::shared_ptr m_speechEncoder; + std::shared_ptr m_audioEncoder; - /// The mock @c EncoderContext - std::shared_ptr m_mockEncoderContext; + /// The mock @c BlockAudioEncoderInterface + std::shared_ptr m_mockBlockAudioEncoder; /// Message ID in directive std::string m_messageId; @@ -1123,30 +1129,30 @@ void AudioInputProcessorTest::SetUp() { m_pattern.resize(PATTERN_WORDS); std::iota(m_pattern.begin(), m_pattern.end(), 0); - m_mockEncoderContext = std::make_shared(); - m_speechEncoder = std::make_shared(m_mockEncoderContext); - EXPECT_CALL(*m_mockEncoderContext, init(_)).Times(AtLeast(0)).WillRepeatedly(Return(true)); - EXPECT_CALL(*m_mockEncoderContext, requiresFullyRead()).Times(AtLeast(0)).WillRepeatedly(Return(true)); - EXPECT_CALL(*m_mockEncoderContext, getAVSFormatName()).WillRepeatedly(Return(std::string(AUDIO_FORMAT_OPUS))); - EXPECT_CALL(*m_mockEncoderContext, getInputFrameSize()).WillRepeatedly(Return(1)); - EXPECT_CALL(*m_mockEncoderContext, getOutputFrameSize()).WillRepeatedly(Return(2)); - EXPECT_CALL(*m_mockEncoderContext, processSamples(_, _, _)) - .WillRepeatedly(Invoke([](void* samples, size_t numberOfWords, uint8_t* buffer) { - uint8_t* src = static_cast(samples); - buffer[0] = src[0]; - buffer[1] = src[1]; - return 2; + m_mockBlockAudioEncoder = std::make_shared(); + m_audioEncoder = audioEncoder::createAudioEncoder(m_mockBlockAudioEncoder); + EXPECT_CALL(*m_mockBlockAudioEncoder, init(_)).Times(AtLeast(0)).WillRepeatedly(Return(true)); + EXPECT_CALL(*m_mockBlockAudioEncoder, requiresFullyRead()).Times(AtLeast(0)).WillRepeatedly(Return(true)); + EXPECT_CALL(*m_mockBlockAudioEncoder, getAVSFormatName()).WillRepeatedly(Return(std::string(AUDIO_FORMAT_OPUS))); + EXPECT_CALL(*m_mockBlockAudioEncoder, getInputFrameSize()).WillRepeatedly(Return(1)); + EXPECT_CALL(*m_mockBlockAudioEncoder, getOutputFrameSize()).WillRepeatedly(Return(2)); + EXPECT_CALL(*m_mockBlockAudioEncoder, processSamples(_, _, _)) + .WillRepeatedly(Invoke([](Bytes::const_iterator begin, Bytes::const_iterator end, Bytes& buffer) { + const size_t bufferOffset = buffer.size(); + buffer.resize(bufferOffset + 2); + std::memcpy(buffer.data() + bufferOffset, &*begin, 2); + return true; })); - EXPECT_CALL(*m_mockEncoderContext, getAudioFormat()) - .WillRepeatedly( - Return(avsCommon::utils::AudioFormat{.encoding = avsCommon::utils::AudioFormat::Encoding::OPUS, - .endianness = avsCommon::utils::AudioFormat::Endianness::LITTLE, - .sampleRateHz = 16000, - .sampleSizeInBits = 16, - .numChannels = 1, - .dataSigned = false, - .layout = avsCommon::utils::AudioFormat::Layout::NON_INTERLEAVED})); - EXPECT_CALL(*m_mockEncoderContext, start()).WillRepeatedly(Return(true)); + EXPECT_CALL(*m_mockBlockAudioEncoder, getAudioFormat()) + .WillRepeatedly(Return(avsCommon::utils::AudioFormat{avsCommon::utils::AudioFormat::Encoding::OPUS, + avsCommon::utils::AudioFormat::Endianness::LITTLE, + 16000, + 16, + 1, + false, + avsCommon::utils::AudioFormat::Layout::NON_INTERLEAVED})); + EXPECT_CALL(*m_mockBlockAudioEncoder, start(_)).WillRepeatedly(Return(true)); + EXPECT_CALL(*m_mockBlockAudioEncoder, flush(_)).WillRepeatedly(Return(true)); } void AudioInputProcessorTest::setupEncoderTest() { @@ -1165,7 +1171,7 @@ void AudioInputProcessorTest::setupEncoderTest() { m_mockSpeechConfirmation, m_capabilityChangeNotifier, m_mockWakeWordSetting, - m_speechEncoder, + m_audioEncoder, *m_audioProvider, m_mockPowerResourceManager, m_metricRecorder); @@ -1261,7 +1267,6 @@ bool AudioInputProcessorTest::testRecognizeSucceeds( } if (!bargeIn) { - EXPECT_CALL(*m_mockUserInactivityMonitor, onUserActive()).Times(2); EXPECT_CALL(*m_mockObserver, onStateChanged(AudioInputProcessorObserverInterface::State::RECOGNIZING)); EXPECT_CALL(*m_mockFocusManager, acquireChannel(CHANNEL_NAME, _)).WillOnce(InvokeWithoutArgs([this, stopPoint] { m_audioInputProcessor->onFocusChanged(avsCommon::avs::FocusState::FOREGROUND, MixingBehavior::PRIMARY); @@ -1324,6 +1329,7 @@ bool AudioInputProcessorTest::testRecognizeSucceeds( EXPECT_CALL(*m_mockPowerResourceManager, release(IsSamePowerResource(COMPONENT_NAME))).Times(AtLeast(1)); } + EXPECT_CALL(*m_mockObserver, onASRProfileChanged(asrProfileToString(audioProvider.profile))).Times(AtLeast(1)); auto sentFuture = m_recognizeEvent->send(m_audioInputProcessor); // If a valid begin index was not provided, load the SDS buffer with the test pattern after recognize() is sent. @@ -1384,7 +1390,6 @@ bool AudioInputProcessorTest::testContextFailure(avsCommon::sdkInterfaces::Conte return CONTEXT_REQUEST_TOKEN; })); EXPECT_CALL(*m_mockObserver, onStateChanged(AudioInputProcessorObserverInterface::State::RECOGNIZING)); - EXPECT_CALL(*m_mockUserInactivityMonitor, onUserActive()).Times(2); EXPECT_CALL(*m_mockObserver, onStateChanged(AudioInputProcessorObserverInterface::State::IDLE)) .WillOnce(InvokeWithoutArgs([&] { std::lock_guard lock(mutex); @@ -1394,6 +1399,8 @@ bool AudioInputProcessorTest::testContextFailure(avsCommon::sdkInterfaces::Conte EXPECT_CALL(*m_mockPowerResourceManager, acquire(IsSamePowerResource(COMPONENT_NAME), _)).Times(AtLeast(1)); EXPECT_CALL(*m_mockPowerResourceManager, release(IsSamePowerResource(COMPONENT_NAME))).Times(AtLeast(1)); + EXPECT_CALL(*m_mockObserver, onASRProfileChanged(asrProfileToString(m_audioProvider->profile))).Times((AtLeast(1))); + if (recognize.send(m_audioInputProcessor).get()) { std::unique_lock lock(mutex); return conditionVariable.wait_for(lock, TEST_TIMEOUT, [&done] { return done; }); @@ -1470,7 +1477,6 @@ bool AudioInputProcessorTest::testExpectSpeechSucceeds(bool withDialogRequestId) EXPECT_CALL(*m_mockObserver, onStateChanged(AudioInputProcessorObserverInterface::State::EXPECTING_SPEECH)); EXPECT_CALL(*m_mockObserver, onStateChanged(AudioInputProcessorObserverInterface::State::RECOGNIZING)); - EXPECT_CALL(*m_mockUserInactivityMonitor, onUserActive()).Times(2); EXPECT_CALL(*m_mockPowerResourceManager, acquire(IsSamePowerResource(COMPONENT_NAME), _)).Times(AtLeast(1)); if (withDialogRequestId) { EXPECT_CALL(*result, setCompleted()); @@ -1482,7 +1488,7 @@ bool AudioInputProcessorTest::testExpectSpeechSucceeds(bool withDialogRequestId) conditionVariable.notify_one(); return CONTEXT_REQUEST_TOKEN; })); - + EXPECT_CALL(*m_mockObserver, onASRProfileChanged(asrProfileToString(m_audioProvider->profile))).Times(AtLeast(1)); if (!withDialogRequestId) { directiveHandler->handleDirectiveImmediately(avsDirective); } else { @@ -1655,11 +1661,10 @@ bool AudioInputProcessorTest::testRecognizeWithExpectSpeechInitiator(bool withIn EXPECT_CALL(*result, setCompleted()); EXPECT_CALL(*m_mockObserver, onStateChanged(AudioInputProcessorObserverInterface::State::EXPECTING_SPEECH)); EXPECT_CALL(*m_mockObserver, onStateChanged(AudioInputProcessorObserverInterface::State::RECOGNIZING)); - EXPECT_CALL(*m_mockUserInactivityMonitor, onUserActive()).Times(2); EXPECT_CALL(*m_mockContextManager, getContextWithoutReportableStateProperties(_, _, _)) .WillOnce(Return(CONTEXT_REQUEST_TOKEN)); EXPECT_CALL(*m_mockPowerResourceManager, acquire(IsSamePowerResource(COMPONENT_NAME), _)).Times(AtLeast(1)); - + EXPECT_CALL(*m_mockObserver, onASRProfileChanged(asrProfileToString(m_audioProvider->profile))).Times(AtLeast(1)); // Set AIP to a sane state. directiveHandler->preHandleDirective(avsDirective, std::move(result)); EXPECT_TRUE(directiveHandler->handleDirective(avsDirective->getMessageId())); @@ -3636,16 +3641,15 @@ TEST_F(AudioInputProcessorTest, test_requestEncodingAudioFormatsSuccess) { */ TEST_F(AudioInputProcessorTest, test_requestEncodingAudioFormatsWithFallbackAndUnsupportedFormats) { setupEncoderTest(); - EXPECT_CALL(*m_mockEncoderContext, getAVSFormatName()).WillRepeatedly(Return(std::string(AUDIO_FORMAT_LPCM))); - EXPECT_CALL(*m_mockEncoderContext, getAudioFormat()) - .WillRepeatedly( - Return(avsCommon::utils::AudioFormat{.encoding = avsCommon::utils::AudioFormat::Encoding::LPCM, - .endianness = avsCommon::utils::AudioFormat::Endianness::LITTLE, - .sampleRateHz = 16000, - .sampleSizeInBits = 16, - .numChannels = 1, - .dataSigned = false, - .layout = avsCommon::utils::AudioFormat::Layout::NON_INTERLEAVED})); + EXPECT_CALL(*m_mockBlockAudioEncoder, getAVSFormatName()).WillRepeatedly(Return(std::string(AUDIO_FORMAT_LPCM))); + EXPECT_CALL(*m_mockBlockAudioEncoder, getAudioFormat()) + .WillRepeatedly(Return(avsCommon::utils::AudioFormat{avsCommon::utils::AudioFormat::Encoding::LPCM, + avsCommon::utils::AudioFormat::Endianness::LITTLE, + 16000, + 16, + 1, + false, + avsCommon::utils::AudioFormat::Layout::NON_INTERLEAVED})); AudioInputProcessor::EncodingFormatRequest encodingReq = { {"CLOUD", {avsCommon::utils::AudioFormat::Encoding::OPUS, avsCommon::utils::AudioFormat::Encoding::OPUS}}, @@ -3822,7 +3826,7 @@ TEST_F(AudioInputProcessorTest, test_recognizeWakewordWithGoodBeginAndEndForMult } /// This function verifies that recognize() works for multiple audio streams in State::RECOGNIZING when the previous * -/// recognize used the CLOSE_TALK profile. Disabled for potential race condition in SpeechEncoder. +/// recognize used the CLOSE_TALK profile. Disabled for potential race condition in audio encoder. TEST_F(AudioInputProcessorTest, test_recognizeBargeInWhileRecognizingCloseTalkForMultiStreams) { auto audioProvider = *m_audioProvider; audioProvider.profile = ASRProfile::CLOSE_TALK; diff --git a/CapabilityAgents/AIP/test/CMakeLists.txt b/CapabilityAgents/AIP/test/CMakeLists.txt index 640e5e0e8b..1b8c692d8e 100644 --- a/CapabilityAgents/AIP/test/CMakeLists.txt +++ b/CapabilityAgents/AIP/test/CMakeLists.txt @@ -5,4 +5,4 @@ set(INCLUDE_PATH "${AVSCommon_SOURCE_DIR}/AVS/test" "${DeviceSettings_SOURCE_DIR}/test") -discover_unit_tests("${INCLUDE_PATH}" "AIP;UtilsCommonTestLib;CertifiedSenderCommonTestLib;AVSSystem;SDKInterfacesTests") +discover_unit_tests("${INCLUDE_PATH}" "AIP;UtilsCommonTestLib;CertifiedSenderCommonTestLib;AVSSystem;SDKInterfacesTests;acsdkAudioEncoder") diff --git a/CapabilityAgents/AIP/test/MockObserver.h b/CapabilityAgents/AIP/test/MockObserver.h index b59764086a..4f191a0767 100644 --- a/CapabilityAgents/AIP/test/MockObserver.h +++ b/CapabilityAgents/AIP/test/MockObserver.h @@ -29,6 +29,7 @@ namespace test { class MockObserver : public avsCommon::sdkInterfaces::AudioInputProcessorObserverInterface { public: MOCK_METHOD1(onStateChanged, void(avsCommon::sdkInterfaces::AudioInputProcessorObserverInterface::State state)); + MOCK_METHOD1(onASRProfileChanged, void(const std::string& profile)); }; } // namespace test diff --git a/CapabilityAgents/Alexa/include/Alexa/AlexaEventProcessedNotifier.h b/CapabilityAgents/Alexa/include/Alexa/AlexaEventProcessedNotifier.h index 5a07998cd8..5dfe0874cc 100644 --- a/CapabilityAgents/Alexa/include/Alexa/AlexaEventProcessedNotifier.h +++ b/CapabilityAgents/Alexa/include/Alexa/AlexaEventProcessedNotifier.h @@ -19,7 +19,7 @@ #include #include -#include +#include #include namespace alexaClientSDK { @@ -30,7 +30,7 @@ namespace alexa { * Relays notifications when Alexa.EventProcessed directive is received. */ class AlexaEventProcessedNotifier - : public acsdkNotifier::Notifier { + : public notifier::Notifier { public: /** * Factory method. diff --git a/CapabilityAgents/Alexa/src/AlexaInterfaceCapabilityAgent.cpp b/CapabilityAgents/Alexa/src/AlexaInterfaceCapabilityAgent.cpp index 86aca70035..6aaad98372 100644 --- a/CapabilityAgents/Alexa/src/AlexaInterfaceCapabilityAgent.cpp +++ b/CapabilityAgents/Alexa/src/AlexaInterfaceCapabilityAgent.cpp @@ -31,7 +31,7 @@ using namespace avsCommon::utils; using DefaultEndpointAnnotation = avsCommon::sdkInterfaces::endpoints::DefaultEndpointAnnotation; /// String to identify log entries originating from this file. -static const std::string TAG{"AlexaInterfaceCapabilityAgent"}; +#define TAG "AlexaInterfaceCapabilityAgent" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -168,7 +168,7 @@ void AlexaInterfaceCapabilityAgent::handleDirectiveImmediately(std::shared_ptr info) { ACSDK_DEBUG5(LX(__func__)); - m_executor.submit([this, info] { + m_executor.execute([this, info] { if (!info || !info->directive) { ACSDK_ERROR(LX("handleDirectiveFailed").d("reason", "nullDirective")); return; diff --git a/CapabilityAgents/Alexa/src/AlexaInterfaceMessageSender.cpp b/CapabilityAgents/Alexa/src/AlexaInterfaceMessageSender.cpp index 4fa4a9f47c..3acf921f82 100644 --- a/CapabilityAgents/Alexa/src/AlexaInterfaceMessageSender.cpp +++ b/CapabilityAgents/Alexa/src/AlexaInterfaceMessageSender.cpp @@ -32,7 +32,7 @@ using namespace avsCommon::utils; using namespace avsCommon::utils::logger; /// String to identify log entries originating from this file. -static const std::string TAG{"AlexaInterfaceMessageSender"}; +#define TAG "AlexaInterfaceMessageSender" /// Name of response events. static const std::string EVENT_NAME_STATE_REPORT_STRING = "StateReport"; @@ -327,7 +327,7 @@ bool AlexaInterfaceMessageSender::sendCommonResponseEvent( endpoint, jsonPayload, responseNamespace == "" ? ALEXA_INTERFACE_NAME : responseNamespace); - m_executor.submit([this, event]() { + m_executor.execute([this, event]() { // Start collecting context for this endpoint. auto token = m_contextManager->getContext(shared_from_this(), event->endpoint.endpointId); m_pendingResponses[token] = event; @@ -408,7 +408,7 @@ void AlexaInterfaceMessageSender::onStateChanged( const CapabilityState& state, const AlexaStateChangeCauseType cause) { auto event = std::make_shared(identifier, state, cause); - m_executor.submit([this, event]() { + m_executor.execute([this, event]() { // Start collecting context for this endpoint. auto token = m_contextManager->getContext(shared_from_this(), event->tag.endpointId); m_pendingChangeReports[token] = event; @@ -419,7 +419,7 @@ void AlexaInterfaceMessageSender::onContextAvailable( const std::string& endpointId, const AVSContext& endpointContext, ContextRequestToken token) { - m_executor.submit([this, endpointId, endpointContext, token]() { + m_executor.execute([this, endpointId, endpointContext, token]() { ACSDK_DEBUG(LX("onContextAvailable").sensitive("endpointId", endpointId)); // Is this for a pending response event? @@ -436,7 +436,8 @@ void AlexaInterfaceMessageSender::onContextAvailable( } void AlexaInterfaceMessageSender::onContextFailure(const ContextRequestError error, ContextRequestToken token) { - m_executor.submit([this, error, token]() { + m_executor.execute([this, error, token]() { + (void)error; // mark capture as used for the case when logging is disabled. ACSDK_ERROR(LX("executeOnContextFailure").d("error", error)); // Is this for a pending response event? diff --git a/CapabilityAgents/ApiGateway/src/ApiGatewayCapabilityAgent.cpp b/CapabilityAgents/ApiGateway/src/ApiGatewayCapabilityAgent.cpp index 9030205d1a..09371f14e9 100644 --- a/CapabilityAgents/ApiGateway/src/ApiGatewayCapabilityAgent.cpp +++ b/CapabilityAgents/ApiGateway/src/ApiGatewayCapabilityAgent.cpp @@ -29,7 +29,7 @@ using namespace avsCommon::sdkInterfaces; using namespace avsCommon::utils::json::jsonUtils; /// String to identify log entries originating from this file. -static const std::string TAG{"ApiGateway"}; +#define TAG "ApiGateway" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -117,7 +117,7 @@ void ApiGatewayCapabilityAgent::handleDirectiveImmediately(std::shared_ptr info) { ACSDK_DEBUG5(LX(__func__)); - m_executor.submit([this, info] { executeHandleDirective(info); }); + m_executor.execute([this, info] { executeHandleDirective(info); }); } void ApiGatewayCapabilityAgent::executeHandleDirective(std::shared_ptr info) { diff --git a/CapabilityAgents/CMakeLists.txt b/CapabilityAgents/CMakeLists.txt index 013ad92743..9987b8170b 100644 --- a/CapabilityAgents/CMakeLists.txt +++ b/CapabilityAgents/CMakeLists.txt @@ -14,8 +14,7 @@ set(CAPABILITY_AGENTS "SoftwareComponentReporter" "SpeakerManager" "SpeechSynthesizer" - "System" - "TemplateRuntime") + "System") if (COMMS) list(APPEND CAPABILITY_AGENTS "CallManager") diff --git a/CapabilityAgents/InteractionModel/acsdkInteractionModel/privateInclude/acsdkInteractionModel/InteractionModelNotifier.h b/CapabilityAgents/InteractionModel/acsdkInteractionModel/privateInclude/acsdkInteractionModel/InteractionModelNotifier.h index a60a79dbdf..8de5238f07 100644 --- a/CapabilityAgents/InteractionModel/acsdkInteractionModel/privateInclude/acsdkInteractionModel/InteractionModelNotifier.h +++ b/CapabilityAgents/InteractionModel/acsdkInteractionModel/privateInclude/acsdkInteractionModel/InteractionModelNotifier.h @@ -20,7 +20,7 @@ #include #include -#include +#include namespace alexaClientSDK { namespace acsdkInteractionModel { @@ -29,7 +29,7 @@ namespace acsdkInteractionModel { * Relays notifications related to acsdkInteractionModel. */ class InteractionModelNotifier - : public acsdkNotifier::Notifier< + : public notifier::Notifier< acsdkInteractionModelInterfaces::InteractionModelRequestProcessingObserverInterface> { public: /* diff --git a/CapabilityAgents/InteractionModel/acsdkInteractionModel/src/InteractionModelCapabilityAgent.cpp b/CapabilityAgents/InteractionModel/acsdkInteractionModel/src/InteractionModelCapabilityAgent.cpp index 236d9e2da9..f61304a6b9 100644 --- a/CapabilityAgents/InteractionModel/acsdkInteractionModel/src/InteractionModelCapabilityAgent.cpp +++ b/CapabilityAgents/InteractionModel/acsdkInteractionModel/src/InteractionModelCapabilityAgent.cpp @@ -34,7 +34,7 @@ using namespace avsCommon::utils; using namespace json::jsonUtils; /// String to identify log entries originating from this file. -static const std::string TAG{"InteractionModel"}; +#define TAG "InteractionModel" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -139,10 +139,44 @@ DirectiveHandlerConfiguration InteractionModelCapabilityAgent::getConfiguration( } void InteractionModelCapabilityAgent::handleDirectiveImmediately(std::shared_ptr directive) { ACSDK_DEBUG5(LX(__func__)); - handleDirective(std::make_shared(directive, nullptr)); + + // The logic for handleDirectiveImmediately and preHandleDirective should be same, + // which we only process directive without dialogRequestId + preHandleDirective(std::make_shared(directive, nullptr)); } void InteractionModelCapabilityAgent::preHandleDirective(std::shared_ptr info) { - // No-op + ACSDK_DEBUG5(LX(__func__)); + if (!info) { + ACSDK_ERROR(LX("preHandleDirectiveFailed").d("reason", "nullInfo")); + return; + } + + if (!info->directive) { + ACSDK_ERROR(LX("preHandleDirectiveFailed").d("reason", "nullDirective")); + return; + } + + // Both preHandleDirective and handleDirectiveImmediately interfaces only handle message + // with empty dialogRequestId. Other messages (having dialogRequestId in header) will be + // queued to handle sequentially. + if (info->directive->getDialogRequestId().empty()) { + std::string errMessage; + ExceptionErrorType errType; + + if (handleDirectiveHelper(info, &errMessage, &errType)) { + if (info->result) { + info->result->setCompleted(); + } + } else { + ACSDK_ERROR(LX("preHandleDirectiveFailed").d("reason", errMessage)); + m_exceptionEncounteredSender->sendExceptionEncountered( + info->directive->getUnparsedDirective(), errType, errMessage); + if (info->result) { + info->result->setFailed(errMessage); + } + } + removeDirective(info->directive->getMessageId()); + } } bool InteractionModelCapabilityAgent::handleDirectiveHelper( @@ -198,6 +232,7 @@ bool InteractionModelCapabilityAgent::handleDirectiveHelper( return false; } m_directiveSequencer->setDialogRequestId(uuid); + ACSDK_DEBUG(LX(__func__).d("processDirective", directiveName).d("dialogRequestId", uuid)); } else { *errMessage = "Dialog Request ID not specified"; *type = ExceptionErrorType::UNEXPECTED_INFORMATION_RECEIVED; @@ -233,10 +268,12 @@ void InteractionModelCapabilityAgent::handleDirective(std::shared_ptrdirective->getDialogRequestId().empty() && handleDirectiveHelper(info, &errMessage, &errType)) { if (info->result) { info->result->setCompleted(); } diff --git a/CapabilityAgents/InteractionModel/acsdkInteractionModel/test/InteractionModelCapabilityAgentTest.cpp b/CapabilityAgents/InteractionModel/acsdkInteractionModel/test/InteractionModelCapabilityAgentTest.cpp index 14e1e660aa..bb372dda65 100644 --- a/CapabilityAgents/InteractionModel/acsdkInteractionModel/test/InteractionModelCapabilityAgentTest.cpp +++ b/CapabilityAgents/InteractionModel/acsdkInteractionModel/test/InteractionModelCapabilityAgentTest.cpp @@ -57,6 +57,21 @@ static const std::string CORRECT_NEW_DIALOG_REQUEST_DIRECTIVE_JSON_STRING = R"de } })delim"; +/// A sample Directive JSON string for the purposes of creating an AVSDirective object. +static const std::string CORRECT_REQUEST_PROCESSING_STARTED_DIRECTIVE_JSON_STRING = R"delim( + { + "directive": { + "header": { + "namespace": "InteractionModel", + "name": "RequestProcessingStarted", + "messageId": "12345", + "dialogRequestId": "3456" + }, + "payload": { + } + } + })delim"; + /// An invalid NewDialogRequest directive with an incorrect name static const std::string INCORRECT_NEW_DIALOG_REQUEST_DIRECTIVE_JSON_STRING_1 = R"delim( { @@ -147,6 +162,21 @@ static const std::string RPC_DIRECTIVE_JSON_STRING = R"delim( // Timeout to wait before indicating a test failed. std::chrono::milliseconds TIMEOUT{500}; +/// A wrapper for InteractionModelCapabilityAgent for easy testing +class InteractionModelCapabilityAgentWrapper : public InteractionModelCapabilityAgent { +public: + static void handleDirectiveWrapper( + std::shared_ptr directive, + std::shared_ptr capabilityAgent) { + capabilityAgent->handleDirective(std::make_shared(directive, nullptr)); + } + static void preHandleDirectiveWrapper( + std::shared_ptr directive, + std::shared_ptr capabilityAgent) { + capabilityAgent->preHandleDirective(std::make_shared(directive, nullptr)); + } +}; + /// Test harness for @c InteractionModelCapabilityAgent class. class InteractionModelCapabilityAgentTest : public Test { public: @@ -320,6 +350,55 @@ TEST_F(InteractionModelCapabilityAgentTest, test_processNewDialogRequestID) { ASSERT_EQ(TEST_DIALOG_REQUEST_AVS, m_mockDirectiveSequencer->getDialogRequestId()); } +/** + * Test to verify if a valid NewDialogRequest directive will set the dialogRequestID in the directive sequencer + * in preHandle hook. + */ +TEST_F(InteractionModelCapabilityAgentTest, test_preHandledNewDialogRequestID) { + // Create a dummy AVSDirective. + auto directivePair = AVSDirective::create(CORRECT_NEW_DIALOG_REQUEST_DIRECTIVE_JSON_STRING, nullptr, ""); + std::shared_ptr directive = std::move(directivePair.first); + std::shared_ptr agent = std::dynamic_pointer_cast(m_interactionModelCA); + + agent->preHandleDirective(directive, nullptr); + ASSERT_EQ(TEST_DIALOG_REQUEST_AVS, m_mockDirectiveSequencer->getDialogRequestId()); +} + +/** + * Test to verify if preHandle interface will NOT process directive having dialogRequestID. + */ +TEST_F(InteractionModelCapabilityAgentTest, test_preHandledRequestProcessingStarted) { + // Create a dummy AVSDirective. + auto directivePair = AVSDirective::create(CORRECT_REQUEST_PROCESSING_STARTED_DIRECTIVE_JSON_STRING, nullptr, ""); + std::shared_ptr directive = std::move(directivePair.first); + + InteractionModelCapabilityAgentWrapper::preHandleDirectiveWrapper(directive, m_interactionModelCA); + ASSERT_EQ("", m_mockDirectiveSequencer->getDialogRequestId()); +} + +/** + * Test to verify if preHandle interface will ignore directives with dialogRequestId. + */ +TEST_F(InteractionModelCapabilityAgentTest, test_preHandledNullNewDialogRequestID) { + std::shared_ptr agent = std::dynamic_pointer_cast(m_interactionModelCA); + + agent->preHandleDirective(nullptr, nullptr); + ASSERT_EQ("", m_mockDirectiveSequencer->getDialogRequestId()); +} + +/** + * Test to verify if a valid NewDialogRequest directive with empty dialogRequestId + * will not be handled in handleDirective hook. + */ +TEST_F(InteractionModelCapabilityAgentTest, test_handledNewDialogRequestID) { + // Create a dummy AVSDirective. + auto directivePair = AVSDirective::create(CORRECT_NEW_DIALOG_REQUEST_DIRECTIVE_JSON_STRING, nullptr, ""); + std::shared_ptr directive = std::move(directivePair.first); + + InteractionModelCapabilityAgentWrapper::handleDirectiveWrapper(directive, m_interactionModelCA); + ASSERT_EQ("", m_mockDirectiveSequencer->getDialogRequestId()); +} + /** * Test to verify if interface will ignore null directives */ diff --git a/CapabilityAgents/InteractionModel/acsdkInteractionModelInterfaces/include/acsdkInteractionModelInterfaces/InteractionModelNotifierInterface.h b/CapabilityAgents/InteractionModel/acsdkInteractionModelInterfaces/include/acsdkInteractionModelInterfaces/InteractionModelNotifierInterface.h index 17170763c3..0f1a8e2cdc 100644 --- a/CapabilityAgents/InteractionModel/acsdkInteractionModelInterfaces/include/acsdkInteractionModelInterfaces/InteractionModelNotifierInterface.h +++ b/CapabilityAgents/InteractionModel/acsdkInteractionModelInterfaces/include/acsdkInteractionModelInterfaces/InteractionModelNotifierInterface.h @@ -18,7 +18,7 @@ #include -#include +#include #include "acsdkInteractionModelInterfaces/InteractionModelRequestProcessingObserverInterface.h" @@ -29,7 +29,7 @@ namespace acsdkInteractionModelInterfaces { * Interface for registering to observe acsdkInteractionModel RequestProcessing notifications. */ using InteractionModelNotifierInterface = - acsdkNotifierInterfaces::NotifierInterface; + notifierInterfaces::NotifierInterface; } // namespace acsdkInteractionModelInterfaces } // namespace alexaClientSDK diff --git a/CapabilityAgents/ModeController/src/ModeControllerAttributeBuilder.cpp b/CapabilityAgents/ModeController/src/ModeControllerAttributeBuilder.cpp index 9186da72b3..3bbef75b6d 100644 --- a/CapabilityAgents/ModeController/src/ModeControllerAttributeBuilder.cpp +++ b/CapabilityAgents/ModeController/src/ModeControllerAttributeBuilder.cpp @@ -27,7 +27,7 @@ using namespace avsCommon::sdkInterfaces::modeController; using namespace avsCommon::utils; /// String to identify log entries originating from this file. -static const std::string TAG{"ModeControllerAttributeBuilder"}; +#define TAG "ModeControllerAttributeBuilder" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/CapabilityAgents/ModeController/src/ModeControllerCapabilityAgent.cpp b/CapabilityAgents/ModeController/src/ModeControllerCapabilityAgent.cpp index 84567f251b..e20280b49b 100644 --- a/CapabilityAgents/ModeController/src/ModeControllerCapabilityAgent.cpp +++ b/CapabilityAgents/ModeController/src/ModeControllerCapabilityAgent.cpp @@ -40,7 +40,7 @@ using namespace avsCommon::utils::json; using namespace rapidjson; /// String to identify log entries originating from this file. -static const std::string TAG{"ModeControllerCapabilityAgent"}; +#define TAG "ModeControllerCapabilityAgent" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -269,7 +269,7 @@ void ModeControllerCapabilityAgent::handleDirective(std::shared_ptrdirective->getName(); if (!info->directive->getEndpoint().hasValue() || @@ -311,7 +311,7 @@ void ModeControllerCapabilityAgent::provideState( ACSDK_DEBUG5( LX(__func__).d("contextRequestToken", contextRequestToken).sensitive("stateProviderName", stateProviderName)); - m_executor.submit([this, stateProviderName, contextRequestToken] { + m_executor.execute([this, stateProviderName, contextRequestToken] { ACSDK_DEBUG5(LX("provideStateInExecutor")); executeProvideState(stateProviderName, contextRequestToken); }); @@ -402,7 +402,7 @@ void ModeControllerCapabilityAgent::onModeChanged(const ModeState& mode, const A return; } - m_executor.submit([this, mode, cause] { + m_executor.execute([this, mode, cause] { m_contextManager->reportStateChange( CapabilityTag(NAMESPACE, MODEVALUE_PROPERTY_NAME, m_endpointId, m_instance), buildCapabilityState(mode), diff --git a/CapabilityAgents/PlaybackController/src/PlaybackCommand.cpp b/CapabilityAgents/PlaybackController/src/PlaybackCommand.cpp index bfb06db7d0..4fed6ecc2d 100644 --- a/CapabilityAgents/PlaybackController/src/PlaybackCommand.cpp +++ b/CapabilityAgents/PlaybackController/src/PlaybackCommand.cpp @@ -28,7 +28,7 @@ using namespace avsCommon::avs; using namespace rapidjson; /// String to identify log entries originating from this file. -static const std::string TAG("PlaybackCommand"); +#define TAG "PlaybackCommand" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/CapabilityAgents/PlaybackController/src/PlaybackController.cpp b/CapabilityAgents/PlaybackController/src/PlaybackController.cpp index 07401dfa16..caacbd1e15 100644 --- a/CapabilityAgents/PlaybackController/src/PlaybackController.cpp +++ b/CapabilityAgents/PlaybackController/src/PlaybackController.cpp @@ -40,7 +40,7 @@ static const std::string PLAYBACKCONTROLLER_CAPABILITY_INTERFACE_NAME = "Playbac static const std::string PLAYBACKCONTROLLER_CAPABILITY_INTERFACE_VERSION = "1.1"; /// String to identify log entries originating from this file. -static const std::string TAG("PlaybackController"); +#define TAG "PlaybackController" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -120,7 +120,7 @@ void PlaybackController::handleCommand(const PlaybackCommand& command) { }; ACSDK_DEBUG9(LX("buttonPressed").d("Button", command)); - m_executor.submit(task); + m_executor.execute(task); } void PlaybackController::onButtonPressed(PlaybackButton button) { @@ -167,11 +167,12 @@ void PlaybackController::onContextAvailable(const std::string& jsonContext) { }; ACSDK_DEBUG9(LX("onContextAvailable")); - m_executor.submit(task); + m_executor.execute(task); } void PlaybackController::onContextFailure(const ContextRequestError error) { auto task = [this, error]() { + (void)error; // mark capture as used when logging is disabled. if (m_commands.empty()) { // The queue shouldn't be empty, log a warning message and return here. ACSDK_WARN(LX("onContextFailureExecutor").m("Queue is empty, return.")); @@ -189,7 +190,7 @@ void PlaybackController::onContextFailure(const ContextRequestError error) { }; ACSDK_DEBUG9(LX("onContextFailure")); - m_executor.submit(task); + m_executor.execute(task); } PlaybackController::PlaybackController( diff --git a/CapabilityAgents/PlaybackController/src/PlaybackRouter.cpp b/CapabilityAgents/PlaybackController/src/PlaybackRouter.cpp index c47f2dcfb4..8f58d5339c 100644 --- a/CapabilityAgents/PlaybackController/src/PlaybackRouter.cpp +++ b/CapabilityAgents/PlaybackController/src/PlaybackRouter.cpp @@ -26,7 +26,7 @@ using namespace avsCommon::sdkInterfaces; using namespace avsCommon::avs; /// String to identify log entries originating from this file. -static const std::string TAG("PlaybackRouter"); +#define TAG "PlaybackRouter" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/CapabilityAgents/PowerController/src/PowerControllerCapabilityAgent.cpp b/CapabilityAgents/PowerController/src/PowerControllerCapabilityAgent.cpp index 21d799a2b8..3e3b85fead 100644 --- a/CapabilityAgents/PowerController/src/PowerControllerCapabilityAgent.cpp +++ b/CapabilityAgents/PowerController/src/PowerControllerCapabilityAgent.cpp @@ -28,7 +28,7 @@ using namespace avsCommon::utils; using namespace avsCommon::utils::configuration; /// String to identify log entries originating from this file. -static const std::string TAG{"PowerControllerCapabilityAgent"}; +#define TAG "PowerControllerCapabilityAgent" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -164,7 +164,7 @@ void PowerControllerCapabilityAgent::handleDirective(std::shared_ptrdirective->getName(); if (!info->directive->getEndpoint().hasValue() || @@ -195,7 +195,7 @@ void PowerControllerCapabilityAgent::provideState( ACSDK_DEBUG5( LX(__func__).d("contextRequestToken", contextRequestToken).sensitive("stateProviderName", stateProviderName)); - m_executor.submit([this, stateProviderName, contextRequestToken] { + m_executor.execute([this, stateProviderName, contextRequestToken] { ACSDK_DEBUG5(LX("provideStateInExecutor")); executeProvideState(stateProviderName, contextRequestToken); }); @@ -254,7 +254,7 @@ void PowerControllerCapabilityAgent::onPowerStateChanged( return; } - m_executor.submit([this, powerState, cause] { + m_executor.execute([this, powerState, cause] { ACSDK_DEBUG5(LX("onPowerStateChangedInExecutor")); m_contextManager->reportStateChange( CapabilityTag(NAMESPACE, POWERSTATE_PROPERTY_NAME, m_endpointId), buildCapabilityState(powerState), cause); diff --git a/CapabilityAgents/RangeController/src/RangeControllerAttributeBuilder.cpp b/CapabilityAgents/RangeController/src/RangeControllerAttributeBuilder.cpp index e68d2ab07b..208bdbb0cc 100644 --- a/CapabilityAgents/RangeController/src/RangeControllerAttributeBuilder.cpp +++ b/CapabilityAgents/RangeController/src/RangeControllerAttributeBuilder.cpp @@ -29,7 +29,7 @@ using namespace avsCommon::sdkInterfaces::rangeController; using namespace avsCommon::utils; /// String to identify log entries originating from this file. -static const std::string TAG{"RangeControllerAttributeBuilder"}; +#define TAG "RangeControllerAttributeBuilder" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/CapabilityAgents/RangeController/src/RangeControllerCapabilityAgent.cpp b/CapabilityAgents/RangeController/src/RangeControllerCapabilityAgent.cpp index d885c0fc63..d21494e33e 100644 --- a/CapabilityAgents/RangeController/src/RangeControllerCapabilityAgent.cpp +++ b/CapabilityAgents/RangeController/src/RangeControllerCapabilityAgent.cpp @@ -41,7 +41,7 @@ using namespace avsCommon::utils::json; using namespace rapidjson; /// String to identify log entries originating from this file. -static const std::string TAG{"RangeControllerCapabilityAgent"}; +#define TAG "RangeControllerCapabilityAgent" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -271,7 +271,7 @@ void RangeControllerCapabilityAgent::handleDirective(std::shared_ptrdirective->getName(); if (!info->directive->getEndpoint().hasValue() || @@ -313,7 +313,7 @@ void RangeControllerCapabilityAgent::provideState( ACSDK_DEBUG5( LX(__func__).d("contextRequestToken", contextRequestToken).sensitive("stateProviderName", stateProviderName)); - m_executor.submit([this, stateProviderName, contextRequestToken] { + m_executor.execute([this, stateProviderName, contextRequestToken] { ACSDK_DEBUG5(LX("provideStateInExecutor")); executeProvideState(stateProviderName, contextRequestToken); }); @@ -417,7 +417,7 @@ void RangeControllerCapabilityAgent::onRangeChanged( return; } - m_executor.submit([this, rangeState, cause] { + m_executor.execute([this, rangeState, cause] { m_contextManager->reportStateChange( CapabilityTag(NAMESPACE, RANGEVALUE_PROPERTY_NAME, m_endpointId, m_instance), buildCapabilityState(rangeState), diff --git a/CapabilityAgents/SoftwareComponentReporter/src/SoftwareComponentReporterCapabilityAgent.cpp b/CapabilityAgents/SoftwareComponentReporter/src/SoftwareComponentReporterCapabilityAgent.cpp index bfa4c8e65c..1784ee49d1 100644 --- a/CapabilityAgents/SoftwareComponentReporter/src/SoftwareComponentReporterCapabilityAgent.cpp +++ b/CapabilityAgents/SoftwareComponentReporter/src/SoftwareComponentReporterCapabilityAgent.cpp @@ -31,7 +31,7 @@ using namespace avsCommon::utils::configuration; using namespace avsCommon::utils::json; /// String to identify log entries originating from this file. -static const std::string TAG{"SoftwareComponentReporterCapabilityAgent"}; +#define TAG "SoftwareComponentReporterCapabilityAgent" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/CapabilityAgents/SpeakerManager/CMakeLists.txt b/CapabilityAgents/SpeakerManager/CMakeLists.txt index d5a55a27b2..7cfaf2ea82 100644 --- a/CapabilityAgents/SpeakerManager/CMakeLists.txt +++ b/CapabilityAgents/SpeakerManager/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.0) -project(SpeakerManager LANGUAGES CXX) +project(acsdkSpeakerManager LANGUAGES CXX) -include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) - -add_subdirectory("src") -add_subdirectory("test") +add_subdirectory("SpeakerManager") +add_subdirectory("SpeakerManagerComponent") diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/CMakeLists.txt b/CapabilityAgents/SpeakerManager/SpeakerManager/CMakeLists.txt new file mode 100644 index 0000000000..9b1c463097 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.0) +project(acsdkSpeakerManager LANGUAGES CXX) + +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) + +add_subdirectory("src") +add_subdirectory("test") diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/doc/Namespaces.dox b/CapabilityAgents/SpeakerManager/SpeakerManager/doc/Namespaces.dox new file mode 100644 index 0000000000..f3667514fc --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/doc/Namespaces.dox @@ -0,0 +1,23 @@ +/* + * 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. + */ + +/** + * @namespace alexaClientSDK::speakerManager + * @brief Speaker API Capability Agent Interface and Implementation. + * + * This namespace contains public API and implementation for Speaker API capability agent. + * @see Lib_acsdkSpeakerManager + * @ingroup Lib_acsdkSpeakerManager + */ diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/doc/SpeakerManager.dox b/CapabilityAgents/SpeakerManager/SpeakerManager/doc/SpeakerManager.dox new file mode 100644 index 0000000000..b9c08883b8 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/doc/SpeakerManager.dox @@ -0,0 +1,27 @@ +/* + * 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. + */ + +/** + * @defgroup Lib_acsdkSpeakerManager Capability Agent for AVS Speaker 1.0 Interface + * @brief Capability Agent API and implementation for + * AVS Speaker 1.0 + * interface. + * + * @see https://developer.amazon.com/en-US/docs/alexa/alexa-voice-service/speaker.html Speaker 1.0 Interface + * + * @see alexaClientSDK::speakerManager + * @see alexaClientSDK::speakerManager::test + * @see Lib_acsdkSpeakerManagerComponent + */ diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/include/acsdk/SpeakerManager/Factories.h b/CapabilityAgents/SpeakerManager/SpeakerManager/include/acsdk/SpeakerManager/Factories.h new file mode 100644 index 0000000000..468f0814ac --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/include/acsdk/SpeakerManager/Factories.h @@ -0,0 +1,143 @@ +/* + * 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. + */ + +#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_INCLUDE_ACSDK_SPEAKERMANAGER_FACTORIES_H_ +#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_INCLUDE_ACSDK_SPEAKERMANAGER_FACTORIES_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace alexaClientSDK { +namespace speakerManager { + +/** + * @brief Creates speaker manager CA. + * + * Method creates new speaker manager capability agent, adds supplied volume interfaces, and registers instance in + * capabilities registry and in shutdown manager. + * + * Additional channel volume interfaces can be added after construction using public SpeakerManagerInterface methods. + * + * Speaker manager groups all channels by type, and applies volume settings and configurations uniformly to all channels + * of the same type. + * + * Speaker manager uses SpeakerManagerConfigInterface to load initial (bootstrap) platform configuration, and + * SpeakerManagerStorageInterface to store and load persistent settings. Those interfaces + * + * @param[in] config Interface to load platform configuration. + * @param[in] storage Interface to load and store persistent configuration. + * @param[in] contextManager A @c ContextManagerInterface to manage the context. + * @param[in] messageSender A @c MessageSenderInterface to send messages to AVS. + * @param[in] exceptionEncounteredSender An @c ExceptionEncounteredSenderInterface to send directive processing + * exceptions to AVS. + * @param[in] metricRecorder The metric recorder. + * @param[in] shutdownNotifier Factory uses this interface to register for shutdown notifications. + * @param[in] endpointCapabilitiesRegistrar Factory uses this interface to register capability. + * @param[in] volumeInterfaces Optional vector of @c ChannelVolumeInterfaces to register. Additional + * interfaces can be added with @c + * SpeakerManagerInterface::addChannelVolumeInterface() calls. + * + * @see createSpeakerManagerStorage() + * @see createSpeakerManagerConfig() + * @see createChannelVolumeFactory() + * + * @ingroup Lib_acsdkSpeakerManager + */ +std::shared_ptr createSpeakerManagerCapabilityAgent( + std::shared_ptr config, + std::shared_ptr storage, + std::shared_ptr contextManager, + std::shared_ptr messageSender, + std::shared_ptr exceptionEncounteredSender, + std::shared_ptr metricRecorder, + const std::shared_ptr& shutdownNotifier, + const std::shared_ptr& + endpointCapabilitiesRegistrar, + const std::vector>& volumeInterfaces = + {}) noexcept; + +/** + * @brief Create default implementation of @c ChannelVolumeFactoryInterface. + * + * @return Channel volume factory interface or nullptr on error. + */ +std::shared_ptr createChannelVolumeFactory() noexcept; + +/** + * @brief Adapt generic @c MiscStorageInterface into @c SpeakerManagerStorageInterface. + * + * Method returns an adapter of @c SpeakerManagerStorageInterface to @c SpeakerManagerStorageInterface. + * + * @param[in] storage Reference of @c MiscStorageInterface. This parameter must not be nullptr. + * + * @return Storage interface or nullptr on error. + * + * @ingroup Lib_acsdkSpeakerManager + */ +std::shared_ptr createSpeakerManagerStorage( + std::shared_ptr storage) noexcept; + +/** + * @brief Creates configuration interface for speaker manager. + * + * The method returns an interface that accesses configuration using @c ConfigurationNode facility. The returned object + * uses "speakerManagerCapabilityAgent" child and looks up the following keys: + * - "persistentStorage" -- Boolean flag that indicates if persistent storage is enabled. + * - "minUnmuteVolume" -- Minimum volume level for unmuting the channel. This setting applies to all channel types. + * - "defaultSpeakerVolume" -- Default speaker volume. + * - "defaultAlertsVolume" -- Default alerts volume. + * - "restoreMuteState" -- Boolean flag that indicates if mute state shall be preserved between device reboots. + * + * If AlexaClientSDKConfig.json configuration file is used, the example configuration may look like: + * @code{.json} + * { + * "speakerManagerCapabilityAgent": { + * "persistentStorage": true, + * "minUnmuteVolume": 10, + * "defaultSpeakerVolume": 40, + * "defaultAlertsVolume": 40, + * "restoreMuteState": true + * } + * } + * @endcode + * + * @return Interface to load initial configuration or nullptr on error. + * + * @ingroup Lib_acsdkSpeakerManager + */ +std::shared_ptr createSpeakerManagerConfig() noexcept; + +} // namespace speakerManager +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_INCLUDE_ACSDK_SPEAKERMANAGER_FACTORIES_H_ diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/include/acsdk/SpeakerManager/SpeakerManagerConfigInterface.h b/CapabilityAgents/SpeakerManager/SpeakerManager/include/acsdk/SpeakerManager/SpeakerManagerConfigInterface.h new file mode 100644 index 0000000000..bc59f3688a --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/include/acsdk/SpeakerManager/SpeakerManagerConfigInterface.h @@ -0,0 +1,85 @@ +/* + * 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. + */ + +#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_INCLUDE_ACSDK_SPEAKERMANAGER_SPEAKERMANAGERCONFIGINTERFACE_H_ +#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_INCLUDE_ACSDK_SPEAKERMANAGER_SPEAKERMANAGERCONFIGINTERFACE_H_ + +#include + +namespace alexaClientSDK { +namespace speakerManager { + +struct SpeakerManagerStorageState; + +/** + * @brief Speaker manager configuration interface. + * + * This interface provides initial configuration for speaker manager capability agent. + * + * @see createSpeakerManagerConfig() + * + * @ingroup Lib_acsdkSpeakerManager + */ +struct SpeakerManagerConfigInterface { + /** + * @brief Virtual destructor to assure proper cleanup of derived types. + */ + virtual ~SpeakerManagerConfigInterface() noexcept = default; + + /** + * @brief Load persistent storage setting from platform configuration. + * + * @param[out] persistentStorage Flag if persistent storage is enabled for speaker settings. + * @return True if value is loaded, false is value is not present, is out of range, or operation failed. + */ + virtual bool getPersistentStorage(bool& persistentStorage) noexcept = 0; + + /** + * @brief Load minimum unmute volume from platform configuration. + * + * @param[out] minUnmuteVolume Minimum volume for unmuting speakers. The value must be in range 0..100 inclusive. + * @return True if value is loaded, false if value is not present, is out of range, or operation failed. + */ + virtual bool getMinUnmuteVolume(std::uint8_t& minUnmuteVolume) noexcept = 0; + + /** + * @brief Load minimum unmute volume from platform configuration. + * + * @param[out] restoreMuteState Flag if the speaker mute state must be preserved between sessions. + * @return True if value is loaded, false if value is not present, is out of range, or operation failed. + */ + virtual bool getRestoreMuteState(bool& restoreMuteState) noexcept = 0; + + /** + * @brief Load minimum unmute volume from platform configuration. + * + * @param[out] defaultSpeakerVolume Default volume for speaker channel. The value must be in range 0..100 inclusive. + * @return True if value is loaded, false if value is not present, is out of range, or operation failed. + */ + virtual bool getDefaultSpeakerVolume(std::uint8_t& defaultSpeakerVolume) noexcept = 0; + + /** + * @brief Load minimum unmute volume from platform configuration. + * + * @param[out] defaultAlertsVolume Default volume for alerts channel. The value must be in range 0..100 inclusive. + * @return True if value is loaded, false if value is not present, is out of range, or operation failed. + */ + virtual bool getDefaultAlertsVolume(std::uint8_t& defaultAlertsVolume) noexcept = 0; +}; + +} // namespace speakerManager +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_INCLUDE_ACSDK_SPEAKERMANAGER_SPEAKERMANAGERCONFIGINTERFACE_H_ diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/include/acsdk/SpeakerManager/SpeakerManagerStorageInterface.h b/CapabilityAgents/SpeakerManager/SpeakerManager/include/acsdk/SpeakerManager/SpeakerManagerStorageInterface.h new file mode 100644 index 0000000000..af34a01834 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/include/acsdk/SpeakerManager/SpeakerManagerStorageInterface.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_INCLUDE_ACSDK_SPEAKERMANAGER_SPEAKERMANAGERSTORAGEINTERFACE_H_ +#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_INCLUDE_ACSDK_SPEAKERMANAGER_SPEAKERMANAGERSTORAGEINTERFACE_H_ + +namespace alexaClientSDK { +namespace speakerManager { + +struct SpeakerManagerStorageState; + +/** + * @brief Speaker manager storage interface. + * + * @see SpeakerManagerStorageState + * + * @ingroup Lib_acsdkSpeakerManager + */ +struct SpeakerManagerStorageInterface { + /** + * Virtual destructor to assure proper cleanup of derived types. + */ + virtual ~SpeakerManagerStorageInterface() noexcept = default; + + /** + * Loads state from underlying storage. + * @param[out] state Pointer to state structure for loaded values. + * @return Boolean flag if the operation is successful. + */ + virtual bool loadState(SpeakerManagerStorageState& state) noexcept = 0; + + /** + * Stores state to underlying storage. + * @param[in] state Reference of state structure for values to store. + * @return Boolean flag if the operation is successful. + */ + virtual bool saveState(const SpeakerManagerStorageState& state) noexcept = 0; +}; + +} // namespace speakerManager +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_INCLUDE_ACSDK_SPEAKERMANAGER_SPEAKERMANAGERSTORAGEINTERFACE_H_ diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/include/acsdk/SpeakerManager/SpeakerManagerStorageState.h b/CapabilityAgents/SpeakerManager/SpeakerManager/include/acsdk/SpeakerManager/SpeakerManagerStorageState.h new file mode 100644 index 0000000000..dc010b9679 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/include/acsdk/SpeakerManager/SpeakerManagerStorageState.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_INCLUDE_ACSDK_SPEAKERMANAGER_SPEAKERMANAGERSTORAGESTATE_H_ +#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_INCLUDE_ACSDK_SPEAKERMANAGER_SPEAKERMANAGERSTORAGESTATE_H_ + +#include + +namespace alexaClientSDK { +namespace speakerManager { + +/** + * @brief Storage state for SpeakerManager. + * + * SpeakerManager configuration includes configuration for two channel types: speaker and alerts. There can be any + * number of channels for each of types, but all of them share the same configuration. + * + * @see SpeakerManagerStorageInterface + * + * @ingroup Lib_acsdkSpeakerManager + */ +struct SpeakerManagerStorageState { + /** + * SpeakerManager channel type configuration state. + */ + struct ChannelState { + /// Channel volume. + uint8_t channelVolume; + /// Channel mute status. + bool channelMuteStatus; + }; + /// Configuration for speaker channels. + ChannelState speakerChannelState; + /// Configuration for alerts channels. + ChannelState alertsChannelState; +}; + +} // namespace speakerManager +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_INCLUDE_ACSDK_SPEAKERMANAGER_SPEAKERMANAGERSTORAGESTATE_H_ diff --git a/CapabilityAgents/SpeakerManager/include/SpeakerManager/ChannelVolumeManager.h b/CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/ChannelVolumeManager.h similarity index 87% rename from CapabilityAgents/SpeakerManager/include/SpeakerManager/ChannelVolumeManager.h rename to CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/ChannelVolumeManager.h index 9f670118b1..369c99e54f 100644 --- a/CapabilityAgents/SpeakerManager/include/SpeakerManager/ChannelVolumeManager.h +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/ChannelVolumeManager.h @@ -13,19 +13,23 @@ * permissions and limitations under the License. */ -#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_CHANNELVOLUMEMANAGER_H_ -#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_CHANNELVOLUMEMANAGER_H_ +#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_CHANNELVOLUMEMANAGER_H_ +#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_CHANNELVOLUMEMANAGER_H_ -#include #include +#include + namespace alexaClientSDK { -namespace capabilityAgents { namespace speakerManager { /** - * ChannelVolumeManager provides a concrete implementation of the ChannelVolumeInterface + * @brief Default implementation for @c ChannelVolumeInterface. + * + * ChannelVolumeManager provides a concrete implementation of the @c ChannelVolumeInterface. * It controls an underlying @c SpeakerInterface object and provides functionality to be able * to set SpeakerSettings and control Channel Volume Attenuation for this underlying SpeakerInterface. + * + * @ingroup Lib_acsdkSpeakerManager */ class ChannelVolumeManager : public avsCommon::sdkInterfaces::ChannelVolumeInterface { public: @@ -41,7 +45,7 @@ class ChannelVolumeManager : public avsCommon::sdkInterfaces::ChannelVolumeInter std::shared_ptr speaker, avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type = avsCommon::sdkInterfaces::ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, - std::function volumeCurve = nullptr); + std::function volumeCurve = nullptr) noexcept; /// ChannelVolumeInterface Functions. /// @{ @@ -73,7 +77,7 @@ class ChannelVolumeManager : public avsCommon::sdkInterfaces::ChannelVolumeInter ChannelVolumeManager( std::shared_ptr speaker, avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, - VolumeCurveFunction volumeCurve); + VolumeCurveFunction volumeCurve) noexcept; /** * Default Volume Curve Implementation that determines the desired attenuated @@ -94,7 +98,7 @@ class ChannelVolumeManager : public avsCommon::sdkInterfaces::ChannelVolumeInter * @param unduckedVolume current volume of the channel * @return int8_t volume to be attenuated to as per the volume curve */ - static int8_t defaultVolumeAttenuateFunction(int8_t unduckedVolume); + static int8_t defaultVolumeAttenuateFunction(int8_t unduckedVolume) noexcept; /// Mutex to synchronize the operations. mutable std::mutex m_mutex; @@ -115,7 +119,6 @@ class ChannelVolumeManager : public avsCommon::sdkInterfaces::ChannelVolumeInter const avsCommon::sdkInterfaces::ChannelVolumeInterface::Type m_type; }; } // namespace speakerManager -} // namespace capabilityAgents } // namespace alexaClientSDK -#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_CHANNELVOLUMEMANAGER_H_ +#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_CHANNELVOLUMEMANAGER_H_ diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/DefaultChannelVolumeFactory.h b/CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/DefaultChannelVolumeFactory.h new file mode 100644 index 0000000000..28a993c115 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/DefaultChannelVolumeFactory.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_DEFAULTCHANNELVOLUMEFACTORY_H_ +#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_DEFAULTCHANNELVOLUMEFACTORY_H_ + +#include +#include + +namespace alexaClientSDK { +namespace speakerManager { + +/** + * @brief Default channel volume factory implementation. + * + * The @c DefaultChannelVolumeFactory provides a default implementation of @c ChannelVolumeFactoryInterface + * using the @c ChannelVolumeManager. + * + * @ingroup Lib_acsdkSpeakerManager + */ +class DefaultChannelVolumeFactory : public alexaClientSDK::avsCommon::sdkInterfaces::ChannelVolumeFactoryInterface { +public: + /// ChannelVolumeFactoryInterface Functions. + /// @{ + virtual std::shared_ptr + createChannelVolumeInterface( + std::shared_ptr speaker, + alexaClientSDK::avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, + std::function volumeCurve) override; + /// @} +}; + +} // namespace speakerManager +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_DEFAULTCHANNELVOLUMEFACTORY_H_ diff --git a/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManager.h b/CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/SpeakerManager.h similarity index 86% rename from CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManager.h rename to CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/SpeakerManager.h index 229b724c5a..cfac317a7a 100644 --- a/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManager.h +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/SpeakerManager.h @@ -13,8 +13,8 @@ * permissions and limitations under the License. */ -#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGER_H_ -#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGER_H_ +#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_SPEAKERMANAGER_H_ +#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_SPEAKERMANAGER_H_ #include #include @@ -22,8 +22,11 @@ #include #include -#include #include +#include +#include +#include +#include #include #include #include @@ -41,15 +44,13 @@ #include #include #include -#include -#include -#include namespace alexaClientSDK { -namespace capabilityAgents { namespace speakerManager { /** + * @brief Capability Agent for Speaker API. + * * This class implements a @c CapabilityAgent that handles the AVS @c Speaker API. * * The @c SpeakerManager can handle multiple @c ChannelVolumeInterface objects and dedupe them with @@ -67,6 +68,8 @@ namespace speakerManager { * @endcode * * Clients may extend the @c ChannelVolumeInterface::Type enum if multiple independent volume controls are needed. + * + * @ingroup Lib_acsdkSpeakerManager */ class SpeakerManager : public avsCommon::avs::CapabilityAgent @@ -90,16 +93,15 @@ class SpeakerManager * @param metricRecorder The metric recorder. */ static std::shared_ptr createSpeakerManagerCapabilityAgent( - const std::shared_ptr& storage, - const std::shared_ptr& contextManager, - const std::shared_ptr& messageSender, - const std::shared_ptr& - exceptionEncounteredSender, + std::shared_ptr config, + std::shared_ptr storage, + std::shared_ptr contextManager, + std::shared_ptr messageSender, + std::shared_ptr exceptionEncounteredSender, const std::shared_ptr& shutdownNotifier, - const acsdkManufactory::Annotated< - avsCommon::sdkInterfaces::endpoints::DefaultEndpointAnnotation, - avsCommon::sdkInterfaces::endpoints::EndpointCapabilitiesRegistrarInterface>& endpointCapabilitiesRegistrar, - const std::shared_ptr& metricRecorder); + const std::shared_ptr& + endpointCapabilitiesRegistrar, + std::shared_ptr metricRecorder) noexcept; /** * Create an instance of @c SpeakerManager, and register the @c ChannelVolumeInterfaces that will be controlled @@ -114,12 +116,13 @@ class SpeakerManager * @param metricRecorder The metric recorder. */ static std::shared_ptr create( - const std::shared_ptr& storage, + std::shared_ptr config, + std::shared_ptr storage, const std::vector>& volumeInterfaces, std::shared_ptr contextManager, std::shared_ptr messageSender, std::shared_ptr exceptionEncounteredSender, - std::shared_ptr metricRecorder = nullptr); + std::shared_ptr metricRecorder = nullptr) noexcept; /// @name CapabilityAgent Functions /// @{ @@ -130,12 +133,12 @@ class SpeakerManager void cancelDirective(std::shared_ptr info) override; /// @} - // @name RequiresShutdown Functions + /// @name RequiresShutdown Functions /// @{ void doShutdown() override; /// @} - // @name SpeakerManagerInterface Functions + /// @name SpeakerManagerInterface Functions /// @{ std::future setVolume( avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, @@ -174,6 +177,10 @@ class SpeakerManager std::future getSpeakerSettings( avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings* settings) override; + void onExternalSpeakerSettingsUpdate( + avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, + const avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings& speakerSettings, + const NotificationProperties& properties) override; void addSpeakerManagerObserver( std::shared_ptr observer) override; void removeSpeakerManagerObserver( @@ -200,17 +207,18 @@ class SpeakerManager * @param metricRecorder The metric recorder. */ SpeakerManager( - const std::shared_ptr& speakerManagerStorage, + std::shared_ptr speakerManagerConfig, + std::shared_ptr speakerManagerStorage, const std::vector>& groupVolumeInterfaces, std::shared_ptr contextManager, std::shared_ptr messageSender, std::shared_ptr exceptionEncounteredSender, - std::shared_ptr metricRecorder); + std::shared_ptr metricRecorder) noexcept; /// Hash functor to use identifier of @c ChannelVolumeInterface as the key in SpeakerSet. struct ChannelVolumeInterfaceHash { public: - size_t operator()(const std::shared_ptr& key) const { + size_t operator()(const std::shared_ptr& key) const noexcept { if (nullptr == key) { /// This should never happen because the only way to add a ChannelVolumeInterface into the SpeakerSet /// has a guard of nullptr. @@ -225,7 +233,7 @@ class SpeakerManager public: bool operator()( const std::shared_ptr channelVolumeInterface1, - std::shared_ptr channelVolumeInterface2) const { + std::shared_ptr channelVolumeInterface2) const noexcept { if (!channelVolumeInterface1 || !channelVolumeInterface2) { /// This should never happen because the only way to add a ChannelVolumeInterface into the SpeakerSet /// has a guard of nullptr. @@ -249,7 +257,7 @@ class SpeakerManager * @param channelVolumeInterface The @c ChannelVolumeInterface object. */ void addChannelVolumeInterfaceIntoSpeakerMap( - std::shared_ptr channelVolumeInterface); + std::shared_ptr channelVolumeInterface) noexcept; /** * Parses the payload from a string into a rapidjson document. @@ -258,21 +266,21 @@ class SpeakerManager * @param document The document that will contain the payload. * @return A bool indicating the results of the operation. */ - bool parseDirectivePayload(std::string payload, rapidjson::Document* document); + bool parseDirectivePayload(std::string payload, rapidjson::Document* document) noexcept; /** * Performs clean-up after a successful handling of a directive. * * @param info The current directive being processed. */ - void executeSetHandlingCompleted(std::shared_ptr info); + void executeSetHandlingCompleted(std::shared_ptr info) noexcept; /** * Removes the directive after it's been processed. * * @param info The current directive being processed. */ - void removeDirective(std::shared_ptr info); + void removeDirective(std::shared_ptr info) noexcept; /** * Sends an exception to AVS. @@ -284,7 +292,7 @@ class SpeakerManager void sendExceptionEncountered( std::shared_ptr info, const std::string& message, - avsCommon::avs::ExceptionErrorType type); + avsCommon::avs::ExceptionErrorType type) noexcept; /** * Function to update the state of the ContextManager. @@ -295,7 +303,7 @@ class SpeakerManager */ bool updateContextManager( const avsCommon::sdkInterfaces::ChannelVolumeInterface::Type& type, - const avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings& settings); + const avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings& settings) noexcept; /** * Sends Changed events to AVS. The events are identical except for the name. @@ -305,7 +313,7 @@ class SpeakerManager */ void executeSendSpeakerSettingsChangedEvent( const std::string& eventName, - avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings settings); + avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings settings) noexcept; /** * Function to set the volume for a specific @c Type. This runs on a worker thread. @@ -319,7 +327,7 @@ class SpeakerManager bool executeSetVolume( avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, int8_t volume, - const avsCommon::sdkInterfaces::SpeakerManagerInterface::NotificationProperties& properties); + const avsCommon::sdkInterfaces::SpeakerManagerInterface::NotificationProperties& properties) noexcept; /** * Function to restore the volume from a mute state. This runs on a worker thread and will not send an event or @@ -331,7 +339,7 @@ class SpeakerManager */ bool executeRestoreVolume( avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, - avsCommon::sdkInterfaces::SpeakerManagerObserverInterface::Source source); + avsCommon::sdkInterfaces::SpeakerManagerObserverInterface::Source source) noexcept; /** * Function to adjust the volume for a specific @c Type. This runs on a worker thread. @@ -345,7 +353,7 @@ class SpeakerManager bool executeAdjustVolume( avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, int8_t delta, - const avsCommon::sdkInterfaces::SpeakerManagerInterface::NotificationProperties& properties); + const avsCommon::sdkInterfaces::SpeakerManagerInterface::NotificationProperties& properties) noexcept; /** * Function to set the mute for a specific @c Type. This runs on a worker thread. @@ -359,7 +367,7 @@ class SpeakerManager bool executeSetMute( avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, bool mute, - const avsCommon::sdkInterfaces::SpeakerManagerInterface::NotificationProperties& properties); + const avsCommon::sdkInterfaces::SpeakerManagerInterface::NotificationProperties& properties) noexcept; #ifdef ENABLE_MAXVOLUME_SETTING /** @@ -368,7 +376,7 @@ class SpeakerManager * @param type The type of speaker to set a limit on the maximum volume. * @return A bool indicating success. */ - bool executeSetMaximumVolumeLimit(const int8_t maximumVolumeLimit); + bool executeSetMaximumVolumeLimit(const int8_t maximumVolumeLimit) noexcept; #endif // ENABLE_MAXVOLUME_SETTING /** @@ -381,7 +389,7 @@ class SpeakerManager */ bool executeGetSpeakerSettings( avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, - avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings* settings); + avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings* settings) noexcept; /** * Function to set the speaker settings for a specific @c ChannelVolumeInterface Type. @@ -393,7 +401,7 @@ class SpeakerManager */ bool executeSetSpeakerSettings( const avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, - const avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings& settings); + const avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings& settings) noexcept; /** * Function that initializes and populates @c m_speakerSettings for the given @c type's speaker settings. @@ -401,7 +409,7 @@ class SpeakerManager * @param type The type of speaker to initialize * @return A bool indicating success. */ - bool executeInitializeSpeakerSettings(avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type); + bool executeInitializeSpeakerSettings(avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type) noexcept; /** * Function to send events when settings have changed. @@ -416,7 +424,7 @@ class SpeakerManager const avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings& settings, const std::string& eventName, const avsCommon::sdkInterfaces::SpeakerManagerObserverInterface::Source& source, - const avsCommon::sdkInterfaces::ChannelVolumeInterface::Type& type); + const avsCommon::sdkInterfaces::ChannelVolumeInterface::Type& type) noexcept; /** * Function to notify the observer when a @c SpeakerSettings change has occurred. @@ -428,12 +436,12 @@ class SpeakerManager void executeNotifyObserver( const avsCommon::sdkInterfaces::SpeakerManagerObserverInterface::Source& source, const avsCommon::sdkInterfaces::ChannelVolumeInterface::Type& type, - const avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings& settings); + const avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings& settings) noexcept; /** * Persist channel configuration. */ - void executePersistConfiguration(); + void executePersistConfiguration() noexcept; /** * Helper method to convert internally stored channel state into config format. @@ -443,14 +451,14 @@ class SpeakerManager */ void convertSettingsToChannelState( avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, - SpeakerManagerStorageState::ChannelState* storageState); + SpeakerManagerStorageState::ChannelState* storageState) noexcept; /** * Get the maximum volume limit. * * @return The maximum volume limit. */ - int8_t getMaximumVolumeLimit(); + int8_t getMaximumVolumeLimit() noexcept; /** * Applies Settings to All Speakers @@ -461,8 +469,7 @@ class SpeakerManager * @param args The arguments to call the task with. * @return A bool indicating success. */ - template - bool retryAndApplySettings(Task task, Args&&... args); + bool retryAndApplySettings(const std::function& task) noexcept; /** * Configure channel volume and mute status defaults. @@ -473,23 +480,23 @@ class SpeakerManager */ void presetChannelDefaults( avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, - const SpeakerManagerStorageState::ChannelState& state); + const SpeakerManagerStorageState::ChannelState& state) noexcept; /** * Configures channels with default values. */ - void loadConfiguration(); + void loadConfiguration() noexcept; /** * Updates volume and mute status on managed channels according to configured settings. */ - void updateChannelSettings(); + void updateChannelSettings() noexcept; /** * Updates managed channels according to configured settings. * @param type Channel type. */ - void updateChannelSettings(avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type); + void updateChannelSettings(avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type) noexcept; /** * Helper function to adjust input volume into acceptable range. @@ -497,7 +504,7 @@ class SpeakerManager * @param volume Input volume. * @return Volume within correct limits. */ - int8_t adjustVolumeRange(int64_t volume); + int8_t adjustVolumeRange(int64_t volume) noexcept; /// Component's configuration access. SpeakerManagerConfigHelper m_config; @@ -512,7 +519,7 @@ class SpeakerManager std::shared_ptr m_messageSender; /// the @c volume to restore to when unmuting at 0 volume - int m_minUnmuteVolume; + uint8_t m_minUnmuteVolume; /// An unordered_map contains ChannelVolumeInterfaces keyed by @c Type. Only internal function /// addChannelVolumeInterfaceIntoSpeakerMap can insert an element into this map to ensure that no invalid element @@ -541,6 +548,9 @@ class SpeakerManager /// maximumVolumeLimit The maximum volume level speakers in this system can reach. int8_t m_maximumVolumeLimit; + /// Persistent Storage flag set from configuration + bool m_persistentStorage; + /// Restore mute state flag from configuration bool m_restoreMuteState; @@ -555,7 +565,6 @@ class SpeakerManager }; } // namespace speakerManager -} // namespace capabilityAgents } // namespace alexaClientSDK -#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGER_H_ +#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_SPEAKERMANAGER_H_ diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/SpeakerManagerConfig.h b/CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/SpeakerManagerConfig.h new file mode 100644 index 0000000000..d94eac7f6e --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/SpeakerManagerConfig.h @@ -0,0 +1,92 @@ +/* + * 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. + */ + +#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_SPEAKERMANAGERCONFIG_H_ +#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_SPEAKERMANAGERCONFIG_H_ + +#include +#include +#include + +namespace alexaClientSDK { +namespace speakerManager { + +/** + * @brief Configuration interface for SpeakerManager. + * + * This class accesses configuration using @c ConfigurationNode facility. Internally class uses + * "speakerManagerCapabilityAgent" child and looks up the following keys: + * - "persistentStorage" -- Boolean flag that indicates if persistent storage is enabled. + * - "minUnmuteVolume" -- Minimum volume level for unmuting the channel. This setting applies to all channel types. + * - "defaultSpeakerVolume" -- Default speaker volume. + * - "defaultAlertsVolume" -- Default alerts volume. + * - "restoreMuteState" -- Boolean flag that indicates if mute state shall be preserved between device reboots. + * + * If AlexaClientSDKConfig.json configuration file is used, the example configuration may look like: + * @code{.json} + * { + * "speakerManagerCapabilityAgent": { + * "persistentStorage": true, + * "minUnmuteVolume": 10, + * "defaultSpeakerVolume": 40, + * "defaultAlertsVolume": 40, + * "restoreMuteState": true + * } + * } + * @endcode + * + * @ingroup Lib_acsdkSpeakerManager + */ +class SpeakerManagerConfig : public SpeakerManagerConfigInterface { +public: + /** + * @brief Construct object and load configuration. + */ + SpeakerManagerConfig() noexcept; + + /// @name SpeakerManagerConfigInterface Functions + /// @{ + bool getPersistentStorage(bool& persistentStorage) noexcept override; + bool getMinUnmuteVolume(std::uint8_t& minUnmuteVolume) noexcept override; + bool getRestoreMuteState(bool& restoreMuteState) noexcept override; + bool getDefaultSpeakerVolume(std::uint8_t& defaultSpeakerVolume) noexcept override; + bool getDefaultAlertsVolume(std::uint8_t& defaultAlertsVolume) noexcept override; + /// @} +private: + /** + * @brief Load and validate values from platform configuration. + */ + void loadPlatformConfig() noexcept; + + /// Flag if persistent storage is enabled for speaker settings. + avsCommon::utils::Optional m_persistentStorage; + + /// Minimum volume for unmuting speakers. The value must be in range 0..100 inclusive. + avsCommon::utils::Optional m_minUnmuteVolume; + + /// Flag if the speaker mute state must be preserved between sessions. + avsCommon::utils::Optional m_restoreMuteState; + + /// Default volume for speaker channel. The value must be in range 0..100 inclusive. + avsCommon::utils::Optional m_defaultSpeakerVolume; + + /// Default volume for alerts channel. The value must be in range 0..100 inclusive. + avsCommon::utils::Optional m_defaultAlertsVolume; +}; + +} // namespace speakerManager +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_SPEAKERMANAGERCONFIG_H_ diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/SpeakerManagerConfigHelper.h b/CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/SpeakerManagerConfigHelper.h new file mode 100644 index 0000000000..f03167e7f1 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/SpeakerManagerConfigHelper.h @@ -0,0 +1,105 @@ +/* + * 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. + */ + +#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_SPEAKERMANAGERCONFIGHELPER_H_ +#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_SPEAKERMANAGERCONFIGHELPER_H_ + +#include +#include + +namespace alexaClientSDK { +namespace speakerManager { + +/** + * @brief Helper class to manage configuration operations for SpeakerManager CA. + * + * This class implements all configuration operations and merges logic of accessing different configuration sources. + * SpeakerManager get configuration values from three sources: hardcoded values, platform configuration, and persistent + * storage. + * + * @ingroup Lib_acsdkSpeakerManager + */ +class SpeakerManagerConfigHelper { +public: + /** + * Creates object. + * @param[in] storage Storage interface. + */ + SpeakerManagerConfigHelper( + std::shared_ptr config, + std::shared_ptr storage) noexcept; + + /** + * Load configuration. + * + * This method always succeeds (assuming @a state is not @a nullptr), as it first tries to load configuration + * from config storage, then from platform configuration files, and falls back to hardcoded values. + * + * @param[out] state Pointer to configuration container to fill with config values. + */ + void loadState(SpeakerManagerStorageState& state) noexcept; + + /** + * Saves configuration to to config storage. + * + * @param[in] state Configuration data to persist. + * + * @return Boolean that indicates operation success. + */ + bool saveState(const SpeakerManagerStorageState& state) noexcept; + + /** + * Loads if persistent storage is enabled from configuration. By default persistent storage is not enabled, but it + * can be overridden by configuration. + * + * @return Returns configured value, where true indicates that the persistent storage flag has been retrieved, and + * false indicates that a default shall be kept. + */ + bool getPersistentStorage() const noexcept; + + /** + * Loads minimum unmute volume level from platform configuration. The method tries to load the unmute value from + * platform configuration files, and if it fails, it returns a hardcoded value. + * + * @return Minimum volume level to unmute speakers. + */ + int getMinUnmuteVolume() const noexcept; + + /** + * Loads mute state handling from configuration. By default the speaker manager sets the mute status to the value + * prior to reboot, but this behaviour can be overridden by configuration. + * + * @return Returns configured value, where true indicates that mute status is configured according to the last + * saved state, and "false" indicates a default shall be kept. + */ + bool getRestoreMuteState() const noexcept; + +private: + /** + * Default values that are used when no other configuration sources are available. + */ + static const SpeakerManagerStorageState c_defaults; + + /// Reference to configuration config interface. + const std::shared_ptr m_config; + + /// Reference to configuration storage interface. + const std::shared_ptr m_storage; +}; + +} // namespace speakerManager +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_SPEAKERMANAGERCONFIGHELPER_H_ diff --git a/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManagerConstants.h b/CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/SpeakerManagerConstants.h similarity index 76% rename from CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManagerConstants.h rename to CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/SpeakerManagerConstants.h index cba1e3fb97..11461ac3fc 100644 --- a/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManagerConstants.h +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/SpeakerManagerConstants.h @@ -16,15 +16,17 @@ /** * @file */ -#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGERCONSTANTS_H_ -#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGERCONSTANTS_H_ +#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_SPEAKERMANAGERCONSTANTS_H_ +#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_SPEAKERMANAGERCONSTANTS_H_ -#include "AVSCommon/AVS/NamespaceAndName.h" +#include namespace alexaClientSDK { -namespace capabilityAgents { namespace speakerManager { +/// @addtogroup Lib_acsdkSpeakerManager +/// @{ + /// The Speaker interface namespace. const std::string NAMESPACE = "Speaker"; @@ -55,8 +57,9 @@ const std::string VOLUME_CHANGED = "VolumeChanged"; /// The @c VolumeMute event. const std::string MUTE_CHANGED = "MuteChanged"; +/// @} + } // namespace speakerManager -} // namespace capabilityAgents } // namespace alexaClientSDK -#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGERCONSTANTS_H_ +#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_SPEAKERMANAGERCONSTANTS_H_ diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/SpeakerManagerMiscStorage.h b/CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/SpeakerManagerMiscStorage.h new file mode 100644 index 0000000000..f777d7b48c --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/privateInclude/acsdk/SpeakerManager/private/SpeakerManagerMiscStorage.h @@ -0,0 +1,101 @@ +/* + * 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. + */ + +#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_SPEAKERMANAGERMISCSTORAGE_H_ +#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_SPEAKERMANAGERMISCSTORAGE_H_ + +#include +#include +#include + +namespace alexaClientSDK { +namespace speakerManager { + +/** + * @brief Configuration interface implementation for speaker manager. + * + * This class adapts @c MiscStorageInterface to @c SpeakerManagerStorageInterface. + * + * @ingroup Lib_acsdkSpeakerManager + */ +class SpeakerManagerMiscStorage : public SpeakerManagerStorageInterface { +public: + /** + * Creates an instance of the @c SpeakerManagerMiscStorage. + * + * @param miscStorage The underlying miscellaneous storage to store @c SpeakerManager data. + * @return A unique pointer to the instance of the newly created @c SpeakerManagerMiscStorage. + */ + static std::shared_ptr create( + std::shared_ptr miscStorage) noexcept; + + /// @name SpeakerManagerStorageInterface Functions + /// @{ + bool loadState(SpeakerManagerStorageState& state) noexcept override; + bool saveState(const SpeakerManagerStorageState& state) noexcept override; + /// @} + +private: + /// Helper to convert structure to JSON string. + static std::string convertToStateString(const SpeakerManagerStorageState& state) noexcept; + static std::string convertToStateString(const SpeakerManagerStorageState::ChannelState& state) noexcept; + + /** + * Constructor. + * + * @param miscStorage The underlying miscellaneous storage used to store component data. + */ + SpeakerManagerMiscStorage( + std::shared_ptr miscStorage) noexcept; + + /** + * Method to initialize the object. + * + * This method connects to underlying storage and performs necessary actions. + * + * @return Boolean status, indicating operation success. + */ + bool init() noexcept; + + /** + * Helper to convert JSON string to structure. + * + * @param stateString JSON string describing @a SpeakerManagerStorageState data. + * @param state Pointer to storage for parsed values. + * + * @return Boolean status, indicating operation success. + */ + bool convertFromStateString(const std::string& stateString, SpeakerManagerStorageState& state) noexcept; + + /** + * Helper to convert JSON string to structure. + * + * @param stateString JSON string describing @a SpeakerManagerStorageState::ChannelState data. + * @param state Pointer to storage for parsed values. + * + * @return Boolean status, indicating operation success. + */ + bool convertFromStateString( + const std::string& stateString, + SpeakerManagerStorageState::ChannelState& state) noexcept; + + /// The Misc storage. + std::shared_ptr m_miscStorage; +}; + +} // namespace speakerManager +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_PRIVATEINCLUDE_ACSDK_SPEAKERMANAGER_PRIVATE_SPEAKERMANAGERMISCSTORAGE_H_ diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/src/CMakeLists.txt b/CapabilityAgents/SpeakerManager/SpeakerManager/src/CMakeLists.txt new file mode 100644 index 0000000000..d90f4e8d34 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/src/CMakeLists.txt @@ -0,0 +1,22 @@ +add_definitions("-DACSDK_LOG_MODULE=speakerManager") + +add_library(acsdkSpeakerManager + SpeakerManager.cpp + ChannelVolumeFactory.cpp + ChannelVolumeManager.cpp + DefaultChannelVolumeFactory.cpp + Factories.cpp + SpeakerManagerMiscStorage.cpp + SpeakerManagerConfig.cpp + SpeakerManagerConfigHelper.cpp) + +target_include_directories(acsdkSpeakerManager + PUBLIC + "${acsdkSpeakerManager_SOURCE_DIR}/include" + PRIVATE + "${acsdkSpeakerManager_SOURCE_DIR}/privateInclude") + +target_link_libraries(acsdkSpeakerManager AVSCommon acsdkShutdownManagerInterfaces Endpoints) + +# install target +asdk_install() diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/src/ChannelVolumeFactory.cpp b/CapabilityAgents/SpeakerManager/SpeakerManager/src/ChannelVolumeFactory.cpp new file mode 100644 index 0000000000..6390b03c6a --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/src/ChannelVolumeFactory.cpp @@ -0,0 +1,28 @@ +/* + * 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 + +namespace alexaClientSDK { +namespace speakerManager { + +std::shared_ptr createChannelVolumeFactory() noexcept { + return std::make_shared(); +} + +} // namespace speakerManager +} // namespace alexaClientSDK diff --git a/CapabilityAgents/SpeakerManager/src/ChannelVolumeManager.cpp b/CapabilityAgents/SpeakerManager/SpeakerManager/src/ChannelVolumeManager.cpp similarity index 89% rename from CapabilityAgents/SpeakerManager/src/ChannelVolumeManager.cpp rename to CapabilityAgents/SpeakerManager/SpeakerManager/src/ChannelVolumeManager.cpp index e70f842d09..eb24ce6acd 100644 --- a/CapabilityAgents/SpeakerManager/src/ChannelVolumeManager.cpp +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/src/ChannelVolumeManager.cpp @@ -13,35 +13,49 @@ * permissions and limitations under the License. */ #include + +#include #include -#include "SpeakerManager/ChannelVolumeManager.h" namespace alexaClientSDK { -namespace capabilityAgents { namespace speakerManager { using namespace avsCommon::sdkInterfaces; using namespace avsCommon::avs::speakerConstants; -static const std::string TAG("ChannelVolumeManager"); +/// @addtogroup Lib_acsdkSpeakerManager +/// @{ + +/// @brief Logging tag +/// @private +#define TAG "ChannelVolumeManager" + +/** + * Create a LogEntry using this file's TAG and the specified event string. + * + * @param event The event string for this @c LogEntry. + * @private + */ #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) /// The fraction of maximum volume used for the upper threshold in the default volume curve. -static const float UPPER_VOLUME_CURVE_FRACTION = 0.40; +/// @private +static constexpr float UPPER_VOLUME_CURVE_FRACTION = 0.40f; /// The fraction of maximum volume used for the lower threshold in the default volume curve. -static const float LOWER_VOLUME_CURVE_FRACTION = 0.20; +/// @private +static constexpr float LOWER_VOLUME_CURVE_FRACTION = 0.20f; /** * Checks whether a value is within the bounds. * - * @tparam T The class type of the input parameters. * @param value The value to check. * @param min The minimum value. * @param max The maximum value. + * + * @private */ -template -static bool withinBounds(T value, T min, T max) { +static bool withinBounds(std::int64_t value, std::int64_t min, std::int64_t max) noexcept { if (value < min || value > max) { ACSDK_ERROR(LX("checkBoundsFailed").d("value", value).d("min", min).d("max", max)); return false; @@ -49,10 +63,12 @@ static bool withinBounds(T value, T min, T max) { return true; } +/// @} + std::shared_ptr ChannelVolumeManager::create( std::shared_ptr speaker, ChannelVolumeInterface::Type type, - VolumeCurveFunction volumeCurve) { + VolumeCurveFunction volumeCurve) noexcept { if (!speaker) { ACSDK_ERROR(LX(__func__).d("reason", "Null SpeakerInterface").m("createFailed")); return nullptr; @@ -75,7 +91,7 @@ std::shared_ptr ChannelVolumeManager::create( ChannelVolumeManager::ChannelVolumeManager( std::shared_ptr speaker, ChannelVolumeInterface::Type type, - VolumeCurveFunction volumeCurve) : + VolumeCurveFunction volumeCurve) noexcept : ChannelVolumeInterface{}, m_speaker{speaker}, m_isDucked{false}, @@ -184,7 +200,7 @@ bool ChannelVolumeManager::getSpeakerSettings( return true; } -int8_t ChannelVolumeManager::defaultVolumeAttenuateFunction(int8_t currentVolume) { +int8_t ChannelVolumeManager::defaultVolumeAttenuateFunction(int8_t currentVolume) noexcept { const int8_t lowerBreakPoint = static_cast(AVS_SET_VOLUME_MAX * LOWER_VOLUME_CURVE_FRACTION); const int8_t upperBreakPoint = static_cast(AVS_SET_VOLUME_MAX * UPPER_VOLUME_CURVE_FRACTION); @@ -197,5 +213,4 @@ int8_t ChannelVolumeManager::defaultVolumeAttenuateFunction(int8_t currentVolume } } } // namespace speakerManager -} // namespace capabilityAgents } // namespace alexaClientSDK diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/src/DefaultChannelVolumeFactory.cpp b/CapabilityAgents/SpeakerManager/SpeakerManager/src/DefaultChannelVolumeFactory.cpp new file mode 100644 index 0000000000..c8c681a031 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/src/DefaultChannelVolumeFactory.cpp @@ -0,0 +1,30 @@ +/* + * 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 + +namespace alexaClientSDK { +namespace speakerManager { + +std::shared_ptr DefaultChannelVolumeFactory:: + createChannelVolumeInterface( + std::shared_ptr speaker, + alexaClientSDK::avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, + std::function volumeCurve) { + return ChannelVolumeManager::create(std::move(speaker), std::move(type), std::move(volumeCurve)); +} + +} // namespace speakerManager +} // namespace alexaClientSDK diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/src/Factories.cpp b/CapabilityAgents/SpeakerManager/SpeakerManager/src/Factories.cpp new file mode 100644 index 0000000000..9d6164c379 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/src/Factories.cpp @@ -0,0 +1,94 @@ +/* + * 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 + +namespace alexaClientSDK { +namespace speakerManager { + +/// @addtogroup Lib_acsdkSpeakerManager +/// @{ + +/// @brief String to identify log entries originating from this file. +/// @private +#define TAG "SpeakerManagerFactories" + +/** + * Create a LogEntry using this file's TAG and the specified event string. + * + * @param event The event string for this @c LogEntry. + * @private + */ +#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) + +/// @} + +std::shared_ptr createSpeakerManagerCapabilityAgent( + std::shared_ptr config, + std::shared_ptr storage, + std::shared_ptr contextManager, + std::shared_ptr messageSender, + std::shared_ptr exceptionEncounteredSender, + std::shared_ptr metricRecorder, + const std::shared_ptr& shutdownNotifier, + const std::shared_ptr& + endpointCapabilitiesRegistrar, + const std::vector>& volumeInterfaces) noexcept { + if (!shutdownNotifier) { + ACSDK_ERROR(LX(__func__).d("reason", "nullShutdownNotifier")); + return nullptr; + } + if (!endpointCapabilitiesRegistrar) { + ACSDK_ERROR(LX(__func__).d("reason", "nullEndpointCapabilitiesRegistrar")); + return nullptr; + } + + auto speakerManager = SpeakerManager::create( + std::move(config), + std::move(storage), + volumeInterfaces, + std::move(contextManager), + std::move(messageSender), + std::move(exceptionEncounteredSender), + std::move(metricRecorder)); + + if (!speakerManager) { + ACSDK_ERROR(LX(__func__).d("reason", "errorSpeakerManagerCreate")); + return nullptr; + } + + shutdownNotifier->addObserver(speakerManager); + endpointCapabilitiesRegistrar->withCapability(speakerManager, speakerManager); + + return speakerManager; +} + +std::shared_ptr createSpeakerManagerStorage( + std::shared_ptr storage) noexcept { + return SpeakerManagerMiscStorage::create(std::move(storage)); +} + +std::shared_ptr createSpeakerManagerConfig() noexcept { + return std::make_shared(); +} + +} // namespace speakerManager +} // namespace alexaClientSDK diff --git a/CapabilityAgents/SpeakerManager/src/SpeakerManager.cpp b/CapabilityAgents/SpeakerManager/SpeakerManager/src/SpeakerManager.cpp similarity index 80% rename from CapabilityAgents/SpeakerManager/src/SpeakerManager.cpp rename to CapabilityAgents/SpeakerManager/SpeakerManager/src/SpeakerManager.cpp index 77cd9a8a75..ab529d73cd 100644 --- a/CapabilityAgents/SpeakerManager/src/SpeakerManager.cpp +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/src/SpeakerManager.cpp @@ -31,12 +31,11 @@ #include #include -#include "SpeakerManager/SpeakerManagerConstants.h" -#include "SpeakerManager/SpeakerManager.h" -#include "SpeakerManager/SpeakerManagerMiscStorage.h" +#include +#include +#include namespace alexaClientSDK { -namespace capabilityAgents { namespace speakerManager { using namespace acsdkManufactory; @@ -52,27 +51,29 @@ using namespace rapidjson; using DefaultEndpointAnnotation = avsCommon::sdkInterfaces::endpoints::DefaultEndpointAnnotation; +/// @addtogroup Lib_acsdkSpeakerManager +/// @{ + /// Speaker capability constants /// Speaker interface type -static const std::string SPEAKER_CAPABILITY_INTERFACE_TYPE = "AlexaInterface"; +static const auto SPEAKER_CAPABILITY_INTERFACE_TYPE = "AlexaInterface"; /// Speaker interface name -static const std::string SPEAKER_CAPABILITY_INTERFACE_NAME = "Speaker"; +static const auto SPEAKER_CAPABILITY_INTERFACE_NAME = "Speaker"; /// Speaker interface version -static const std::string SPEAKER_CAPABILITY_INTERFACE_VERSION = "1.0"; +static const auto SPEAKER_CAPABILITY_INTERFACE_VERSION = "1.0"; /// Retry timeout table -static const std::vector DEFAULT_RETRY_TABLE = {std::chrono::milliseconds(10).count(), - std::chrono::milliseconds(20).count(), - std::chrono::milliseconds(40).count()}; - -/// String to identify log entries originating from this file. -static const std::string TAG{"SpeakerManager"}; +static const std::vector DEFAULT_RETRY_TABLE = {static_cast(std::chrono::milliseconds(10).count()), + static_cast(std::chrono::milliseconds(20).count()), + static_cast(std::chrono::milliseconds(40).count())}; /// prefix for metrics emitted from the SpeakerManager CA -static const std::string SPEAKER_MANAGER_METRIC_PREFIX = "SPEAKER_MANAGER-"; +static const auto SPEAKER_MANAGER_METRIC_PREFIX = "SPEAKER_MANAGER-"; +/// String to identify log entries originating from this file. +#define TAG "SpeakerManager" /** * Create a LogEntry using this file's TAG and the specified event string. * - * @param The event string for this @c LogEntry. + * @param event The event string for this @c LogEntry. */ #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) @@ -82,9 +83,9 @@ static const std::string SPEAKER_MANAGER_METRIC_PREFIX = "SPEAKER_MANAGER-"; * @param value The value to check. * @param min The minimum value. * @param max The maximum value. + * @private */ -template -static bool withinBounds(T value, T min, T max) { +static bool withinBounds(std::int64_t value, std::int64_t min, std::int64_t max) noexcept { if (value < min || value > max) { ACSDK_ERROR(LX("checkBoundsFailed").d("value", value).d("min", min).d("max", max)); return false; @@ -97,8 +98,9 @@ static bool withinBounds(T value, T min, T max) { * * @param source The input @c SpeakerManagerObserverInterface::Source. * @return The @c String representing the source. + * @private */ -static inline std::string getSourceString(SpeakerManagerObserverInterface::Source source) { +static std::string getSourceString(SpeakerManagerObserverInterface::Source source) noexcept { std::stringstream ss; ss << source; return ss.str(); @@ -111,13 +113,17 @@ static inline std::string getSourceString(SpeakerManagerObserverInterface::Sourc * @param metricRecorder The @c MetricRecorderInterface which records Metric events. * @param eventName The name of the Metric event. * @param count Count of the metric that is emitted. + * @private */ static void submitMetric( const std::shared_ptr& metricRecorder, const std::string& eventName, - int count) { + int count) noexcept { + std::string activityName; + activityName.reserve(sizeof(SPEAKER_MANAGER_METRIC_PREFIX) - 1 + eventName.size()); + activityName.append(SPEAKER_MANAGER_METRIC_PREFIX, sizeof(SPEAKER_MANAGER_METRIC_PREFIX) - 1).append(eventName); auto metricEventBuilder = MetricEventBuilder{} - .setActivityName(SPEAKER_MANAGER_METRIC_PREFIX + eventName) + .setActivityName(activityName) .addDataPoint(DataPointCounterBuilder{}.setName(eventName).increment(count).build()); auto metricEvent = metricEventBuilder.build(); @@ -133,49 +139,20 @@ static void submitMetric( * Creates the Speaker capability configuration. * * @return The Speaker capability configuration. + * @private */ -static std::shared_ptr getSpeakerCapabilityConfiguration(); - -std::shared_ptr SpeakerManager::createSpeakerManagerCapabilityAgent( - const std::shared_ptr& storage, - const std::shared_ptr& contextManager, - const std::shared_ptr& messageSender, - const std::shared_ptr& exceptionEncounteredSender, - const std::shared_ptr& shutdownNotifier, - const Annotated& - endpointCapabilitiesRegistrar, - const std::shared_ptr& metricRecorder) { - if (!shutdownNotifier || !endpointCapabilitiesRegistrar) { - ACSDK_ERROR(LX("createSpeakerManagerCapabilityAgentFailed") - .d("reason", "nullParameter") - .d("isContextManagerNull", !contextManager) - .d("isDefaultEndpointCapabilitiesRegistrarNull", !endpointCapabilitiesRegistrar)); - return nullptr; - } - - auto speakerStorage = SpeakerManagerMiscStorage::create(storage); - std::vector> speakers; - auto speakerManager = SpeakerManager::create( - speakerStorage, speakers, contextManager, messageSender, exceptionEncounteredSender, metricRecorder); - - if (!speakerManager) { - ACSDK_ERROR(LX("createSpeakerManagerCapabilityAgentFailed")); - return nullptr; - } - - shutdownNotifier->addObserver(speakerManager); - endpointCapabilitiesRegistrar->withCapability(speakerManager, speakerManager); +static std::shared_ptr getSpeakerCapabilityConfiguration() noexcept; - return speakerManager; -} +/// @} std::shared_ptr SpeakerManager::create( - const std::shared_ptr& storage, + std::shared_ptr config, + std::shared_ptr storage, const std::vector>& groupVolumeInterfaces, std::shared_ptr contextManager, std::shared_ptr messageSender, std::shared_ptr exceptionEncounteredSender, - std::shared_ptr metricRecorder) { + std::shared_ptr metricRecorder) noexcept { if (!contextManager) { ACSDK_ERROR(LX("createFailed").d("reason", "nullContextManager")); return nullptr; @@ -190,25 +167,30 @@ std::shared_ptr SpeakerManager::create( return nullptr; } - auto speakerManager = std::shared_ptr(new SpeakerManager( - storage, groupVolumeInterfaces, contextManager, messageSender, exceptionEncounteredSender, metricRecorder)); - - return speakerManager; + return std::shared_ptr(new SpeakerManager( + std::move(config), + std::move(storage), + groupVolumeInterfaces, + std::move(contextManager), + std::move(messageSender), + std::move(exceptionEncounteredSender), + std::move(metricRecorder))); } SpeakerManager::SpeakerManager( - const std::shared_ptr& speakerManagerStorage, - const std::vector>& groupVolumeInterfaces, - std::shared_ptr contextManager, - std::shared_ptr messageSender, - std::shared_ptr exceptionEncounteredSender, - std::shared_ptr metricRecorder) : + std::shared_ptr speakerManagerConfig, + std::shared_ptr speakerManagerStorage, + const std::vector>& groupVolumeInterfaces, + std::shared_ptr contextManager, + std::shared_ptr messageSender, + std::shared_ptr exceptionEncounteredSender, + std::shared_ptr metricRecorder) noexcept : CapabilityAgent{NAMESPACE, exceptionEncounteredSender}, RequiresShutdown{"SpeakerManager"}, - m_config{speakerManagerStorage}, - m_metricRecorder{metricRecorder}, - m_contextManager{contextManager}, - m_messageSender{messageSender}, + m_config{std::move(speakerManagerConfig), std::move(speakerManagerStorage)}, + m_metricRecorder{std::move(metricRecorder)}, + m_contextManager{std::move(contextManager)}, + m_messageSender{std::move(messageSender)}, m_minUnmuteVolume{MIN_UNMUTE_VOLUME}, m_retryTimer{DEFAULT_RETRY_TABLE}, m_maxRetries{DEFAULT_RETRY_TABLE.size()}, @@ -217,15 +199,17 @@ SpeakerManager::SpeakerManager( for (auto& groupVolume : groupVolumeInterfaces) { addChannelVolumeInterfaceIntoSpeakerMap(groupVolume); } - - loadConfiguration(); // Load configuration (either from previous run, or from configuration). - updateChannelSettings(); // Apply loaded configuration - + m_persistentStorage = m_config.getPersistentStorage(); + if (m_persistentStorage) { + ACSDK_DEBUG5(LX("SpeakerManager").m("Persistent Storage is enabled.")); + loadConfiguration(); // Load configuration (either from storage, or from configuration). + updateChannelSettings(); // Apply loaded configuration + } m_capabilityConfigurations.insert(getSpeakerCapabilityConfiguration()); } void SpeakerManager::addChannelVolumeInterfaceIntoSpeakerMap( - std::shared_ptr channelVolumeInterface) { + std::shared_ptr channelVolumeInterface) noexcept { if (!channelVolumeInterface) { ACSDK_ERROR(LX("addChannelVolumeInterfaceFailed").d("reason", "nullChannelVolumeInterface")); return; @@ -258,11 +242,13 @@ void SpeakerManager::addChannelVolumeInterfaceIntoSpeakerMap( ACSDK_DEBUG(LX(__func__).d("type", type).d("sizeOfSpeakerSet", m_speakerMap[type].size())); } -std::shared_ptr getSpeakerCapabilityConfiguration() { +std::shared_ptr getSpeakerCapabilityConfiguration() noexcept { std::unordered_map configMap; - configMap.insert({CAPABILITY_INTERFACE_TYPE_KEY, SPEAKER_CAPABILITY_INTERFACE_TYPE}); - configMap.insert({CAPABILITY_INTERFACE_NAME_KEY, SPEAKER_CAPABILITY_INTERFACE_NAME}); - configMap.insert({CAPABILITY_INTERFACE_VERSION_KEY, SPEAKER_CAPABILITY_INTERFACE_VERSION}); + // Create a type for gcc < 6.x + using s = std::string; + configMap.insert({s(CAPABILITY_INTERFACE_TYPE_KEY), s(SPEAKER_CAPABILITY_INTERFACE_TYPE)}); + configMap.insert({s(CAPABILITY_INTERFACE_NAME_KEY), s(SPEAKER_CAPABILITY_INTERFACE_NAME)}); + configMap.insert({s(CAPABILITY_INTERFACE_VERSION_KEY), s(SPEAKER_CAPABILITY_INTERFACE_VERSION)}); return std::make_shared(configMap); } @@ -293,7 +279,7 @@ void SpeakerManager::handleDirectiveImmediately(std::shared_ptr di handleDirective(std::make_shared(directive, nullptr)); }; -bool SpeakerManager::parseDirectivePayload(std::string payload, Document* document) { +bool SpeakerManager::parseDirectivePayload(std::string payload, Document* document) noexcept { if (!document) { ACSDK_ERROR(LX("parseDirectivePayloadFailed").d("reason", "nullDocument")); return false; @@ -314,8 +300,8 @@ bool SpeakerManager::parseDirectivePayload(std::string payload, Document* docume void SpeakerManager::sendExceptionEncountered( std::shared_ptr info, const std::string& message, - avsCommon::avs::ExceptionErrorType type) { - m_executor.submit([this, info, message, type] { + avsCommon::avs::ExceptionErrorType type) noexcept { + m_executor.execute([this, info, message, type] { m_exceptionEncounteredSender->sendExceptionEncountered(info->directive->getUnparsedDirective(), type, message); if (info && info->result) { info->result->setFailed(message); @@ -324,14 +310,14 @@ void SpeakerManager::sendExceptionEncountered( }); } -void SpeakerManager::executeSetHandlingCompleted(std::shared_ptr info) { +void SpeakerManager::executeSetHandlingCompleted(std::shared_ptr info) noexcept { if (info && info->result) { info->result->setCompleted(); } removeDirective(info); } -void SpeakerManager::removeDirective(std::shared_ptr info) { +void SpeakerManager::removeDirective(std::shared_ptr info) noexcept { // Check result too, to catch cases where DirectiveInfo was created locally, without a nullptr result. // In those cases there is no messageId to remove because no result was expected. if (info->directive && info->result) { @@ -342,7 +328,7 @@ void SpeakerManager::removeDirective(std::shared_ptr info) { void SpeakerManager::executeSendSpeakerSettingsChangedEvent( const std::string& eventName, - SpeakerInterface::SpeakerSettings settings) { + SpeakerInterface::SpeakerSettings settings) noexcept { ACSDK_DEBUG9(LX("executeSendSpeakerSettingsChangedEvent")); rapidjson::Document payload(rapidjson::kObjectType); payload.AddMember(VOLUME_KEY, settings.volume, payload.GetAllocator()); @@ -385,7 +371,7 @@ void SpeakerManager::handleDirective(std::shared_ptr(AVS_SET_VOLUME_MIN), static_cast(AVS_SET_VOLUME_MAX))) { - m_executor.submit([this, volume, directiveType, info] { + m_executor.execute([this, volume, directiveType, info] { /* * Since AVS doesn't have a concept of Speaker IDs or types, no-op if a directive * comes in and there are no AVS_SPEAKER_VOLUME speakers. @@ -423,7 +409,7 @@ void SpeakerManager::handleDirective(std::shared_ptr(AVS_ADJUST_VOLUME_MIN), static_cast(AVS_ADJUST_VOLUME_MAX))) { - m_executor.submit([this, delta, directiveType, info] { + m_executor.execute([this, delta, directiveType, info] { /* * Since AVS doesn't have a concept of Speaker IDs or types, no-op if a directive * comes in and there are no AVS_SPEAKER_VOLUME speakers. @@ -459,7 +445,7 @@ void SpeakerManager::handleDirective(std::shared_ptr SpeakerManager::setVolume( bool SpeakerManager::executeSetVolume( ChannelVolumeInterface::Type type, int8_t volume, - const SpeakerManagerInterface::NotificationProperties& properties) { + const SpeakerManagerInterface::NotificationProperties& properties) noexcept { ACSDK_DEBUG9(LX("executeSetVolumeCalled").d("volume", static_cast(volume))); auto it = m_speakerMap.find(type); if (m_speakerMap.end() == it) { @@ -616,8 +602,8 @@ bool SpeakerManager::executeSetVolume( if (volume > maximumVolumeLimit) { ACSDK_DEBUG0(LX("adjustingSetVolumeValue") .d("reason", "valueHigherThanLimit") - .d("value", (int)volume) - .d("maximumVolumeLimitSetting", (int)maximumVolumeLimit)); + .d("value", static_cast(volume)) + .d("maximumVolumeLimitSetting", static_cast(maximumVolumeLimit))); adjustedVolume = maximumVolumeLimit; } @@ -658,8 +644,10 @@ bool SpeakerManager::executeSetVolume( ACSDK_DEBUG(LX("executeSetVolumeSuccess").d("newVolume", static_cast(settings.volume))); - if (previousVolume != settings.volume) { - executePersistConfiguration(); + if (m_persistentStorage) { + if (previousVolume != settings.volume) { + executePersistConfiguration(); + } } updateContextManager(type, settings); @@ -678,13 +666,13 @@ bool SpeakerManager::executeSetVolume( void SpeakerManager::convertSettingsToChannelState( avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, - SpeakerManagerStorageState::ChannelState* storageState) { + SpeakerManagerStorageState::ChannelState* storageState) noexcept { const auto& settings = m_speakerSettings[type]; storageState->channelVolume = settings.volume; storageState->channelMuteStatus = settings.mute; } -void SpeakerManager::executePersistConfiguration() { +void SpeakerManager::executePersistConfiguration() noexcept { SpeakerManagerStorageState state; convertSettingsToChannelState(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, &state.speakerChannelState); convertSettingsToChannelState(ChannelVolumeInterface::Type::AVS_ALERTS_VOLUME, &state.alertsChannelState); @@ -698,7 +686,7 @@ void SpeakerManager::executePersistConfiguration() { bool SpeakerManager::executeRestoreVolume( ChannelVolumeInterface::Type type, - SpeakerManagerObserverInterface::Source source) { + SpeakerManagerObserverInterface::Source source) noexcept { SpeakerInterface::SpeakerSettings settings; if (!executeGetSpeakerSettings(type, &settings)) { @@ -710,7 +698,8 @@ bool SpeakerManager::executeRestoreVolume( return true; } - return executeSetVolume(type, m_minUnmuteVolume, SpeakerManagerInterface::NotificationProperties(source)); + return executeSetVolume( + type, static_cast(m_minUnmuteVolume), SpeakerManagerInterface::NotificationProperties(source)); } std::future SpeakerManager::adjustVolume( @@ -745,7 +734,7 @@ std::future SpeakerManager::adjustVolume( bool SpeakerManager::executeAdjustVolume( ChannelVolumeInterface::Type type, int8_t delta, - const SpeakerManagerInterface::NotificationProperties& properties) { + const SpeakerManagerInterface::NotificationProperties& properties) noexcept { ACSDK_DEBUG9(LX("executeAdjustVolumeCalled").d("delta", static_cast(delta))); auto it = m_speakerMap.find(type); if (m_speakerMap.end() == it) { @@ -826,8 +815,10 @@ bool SpeakerManager::executeAdjustVolume( ACSDK_DEBUG(LX("executeAdjustVolumeSuccess").d("newVolume", static_cast(settings.volume))); - if (previousVolume != settings.volume) { - executePersistConfiguration(); + if (m_persistentStorage) { + if (previousVolume != settings.volume) { + executePersistConfiguration(); + } } updateContextManager(type, settings); @@ -873,7 +864,7 @@ std::future SpeakerManager::setMute( bool SpeakerManager::executeSetMute( ChannelVolumeInterface::Type type, bool mute, - const SpeakerManagerInterface::NotificationProperties& properties) { + const SpeakerManagerInterface::NotificationProperties& properties) noexcept { ACSDK_DEBUG9(LX("executeSetMuteCalled").d("mute", mute)); SpeakerInterface::SpeakerSettings settings; if (!executeGetSpeakerSettings(type, &settings)) { @@ -925,7 +916,9 @@ bool SpeakerManager::executeSetMute( ACSDK_DEBUG(LX("executeSetMuteSuccess").d("mute", mute)); - executePersistConfiguration(); + if (m_persistentStorage) { + executePersistConfiguration(); + } updateContextManager(type, settings); @@ -941,14 +934,14 @@ bool SpeakerManager::executeSetMute( } #ifdef ENABLE_MAXVOLUME_SETTING -std::future SpeakerManager::setMaximumVolumeLimit(const int8_t maximumVolumeLimit) { +std::future SpeakerManager::setMaximumVolumeLimit(const int8_t maximumVolumeLimit) noexcept { return m_executor.submit([this, maximumVolumeLimit] { return withinBounds(maximumVolumeLimit, AVS_ADJUST_VOLUME_MIN, AVS_ADJUST_VOLUME_MAX) && executeSetMaximumVolumeLimit(maximumVolumeLimit); }); } -bool SpeakerManager::executeSetMaximumVolumeLimit(const int8_t maximumVolumeLimit) { +bool SpeakerManager::executeSetMaximumVolumeLimit(const int8_t maximumVolumeLimit) noexcept { ACSDK_DEBUG3(LX(__func__).d("maximumVolumeLimit", static_cast(maximumVolumeLimit))); // First adjust current volumes. @@ -987,7 +980,7 @@ void SpeakerManager::executeNotifySettingsChanged( const SpeakerInterface::SpeakerSettings& settings, const std::string& eventName, const SpeakerManagerObserverInterface::Source& source, - const ChannelVolumeInterface::Type& type) { + const ChannelVolumeInterface::Type& type) noexcept { // Only send an event if the AVS_SPEAKER_VOLUME settings changed. if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { executeSendSpeakerSettingsChangedEvent(eventName, settings); @@ -999,7 +992,7 @@ void SpeakerManager::executeNotifySettingsChanged( void SpeakerManager::executeNotifyObserver( const SpeakerManagerObserverInterface::Source& source, const ChannelVolumeInterface::Type& type, - const SpeakerInterface::SpeakerSettings& settings) { + const SpeakerInterface::SpeakerSettings& settings) noexcept { ACSDK_DEBUG9(LX("executeNotifyObserverCalled")); for (auto observer : m_observers) { observer->onSpeakerSettingsChanged(source, type, settings); @@ -1015,7 +1008,7 @@ std::future SpeakerManager::getSpeakerSettings( bool SpeakerManager::executeGetSpeakerSettings( ChannelVolumeInterface::Type type, - SpeakerInterface::SpeakerSettings* settings) { + SpeakerInterface::SpeakerSettings* settings) noexcept { ACSDK_DEBUG9(LX("executeGetSpeakerSettingsCalled")); if (!settings) { ACSDK_ERROR(LX("executeGetSpeakerSettingsFailed").d("reason", "nullSettings")); @@ -1037,7 +1030,8 @@ bool SpeakerManager::executeGetSpeakerSettings( return true; } -bool SpeakerManager::executeInitializeSpeakerSettings(avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type) { +bool SpeakerManager::executeInitializeSpeakerSettings( + avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type) noexcept { ACSDK_DEBUG5(LX("executeInitializeSpeakerSettings").d("type", type)); auto it = m_speakerMap.find(type); if (m_speakerMap.end() == it) { @@ -1052,13 +1046,93 @@ bool SpeakerManager::executeInitializeSpeakerSettings(avsCommon::sdkInterfaces:: return false; } - m_speakerSettings[type] = settings; + if (m_persistentStorage) { + auto defaultSettingsIt = m_speakerSettings.find(type); + if (m_speakerSettings.end() == defaultSettingsIt) { + ACSDK_DEBUG5(LX("executeInitializeSpeakerSettings").d("Initializing new speaker setting", type)); + m_speakerSettings[type] = settings; + } + } else { + m_speakerSettings[type] = settings; + } + return true; } +void SpeakerManager::onExternalSpeakerSettingsUpdate( + ChannelVolumeInterface::Type type, + const SpeakerInterface::SpeakerSettings& speakerSettings, + const NotificationProperties& properties) { + ACSDK_DEBUG9(LX("onExternalSpeakerSettingsUpdate")); + m_executor.execute([this, type, speakerSettings, properties] { + auto it = m_speakerMap.find(type); + if (m_speakerMap.end() == it) { + ACSDK_ERROR(LX("onExternalSpeakerSettingsUpdateFailed").d("reason", "noSpeakersWithType").d("type", type)); + submitMetric(m_metricRecorder, "onExternalSpeakerSettingsUpdateFailedZeroSpeakers", 1); + return; + } + + submitMetric(m_metricRecorder, "onExternalSpeakerSettingsUpdateFailedZeroSpeakers", 0); + submitMetric(m_metricRecorder, "onExternalSpeakerSettingsUpdate", 1); + submitMetric( + m_metricRecorder, "onExternalSpeakerSettingsUpdateSource_" + getSourceString(properties.source), 1); + + auto adjustedVolume = + (speakerSettings.volume > AVS_SET_VOLUME_MIN) ? speakerSettings.volume : AVS_SET_VOLUME_MIN; + + auto maximumVolumeLimit = getMaximumVolumeLimit(); + + if (speakerSettings.volume > maximumVolumeLimit) { + ACSDK_DEBUG0(LX("adjustingUpdatedVolumeValue") + .d("reason", "valueHigherThanLimit") + .d("value", static_cast(speakerSettings.volume)) + .d("maximumVolumeLimitSetting", static_cast(maximumVolumeLimit))); + adjustedVolume = maximumVolumeLimit; + } + SpeakerInterface::SpeakerSettings settings; + if (!executeGetSpeakerSettings(type, &settings)) { + ACSDK_ERROR(LX("onExternalSpeakerSettingsUpdateFailed").d("reason", "executeGetSpeakerSettingsFailed")); + return; + } + auto previousVolume = settings.volume; + auto previousMute = settings.mute; + + // update the new settings. + settings.volume = adjustedVolume; + settings.mute = speakerSettings.mute; + if (!executeSetSpeakerSettings(type, settings)) { + ACSDK_ERROR(LX("executeOnVolumeUpdatedFailed").d("reason", "executeSetSpeakerSettingsFailed")); + return; + } + + if (m_persistentStorage) { + if ((previousVolume != settings.volume) || (previousMute != settings.mute)) { + executePersistConfiguration(); + } + } + + updateContextManager(type, settings); + + if (properties.notifyObservers) { + executeNotifyObserver(properties.source, type, settings); + } + + if (properties.notifyAVS) { + if (!(previousVolume == settings.volume && + SpeakerManagerObserverInterface::Source::LOCAL_API == properties.source)) { + executeNotifySettingsChanged(settings, VOLUME_CHANGED, properties.source, type); + } + if (previousMute != settings.mute) { + executeNotifySettingsChanged(settings, MUTE_CHANGED, properties.source, type); + } + } + return; + }); +} + bool SpeakerManager::executeSetSpeakerSettings( const ChannelVolumeInterface::Type type, - const SpeakerInterface::SpeakerSettings& settings) { + const SpeakerInterface::SpeakerSettings& settings) noexcept { ACSDK_DEBUG9(LX("executeSetSpeakerSettingsCalled")); if (m_speakerMap.end() == m_speakerMap.find(type)) { ACSDK_ERROR(LX("executeSetSpeakerSettings").d("reason", "noSpeakersWithTypeFound").d("type", type)); @@ -1071,6 +1145,26 @@ bool SpeakerManager::executeSetSpeakerSettings( void SpeakerManager::addChannelVolumeInterface(std::shared_ptr channelVolumeInterface) { addChannelVolumeInterfaceIntoSpeakerMap(channelVolumeInterface); + if (m_persistentStorage) { + SpeakerInterface::SpeakerSettings& settings = m_speakerSettings[channelVolumeInterface->getSpeakerType()]; + retryAndApplySettings([this, &settings, &channelVolumeInterface]() -> bool { + ACSDK_DEBUG9(LX(__func__) + .d("speaker id", channelVolumeInterface->getId()) + .d("speaker type", channelVolumeInterface->getSpeakerType()) + .d("default volume set to ", settings.volume)); + if (!channelVolumeInterface->setUnduckedVolume(settings.volume)) { + submitMetric(m_metricRecorder, "setVolumeFailed", 1); + return false; + } + if (!channelVolumeInterface->setMute(settings.mute)) { + submitMetric(m_metricRecorder, "setMuteFailed", 1); + return false; + } + submitMetric(m_metricRecorder, "setVolumeFailed", 0); + submitMetric(m_metricRecorder, "setMuteFailed", 0); + return true; + }); + } } std::unordered_set> SpeakerManager:: @@ -1078,17 +1172,15 @@ std::unordered_set> Spe return m_capabilityConfigurations; } -int8_t SpeakerManager::getMaximumVolumeLimit() { +int8_t SpeakerManager::getMaximumVolumeLimit() noexcept { return m_maximumVolumeLimit; } -template -bool SpeakerManager::retryAndApplySettings(Task task, Args&&... args) { - auto boundTask = std::bind(std::forward(task), std::forward(args)...); +bool SpeakerManager::retryAndApplySettings(const std::function& task) noexcept { size_t attempt = 0; m_waitCancelEvent.reset(); while (attempt < m_maxRetries) { - if (boundTask()) { + if (task()) { break; } @@ -1102,7 +1194,7 @@ bool SpeakerManager::retryAndApplySettings(Task task, Args&&... args) { return attempt < m_maxRetries; } -int8_t SpeakerManager::adjustVolumeRange(int64_t volume) { +int8_t SpeakerManager::adjustVolumeRange(int64_t volume) noexcept { auto adjustedVolume = std::min( static_cast(AVS_ADJUST_VOLUME_MAX), std::max(static_cast(AVS_ADJUST_VOLUME_MIN), volume)); return static_cast(adjustedVolume); @@ -1110,7 +1202,7 @@ int8_t SpeakerManager::adjustVolumeRange(int64_t volume) { void SpeakerManager::presetChannelDefaults( ChannelVolumeInterface::Type type, - const SpeakerManagerStorageState::ChannelState& state) { + const SpeakerManagerStorageState::ChannelState& state) noexcept { auto adjustedVolume = adjustVolumeRange(state.channelVolume); if (adjustedVolume != state.channelVolume) { @@ -1127,7 +1219,7 @@ void SpeakerManager::presetChannelDefaults( } } -void SpeakerManager::loadConfiguration() { +void SpeakerManager::loadConfiguration() noexcept { ACSDK_DEBUG5(LX("configureDefaults").m("Loading configuration")); m_minUnmuteVolume = m_config.getMinUnmuteVolume(); @@ -1140,12 +1232,12 @@ void SpeakerManager::loadConfiguration() { presetChannelDefaults(ChannelVolumeInterface::Type::AVS_ALERTS_VOLUME, state.alertsChannelState); } -void SpeakerManager::updateChannelSettings() { +void SpeakerManager::updateChannelSettings() noexcept { updateChannelSettings(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME); updateChannelSettings(ChannelVolumeInterface::Type::AVS_ALERTS_VOLUME); } -void SpeakerManager::updateChannelSettings(ChannelVolumeInterface::Type type) { +void SpeakerManager::updateChannelSettings(ChannelVolumeInterface::Type type) noexcept { auto it = m_speakerMap.find(type); if (it != m_speakerMap.end()) { SpeakerInterface::SpeakerSettings& settings = m_speakerSettings[type]; @@ -1181,5 +1273,4 @@ void SpeakerManager::updateChannelSettings(ChannelVolumeInterface::Type type) { } } // namespace speakerManager -} // namespace capabilityAgents } // namespace alexaClientSDK diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/src/SpeakerManagerConfig.cpp b/CapabilityAgents/SpeakerManager/SpeakerManager/src/SpeakerManagerConfig.cpp new file mode 100644 index 0000000000..26632b3f72 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/src/SpeakerManagerConfig.cpp @@ -0,0 +1,144 @@ +/* + * 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 + +namespace alexaClientSDK { +namespace speakerManager { + +using namespace alexaClientSDK::avsCommon::avs::speakerConstants; +using namespace alexaClientSDK::avsCommon::utils::configuration; +using namespace alexaClientSDK::avsCommon::utils; + +/// @addtogroup Lib_acsdkSpeakerManager +/// @{ + +/// String to identify log entries originating from this file. +/// @private +#define TAG "SpeakerManagerConfig" + +/** + * Create a LogEntry using this file's TAG and the specified event string. + * + * @param event The event string for this @c LogEntry. + * @private + */ +#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) + +/// The key in our config file to find the root of speaker manager configuration. +/// @private +static const auto SPEAKERMANAGER_CONFIGURATION_ROOT_KEY = "speakerManagerCapabilityAgent"; +/// The key in our config file to find the persistentStorage value. +/// @private +static const auto SPEAKERMANAGER_PERSISTENT_STORAGE_SETTING_KEY = "persistentStorage"; +/// The key in our config file to find the minUnmuteVolume value. +/// @private +static const auto SPEAKERMANAGER_MIN_UNMUTE_VOLUME_KEY = "minUnmuteVolume"; +/// The key in our config file to find the defaultSpeakerVolume value. +/// @private +static const auto SPEAKERMANAGER_DEFAULT_SPEAKER_VOLUME_KEY = "defaultSpeakerVolume"; +/// The key in our config file to find the defaultAlertsVolume value. +/// @private +static const auto SPEAKERMANAGER_DEFAULT_ALERTS_VOLUME_KEY = "defaultAlertsVolume"; +/// The key in our config file to find mute status keep flag +/// @private +static const auto SPEAKERMANAGER_RESTORE_MUTE_STATE_KEY = "restoreMuteState"; + +/** + * @brief Helper to load a single volume level entry from configuration. + * + * @param[in] node Configuration node. + * @param[in] key Entry name. + * @param[out] value Volume entry result. + * + * @return True if entry with name @a key have been found, and it's value is within valid range. False otherwise. + * @private + */ +static bool loadVolumeConfig(ConfigurationNode& node, const char* key, Optional& value) noexcept { + int tmp; + if (node.getInt(key, &tmp) && tmp >= AVS_SET_VOLUME_MIN && tmp <= AVS_SET_VOLUME_MAX) { + value = static_cast(tmp); + return true; + } else { + return false; + } +} + +/// @} + +SpeakerManagerConfig::SpeakerManagerConfig() noexcept { + loadPlatformConfig(); +} + +bool SpeakerManagerConfig::getPersistentStorage(bool& persistentStorage) noexcept { + if (m_persistentStorage.hasValue()) { + persistentStorage = m_persistentStorage.value(); + return true; + } + return false; +} + +bool SpeakerManagerConfig::getMinUnmuteVolume(std::uint8_t& minUnmuteVolume) noexcept { + if (m_minUnmuteVolume.hasValue()) { + minUnmuteVolume = m_minUnmuteVolume.value(); + return true; + } + return false; +} + +bool SpeakerManagerConfig::getRestoreMuteState(bool& restoreMuteState) noexcept { + if (m_restoreMuteState.hasValue()) { + restoreMuteState = m_restoreMuteState.value(); + return true; + } + return false; +} + +bool SpeakerManagerConfig::getDefaultSpeakerVolume(std::uint8_t& defaultSpeakerVolume) noexcept { + if (m_defaultSpeakerVolume.hasValue()) { + defaultSpeakerVolume = m_defaultSpeakerVolume.value(); + return true; + } + return false; +} + +bool SpeakerManagerConfig::getDefaultAlertsVolume(std::uint8_t& defaultAlertsVolume) noexcept { + if (m_defaultAlertsVolume.hasValue()) { + defaultAlertsVolume = m_defaultAlertsVolume.value(); + return true; + } + return false; +} + +void SpeakerManagerConfig::loadPlatformConfig() noexcept { + auto configRoot = ConfigurationNode::getRoot().getChildNode(SPEAKERMANAGER_CONFIGURATION_ROOT_KEY); + + loadVolumeConfig(configRoot, SPEAKERMANAGER_DEFAULT_SPEAKER_VOLUME_KEY, m_defaultSpeakerVolume); + loadVolumeConfig(configRoot, SPEAKERMANAGER_DEFAULT_ALERTS_VOLUME_KEY, m_defaultAlertsVolume); + loadVolumeConfig(configRoot, SPEAKERMANAGER_MIN_UNMUTE_VOLUME_KEY, m_minUnmuteVolume); + + bool tmp; + if (configRoot.getBool(SPEAKERMANAGER_RESTORE_MUTE_STATE_KEY, &tmp)) { + m_restoreMuteState = tmp; + } + if (configRoot.getBool(SPEAKERMANAGER_PERSISTENT_STORAGE_SETTING_KEY, &tmp)) { + m_persistentStorage = tmp; + } +} + +} // namespace speakerManager +} // namespace alexaClientSDK diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/src/SpeakerManagerConfigHelper.cpp b/CapabilityAgents/SpeakerManager/SpeakerManager/src/SpeakerManagerConfigHelper.cpp new file mode 100644 index 0000000000..f406114f56 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/src/SpeakerManagerConfigHelper.cpp @@ -0,0 +1,94 @@ +/* + * 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 + +namespace alexaClientSDK { +namespace speakerManager { + +using namespace avsCommon::sdkInterfaces; +using namespace avsCommon::sdkInterfaces::storage; +using namespace avsCommon::avs::speakerConstants; +using namespace avsCommon::utils::configuration; + +/// String to identify log entries originating from this file. +/// @private +#define TAG "SpeakerManagerConfigHelper" + +/** + * Create a LogEntry using this file's TAG and the specified event string. + * + * @param event The event string for this @c LogEntry. + * @private + */ +#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) + +const SpeakerManagerStorageState SpeakerManagerConfigHelper::c_defaults = {{DEFAULT_SPEAKER_VOLUME, false}, + {DEFAULT_ALERTS_VOLUME, false}}; + +SpeakerManagerConfigHelper::SpeakerManagerConfigHelper( + std::shared_ptr config, + std::shared_ptr storage) noexcept : + m_config{std::move(config)}, + m_storage{std::move(storage)} { +} + +int SpeakerManagerConfigHelper::getMinUnmuteVolume() const noexcept { + std::uint8_t minUnmuteVolume; + if (!m_config->getMinUnmuteVolume(minUnmuteVolume)) { + minUnmuteVolume = MIN_UNMUTE_VOLUME; + } + return minUnmuteVolume; +} + +bool SpeakerManagerConfigHelper::getPersistentStorage() const noexcept { + bool speakerPersistentStorageSetting; + if (!m_config->getPersistentStorage(speakerPersistentStorageSetting)) { + speakerPersistentStorageSetting = false; + } + return speakerPersistentStorageSetting; +} + +void SpeakerManagerConfigHelper::loadState(SpeakerManagerStorageState& state) noexcept { + if (m_storage->loadState(state)) { + return; + } + + state = c_defaults; + + std::uint8_t tmp; + if (m_config->getDefaultSpeakerVolume(tmp)) { + state.speakerChannelState.channelVolume = tmp; + } + if (m_config->getDefaultAlertsVolume(tmp)) { + state.alertsChannelState.channelVolume = tmp; + } +} + +bool SpeakerManagerConfigHelper::saveState(const SpeakerManagerStorageState& state) noexcept { + return m_storage->saveState(state); +} + +bool SpeakerManagerConfigHelper::getRestoreMuteState() const noexcept { + bool restoreMuteState; + if (!m_config->getRestoreMuteState(restoreMuteState)) { + restoreMuteState = true; + } + return restoreMuteState; +} + +} // namespace speakerManager +} // namespace alexaClientSDK diff --git a/CapabilityAgents/SpeakerManager/src/SpeakerManagerMiscStorage.cpp b/CapabilityAgents/SpeakerManager/SpeakerManager/src/SpeakerManagerMiscStorage.cpp similarity index 77% rename from CapabilityAgents/SpeakerManager/src/SpeakerManagerMiscStorage.cpp rename to CapabilityAgents/SpeakerManager/SpeakerManager/src/SpeakerManagerMiscStorage.cpp index 1209c2d99c..2d3690343b 100644 --- a/CapabilityAgents/SpeakerManager/src/SpeakerManagerMiscStorage.cpp +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/src/SpeakerManagerMiscStorage.cpp @@ -13,54 +13,62 @@ * permissions and limitations under the License. */ -#include +#include +#include +#include #include #include #include -#include "SpeakerManager/SpeakerManager.h" -#include "SpeakerManager/SpeakerManagerMiscStorage.h" -#include "SpeakerManager/SpeakerManagerStorageState.h" - namespace alexaClientSDK { -namespace capabilityAgents { namespace speakerManager { using namespace avsCommon::sdkInterfaces::storage; using namespace avsCommon::utils::json; +/// @addtogroup Lib_acsdkSpeakerManager +/// @{ + /// String to identify log entries originating from this file. -static const std::string TAG("SpeakerManagerMiscStorage"); +/// @private +#define TAG "SpeakerManagerMiscStorage" /** * Create a LogEntry using the file's TAG and the specified event string. * - * @param The event string for this @c LogEntry. + * @param event The event string for this @c LogEntry. + * @private */ #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) /// Component name for Misc DB. -static const std::string COMPONENT_NAME = "SpeakerManager"; +/// @private +static const auto COMPONENT_NAME = "SpeakerManager"; /// Misc DB table for component state. -static const std::string COMPONENT_STATE_TABLE = "SpeakerManagerConfig"; +/// @private +static const auto COMPONENT_STATE_TABLE = "SpeakerManagerConfig"; /// Misc DB table entry for component state. -static const std::string COMPONENT_STATE_KEY = "SpeakerManagerConfig"; - +/// @private +static const auto COMPONENT_STATE_KEY = "SpeakerManagerConfig"; /// The key in our config for speaker volume. -static const std::string SPEAKER_CHANNEL_STATE = "speakerChannelState"; +/// @private +static const auto SPEAKER_CHANNEL_STATE = "speakerChannelState"; /// The key in our config for speaker volume. -static const std::string ALERTS_CHANNEL_STATE = "alertsChannelState"; -/// The key in our config for alerts volume. -static const std::string ALERTS_VOLUME_KEY = "alertsVolume"; +/// @private +static const auto ALERTS_CHANNEL_STATE = "alertsChannelState"; /// The key in our config for speaker volume. -static const std::string CHANNEL_VOLUME_KEY = "channelVolume"; +/// @private +static const auto CHANNEL_VOLUME_KEY = "channelVolume"; /// The key in our config for speaker volume. -static const std::string CHANNEL_MUTE_STATUS_KEY = "channelMuteStatus"; +/// @private +static const auto CHANNEL_MUTE_STATUS_KEY = "channelMuteStatus"; + +/// @} std::shared_ptr SpeakerManagerMiscStorage::create( - const std::shared_ptr& miscStorage) { + std::shared_ptr miscStorage) noexcept { if (miscStorage) { - auto res = std::shared_ptr(new SpeakerManagerMiscStorage(miscStorage)); + auto res = std::shared_ptr(new SpeakerManagerMiscStorage(std::move(miscStorage))); if (res->init()) { return res; } else { @@ -73,11 +81,11 @@ std::shared_ptr SpeakerManagerMiscStorage::create( } SpeakerManagerMiscStorage::SpeakerManagerMiscStorage( - const std::shared_ptr& miscStorage) : - m_miscStorage{miscStorage} { + std::shared_ptr miscStorage) noexcept : + m_miscStorage{std::move(miscStorage)} { } -bool SpeakerManagerMiscStorage::init() { +bool SpeakerManagerMiscStorage::init() noexcept { if (!m_miscStorage->isOpened() && !m_miscStorage->open()) { ACSDK_DEBUG3(LX(__func__).m("Couldn't open misc database. Creating.")); if (!m_miscStorage->createDatabase()) { @@ -112,7 +120,7 @@ bool SpeakerManagerMiscStorage::init() { bool SpeakerManagerMiscStorage::convertFromStateString( const std::string& stateString, - SpeakerManagerStorageState::ChannelState& state) { + SpeakerManagerStorageState::ChannelState& state) noexcept { rapidjson::Document document; if (!jsonUtils::parseJSON(stateString, &document)) { ACSDK_ERROR(LX("convertFromStateString").d("reason", "parsingError")); @@ -122,7 +130,7 @@ bool SpeakerManagerMiscStorage::convertFromStateString( int64_t tmpVolume; if (jsonUtils::retrieveValue(document, CHANNEL_VOLUME_KEY, &tmpVolume)) { - state.channelVolume = tmpVolume; + state.channelVolume = static_cast(tmpVolume); } else { return false; } @@ -135,7 +143,7 @@ bool SpeakerManagerMiscStorage::convertFromStateString( bool SpeakerManagerMiscStorage::convertFromStateString( const std::string& stateString, - SpeakerManagerStorageState& state) { + SpeakerManagerStorageState& state) noexcept { std::string tmp; return jsonUtils::retrieveValue(stateString, SPEAKER_CHANNEL_STATE, &tmp) && @@ -144,21 +152,22 @@ bool SpeakerManagerMiscStorage::convertFromStateString( convertFromStateString(tmp, state.alertsChannelState); } -bool SpeakerManagerMiscStorage::loadState(SpeakerManagerStorageState& state) { +bool SpeakerManagerMiscStorage::loadState(SpeakerManagerStorageState& state) noexcept { std::string stateString; return m_miscStorage->get(COMPONENT_NAME, COMPONENT_STATE_TABLE, COMPONENT_STATE_KEY, &stateString) && !stateString.empty() && convertFromStateString(stateString, state); } -std::string SpeakerManagerMiscStorage::convertToStateString(const SpeakerManagerStorageState::ChannelState& state) { +std::string SpeakerManagerMiscStorage::convertToStateString( + const SpeakerManagerStorageState::ChannelState& state) noexcept { JsonGenerator generator; generator.addMember(CHANNEL_VOLUME_KEY, state.channelVolume); generator.addMember(CHANNEL_MUTE_STATUS_KEY, state.channelMuteStatus); return generator.toString(); } -std::string SpeakerManagerMiscStorage::convertToStateString(const SpeakerManagerStorageState& state) { +std::string SpeakerManagerMiscStorage::convertToStateString(const SpeakerManagerStorageState& state) noexcept { ACSDK_DEBUG5(LX(__func__)); JsonGenerator generator; generator.addRawJsonMember(SPEAKER_CHANNEL_STATE, convertToStateString(state.speakerChannelState)); @@ -166,7 +175,7 @@ std::string SpeakerManagerMiscStorage::convertToStateString(const SpeakerManager return generator.toString(); } -bool SpeakerManagerMiscStorage::saveState(const SpeakerManagerStorageState& state) { +bool SpeakerManagerMiscStorage::saveState(const SpeakerManagerStorageState& state) noexcept { std::string stateString = convertToStateString(state); if (!m_miscStorage->put(COMPONENT_NAME, COMPONENT_STATE_TABLE, COMPONENT_STATE_KEY, stateString)) { ACSDK_ERROR(LX("saveStateFailed") @@ -180,5 +189,4 @@ bool SpeakerManagerMiscStorage::saveState(const SpeakerManagerStorageState& stat } } // namespace speakerManager -} // namespace capabilityAgents } // namespace alexaClientSDK diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/test/CMakeLists.txt b/CapabilityAgents/SpeakerManager/SpeakerManager/test/CMakeLists.txt new file mode 100644 index 0000000000..2d2df6e18e --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/test/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.1 FATAL_ERROR) + +if(BUILD_TESTING) + add_library(acsdkSpeakerManagerTestLib INTERFACE) + target_include_directories(acsdkSpeakerManagerTestLib INTERFACE "${acsdkSpeakerManager_SOURCE_DIR}/test/include") + + set(INCLUDE_PATH "${acsdkSpeakerManager_SOURCE_DIR}/privateInclude") + discover_unit_tests("${INCLUDE_PATH}" "acsdkSpeakerManager;UtilsCommonTestLib;SDKInterfacesTests;AVSCommonTestLib;acsdkSpeakerManagerTestLib") +endif() diff --git a/CapabilityAgents/SpeakerManager/test/ChannelVolumeManagerTest.cpp b/CapabilityAgents/SpeakerManager/SpeakerManager/test/ChannelVolumeManagerTest.cpp similarity index 95% rename from CapabilityAgents/SpeakerManager/test/ChannelVolumeManagerTest.cpp rename to CapabilityAgents/SpeakerManager/SpeakerManager/test/ChannelVolumeManagerTest.cpp index 9f3219623d..23aa44cb4d 100644 --- a/CapabilityAgents/SpeakerManager/test/ChannelVolumeManagerTest.cpp +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/test/ChannelVolumeManagerTest.cpp @@ -17,22 +17,23 @@ #include #include -#include "SpeakerManager/ChannelVolumeManager.h" +#include namespace alexaClientSDK { -namespace capabilityAgents { namespace speakerManager { namespace test { + using namespace avsCommon::avs; using namespace avsCommon::avs::speakerConstants; using namespace avsCommon::sdkInterfaces; using namespace avsCommon::sdkInterfaces::test; using namespace ::testing; -using namespace alexaClientSDK::capabilityAgents::speakerManager; /// Initial Settings for underlying @c SpeakerInterface. static const SpeakerInterface::SpeakerSettings INITIAL_SETTINGS{AVS_SET_VOLUME_MAX / 2, false}; +/// @brief Test fixture for ChannelVolumeManager. +/// @ingroup Test_acsdkSpeakerManager class ChannelVolumeManagerTest : public Test { public: /// SetUp before each test. @@ -69,7 +70,7 @@ void ChannelVolumeManagerTest::TearDown() { } static int8_t defaultVolumeCurve(int8_t currentVolume) { - const float lowerBreakPointFraction = 0.20, upperBreakPointFraction = 0.40; + const float lowerBreakPointFraction = 0.20f, upperBreakPointFraction = 0.40f; const int8_t lowerBreakPoint = static_cast(AVS_SET_VOLUME_MAX * lowerBreakPointFraction); const int8_t upperBreakPoint = static_cast(AVS_SET_VOLUME_MAX * upperBreakPointFraction); @@ -135,7 +136,7 @@ TEST_F(ChannelVolumeManagerTest, test_volumeIsRestoredToLatestUnduckedVolume) { ASSERT_TRUE(unit->startDucking()); // set unducked volume - auto newUnduckedVolume = INITIAL_SETTINGS.volume * 2; + auto newUnduckedVolume = static_cast(INITIAL_SETTINGS.volume * 2); ASSERT_EQ(true, unit->setUnduckedVolume(newUnduckedVolume)); // when stopDucking is called : volume should be restored to unducked volume @@ -173,5 +174,4 @@ TEST_F(ChannelVolumeManagerTest, test_stopDuckingWhenAlreadyUnducked) { } } // namespace test } // namespace speakerManager -} // namespace capabilityAgents } // namespace alexaClientSDK diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/test/Namespaces.dox b/CapabilityAgents/SpeakerManager/SpeakerManager/test/Namespaces.dox new file mode 100644 index 0000000000..e8f96a463e --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/test/Namespaces.dox @@ -0,0 +1,24 @@ +/* + * 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. + */ + +/** + * @namespace alexaClientSDK::speakerManager::test + * @brief Unit Tests and Mocks for Speaker API Capability Agent + * + * This namespace contains unit test for Speaker API capability agent implementation. + * + * @see Lib_acsdkSpeakerManager + * @ingroup Test_acsdkSpeakerManager + */ diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/test/SpeakerManagerConfigHelperTest.cpp b/CapabilityAgents/SpeakerManager/SpeakerManager/test/SpeakerManagerConfigHelperTest.cpp new file mode 100644 index 0000000000..97ffd40592 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/test/SpeakerManagerConfigHelperTest.cpp @@ -0,0 +1,271 @@ +/* + * 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 + +namespace alexaClientSDK { +namespace speakerManager { +namespace test { + +using namespace avsCommon::avs; +using namespace avsCommon::avs::speakerConstants; +using namespace avsCommon::sdkInterfaces; +using namespace ::testing; + +/// @addtogroup Test_acsdkSpeakerManager +/// @{ + +/// @brief Test fixture for SpeakerManagerConfigHelper. +class SpeakerManagerConfigHelperTest : public Test { +public: + SpeakerManagerConfigHelperTest(); + +protected: + /// SetUp before each test. + void SetUp() override; + + /// TearDown after each test. + void TearDown() override; + + /// Upstream interface mock + std::shared_ptr> m_stubStorage; + /// Upstream interface mock + std::shared_ptr m_stubConfig; +}; + +/// @} + +SpeakerManagerConfigHelperTest::SpeakerManagerConfigHelperTest() : m_stubStorage() { +} + +void SpeakerManagerConfigHelperTest::SetUp() { + m_stubStorage = std::make_shared>(); + m_stubConfig = std::make_shared>(); +} + +void SpeakerManagerConfigHelperTest::TearDown() { + m_stubStorage.reset(); + m_stubConfig.reset(); +} + +TEST_F(SpeakerManagerConfigHelperTest, test_initDoesntCallLoadSave) { + EXPECT_CALL(*m_stubStorage, loadState(_)).Times(0); + EXPECT_CALL(*m_stubStorage, saveState(_)).Times(0); + + SpeakerManagerConfigHelper helper(m_stubConfig, m_stubStorage); +} + +TEST_F(SpeakerManagerConfigHelperTest, test_getPersistentStorageFromConfiguration) { + EXPECT_CALL(*m_stubStorage, loadState(_)).Times(0); + EXPECT_CALL(*m_stubStorage, saveState(_)).Times(0); + + // Provide a valid configuration. + EXPECT_CALL(*m_stubConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = true; + return true; + })); + + SpeakerManagerConfigHelper helper(m_stubConfig, m_stubStorage); + ASSERT_TRUE(helper.getPersistentStorage()); +} + +TEST_F(SpeakerManagerConfigHelperTest, test_getMinUnmuteVolumeFromConfiguration) { + EXPECT_CALL(*m_stubStorage, loadState(_)).Times(0); + EXPECT_CALL(*m_stubStorage, saveState(_)).Times(0); + + // Provide a valid configuration. + EXPECT_CALL(*m_stubConfig, getMinUnmuteVolume(_)).Times(1).WillOnce(Invoke([](std::uint8_t& res) { + res = 3; + return true; + })); + + SpeakerManagerConfigHelper helper(m_stubConfig, m_stubStorage); + ASSERT_EQ(3, helper.getMinUnmuteVolume()); +} + +TEST_F(SpeakerManagerConfigHelperTest, test_getPersistentStorageReturnsDefaults) { + EXPECT_CALL(*m_stubStorage, loadState(_)).Times(0); + EXPECT_CALL(*m_stubStorage, saveState(_)).Times(0); + + // Provide an empty configuration. + EXPECT_CALL(*m_stubStorage, loadState(_)).Times(0); + + SpeakerManagerConfigHelper helper(m_stubConfig, m_stubStorage); + ASSERT_FALSE(helper.getPersistentStorage()); +} + +TEST_F(SpeakerManagerConfigHelperTest, test_getMinUnmuteVolumeReturnsDefaults) { + EXPECT_CALL(*m_stubStorage, loadState(_)).Times(0); + EXPECT_CALL(*m_stubStorage, saveState(_)).Times(0); + + // Provide an empty configuration. + EXPECT_CALL(*m_stubConfig, getMinUnmuteVolume(_)).Times(1).WillOnce(Return(false)); + + SpeakerManagerConfigHelper helper(m_stubConfig, m_stubStorage); + ASSERT_EQ(MIN_UNMUTE_VOLUME, helper.getMinUnmuteVolume()); +} + +TEST_F(SpeakerManagerConfigHelperTest, test_getRestoreMuteStateReturnsDefaults) { + // Provide an empty configuration. + EXPECT_CALL(*m_stubConfig, getRestoreMuteState(_)).Times(1).WillOnce(Return(false)); + + SpeakerManagerConfigHelper helper(m_stubConfig, m_stubStorage); + ASSERT_TRUE(helper.getRestoreMuteState()); +} + +TEST_F(SpeakerManagerConfigHelperTest, test_getPersistentStorageReturnsTrue) { + EXPECT_CALL(*m_stubConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = true; + return true; + })); + + SpeakerManagerConfigHelper helper(m_stubConfig, m_stubStorage); + ASSERT_TRUE(helper.getPersistentStorage()); +} + +TEST_F(SpeakerManagerConfigHelperTest, test_getPersistentStorageReturnsFalse) { + EXPECT_CALL(*m_stubConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = false; + return true; + })); + + SpeakerManagerConfigHelper helper(m_stubConfig, m_stubStorage); + ASSERT_FALSE(helper.getPersistentStorage()); +} + +TEST_F(SpeakerManagerConfigHelperTest, test_getRestoreMuteStateReturnsTrue) { + EXPECT_CALL(*m_stubConfig, getRestoreMuteState(_)).Times(1).WillOnce(Invoke([](bool& restoreMuteState) { + restoreMuteState = true; + return true; + })); + + SpeakerManagerConfigHelper helper(m_stubConfig, m_stubStorage); + ASSERT_TRUE(helper.getRestoreMuteState()); +} + +TEST_F(SpeakerManagerConfigHelperTest, test_getRestoreMuteStateReturnsFalse) { + EXPECT_CALL(*m_stubConfig, getRestoreMuteState(_)).Times(1).WillOnce(Invoke([](bool& restoreMuteState) { + restoreMuteState = false; + return true; + })); + + SpeakerManagerConfigHelper helper(m_stubConfig, m_stubStorage); + ASSERT_FALSE(helper.getRestoreMuteState()); +} + +TEST_F(SpeakerManagerConfigHelperTest, test_loadStateDelegate) { + EXPECT_CALL(*m_stubStorage, loadState(_)).Times(1); + EXPECT_CALL(*m_stubStorage, saveState(_)).Times(0); + + SpeakerManagerConfigHelper helper(m_stubConfig, m_stubStorage); + + ON_CALL(*m_stubStorage, loadState(_)).WillByDefault(Invoke([](SpeakerManagerStorageState& state) { + state.speakerChannelState.channelVolume = 10; + state.speakerChannelState.channelMuteStatus = true; + state.alertsChannelState.channelMuteStatus = false; + state.alertsChannelState.channelVolume = 20; + return true; + })); + + SpeakerManagerStorageState state = {{255, false}, {255, false}}; + helper.loadState(state); + + EXPECT_EQ(10, state.speakerChannelState.channelVolume); + EXPECT_TRUE(state.speakerChannelState.channelMuteStatus); + EXPECT_EQ(20, state.alertsChannelState.channelVolume); + EXPECT_FALSE(state.alertsChannelState.channelMuteStatus); +} + +TEST_F(SpeakerManagerConfigHelperTest, test_loadStateFromConfig) { + EXPECT_CALL(*m_stubStorage, loadState(_)).Times(1); + EXPECT_CALL(*m_stubStorage, saveState(_)).Times(0); + + EXPECT_CALL(*m_stubConfig, getDefaultSpeakerVolume(_)) + .Times(1) + .WillOnce(Invoke([](std::uint8_t& defaultSpeakerVolume) { + defaultSpeakerVolume = 5; + return true; + })); + EXPECT_CALL(*m_stubConfig, getDefaultAlertsVolume(_)) + .Times(1) + .WillOnce(Invoke([](std::uint8_t& defaultSpeakerVolume) { + defaultSpeakerVolume = 6; + return true; + })); + + SpeakerManagerConfigHelper helper(m_stubConfig, m_stubStorage); + + ON_CALL(*m_stubStorage, loadState(_)).WillByDefault(Return(false)); + + SpeakerManagerStorageState state = {{255, true}, {255, true}}; + helper.loadState(state); + + EXPECT_EQ(5, state.speakerChannelState.channelVolume); + EXPECT_FALSE(state.speakerChannelState.channelMuteStatus); + EXPECT_EQ(6, state.alertsChannelState.channelVolume); + EXPECT_FALSE(state.alertsChannelState.channelMuteStatus); +} + +TEST_F(SpeakerManagerConfigHelperTest, test_loadStateDefaults) { + EXPECT_CALL(*m_stubStorage, loadState(_)).Times(1); + EXPECT_CALL(*m_stubStorage, saveState(_)).Times(0); + + EXPECT_CALL(*m_stubConfig, getDefaultSpeakerVolume(_)).Times(1).WillOnce(Return(false)); + EXPECT_CALL(*m_stubConfig, getDefaultAlertsVolume(_)).Times(1).WillOnce(Return(false)); + + SpeakerManagerConfigHelper helper(m_stubConfig, m_stubStorage); + + ON_CALL(*m_stubStorage, loadState(_)).WillByDefault(Return(false)); + + SpeakerManagerStorageState state = {{255, false}, {255, false}}; + helper.loadState(state); + + EXPECT_EQ(DEFAULT_SPEAKER_VOLUME, state.speakerChannelState.channelVolume); + EXPECT_FALSE(state.speakerChannelState.channelMuteStatus); + EXPECT_EQ(DEFAULT_ALERTS_VOLUME, state.alertsChannelState.channelVolume); + EXPECT_FALSE(state.alertsChannelState.channelMuteStatus); +} + +TEST_F(SpeakerManagerConfigHelperTest, test_saveState) { + EXPECT_CALL(*m_stubStorage, loadState(_)).Times(0); + EXPECT_CALL(*m_stubStorage, saveState(_)).Times(1); + + SpeakerManagerConfigHelper helper(m_stubConfig, m_stubStorage); + SpeakerManagerStorageState saved = {{0, true}, {0, true}}; + + ON_CALL(*m_stubStorage, saveState(_)).WillByDefault(Invoke([&saved](const SpeakerManagerStorageState& state) { + saved = state; + return true; + })); + + SpeakerManagerStorageState state = {{255, false}, {255, false}}; + ASSERT_TRUE(helper.saveState(state)); + + ASSERT_EQ(255, saved.speakerChannelState.channelVolume); + ASSERT_FALSE(saved.speakerChannelState.channelMuteStatus); + ASSERT_EQ(255, saved.alertsChannelState.channelVolume); + ASSERT_FALSE(saved.alertsChannelState.channelMuteStatus); +} + +} // namespace test +} // namespace speakerManager +} // namespace alexaClientSDK diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/test/SpeakerManagerConfigTest.cpp b/CapabilityAgents/SpeakerManager/SpeakerManager/test/SpeakerManagerConfigTest.cpp new file mode 100644 index 0000000000..9cb9e8e86d --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/test/SpeakerManagerConfigTest.cpp @@ -0,0 +1,276 @@ +/* + * 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 + +namespace alexaClientSDK { +namespace speakerManager { +namespace test { + +using namespace avsCommon::utils::configuration; +using namespace ::testing; + +/// @addtogroup Test_acsdkSpeakerManager +/// @{ +/// Configuration without speaker manager root. +static const auto JSON_TEST_CONFIG_MISSING = R"( +{ +})"; +/// Configuration with speaker manager root but without entries. +static const auto JSON_TEST_CONFIG_EMPTY = R"( +{ + "speakerManagerCapabilityAgent": {} +})"; +/// Configuration with speaker manager root with persistentStorage. +static const auto JSON_TEST_CONFIG_PERSISTENT_STORAGE = R"( +{ + "speakerManagerCapabilityAgent": { + "persistentStorage": true + } +})"; +/// Configuration with speaker manager root with minUnmuteVolume. +static const auto JSON_TEST_CONFIG_MIN_UNMUTE_VOLUME = R"( +{ + "speakerManagerCapabilityAgent": { + "minUnmuteVolume": 3 + } +})"; +/// Configuration with speaker manager root with defaultSpeakerVolume. +static const auto JSON_TEST_CONFIG_DEFAULT_SPEAKER_VOLUME = R"( +{ + "speakerManagerCapabilityAgent": { + "defaultSpeakerVolume": 5 + } +})"; +/// Configuration with speaker manager root with defaultAlertsVolume. +static const auto JSON_TEST_CONFIG_DEFAULT_ALERTS_VOLUME = R"( +{ + "speakerManagerCapabilityAgent": { + "defaultAlertsVolume": 6 + } +})"; +/// Configuration with speaker manager root with defaultAlertsVolume. +static const auto JSON_TEST_CONFIG_RESTORE_MUTE_STATE = R"( +{ + "speakerManagerCapabilityAgent": { + "restoreMuteState": false + } +})"; +/// Full configuration. +static const auto JSON_TEST_CONFIG = R"( +{ + "speakerManagerCapabilityAgent": { + "persistentStorage": true, + "minUnmuteVolume": 3, + "defaultSpeakerVolume": 5, + "defaultAlertsVolume": 6, + "restoreMuteState": true + } +} +)"; + +/// @brief Test fixture for SpeakerManagerConfig. +class SpeakerManagerConfigTest : public Test { +protected: + /** + * Configure ConfigurationNode with a given string. + * + * @param jsonConfig Configuration string. + * @return True on success. + */ + bool configureJsonConfig(const char* jsonConfig); + + /// SetUp before each test. + void SetUp() override; + + /// TearDown after each test. + void TearDown() override; +}; + +/// @} + +void SpeakerManagerConfigTest::SetUp() { + ConfigurationNode::uninitialize(); +} + +void SpeakerManagerConfigTest::TearDown() { + ConfigurationNode::uninitialize(); +} + +bool SpeakerManagerConfigTest::configureJsonConfig(const char* jsonConfig) { + ConfigurationNode::uninitialize(); + std::shared_ptr istr(new std::istringstream(jsonConfig)); + return ConfigurationNode::initialize({istr}); +} + +/// Validate nothing breaks when config is missing. +TEST_F(SpeakerManagerConfigTest, test_ValidateMissingConfig) { + ASSERT_TRUE(configureJsonConfig(JSON_TEST_CONFIG_MISSING)); + + SpeakerManagerConfig config; + bool persistentStorage; + bool restoreMuteState; + uint8_t minUnmuteVolume; + uint8_t defaultSpeakerVolume; + uint8_t defaultAlertsVolume; + + ASSERT_FALSE(config.getPersistentStorage(persistentStorage)); + ASSERT_FALSE(config.getRestoreMuteState(restoreMuteState)); + ASSERT_FALSE(config.getMinUnmuteVolume(minUnmuteVolume)); + ASSERT_FALSE(config.getDefaultSpeakerVolume(defaultSpeakerVolume)); + ASSERT_FALSE(config.getDefaultAlertsVolume(defaultAlertsVolume)); +} + +/// Validate nothing breaks when config is empty. +TEST_F(SpeakerManagerConfigTest, test_ValidateEmptyConfig) { + ASSERT_TRUE(configureJsonConfig(JSON_TEST_CONFIG_EMPTY)); + + SpeakerManagerConfig config; + bool persistentStorage; + bool restoreMuteState; + uint8_t minUnmuteVolume; + uint8_t defaultSpeakerVolume; + uint8_t defaultAlertsVolume; + + ASSERT_FALSE(config.getPersistentStorage(persistentStorage)); + ASSERT_FALSE(config.getRestoreMuteState(restoreMuteState)); + ASSERT_FALSE(config.getMinUnmuteVolume(minUnmuteVolume)); + ASSERT_FALSE(config.getDefaultSpeakerVolume(defaultSpeakerVolume)); + ASSERT_FALSE(config.getDefaultAlertsVolume(defaultAlertsVolume)); +} + +/// Validate persistentVolume entry is read correctly. +TEST_F(SpeakerManagerConfigTest, test_ValidatePersistentStorageConfig) { + ASSERT_TRUE(configureJsonConfig(JSON_TEST_CONFIG_PERSISTENT_STORAGE)); + + SpeakerManagerConfig config; + bool persistentStorage = false; + bool restoreMuteState; + uint8_t minUnmuteVolume; + uint8_t defaultSpeakerVolume; + uint8_t defaultAlertsVolume; + + ASSERT_TRUE(config.getPersistentStorage(persistentStorage)); + ASSERT_TRUE(persistentStorage); + ASSERT_FALSE(config.getRestoreMuteState(restoreMuteState)); + ASSERT_FALSE(config.getMinUnmuteVolume(minUnmuteVolume)); + ASSERT_FALSE(config.getDefaultSpeakerVolume(defaultSpeakerVolume)); + ASSERT_FALSE(config.getDefaultAlertsVolume(defaultAlertsVolume)); +} + +/// Validate restoreMuteState entry is read correctly. +TEST_F(SpeakerManagerConfigTest, test_ValidateRestoreMuteStateConfig) { + ASSERT_TRUE(configureJsonConfig(JSON_TEST_CONFIG_RESTORE_MUTE_STATE)); + + SpeakerManagerConfig config; + bool persistentStorage; + bool restoreMuteState = true; + uint8_t minUnmuteVolume; + uint8_t defaultSpeakerVolume; + uint8_t defaultAlertsVolume; + + ASSERT_FALSE(config.getPersistentStorage(persistentStorage)); + ASSERT_TRUE(config.getRestoreMuteState(restoreMuteState)); + ASSERT_FALSE(restoreMuteState); + ASSERT_FALSE(config.getMinUnmuteVolume(minUnmuteVolume)); + ASSERT_FALSE(config.getDefaultSpeakerVolume(defaultSpeakerVolume)); + ASSERT_FALSE(config.getDefaultAlertsVolume(defaultAlertsVolume)); +} + +/// Validate minUnmuteVolume entry is read correctly. +TEST_F(SpeakerManagerConfigTest, test_ValidateMinUnmuteVolumeConfig) { + ASSERT_TRUE(configureJsonConfig(JSON_TEST_CONFIG_MIN_UNMUTE_VOLUME)); + + SpeakerManagerConfig config; + bool persistentStorage; + bool restoreMuteState; + uint8_t minUnmuteVolume = 0; + uint8_t defaultSpeakerVolume; + uint8_t defaultAlertsVolume; + + ASSERT_FALSE(config.getPersistentStorage(persistentStorage)); + ASSERT_FALSE(config.getRestoreMuteState(restoreMuteState)); + ASSERT_TRUE(config.getMinUnmuteVolume(minUnmuteVolume)); + ASSERT_EQ(3u, minUnmuteVolume); + ASSERT_FALSE(config.getDefaultSpeakerVolume(defaultSpeakerVolume)); + ASSERT_FALSE(config.getDefaultAlertsVolume(defaultAlertsVolume)); +} + +/// Validate defaultSpeakerVolume entry is read correctly. +TEST_F(SpeakerManagerConfigTest, test_ValidateDefaultSpeakerVolumeConfig) { + ASSERT_TRUE(configureJsonConfig(JSON_TEST_CONFIG_DEFAULT_SPEAKER_VOLUME)); + + SpeakerManagerConfig config; + bool persistentStorage; + bool restoreMuteState; + uint8_t minUnmuteVolume; + uint8_t defaultSpeakerVolume = 0; + uint8_t defaultAlertsVolume; + + ASSERT_FALSE(config.getPersistentStorage(persistentStorage)); + ASSERT_FALSE(config.getRestoreMuteState(restoreMuteState)); + ASSERT_FALSE(config.getMinUnmuteVolume(minUnmuteVolume)); + ASSERT_TRUE(config.getDefaultSpeakerVolume(defaultSpeakerVolume)); + ASSERT_EQ(5u, defaultSpeakerVolume); + ASSERT_FALSE(config.getDefaultAlertsVolume(defaultAlertsVolume)); +} + +/// Validate defaultAlertsVolume entry is read correctly. +TEST_F(SpeakerManagerConfigTest, test_ValidateDefaultAlertsVolumeConfig) { + ASSERT_TRUE(configureJsonConfig(JSON_TEST_CONFIG_DEFAULT_ALERTS_VOLUME)); + + SpeakerManagerConfig config; + bool persistentStorage; + bool restoreMuteState; + uint8_t minUnmuteVolume; + uint8_t defaultSpeakerVolume; + uint8_t defaultAlertsVolume = 0; + + ASSERT_FALSE(config.getPersistentStorage(persistentStorage)); + ASSERT_FALSE(config.getRestoreMuteState(restoreMuteState)); + ASSERT_FALSE(config.getMinUnmuteVolume(minUnmuteVolume)); + ASSERT_FALSE(config.getDefaultSpeakerVolume(defaultSpeakerVolume)); + ASSERT_TRUE(config.getDefaultAlertsVolume(defaultAlertsVolume)); + ASSERT_EQ(6u, defaultAlertsVolume); +} + +TEST_F(SpeakerManagerConfigTest, test_ValidateAllValues) { + ASSERT_TRUE(configureJsonConfig(JSON_TEST_CONFIG)); + + SpeakerManagerConfig config; + bool persistentStorage; + bool restoreMuteState; + uint8_t minUnmuteVolume; + uint8_t defaultSpeakerVolume; + uint8_t defaultAlertsVolume; + + ASSERT_TRUE(config.getPersistentStorage(persistentStorage)); + ASSERT_TRUE(persistentStorage); + ASSERT_TRUE(config.getRestoreMuteState(restoreMuteState)); + ASSERT_TRUE(restoreMuteState); + ASSERT_TRUE(config.getMinUnmuteVolume(minUnmuteVolume)); + ASSERT_EQ(3u, minUnmuteVolume); + ASSERT_TRUE(config.getDefaultSpeakerVolume(defaultSpeakerVolume)); + ASSERT_EQ(5u, defaultSpeakerVolume); + ASSERT_TRUE(config.getDefaultAlertsVolume(defaultAlertsVolume)); + ASSERT_EQ(6u, defaultAlertsVolume); +} + +} // namespace test +} // namespace speakerManager +} // namespace alexaClientSDK diff --git a/CapabilityAgents/SpeakerManager/test/SpeakerManagerMiscStorageTest.cpp b/CapabilityAgents/SpeakerManager/SpeakerManager/test/SpeakerManagerMiscStorageTest.cpp similarity index 95% rename from CapabilityAgents/SpeakerManager/test/SpeakerManagerMiscStorageTest.cpp rename to CapabilityAgents/SpeakerManager/SpeakerManager/test/SpeakerManagerMiscStorageTest.cpp index b672df5a81..d0e2a6b9e5 100644 --- a/CapabilityAgents/SpeakerManager/test/SpeakerManagerMiscStorageTest.cpp +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/test/SpeakerManagerMiscStorageTest.cpp @@ -13,30 +13,27 @@ * permissions and limitations under the License. */ +#include #include #include +#include #include -#include -#include -#include - -#include "SpeakerManager/SpeakerManagerMiscStorage.h" +#include namespace alexaClientSDK { -namespace capabilityAgents { namespace speakerManager { namespace test { using namespace avsCommon::avs; using namespace avsCommon::avs::speakerConstants; using namespace avsCommon::sdkInterfaces; -using namespace avsCommon::sdkInterfaces::storage; -using namespace rapidjson; using namespace ::testing; -using namespace alexaClientSDK::capabilityAgents::speakerManager; -class MockMiscStorageInterface : public MiscStorageInterface { +/// @addtogroup Test_acsdkSpeakerManager +/// @{ + +class MockMiscStorage : public storage::MiscStorageInterface { public: MOCK_METHOD0(createDatabase, bool()); MOCK_METHOD0(open, bool()); @@ -71,6 +68,7 @@ static const std::string JSON_PAYLOAD = " }" "}"; +/// @brief Test fixture for SpeakerManagerMiscStorage. class SpeakerManagerMiscStorageTest : public ::testing::TestWithParam> { public: /// SetUp before each test. @@ -81,11 +79,13 @@ class SpeakerManagerMiscStorageTest : public ::testing::TestWithParam> m_stubMiscStorage; + std::shared_ptr> m_stubMiscStorage; }; +/// @} + void SpeakerManagerMiscStorageTest::SetUp() { - m_stubMiscStorage = std::make_shared>(); + m_stubMiscStorage = std::make_shared>(); } void SpeakerManagerMiscStorageTest::TearDown() { @@ -287,5 +287,4 @@ TEST_F(SpeakerManagerMiscStorageTest, test_parseJson) { } // namespace test } // namespace speakerManager -} // namespace capabilityAgents -} // namespace alexaClientSDK \ No newline at end of file +} // namespace alexaClientSDK diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/test/SpeakerManagerTest.cpp b/CapabilityAgents/SpeakerManager/SpeakerManager/test/SpeakerManagerTest.cpp new file mode 100644 index 0000000000..3a5590d445 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/test/SpeakerManagerTest.cpp @@ -0,0 +1,2794 @@ +/* + * 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 + +namespace alexaClientSDK { +namespace speakerManager { +namespace test { + +using namespace avsCommon::avs; +using namespace avsCommon::avs::attachment::test; +using namespace avsCommon::avs::speakerConstants; +using namespace avsCommon::sdkInterfaces; +using namespace avsCommon::sdkInterfaces::test; +using namespace avsCommon::utils::memory; +using namespace rapidjson; +using namespace ::testing; + +/// @addtogroup Test_acsdkSpeakerManager +/// @{ + +/// Timeout when waiting for futures to be set. +static std::chrono::milliseconds TIMEOUT(1000); + +/// The @c MessageId identifer. +static const auto MESSAGE_ID = "messageId"; + +/// A @c SetVolume/AdjustVolume payload. +static const auto VOLUME_PAYLOAD = R"( +{ + "volume":100 +} +)"; + +/// A @c SetMute payload. +static const auto MUTE_PAYLOAD = R"( +{ + "mute": true +} +)"; + +/// A @c SetMute payload to unmute. +static const auto UNMUTE_PAYLOAD = R"( +{ + "mute": false +} +)"; + +#ifdef ENABLE_MAXVOLUME_SETTING +/// A valid value to be used as maximum volume limit. +static const int8_t VALID_MAXIMUM_VOLUME_LIMIT = AVS_SET_VOLUME_MAX - 10; + +/// An invalid maximum volume limit value +static const int8_t INVALID_MAXIMUM_VOLUME_LIMIT = AVS_SET_VOLUME_MAX + 10; +#endif + +/// A valid delta to adjust the volume. +static const int8_t VALID_VOLUME_ADJUSTMENT = 10; + +/** + * @brief Extend MockSpeakerManagerStorage with helpers. + */ +class MockSpeakerManagerStorageWithHelpers : public MockSpeakerManagerStorage { +public: + /** + * Constructs object with default values and configures methods to return success. + */ + MockSpeakerManagerStorageWithHelpers() { + setDefaults(); + setSuccessMode(); + } + + /** + * @brief Sets default values for cached channels' values. + */ + void setDefaults() { + m_state = {{AVS_SET_VOLUME_MIN, UNMUTE}, {AVS_SET_VOLUME_MIN, UNMUTE}}; + } + + /** + * @brief Configures mocked methods to load/store cached values. + */ + void setSuccessMode() { + ON_CALL(*this, loadState(_)).WillByDefault(Invoke([this](SpeakerManagerStorageState& state) { + state = this->m_state; + return true; + })); + ON_CALL(*this, saveState(_)).WillByDefault(Invoke([this](const SpeakerManagerStorageState& state) { + this->m_state = state; + return true; + })); + } + + /** + * @brief Configures mocked methods to fail. + */ + void setFailureMode() { + ON_CALL(*this, loadState(_)).WillByDefault(Return(false)); + ON_CALL(*this, saveState(_)).WillByDefault(Return(false)); + } + + /// @brief Cached values for channels. + SpeakerManagerStorageState m_state; +}; + +/** + * @brief Test fixture for SpeakerManager unit tests. + */ +class SpeakerManagerTest : public ::testing::TestWithParam> { +public: + /// SetUp before each test. + void SetUp(); + + /// TearDown after each test. + void TearDown(); + + /// CleanUp and reset the SpeakerManager. + void cleanUp(); + + /// Function to wait for @c m_wakeSetCompleteFuture to be set. + void wakeOnSetCompleted(); + + /// Helper function to get unique @c Type. + std::set getUniqueTypes(std::vector>& groups); + +#ifdef ENABLE_MAXVOLUME_SETTING + /** + * Helper function for create and sent a directive + * + * @param directiveName The directive name. One of SetVolume or AdjustVolume. + * @param volume The value of the volume files within the directive. + */ + void createAndSendVolumeDirective(const std::string directiveName, const int8_t volume); +#endif + + std::vector> createChannelVolumeInterfaces() { + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + return {channelVolumeInterface}; + } + + /// A constructor which initializes the promises and futures needed for the test class. + SpeakerManagerTest() : + 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; + + /// The metric recorder. + std::shared_ptr m_metricRecorder; + + /** + * Set this to a nice mock. The only instance of the mock being called is the setStateProvider member, which we + * explicitly test. + */ + std::shared_ptr> m_mockContextManager; + + /// Configuration interface mock. + std::shared_ptr m_mockConfig; + + /// Storage interface mock. + std::shared_ptr m_mockStorage; + + /// A strict mock that allows the test to strictly monitor the messages sent. + std::shared_ptr> m_mockMessageSender; + + /// A strict mock that allows the test to strictly monitor the exceptions being sent. + std::shared_ptr> m_mockExceptionSender; + + /// A strict mock that allows the test to strictly monitor the handling of directives. + std::unique_ptr> m_mockDirectiveHandlerResult; + + /// A mock to allow testing of the observer callback behavior. + std::shared_ptr> m_observer; + + /// A pointer to an instance of the SpeakerManager that will be instantiated per test. + std::shared_ptr m_speakerManager; +}; + +/// @brief Test fixture for SpeakerManager. +void SpeakerManagerTest::SetUp() { + m_mockConfig = std::make_shared>(); + m_mockStorage = std::make_shared>(); + + m_metricRecorder = std::make_shared>(); + m_mockContextManager = std::make_shared>(); + m_mockMessageSender = std::make_shared>(); + m_mockExceptionSender = std::make_shared>(); + m_mockDirectiveHandlerResult = make_unique>(); + m_observer = std::make_shared>(); +} + +/// @} + +void SpeakerManagerTest::TearDown() { + if (m_speakerManager) { + m_speakerManager->shutdown(); + m_speakerManager.reset(); + } +} + +void SpeakerManagerTest::wakeOnSetCompleted() { + m_wakeSetCompletedPromise.set_value(); +} + +/** + * Helper function to get unique @c Type from a vector of speakers. + */ +std::set SpeakerManagerTest::getUniqueTypes( + std::vector>& groups) { + std::set types; + for (auto item : groups) { + types.insert(item->getSpeakerType()); + } + return types; +} + +#ifdef ENABLE_MAXVOLUME_SETTING +void SpeakerManagerTest::createAndSendVolumeDirective(const std::string directiveName, const int8_t volume) { + EXPECT_CALL(*(m_mockDirectiveHandlerResult.get()), setCompleted()) + .Times(1) + .WillOnce(InvokeWithoutArgs(this, &SpeakerManagerTest::wakeOnSetCompleted)); + + static int id = 1; + const std::string messageId = MESSAGE_ID + std::to_string(id++); + std::string payload = + "{" + "\"volume\":" + + std::to_string(volume) + "}"; + + // Create Directive. + auto attachmentManager = std::make_shared>(); + auto avsMessageHeader = std::make_shared(SET_VOLUME.nameSpace, directiveName, messageId); + + std::shared_ptr directive = + AVSDirective::create("", avsMessageHeader, payload, attachmentManager, ""); + + m_speakerManager->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); + m_speakerManager->CapabilityAgent::handleDirective(messageId); + m_wakeSetCompletedFuture.wait_for(TIMEOUT); +} + +static int8_t getSpeakerVolume(std::shared_ptr channelVolumeInterface) { + SpeakerInterface::SpeakerSettings speakerSettings; + channelVolumeInterface->getSpeakerSettings(&speakerSettings); + return speakerSettings.volume; +} +#endif + +/// Helper function to generate the VolumeState in JSON for the ContextManager. +std::string generateVolumeStateJson(SpeakerInterface::SpeakerSettings settings) { + rapidjson::Document state(rapidjson::kObjectType); + state.AddMember(VOLUME_KEY, settings.volume, state.GetAllocator()); + state.AddMember(MUTED_KEY, settings.mute, state.GetAllocator()); + + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + if (!state.Accept(writer)) { + return ""; + } + + return buffer.GetString(); +} + +/** + * Tests creating the SpeakerManager with a null contextManager. + */ +TEST_F(SpeakerManagerTest, test_nullContextManager) { + auto channelVolumeInterfaces = createChannelVolumeInterfaces(); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + channelVolumeInterfaces, + nullptr, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + ASSERT_EQ(m_speakerManager, nullptr); +} + +/** + * Tests creating the SpeakerManager with a null messageSender. + */ +TEST_F(SpeakerManagerTest, test_nullMessageSender) { + auto channelVolumeInterfaces = createChannelVolumeInterfaces(); + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + channelVolumeInterfaces, + m_mockContextManager, + nullptr, + m_mockExceptionSender, + m_metricRecorder); + + ASSERT_EQ(m_speakerManager, nullptr); +} + +/** + * Tests creating the SpeakerManager with a null exceptionSender. + */ +TEST_F(SpeakerManagerTest, test_nullExceptionSender) { + auto channelVolumeInterfaces = createChannelVolumeInterfaces(); + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + channelVolumeInterfaces, + m_mockContextManager, + m_mockMessageSender, + nullptr, + m_metricRecorder); + + ASSERT_EQ(m_speakerManager, nullptr); +} + +/** + * Tests creating the SpeakerManager with no channelVolumeInterfaces. + */ +TEST_F(SpeakerManagerTest, test_noChannelVolumeInterfaces) { + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + ASSERT_NE(m_speakerManager, nullptr); +} + +/** + * Tests that adding a channel volume interface does not overwrite existing default volume settings when persistent + * storage is enabled. + */ +TEST_F(SpeakerManagerTest, test_addChannelVolumeInterfaceDoesNotOverwriteDefaults) { + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + + // Enable Persistent Storage Setting. + EXPECT_CALL(*m_mockConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = true; + return true; + })); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + channelVolumeInterface->setUnduckedVolume(AVS_SET_VOLUME_MAX); + channelVolumeInterface->setMute(MUTE); + + SpeakerInterface::SpeakerSettings settings; + ASSERT_TRUE(channelVolumeInterface->getSpeakerSettings(&settings)); + ASSERT_EQ(settings.volume, AVS_SET_VOLUME_MAX); + ASSERT_EQ(settings.mute, MUTE); + + m_speakerManager->addChannelVolumeInterface(channelVolumeInterface); + auto future = m_speakerManager->getSpeakerSettings(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, &settings); + ASSERT_TRUE(future.get()); + ASSERT_EQ(settings.volume, AVS_SET_VOLUME_MIN); + ASSERT_EQ(settings.mute, UNMUTE); +} + +/** + * Tests that the SpeakerManager initially provides the state at constructor time. + */ +TEST_F(SpeakerManagerTest, test_contextManagerSetStateConstructor) { + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(DEFAULT_SETTINGS), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + auto groups = createChannelVolumeInterfaces(); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groups, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); +} + +/** + * Test setVolume with a value that's under the bounds. The operation should fail. + */ +TEST_F(SpeakerManagerTest, test_setVolumeUnderBounds) { + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + + // Expect call on initialization + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(0)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {channelVolumeInterface}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + // Expect no more calls + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(0)); + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); + + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties; + std::future future = m_speakerManager->setVolume( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_SET_VOLUME_MIN - 1, properties); + ASSERT_FALSE(future.get()); +} + +/** + * Test setVolume with a value that's under the bounds with persistent storage enabled. The operation should fail. + */ +TEST_F(SpeakerManagerTest, test_setVolumeUnderBoundsWithPersistentStorage) { + // Enable Persistent Storage Setting. + EXPECT_CALL(*m_mockConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = true; + return true; + })); + + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + + // Expect call on initialization + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(1)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {channelVolumeInterface}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + // Expect no more calls + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(0)); + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); + + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties; + std::future future = m_speakerManager->setVolume( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_SET_VOLUME_MIN - 1, properties); + ASSERT_FALSE(future.get()); +} + +/** + * Test setVolume with a value that's over the bounds. The operation should fail. + */ +TEST_F(SpeakerManagerTest, test_setVolumeOverBounds) { + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + + // Expect call on initialization. + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(0)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {channelVolumeInterface}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + // Expect no more calls. + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(0)); + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties; + std::future future = m_speakerManager->setVolume( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_SET_VOLUME_MAX + 1, properties); + ASSERT_FALSE(future.get()); +} + +/** + * Test setVolume with a value that's over the bounds with persistent storage enabled. The operation should fail. + */ +TEST_F(SpeakerManagerTest, test_setVolumeOverBoundsWithPersistentStorage) { + // Enable Persistent Storage Setting. + EXPECT_CALL(*m_mockConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = true; + return true; + })); + + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + + // Expect call on initialization. + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(1)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {channelVolumeInterface}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + // Expect no more calls. + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(0)); + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties; + std::future future = m_speakerManager->setVolume( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_SET_VOLUME_MAX + 1, properties); + ASSERT_FALSE(future.get()); +} + +/** + * Test adjustVolume with a value that's under the bounds. The operation should fail. + */ +TEST_F(SpeakerManagerTest, test_adjustVolumeUnderBounds) { + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + + // Expect call on initialization. + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(0)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {channelVolumeInterface}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + // Expect no more calls. + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(0)); + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); + m_speakerManager->addSpeakerManagerObserver(m_observer); + + SpeakerManagerInterface::NotificationProperties properties; + std::future future = m_speakerManager->adjustVolume( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_ADJUST_VOLUME_MIN - 1, properties); + ASSERT_FALSE(future.get()); +} + +/** + * Test adjustVolume with a value that's under the bounds with persistent storage enabled. The operation should fail. + */ +TEST_F(SpeakerManagerTest, test_adjustVolumeUnderBoundsWithPersistentStorage) { + // Enable Persistent Storage Setting. + EXPECT_CALL(*m_mockConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = true; + return true; + })); + + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + + // Expect call on initialization. + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(1)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {channelVolumeInterface}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + // Expect no more calls. + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(0)); + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); + m_speakerManager->addSpeakerManagerObserver(m_observer); + + SpeakerManagerInterface::NotificationProperties properties; + std::future future = m_speakerManager->adjustVolume( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_ADJUST_VOLUME_MIN - 1, properties); + ASSERT_FALSE(future.get()); +} + +/** + * Test adjustVolume with a value that's over the bounds. The operation should fail. + */ +TEST_F(SpeakerManagerTest, test_adjustVolumeOverBounds) { + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + // Expect call on initialization. + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(0)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {channelVolumeInterface}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + // Expect no more calls. + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(0)); + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties; + std::future future = m_speakerManager->adjustVolume( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_ADJUST_VOLUME_MAX + 1, properties); + ASSERT_FALSE(future.get()); +} + +/** + * Test adjustVolume with a value that's over the bounds with persistent storage enabled. The operation should fail. + */ +TEST_F(SpeakerManagerTest, test_adjustVolumeOverBoundsWithPersistentStorage) { + // Enable Persistent Storage Setting. + EXPECT_CALL(*m_mockConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = true; + return true; + })); + + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + // Expect call on initialization. + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(1)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {channelVolumeInterface}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + // Expect no more calls. + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(0)); + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties; + std::future future = m_speakerManager->adjustVolume( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_ADJUST_VOLUME_MAX + 1, properties); + ASSERT_FALSE(future.get()); +} + +/** + * Test if one speaker is out of sync, getSpeakingSettings should return the cached value correctly. + */ +TEST_F(SpeakerManagerTest, test_getCachedSettings) { + // Prepare two speakers with the same type AVS_SPEAKER_VOLUME + auto channelVolumeInterface1 = std::make_shared>(); + auto channelVolumeInterface2 = std::make_shared>(); + channelVolumeInterface1->DelegateToReal(); + channelVolumeInterface2->DelegateToReal(); + // Get speaker settings from the first speaker of each type during initialization. + EXPECT_CALL(*channelVolumeInterface1, getSpeakerSettings(_)).Times(Exactly(1)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {channelVolumeInterface1, channelVolumeInterface2}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + // If a speaker changes its volume and is out of sync with the rest speakers of the same type, querying speaker + // settings from SpeakerManager should return the cached volume correctly. + channelVolumeInterface2->setUnduckedVolume(AVS_SET_VOLUME_MAX); + channelVolumeInterface2->setMute(MUTE); + SpeakerInterface::SpeakerSettings settings; + std::future future = + m_speakerManager->getSpeakerSettings(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, &settings); + ASSERT_TRUE(future.get()); + ASSERT_EQ(settings.volume, DEFAULT_SETTINGS.volume); + ASSERT_EQ(settings.mute, DEFAULT_SETTINGS.mute); + + ASSERT_TRUE(channelVolumeInterface2->getSpeakerSettings(&settings)); + ASSERT_EQ(settings.volume, AVS_SET_VOLUME_MAX); + ASSERT_EQ(settings.mute, MUTE); +} + +/** + * Test adjustVolume when the adjusted volume is unchanged. Should not send an event. + */ +TEST_F(SpeakerManagerTest, test_eventNotSentWhenAdjustVolumeUnchanged) { + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + + auto groupVec = std::vector>{channelVolumeInterface}; + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + // The test adjusts the volume by AVS_ADJUST_VOLUME_MIN, which results in the lowest volume possible. + SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MIN, UNMUTE}; + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties; + + for (auto type : getUniqueTypes(groupVec)) { + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged(SpeakerManagerObserverInterface::Source::LOCAL_API, type, expectedSettings)) + .Times(Exactly(1)); + if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(0)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)) + .Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } + + std::future future = m_speakerManager->adjustVolume(type, AVS_ADJUST_VOLUME_MIN, properties); + ASSERT_TRUE(future.get()); + } +} + +/** + * Test adjustVolume when the adjusted volume is unchanged with persistent storage enabled. Should not send an event. + */ +TEST_F(SpeakerManagerTest, test_eventNotSentWhenAdjustVolumeUnchangedWithPersistentStorage) { + // Enable Persistent Storage Setting. + EXPECT_CALL(*m_mockConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = true; + return true; + })); + + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + + auto groupVec = std::vector>{channelVolumeInterface}; + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + // The test adjusts the volume by AVS_ADJUST_VOLUME_MIN, which results in the lowest volume possible. + SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MIN, UNMUTE}; + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties; + + for (auto type : getUniqueTypes(groupVec)) { + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged(SpeakerManagerObserverInterface::Source::LOCAL_API, type, expectedSettings)) + .Times(Exactly(1)); + if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(0)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)) + .Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } + + std::future future = m_speakerManager->adjustVolume(type, AVS_ADJUST_VOLUME_MIN, properties); + ASSERT_TRUE(future.get()); + } +} + +/** + * Test setVolume when the new volume is unchanged. Should not send an event. + */ +TEST_F(SpeakerManagerTest, test_eventNotSentWhenSetVolumeUnchanged) { + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(AVS_SET_VOLUME_MIN)).Times(Exactly(1)); + + auto groupVec = std::vector>{channelVolumeInterface}; + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MIN, UNMUTE}; + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties; + + for (auto type : getUniqueTypes(groupVec)) { + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged(SpeakerManagerObserverInterface::Source::LOCAL_API, type, expectedSettings)) + .Times(Exactly(1)); + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); + if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(0)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)) + .Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } + + std::future future = m_speakerManager->setVolume(type, AVS_SET_VOLUME_MIN, properties); + ASSERT_TRUE(future.get()); + } +} + +/** + * Test setVolume when the new volume is unchanged with persistent storage enabled. Should not send an event. + */ +TEST_F(SpeakerManagerTest, test_eventNotSentWhenSetVolumeUnchangedWithPersistentStorage) { + // Enable Persistent Storage Setting. + EXPECT_CALL(*m_mockConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = true; + return true; + })); + + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(AVS_SET_VOLUME_MIN)).Times(Exactly(2)); + + auto groupVec = std::vector>{channelVolumeInterface}; + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MIN, UNMUTE}; + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties; + + for (auto type : getUniqueTypes(groupVec)) { + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged(SpeakerManagerObserverInterface::Source::LOCAL_API, type, expectedSettings)) + .Times(Exactly(1)); + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); + if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(0)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)) + .Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } + + std::future future = m_speakerManager->setVolume(type, AVS_SET_VOLUME_MIN, properties); + ASSERT_TRUE(future.get()); + } +} + +/** + * Test getConfiguration and ensure that all directives are handled. + */ +TEST_F(SpeakerManagerTest, test_getConfiguration) { + auto channelVolumeInterfaceVec = createChannelVolumeInterfaces(); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + channelVolumeInterfaceVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + auto configuration = m_speakerManager->getConfiguration(); + auto neitherNonBlockingPolicy = BlockingPolicy(BlockingPolicy::MEDIUMS_NONE, false); + ASSERT_EQ(configuration[SET_VOLUME], neitherNonBlockingPolicy); + ASSERT_EQ(configuration[ADJUST_VOLUME], neitherNonBlockingPolicy); + ASSERT_EQ(configuration[SET_MUTE], neitherNonBlockingPolicy); +} + +/** + * Test that adding duplicated ChannelVolumeInterface instances in the SpeakerManager works correctly. + */ +TEST_F(SpeakerManagerTest, test_addDuplicatedChannelVolumeInterfaces) { + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + std::vector> channelVolumeInterfaceVec = {channelVolumeInterface, + channelVolumeInterface}; + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + channelVolumeInterfaceVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(1)); + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); + SpeakerManagerInterface::NotificationProperties properties; + std::future future = m_speakerManager->adjustVolume( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_ADJUST_VOLUME_MAX, properties); + ASSERT_TRUE(future.get()); +} + +/** + * Test that adding a null observer does not cause any errors in the SpeakerManager. + */ +TEST_F(SpeakerManagerTest, test_addNullObserver) { + auto channelVolumeInterfaceVec = createChannelVolumeInterfaces(); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + channelVolumeInterfaceVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + m_speakerManager->addSpeakerManagerObserver(nullptr); + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(2)); + SpeakerManagerInterface::NotificationProperties properties; + + m_speakerManager->setVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_SET_VOLUME_MAX, properties) + .wait(); + m_speakerManager->adjustVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_ADJUST_VOLUME_MAX, properties) + .wait(); + m_speakerManager->setMute(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, MUTE, properties).wait(); + m_speakerManager->onExternalSpeakerSettingsUpdate( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, {AVS_SET_VOLUME_MAX, MUTE}, properties); + m_wakeSetCompletedFuture.wait_for(TIMEOUT); +} + +/** + * Test that removing an observer works correctly. + */ +TEST_F(SpeakerManagerTest, test_removeSpeakerManagerObserver) { + auto channelVolumeInterfaceVec = createChannelVolumeInterfaces(); + + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(2)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + channelVolumeInterfaceVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + m_speakerManager->addSpeakerManagerObserver(m_observer); + m_speakerManager->removeSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties; + + m_speakerManager->setVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_SET_VOLUME_MAX, properties) + .wait(); + m_speakerManager->adjustVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_ADJUST_VOLUME_MAX, properties) + .wait(); + m_speakerManager->setMute(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, MUTE, properties).wait(); + m_speakerManager->onExternalSpeakerSettingsUpdate( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, {AVS_SET_VOLUME_MAX, MUTE}, properties); + m_wakeSetCompletedFuture.wait_for(TIMEOUT); +} + +/** + * Test that removing a null observer does not cause any errors in the SpeakerManager. + */ +TEST_F(SpeakerManagerTest, test_removeNullObserver) { + auto channelVolumeInterfaceVec = createChannelVolumeInterfaces(); + + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(2)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + channelVolumeInterfaceVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + m_speakerManager->removeSpeakerManagerObserver(nullptr); + SpeakerManagerInterface::NotificationProperties properties; + + m_speakerManager->setVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_SET_VOLUME_MAX, properties) + .wait(); + m_speakerManager->adjustVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_ADJUST_VOLUME_MAX, properties) + .wait(); + m_speakerManager->setMute(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, MUTE, properties).wait(); + m_speakerManager->onExternalSpeakerSettingsUpdate( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, {AVS_SET_VOLUME_MAX, MUTE}, properties); + m_wakeSetCompletedFuture.wait_for(TIMEOUT); +} + +/** + * Test retry logic for SetVolume on speaker type AVS_SPEAKER_VOLUME. Returning false once for speaker->setVolume() + * triggers retry and when successful returns the future of value true. + */ +TEST_F(SpeakerManagerTest, test_retryAndApplySettingsForSetVolume) { + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {channelVolumeInterface}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + auto retryTimes = 0; + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).WillRepeatedly(InvokeWithoutArgs([&retryTimes] { + return retryTimes++ > 0; + })); + + SpeakerManagerInterface::NotificationProperties properties; + std::future future = + m_speakerManager->setVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_SET_VOLUME_MIN, properties); + ASSERT_TRUE(future.get()); +} + +/** + * Test retry logic for AdjustVolume on speakers of type AVS_SPEAKER_VOLUME. Return false once for the second speaker + * during adjustVolume() to trigger a retry. The delta should not be applied again to the first speaker during retry. + */ +TEST_F(SpeakerManagerTest, test_retryAndApplySettingsForAdjustVolume) { + auto channelVolumeInterface1 = std::make_shared>(); + auto channelVolumeInterface2 = std::make_shared>(); + channelVolumeInterface1->DelegateToReal(); + channelVolumeInterface2->DelegateToReal(); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {channelVolumeInterface1, channelVolumeInterface2}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + auto retryTimes = 0; + EXPECT_CALL(*channelVolumeInterface2, setUnduckedVolume(_)).WillRepeatedly(InvokeWithoutArgs([&retryTimes] { + return retryTimes++ > 0; + })); + + // Expect volumeChanged event. + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); + std::future future = m_speakerManager->adjustVolume( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, + VALID_VOLUME_ADJUSTMENT, + SpeakerManagerInterface::NotificationProperties()); + ASSERT_TRUE(future.get()); + + SpeakerInterface::SpeakerSettings settings1; + ASSERT_TRUE(channelVolumeInterface1->getSpeakerSettings(&settings1)); + ASSERT_EQ(settings1.volume, DEFAULT_SETTINGS.volume + VALID_VOLUME_ADJUSTMENT); + + SpeakerInterface::SpeakerSettings speakerSettings; + std::future settingsFuture = + m_speakerManager->getSpeakerSettings(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, &speakerSettings); + ASSERT_TRUE(settingsFuture.get()); + ASSERT_EQ(speakerSettings.volume, DEFAULT_SETTINGS.volume + VALID_VOLUME_ADJUSTMENT); +} + +/** + * Test retry logic for SetMute on speaker type AVS_SPEAKER_VOLUME. Returning false once for speaker->setMute() + * triggers retry and when successful returns the future of value true. + */ +TEST_F(SpeakerManagerTest, test_retryAndApplySettingsForSetMute) { + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {channelVolumeInterface}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + auto retryTimes = 0; + EXPECT_CALL(*channelVolumeInterface, setMute(_)).WillRepeatedly(InvokeWithoutArgs([&retryTimes] { + return retryTimes++ > 0; + })); + + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); + SpeakerManagerInterface::NotificationProperties properties; + + std::future future = + m_speakerManager->setMute(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, MUTE, properties); + ASSERT_TRUE(future.get()); +} + +/** + * Test retryAndApplySettings() failure for setVolume, adjustVolume and setMute on speaker type AVS_SPEAKER_VOLUME. + * Repeatedly returning false for adjustVolume() and setMute() to trigger retries. After retrying maximum times, + * returning the future of false. + */ +TEST_F(SpeakerManagerTest, test_retryAndApplySettingsFails) { + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {channelVolumeInterface}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).WillRepeatedly(Return(false)); + EXPECT_CALL(*channelVolumeInterface, setMute(_)).WillRepeatedly(Return(false)); + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(0); + + std::future setVolumeResult = m_speakerManager->setVolume( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, + AVS_SET_VOLUME_MIN, + SpeakerManagerInterface::NotificationProperties()); + ASSERT_FALSE(setVolumeResult.get()); + + std::future adjustVolumeResult = m_speakerManager->adjustVolume( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, + VALID_VOLUME_ADJUSTMENT, + SpeakerManagerInterface::NotificationProperties()); + ASSERT_FALSE(adjustVolumeResult.get()); + + std::future setMuteResult = m_speakerManager->setMute( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, MUTE, SpeakerManagerInterface::NotificationProperties()); + ASSERT_FALSE(setMuteResult.get()); + + SpeakerInterface::SpeakerSettings speakerSettings; + std::future settingsFuture = + m_speakerManager->getSpeakerSettings(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, &speakerSettings); + ASSERT_TRUE(settingsFuture.get()); + ASSERT_EQ(speakerSettings.volume, DEFAULT_SETTINGS.volume); + ASSERT_EQ(speakerSettings.mute, DEFAULT_SETTINGS.mute); +} + +#ifdef ENABLE_MAXVOLUME_SETTING +/** + * Test that setting a maximum volume limit succeeds and a local call to setVolume or adjustVolume will + * completely fail. + */ +TEST_F(SpeakerManagerTest, test_setMaximumVolumeLimit) { + auto avsChannelVolumeInterface = + std::make_shared>(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME); + avsChannelVolumeInterface->DelegateToReal(); + auto alertsChannelVolumeInterface = + std::make_shared>(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME); + alertsChannelVolumeInterface->DelegateToReal(); + + avsChannelVolumeInterface->setUnduckedVolume(VALID_MAXIMUM_VOLUME_LIMIT - 1); + alertsChannelVolumeInterface->setUnduckedVolume(VALID_MAXIMUM_VOLUME_LIMIT - 1); + + // Expect volumeChanged event. + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); + EXPECT_CALL(*avsChannelVolumeInterface, setUnduckedVolume(_)).Times(AtLeast(1)); + EXPECT_CALL(*alertsChannelVolumeInterface, setUnduckedVolume(_)).Times(AtLeast(1)); + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(0); + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(1)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {avsChannelVolumeInterface, alertsChannelVolumeInterface}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + SpeakerManagerInterface::NotificationProperties properties; + + EXPECT_TRUE(m_speakerManager->setMaximumVolumeLimit(VALID_MAXIMUM_VOLUME_LIMIT).get()); + + // Local change either with setVolume will set to limit but with adjustVolume will fail + EXPECT_TRUE( + m_speakerManager + ->setVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, VALID_MAXIMUM_VOLUME_LIMIT + 1, properties) + .get()); + EXPECT_FALSE( + m_speakerManager + ->adjustVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, VALID_MAXIMUM_VOLUME_LIMIT + 1, properties) + .get()); + + // The volume went to upper limit. + EXPECT_EQ(getSpeakerVolume(avsChannelVolumeInterface), VALID_MAXIMUM_VOLUME_LIMIT); + EXPECT_EQ(getSpeakerVolume(alertsChannelVolumeInterface), VALID_MAXIMUM_VOLUME_LIMIT); + + // Increase the volume by 2, so end result will exceed the limit. + EXPECT_TRUE(m_speakerManager->adjustVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, 2, properties).get()); + + // Following the 2nd adjustVolume, the volume will change to the limit. + EXPECT_EQ(getSpeakerVolume(alertsChannelVolumeInterface), VALID_MAXIMUM_VOLUME_LIMIT); +} + +/** + * Test that if a new limit was set while the volume was higher than the new limit, operation will succeed and the + * volume will be decreased. + */ + +TEST_F(SpeakerManagerTest, testSetMaximumVolumeLimitWhileVolumeIsHigher) { + auto avsChannelVolumeInterface = + std::make_shared>(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME); + auto alertsChannelVolumeInterface = + std::make_shared>(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME); + + avsChannelVolumeInterface->DelegateToReal(); + alertsChannelVolumeInterface->DelegateToReal(); + + EXPECT_TRUE(avsChannelVolumeInterface->setUnduckedVolume(VALID_MAXIMUM_VOLUME_LIMIT + 1)); + EXPECT_TRUE(alertsChannelVolumeInterface->setUnduckedVolume(VALID_MAXIMUM_VOLUME_LIMIT + 1)); + + EXPECT_CALL(*avsChannelVolumeInterface, setUnduckedVolume(VALID_MAXIMUM_VOLUME_LIMIT)).Times(1); + EXPECT_CALL(*alertsChannelVolumeInterface, setUnduckedVolume(VALID_MAXIMUM_VOLUME_LIMIT)).Times(1); + + // Expect volumeChanged event. + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); + + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(1)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {avsChannelVolumeInterface, alertsChannelVolumeInterface}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + EXPECT_TRUE(m_speakerManager->setMaximumVolumeLimit(VALID_MAXIMUM_VOLUME_LIMIT).get()); + + EXPECT_EQ(getSpeakerVolume(avsChannelVolumeInterface), VALID_MAXIMUM_VOLUME_LIMIT); + EXPECT_EQ(getSpeakerVolume(alertsChannelVolumeInterface), VALID_MAXIMUM_VOLUME_LIMIT); +} + +/** + * Test that SetVolume directive with volume > limit should set the volume to the limit + */ + +TEST_F(SpeakerManagerTest, testAVSSetVolumeHigherThanLimit) { + avsCommon::utils::logger::getConsoleLogger()->setLevel(avsCommon::utils::logger::Level::DEBUG9); + auto avsChannelVolumeInterface = + std::make_shared>(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME); + auto alertsChannelVolumeInterface = + std::make_shared>(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME); + + avsChannelVolumeInterface->DelegateToReal(); + alertsChannelVolumeInterface->DelegateToReal(); + + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(1)); + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); + + EXPECT_TRUE(avsChannelVolumeInterface->setUnduckedVolume(VALID_MAXIMUM_VOLUME_LIMIT - 1)); + EXPECT_TRUE(alertsChannelVolumeInterface->setUnduckedVolume(VALID_MAXIMUM_VOLUME_LIMIT - 1)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {avsChannelVolumeInterface, alertsChannelVolumeInterface}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + EXPECT_TRUE(m_speakerManager->setMaximumVolumeLimit(VALID_MAXIMUM_VOLUME_LIMIT).get()); + + createAndSendVolumeDirective(SET_VOLUME.name, VALID_MAXIMUM_VOLUME_LIMIT + 1); + + ASSERT_EQ(getSpeakerVolume(avsChannelVolumeInterface), VALID_MAXIMUM_VOLUME_LIMIT); + ASSERT_EQ(getSpeakerVolume(alertsChannelVolumeInterface), VALID_MAXIMUM_VOLUME_LIMIT); +} + +/** + * Test that a call to @c setMaximumVolumeLimit with invalid value fails. + */ +TEST_F(SpeakerManagerTest, testSetMaximumVolumeLimitWithInvalidValue) { + auto avsChannelVolumeInterface = createChannelVolumeInterfaces(); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + avsChannelVolumeInterface, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + EXPECT_FALSE(m_speakerManager->setMaximumVolumeLimit(INVALID_MAXIMUM_VOLUME_LIMIT).get()); +} +#endif + +/** + * Create different combinations of @c Type for parameterized tests (TEST_P). + */ +INSTANTIATE_TEST_CASE_P( + Parameterized, + SpeakerManagerTest, + // clang-format off + ::testing::Values( + std::vector{ + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME + }, + std::vector{ + ChannelVolumeInterface::Type::AVS_ALERTS_VOLUME + }, + std::vector{ + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME + }, + std::vector{ + ChannelVolumeInterface::Type::AVS_ALERTS_VOLUME, + ChannelVolumeInterface::Type::AVS_ALERTS_VOLUME, + }, + std::vector{ + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, + ChannelVolumeInterface::Type::AVS_ALERTS_VOLUME, + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, + ChannelVolumeInterface::Type::AVS_ALERTS_VOLUME + })); +// clang-format on + +/** + * Parameterized test for setVolume. One event should be sent if an AVS_SPEAKER_VOLUME typed speaker is modified. + */ +TEST_P(SpeakerManagerTest, test_setVolume) { + std::vector> groupVec; + + for (auto& typeOfSpeaker : GetParam()) { + auto group = std::make_shared>(typeOfSpeaker); + group->DelegateToReal(); + EXPECT_CALL(*group, setUnduckedVolume(_)).Times(Exactly(0)); + EXPECT_CALL(*group, setUnduckedVolume(AVS_SET_VOLUME_MAX)).Times(Exactly(1)); + groupVec.push_back(group); + } + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MAX, UNMUTE}; + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties(SpeakerManagerObserverInterface::Source::DIRECTIVE); + + for (auto type : getUniqueTypes(groupVec)) { + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged(SpeakerManagerObserverInterface::Source::DIRECTIVE, type, expectedSettings)) + .Times(Exactly(1)); + if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)) + .Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } + + std::future future = m_speakerManager->setVolume(type, AVS_SET_VOLUME_MAX, properties); + ASSERT_TRUE(future.get()); + } +} + +/** + * Parameterized test for setVolume with persistent storage enabled. One event should be sent if an AVS_SPEAKER_VOLUME + * typed speaker is modified. + */ +TEST_P(SpeakerManagerTest, test_setVolumeWithPersistentStorage) { + // Enable Persistent Storage Setting. + EXPECT_CALL(*m_mockConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = true; + return true; + })); + + std::vector> groupVec; + + for (auto& typeOfSpeaker : GetParam()) { + auto group = std::make_shared>(typeOfSpeaker); + group->DelegateToReal(); + EXPECT_CALL(*group, setUnduckedVolume(_)).Times(Exactly(1)); + EXPECT_CALL(*group, setUnduckedVolume(AVS_SET_VOLUME_MAX)).Times(Exactly(1)); + groupVec.push_back(group); + } + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MAX, UNMUTE}; + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties(SpeakerManagerObserverInterface::Source::DIRECTIVE); + + for (auto type : getUniqueTypes(groupVec)) { + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(1)); + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged(SpeakerManagerObserverInterface::Source::DIRECTIVE, type, expectedSettings)) + .Times(Exactly(1)); + if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)) + .Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } + + std::future future = m_speakerManager->setVolume(type, AVS_SET_VOLUME_MAX, properties); + ASSERT_TRUE(future.get()); + } +} + +/** + * Parameterized test for onExternalSpeakerSettingsUpdate. One event should be sent if an AVS_SPEAKER_VOLUME typed + * speaker is modified. + */ +TEST_P(SpeakerManagerTest, test_onExternalSpeakerSettingsUpdate) { + std::vector> groupVec; + + for (auto& typeOfSpeaker : GetParam()) { + auto group = std::make_shared>(typeOfSpeaker); + group->DelegateToReal(); + groupVec.push_back(group); + } + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MAX, UNMUTE}; + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties; + + for (auto type : getUniqueTypes(groupVec)) { + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged(SpeakerManagerObserverInterface::Source::LOCAL_API, type, expectedSettings)) + .Times(Exactly(1)); + if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)) + .Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } + + m_speakerManager->onExternalSpeakerSettingsUpdate(type, expectedSettings, properties); + m_wakeSetCompletedFuture.wait_for(TIMEOUT); + } +} + +/** + * Parameterized test for onExternalSpeakerSettingsUpdate with persistent storage enabled. One event should be sent if + * an AVS_SPEAKER_VOLUME typed speaker is modified. + */ +TEST_P(SpeakerManagerTest, test_onExternalSpeakerSettingsUpdateWithPersistentStorage) { + // Enable Persistent Storage Setting. + EXPECT_CALL(*m_mockConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = true; + return true; + })); + + std::vector> groupVec; + + for (auto& typeOfSpeaker : GetParam()) { + auto group = std::make_shared>(typeOfSpeaker); + group->DelegateToReal(); + groupVec.push_back(group); + } + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MAX, UNMUTE}; + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties; + + for (auto type : getUniqueTypes(groupVec)) { + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(1)); + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged(SpeakerManagerObserverInterface::Source::LOCAL_API, type, expectedSettings)) + .Times(Exactly(1)); + if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)) + .Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } + + m_speakerManager->onExternalSpeakerSettingsUpdate(type, expectedSettings, properties); + m_wakeSetCompletedFuture.wait_for(TIMEOUT); + } +} + +/** + * Test onExternalSpeakerSettingsUpdate when the new volume is unchanged. Should not send an event. + */ +TEST_F(SpeakerManagerTest, test_eventNotSentWhenOnExternalSpeakerSettingsUpdateUnchanged) { + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + // expect call during initialization + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(AVS_SET_VOLUME_MIN)).Times(Exactly(0)); + + auto groupVec = std::vector>{channelVolumeInterface}; + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MIN, UNMUTE}; + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties; + + for (auto type : getUniqueTypes(groupVec)) { + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged(SpeakerManagerObserverInterface::Source::LOCAL_API, type, expectedSettings)) + .Times(Exactly(1)); + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); + if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(0)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)) + .Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } + + m_speakerManager->onExternalSpeakerSettingsUpdate(type, expectedSettings, properties); + m_wakeSetCompletedFuture.wait_for(TIMEOUT); + } +} + +/** + * Test onExternalSpeakerSettingsUpdate when the new volume is unchanged with persistent storage enabled. Should not + * send an event. + */ +TEST_F(SpeakerManagerTest, test_eventNotSentWhenOnExternalSpeakerSettingsUpdateUnchangedWithPersistentStorage) { + // Enable Persistent Storage Setting. + EXPECT_CALL(*m_mockConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = true; + return true; + })); + + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + // expect call during initialization + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(AVS_SET_VOLUME_MIN)).Times(Exactly(1)); + + auto groupVec = std::vector>{channelVolumeInterface}; + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MIN, UNMUTE}; + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties; + + for (auto type : getUniqueTypes(groupVec)) { + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged(SpeakerManagerObserverInterface::Source::LOCAL_API, type, expectedSettings)) + .Times(Exactly(1)); + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); + if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(0)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)) + .Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } + + m_speakerManager->onExternalSpeakerSettingsUpdate(type, expectedSettings, properties); + m_wakeSetCompletedFuture.wait_for(TIMEOUT); + } +} + +/** + * Test onExternalSpeakerSettingsUpdate with a value that's under the bounds. The operation should fail. + */ +TEST_F(SpeakerManagerTest, test_onExternalSpeakerSettingsUpdateUnderBounds) { + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + + // Expect call on initialization + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(0)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {channelVolumeInterface}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + // Expect calls with volume set to minimum + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(1)); + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties; + m_speakerManager->onExternalSpeakerSettingsUpdate( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, {AVS_SET_VOLUME_MIN - 1, MUTE}, properties); + m_wakeSetCompletedFuture.wait_for(TIMEOUT); + + SpeakerInterface::SpeakerSettings settings; + // Query SpeakerMananger for speaker settings + auto future = m_speakerManager->getSpeakerSettings(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, &settings); + ASSERT_TRUE(future.get()); + ASSERT_EQ(settings.volume, AVS_SET_VOLUME_MIN); + ASSERT_EQ(settings.mute, MUTE); +} + +/** + * Test onExternalSpeakerSettingsUpdate with a value that's under the bounds with persistent storage enabled. + * The operation should fail. + */ +TEST_F(SpeakerManagerTest, test_onExternalSpeakerSettingsUpdateUnderBoundsWithPersistentStorage) { + // Enable Persistent Storage Setting. + EXPECT_CALL(*m_mockConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = true; + return true; + })); + + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + + // Expect call on initialization + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(1)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {channelVolumeInterface}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + // Expect calls with volume set to minimum + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(1)); + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(1)); + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties; + m_speakerManager->onExternalSpeakerSettingsUpdate( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, {AVS_SET_VOLUME_MIN - 1, MUTE}, properties); + m_wakeSetCompletedFuture.wait_for(TIMEOUT); + + SpeakerInterface::SpeakerSettings settings; + // Query SpeakerMananger for speaker settings + auto future = m_speakerManager->getSpeakerSettings(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, &settings); + ASSERT_TRUE(future.get()); + ASSERT_EQ(settings.volume, AVS_SET_VOLUME_MIN); + ASSERT_EQ(settings.mute, MUTE); +} + +/** + * Test onExternalSpeakerSettingsUpdate with a value that's over the bounds. The operation should fail. + */ +TEST_F(SpeakerManagerTest, test_onExternalSpeakerSettingsUpdateOverBounds) { + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + + // Expect call on initialization. + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(0)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {channelVolumeInterface}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + // Expect no more calls. + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(1)); + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties; + m_speakerManager->onExternalSpeakerSettingsUpdate( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, {AVS_SET_VOLUME_MAX + 1, UNMUTE}, properties); + m_wakeSetCompletedFuture.wait_for(TIMEOUT); + + SpeakerInterface::SpeakerSettings settings; + // Query SpeakerMananger for speaker settings + auto future = m_speakerManager->getSpeakerSettings(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, &settings); + ASSERT_TRUE(future.get()); + ASSERT_EQ(settings.volume, AVS_SET_VOLUME_MAX); + ASSERT_EQ(settings.mute, UNMUTE); +} + +/** + * Test onExternalSpeakerSettingsUpdate with a value that's over the bounds with persistent storage enabled. The + * operation should fail. + */ +TEST_F(SpeakerManagerTest, test_onExternalSpeakerSettingsUpdateOverBoundsWithPersistentStorage) { + // Enable Persistent Storage Setting. + EXPECT_CALL(*m_mockConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = true; + return true; + })); + + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + + // Expect call on initialization. + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(1)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + {channelVolumeInterface}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + // Expect no more calls. + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(1)); + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(1)); + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties; + m_speakerManager->onExternalSpeakerSettingsUpdate( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, {AVS_SET_VOLUME_MAX + 1, UNMUTE}, properties); + m_wakeSetCompletedFuture.wait_for(TIMEOUT); + + SpeakerInterface::SpeakerSettings settings; + // Query SpeakerMananger for speaker settings + auto future = m_speakerManager->getSpeakerSettings(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, &settings); + ASSERT_TRUE(future.get()); + ASSERT_EQ(settings.volume, AVS_SET_VOLUME_MAX); + ASSERT_EQ(settings.mute, UNMUTE); +} + +/** + * Parameterized test for adjustVolume. One event should be sent if an AVS_SPEAKER_VOLUME typed speaker is modified. + */ +TEST_P(SpeakerManagerTest, test_adjustVolume) { + std::vector> groupVec; + + for (auto& typeOfSpeaker : GetParam()) { + auto group = std::make_shared>(typeOfSpeaker); + group->DelegateToReal(); + groupVec.push_back(group); + } + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + // The test adjusts the volume by AVS_ADJUST_VOLUME_MAX, which results in the lowest volume possible. + SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MAX, UNMUTE}; + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties(SpeakerManagerObserverInterface::Source::DIRECTIVE); + + for (auto type : getUniqueTypes(groupVec)) { + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged(SpeakerManagerObserverInterface::Source::DIRECTIVE, type, expectedSettings)) + .Times(Exactly(1)); + if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)) + .Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } + + std::future future = m_speakerManager->adjustVolume(type, AVS_ADJUST_VOLUME_MAX, properties); + ASSERT_TRUE(future.get()); + } +} + +/** + * Parameterized test for adjustVolume with persistent storage enabled. One event should be sent if an + * AVS_SPEAKER_VOLUME typed speaker is modified. + */ +TEST_P(SpeakerManagerTest, test_adjustVolumeWithPersistentStorage) { + // Enable Persistent Storage Setting. + EXPECT_CALL(*m_mockConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = true; + return true; + })); + + std::vector> groupVec; + + for (auto& typeOfSpeaker : GetParam()) { + auto group = std::make_shared>(typeOfSpeaker); + group->DelegateToReal(); + groupVec.push_back(group); + } + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + // The test adjusts the volume by AVS_ADJUST_VOLUME_MAX, which results in the lowest volume possible. + SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MAX, UNMUTE}; + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties(SpeakerManagerObserverInterface::Source::DIRECTIVE); + + for (auto type : getUniqueTypes(groupVec)) { + EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(1)); + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged(SpeakerManagerObserverInterface::Source::DIRECTIVE, type, expectedSettings)) + .Times(Exactly(1)); + if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)) + .Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } + + std::future future = m_speakerManager->adjustVolume(type, AVS_ADJUST_VOLUME_MAX, properties); + ASSERT_TRUE(future.get()); + } +} + +/** + * Parameterized test for setMute. One event should be sent if an AVS_SPEAKER_VOLUME typed speaker is modified. + */ +TEST_P(SpeakerManagerTest, test_setMute) { + std::vector> groupVec; + + for (auto& typeOfSpeaker : GetParam()) { + auto group = std::make_shared>(typeOfSpeaker); + group->DelegateToReal(); + EXPECT_CALL(*group, setMute(_)).Times(Exactly(0)); + EXPECT_CALL(*group, setMute(MUTE)).Times(Exactly(1)); + groupVec.push_back(group); + } + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + SpeakerInterface::SpeakerSettings expectedSettings{DEFAULT_SETTINGS.volume, MUTE}; + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties(SpeakerManagerObserverInterface::Source::DIRECTIVE); + + for (auto type : getUniqueTypes(groupVec)) { + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged(SpeakerManagerObserverInterface::Source::DIRECTIVE, type, expectedSettings)) + .Times(Exactly(1)); + if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)) + .Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } + + std::future future = m_speakerManager->setMute(type, MUTE, properties); + ASSERT_TRUE(future.get()); + } +} + +/** + * Parameterized test for setMute with persistent storage enabled. One event should be sent if an AVS_SPEAKER_VOLUME + * typed speaker is modified. + */ +TEST_P(SpeakerManagerTest, test_setMuteWithPersistentStorage) { + // Enable Persistent Storage Setting. + EXPECT_CALL(*m_mockConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = true; + return true; + })); + + std::vector> groupVec; + + for (auto& typeOfSpeaker : GetParam()) { + auto group = std::make_shared>(typeOfSpeaker); + group->DelegateToReal(); + EXPECT_CALL(*group, setMute(_)).Times(Exactly(1)); + EXPECT_CALL(*group, setMute(MUTE)).Times(Exactly(1)); + groupVec.push_back(group); + } + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + SpeakerInterface::SpeakerSettings expectedSettings{DEFAULT_SETTINGS.volume, MUTE}; + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties(SpeakerManagerObserverInterface::Source::DIRECTIVE); + + for (auto type : getUniqueTypes(groupVec)) { + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged(SpeakerManagerObserverInterface::Source::DIRECTIVE, type, expectedSettings)) + .Times(Exactly(1)); + if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)) + .Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } + + std::future future = m_speakerManager->setMute(type, MUTE, properties); + ASSERT_TRUE(future.get()); + } +} + +/** + * Parameterized test for getSpeakerSettings. Operation should succeed with default speaker settings. + */ +TEST_P(SpeakerManagerTest, test_getSpeakerSettings) { + std::vector> groupVec; + std::set uniqueTypes; + + for (auto& typeOfSpeaker : GetParam()) { + auto group = std::make_shared>(typeOfSpeaker); + group->DelegateToReal(); + + // There should be one call to getSpeakerSettings for the first speaker of each type. + if (uniqueTypes.find(typeOfSpeaker) == uniqueTypes.end()) { + EXPECT_CALL(*group, getSpeakerSettings(_)).Times(AtLeast(1)); + uniqueTypes.insert(typeOfSpeaker); + } + + groupVec.push_back(group); + } + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); + m_speakerManager->addSpeakerManagerObserver(m_observer); + + for (auto speaker : groupVec) { + // SpeakerManager attempts to cache speaker settings initially. No getSpeakerSettings() call should be made to + // each speaker. + auto mockSpeaker = std::dynamic_pointer_cast>(speaker); + ASSERT_TRUE(mockSpeaker); + EXPECT_CALL(*mockSpeaker, getSpeakerSettings(_)).Times(0); + } + + for (auto type : uniqueTypes) { + SpeakerInterface::SpeakerSettings settings; + // Query SpeakerMananger for speaker settings, value should be cached and not queried from each speaker. + std::future future = m_speakerManager->getSpeakerSettings(type, &settings); + ASSERT_TRUE(future.get()); + ASSERT_EQ(settings.volume, DEFAULT_SETTINGS.volume); + ASSERT_EQ(settings.mute, DEFAULT_SETTINGS.mute); + } +} + +/** + * Tests SetVolume Directive. Expect that the volume is unmuted and set, as well at most one + * event is sent. In the event there are no AVS_SPEAKER_VOLUME speakers registered, no event will be sent. + * In addition, only AVS_SPEAKER_VOLUME speakers should be affected. + */ +TEST_P(SpeakerManagerTest, test_setVolumeDirective) { + std::vector> groupVec; + int eventsSent = 0; + SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MAX, UNMUTE}; + + // Create Speaker objects. + for (auto& typeOfSpeaker : GetParam()) { + auto group = std::make_shared>(typeOfSpeaker); + group->DelegateToReal(); + int timesCalled = 0; + if (typeOfSpeaker == ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME) { + timesCalled = 1; + } + + SpeakerInterface::SpeakerSettings temp; + group->getSpeakerSettings(&temp); + if (temp.mute) { + EXPECT_CALL(*group, setMute(_)).Times(Exactly(1)); + EXPECT_CALL(*group, setMute(UNMUTE)).Times(Exactly(timesCalled)); + } + EXPECT_CALL(*group, setUnduckedVolume(_)).Times(Exactly(0)); + EXPECT_CALL(*group, setUnduckedVolume(AVS_SET_VOLUME_MAX)).Times(Exactly(timesCalled)); + + groupVec.push_back(group); + } + + auto uniqueTypes = getUniqueTypes(groupVec); + + // Creation expectations based on type. + if (uniqueTypes.count(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME)) { + eventsSent = 1; + + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged( + SpeakerManagerObserverInterface::Source::DIRECTIVE, + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, + expectedSettings)) + .Times(Exactly(1)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } else { + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(Exactly(0)); + } + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(eventsSent)); + EXPECT_CALL(*(m_mockDirectiveHandlerResult.get()), setCompleted()) + .Times(1) + .WillOnce(InvokeWithoutArgs(this, &SpeakerManagerTest::wakeOnSetCompleted)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + m_speakerManager->addSpeakerManagerObserver(m_observer); + + // Create Directive. + auto attachmentManager = std::make_shared>(); + auto avsMessageHeader = std::make_shared(SET_VOLUME.nameSpace, SET_VOLUME.name, MESSAGE_ID); + std::shared_ptr directive = + AVSDirective::create("", avsMessageHeader, VOLUME_PAYLOAD, attachmentManager, ""); + + m_speakerManager->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); + m_speakerManager->CapabilityAgent::handleDirective(MESSAGE_ID); + m_wakeSetCompletedFuture.wait_for(TIMEOUT); +} + +/** + * Tests SetVolume Directive with persistent storage enabled. Expect that the volume is unmuted and set, as well at most + * one event is sent. In the event there are no AVS_SPEAKER_VOLUME speakers registered, no event will be sent. + * In addition, only AVS_SPEAKER_VOLUME speakers should be affected. + */ +TEST_P(SpeakerManagerTest, test_setVolumeDirectiveWithPersistentStorage) { + // Enable Persistent Storage Setting. + EXPECT_CALL(*m_mockConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = true; + return true; + })); + + std::vector> groupVec; + int eventsSent = 0; + SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MAX, UNMUTE}; + + // Create Speaker objects. + for (auto& typeOfSpeaker : GetParam()) { + auto group = std::make_shared>(typeOfSpeaker); + group->DelegateToReal(); + int timesCalled = 0; + if (typeOfSpeaker == ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME) { + timesCalled = 1; + } + + SpeakerInterface::SpeakerSettings temp; + group->getSpeakerSettings(&temp); + if (temp.mute) { + EXPECT_CALL(*group, setMute(_)).Times(Exactly(1)); + EXPECT_CALL(*group, setMute(UNMUTE)).Times(Exactly(timesCalled)); + } + EXPECT_CALL(*group, setUnduckedVolume(_)).Times(Exactly(1)); + EXPECT_CALL(*group, setUnduckedVolume(AVS_SET_VOLUME_MAX)).Times(Exactly(timesCalled)); + + groupVec.push_back(group); + } + + auto uniqueTypes = getUniqueTypes(groupVec); + + // Creation expectations based on type. + if (uniqueTypes.count(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME)) { + eventsSent = 1; + + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged( + SpeakerManagerObserverInterface::Source::DIRECTIVE, + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, + expectedSettings)) + .Times(Exactly(1)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } else { + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(Exactly(0)); + } + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(eventsSent)); + EXPECT_CALL(*(m_mockDirectiveHandlerResult.get()), setCompleted()) + .Times(1) + .WillOnce(InvokeWithoutArgs(this, &SpeakerManagerTest::wakeOnSetCompleted)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + m_speakerManager->addSpeakerManagerObserver(m_observer); + + // Create Directive. + auto attachmentManager = std::make_shared>(); + auto avsMessageHeader = std::make_shared(SET_VOLUME.nameSpace, SET_VOLUME.name, MESSAGE_ID); + std::shared_ptr directive = + AVSDirective::create("", avsMessageHeader, VOLUME_PAYLOAD, attachmentManager, ""); + + m_speakerManager->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); + m_speakerManager->CapabilityAgent::handleDirective(MESSAGE_ID); + m_wakeSetCompletedFuture.wait_for(TIMEOUT); +} + +/** + * Tests AdjustVolume Directive. Expect that the volume is unmuted and adjusted, as well at most one + * event is sent. In the event there are no AVS_SPEAKER_VOLUME speakers registered, no event will be sent. + * In addition, only AVS_SPEAKER_VOLUME speakers should be affected. + */ +TEST_P(SpeakerManagerTest, test_adjustVolumeDirective) { + std::vector> groupVec; + int eventsSent = 0; + SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MAX, UNMUTE}; + + // Create Speaker objects. + for (auto& typeOfSpeaker : GetParam()) { + auto group = std::make_shared>(typeOfSpeaker); + group->DelegateToReal(); + int timesCalled = 0; + if (typeOfSpeaker == ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME) { + timesCalled = 1; + } + + SpeakerInterface::SpeakerSettings temp; + group->getSpeakerSettings(&temp); + if (temp.mute) { + EXPECT_CALL(*group, setMute(_)).Times(Exactly(1)); + EXPECT_CALL(*group, setMute(UNMUTE)).Times(Exactly(timesCalled)); + } + EXPECT_CALL(*group, setUnduckedVolume(_)).Times(Exactly(0)); + EXPECT_CALL(*group, setUnduckedVolume(AVS_SET_VOLUME_MAX)).Times(Exactly(timesCalled)); + + groupVec.push_back(group); + } + + auto uniqueTypes = getUniqueTypes(groupVec); + + // Creation expectations based on type. + if (uniqueTypes.count(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME)) { + eventsSent = 1; + + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged( + SpeakerManagerObserverInterface::Source::DIRECTIVE, + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, + expectedSettings)) + .Times(Exactly(1)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } else { + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(Exactly(0)); + } + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(eventsSent)); + EXPECT_CALL(*(m_mockDirectiveHandlerResult.get()), setCompleted()) + .Times(1) + .WillOnce(InvokeWithoutArgs(this, &SpeakerManagerTest::wakeOnSetCompleted)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + m_speakerManager->addSpeakerManagerObserver(m_observer); + + // Create Directive. + auto attachmentManager = std::make_shared>(); + auto avsMessageHeader = std::make_shared(ADJUST_VOLUME.nameSpace, ADJUST_VOLUME.name, MESSAGE_ID); + std::shared_ptr directive = + AVSDirective::create("", avsMessageHeader, VOLUME_PAYLOAD, attachmentManager, ""); + + m_speakerManager->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); + m_speakerManager->CapabilityAgent::handleDirective(MESSAGE_ID); + m_wakeSetCompletedFuture.wait_for(TIMEOUT); +} + +/** + * Tests AdjustVolume Directive with persistent storage enabled. Expect that the volume is unmuted and adjusted, as well + * at most one event is sent. In the event there are no AVS_SPEAKER_VOLUME speakers registered, no event will be sent. + * In addition, only AVS_SPEAKER_VOLUME speakers should be affected. + */ +TEST_P(SpeakerManagerTest, test_adjustVolumeDirectiveWithPersistentStorage) { + // Enable Persistent Storage Setting. + EXPECT_CALL(*m_mockConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = true; + return true; + })); + + std::vector> groupVec; + int eventsSent = 0; + SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MAX, UNMUTE}; + + // Create Speaker objects. + for (auto& typeOfSpeaker : GetParam()) { + auto group = std::make_shared>(typeOfSpeaker); + group->DelegateToReal(); + int timesCalled = 0; + if (typeOfSpeaker == ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME) { + timesCalled = 1; + } + + SpeakerInterface::SpeakerSettings temp; + group->getSpeakerSettings(&temp); + if (temp.mute) { + EXPECT_CALL(*group, setMute(_)).Times(Exactly(1)); + EXPECT_CALL(*group, setMute(UNMUTE)).Times(Exactly(timesCalled)); + } + EXPECT_CALL(*group, setUnduckedVolume(_)).Times(Exactly(1)); + EXPECT_CALL(*group, setUnduckedVolume(AVS_SET_VOLUME_MAX)).Times(Exactly(timesCalled)); + + groupVec.push_back(group); + } + + auto uniqueTypes = getUniqueTypes(groupVec); + + // Creation expectations based on type. + if (uniqueTypes.count(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME)) { + eventsSent = 1; + + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged( + SpeakerManagerObserverInterface::Source::DIRECTIVE, + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, + expectedSettings)) + .Times(Exactly(1)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } else { + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(Exactly(0)); + } + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(eventsSent)); + EXPECT_CALL(*(m_mockDirectiveHandlerResult.get()), setCompleted()) + .Times(1) + .WillOnce(InvokeWithoutArgs(this, &SpeakerManagerTest::wakeOnSetCompleted)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + m_speakerManager->addSpeakerManagerObserver(m_observer); + + // Create Directive. + auto attachmentManager = std::make_shared>(); + auto avsMessageHeader = std::make_shared(ADJUST_VOLUME.nameSpace, ADJUST_VOLUME.name, MESSAGE_ID); + std::shared_ptr directive = + AVSDirective::create("", avsMessageHeader, VOLUME_PAYLOAD, attachmentManager, ""); + + m_speakerManager->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); + m_speakerManager->CapabilityAgent::handleDirective(MESSAGE_ID); + m_wakeSetCompletedFuture.wait_for(TIMEOUT); +} + +/** + * Tests SetMute Directive. Expect that the volume is muted, as well at most one + * event is sent. In the event there are no AVS_SPEAKER_VOLUME speakers registered, no event will be sent. + * In addition, only AVS_SPEAKER_VOLUME speakers should be affected. + */ +TEST_P(SpeakerManagerTest, test_setMuteDirective) { + std::vector> groupVec; + int eventsSent = 0; + SpeakerInterface::SpeakerSettings expectedSettings{DEFAULT_SETTINGS.volume, MUTE}; + + // Create Speaker objects. + for (auto& typeOfSpeaker : GetParam()) { + auto group = std::make_shared>(typeOfSpeaker); + group->DelegateToReal(); + int timesCalled = 0; + if (typeOfSpeaker == ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME) { + timesCalled = 1; + } + + EXPECT_CALL(*group, setMute(_)).Times(Exactly(0)); + EXPECT_CALL(*group, setMute(MUTE)).Times(Exactly(timesCalled)); + + groupVec.push_back(group); + } + + auto uniqueTypes = getUniqueTypes(groupVec); + + // Creation expectations based on type. + if (uniqueTypes.count(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME)) { + eventsSent = 1; + + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged( + SpeakerManagerObserverInterface::Source::DIRECTIVE, + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, + expectedSettings)) + .Times(Exactly(1)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } else { + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(Exactly(0)); + } + + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(eventsSent)); + EXPECT_CALL(*(m_mockDirectiveHandlerResult.get()), setCompleted()) + .Times(1) + .WillOnce(InvokeWithoutArgs(this, &SpeakerManagerTest::wakeOnSetCompleted)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + m_speakerManager->addSpeakerManagerObserver(m_observer); + + // Create Directive. + auto attachmentManager = std::make_shared>(); + auto avsMessageHeader = std::make_shared(SET_MUTE.nameSpace, SET_MUTE.name, MESSAGE_ID); + std::shared_ptr directive = + AVSDirective::create("", avsMessageHeader, MUTE_PAYLOAD, attachmentManager, ""); + + m_speakerManager->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); + m_speakerManager->CapabilityAgent::handleDirective(MESSAGE_ID); + m_wakeSetCompletedFuture.wait_for(TIMEOUT); +} + +/** + * Tests SetMute Directive with persistent storage enabled. Expect that the volume is muted, as well at most one + * event is sent. In the event there are no AVS_SPEAKER_VOLUME speakers registered, no event will be sent. + * In addition, only AVS_SPEAKER_VOLUME speakers should be affected. + */ +TEST_P(SpeakerManagerTest, test_setMuteDirectiveWithPersistentStorage) { + // Enable Persistent Storage Setting. + EXPECT_CALL(*m_mockConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = true; + return true; + })); + + std::vector> groupVec; + int eventsSent = 0; + SpeakerInterface::SpeakerSettings expectedSettings{DEFAULT_SETTINGS.volume, MUTE}; + + // Create Speaker objects. + for (auto& typeOfSpeaker : GetParam()) { + auto group = std::make_shared>(typeOfSpeaker); + group->DelegateToReal(); + int timesCalled = 0; + if (typeOfSpeaker == ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME) { + timesCalled = 1; + } + + EXPECT_CALL(*group, setMute(_)).Times(Exactly(1)); + EXPECT_CALL(*group, setMute(MUTE)).Times(Exactly(timesCalled)); + + groupVec.push_back(group); + } + + auto uniqueTypes = getUniqueTypes(groupVec); + + // Creation expectations based on type. + if (uniqueTypes.count(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME)) { + eventsSent = 1; + + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged( + SpeakerManagerObserverInterface::Source::DIRECTIVE, + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, + expectedSettings)) + .Times(Exactly(1)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } else { + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(Exactly(0)); + } + + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(eventsSent)); + EXPECT_CALL(*(m_mockDirectiveHandlerResult.get()), setCompleted()) + .Times(1) + .WillOnce(InvokeWithoutArgs(this, &SpeakerManagerTest::wakeOnSetCompleted)); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + m_speakerManager->addSpeakerManagerObserver(m_observer); + + // Create Directive. + auto attachmentManager = std::make_shared>(); + auto avsMessageHeader = std::make_shared(SET_MUTE.nameSpace, SET_MUTE.name, MESSAGE_ID); + std::shared_ptr directive = + AVSDirective::create("", avsMessageHeader, MUTE_PAYLOAD, attachmentManager, ""); + + m_speakerManager->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); + m_speakerManager->CapabilityAgent::handleDirective(MESSAGE_ID); + m_wakeSetCompletedFuture.wait_for(TIMEOUT); +} + +/** + * Test setVolume when unmute Directive sent. Setup test by setting volume to 0 and mute to true. + * Expect that the volume is unmuted and set to MIN_UNMUTE_VOLUME, as well at most one + * event is sent. In the event there are no AVS_SPEAKER_VOLUME speakers registered, no event will be sent. + * In addition, only AVS_SPEAKER_VOLUME speakers should be affected. + */ +TEST_P(SpeakerManagerTest, test_setVolumeDirectiveWhenMuted) { + std::vector> groupVec; + + for (auto& typeOfSpeaker : GetParam()) { + auto group = std::make_shared>(typeOfSpeaker); + group->DelegateToReal(); + groupVec.push_back(group); + } + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + for (auto& group : groupVec) { + auto mockGroup = std::dynamic_pointer_cast>(group); + EXPECT_CALL(*mockGroup, setUnduckedVolume(AVS_SET_VOLUME_MIN)).Times(1); + EXPECT_CALL(*mockGroup, setMute(MUTE)).Times(1); + auto typeOfSpeaker = mockGroup->getSpeakerType(); + if (typeOfSpeaker == ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME) { + EXPECT_CALL(*mockGroup, setMute(UNMUTE)).Times(1); + EXPECT_CALL(*mockGroup, setUnduckedVolume(MIN_UNMUTE_VOLUME)).Times(1); + } + } + + m_speakerManager->addSpeakerManagerObserver(m_observer); + SpeakerManagerInterface::NotificationProperties properties( + SpeakerManagerObserverInterface::Source::LOCAL_API, false, false); + + for (auto type : getUniqueTypes(groupVec)) { + m_speakerManager->setVolume(type, AVS_SET_VOLUME_MIN, properties); + } + + for (auto type : getUniqueTypes(groupVec)) { + std::future future = m_speakerManager->setMute(type, MUTE, properties); + } + + // Check to see if AVS_SPEAKER_VOLUME speakers exist and set EXPECT_CALL accordingly + auto uniqueTypes = getUniqueTypes(groupVec); + int eventsSent = 0; + SpeakerInterface::SpeakerSettings unMuteSettings{MIN_UNMUTE_VOLUME, UNMUTE}; + + if (uniqueTypes.count(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME)) { + // 2 events : {MIN_UNMUTE_VOLUME, MUTE} followed by {MIN_UNMUTE_VOLUME, UNMUTE} + eventsSent = 2; + + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged( + SpeakerManagerObserverInterface::Source::DIRECTIVE, + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, + SpeakerInterface::SpeakerSettings{MIN_UNMUTE_VOLUME, MUTE})) + .Times(Exactly(1)); + EXPECT_CALL( + *m_observer, + onSpeakerSettingsChanged( + SpeakerManagerObserverInterface::Source::DIRECTIVE, + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, + unMuteSettings)) + .Times(Exactly(1)); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(AnyNumber()); + EXPECT_CALL( + *m_mockContextManager, + setState(VOLUME_STATE, generateVolumeStateJson(unMuteSettings), StateRefreshPolicy::NEVER, _)) + .Times(Exactly(1)); + } else { + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(0); + EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(0); + } + + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(eventsSent); + EXPECT_CALL(*(m_mockDirectiveHandlerResult.get()), setCompleted()) + .Times(1) + .WillOnce(InvokeWithoutArgs(this, &SpeakerManagerTest::wakeOnSetCompleted)); + + // Create Directive to unmute the device. + auto attachmentManager = std::make_shared>(); + auto avsMessageHeader = std::make_shared(SET_MUTE.nameSpace, SET_MUTE.name, MESSAGE_ID); + std::shared_ptr directive = + AVSDirective::create("", avsMessageHeader, UNMUTE_PAYLOAD, attachmentManager, ""); + + m_speakerManager->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); + m_speakerManager->CapabilityAgent::handleDirective(MESSAGE_ID); + m_wakeSetCompletedFuture.wait_for(TIMEOUT); +} + +/** + * Parameterized test for getSpeakerSettings. Operation should succeed with default speaker settings. + */ +TEST_P(SpeakerManagerTest, test_getSpeakerConfigDefaults) { + std::vector> groupVec; + std::set uniqueTypes; + + // Enable Persistent Storage Setting. + EXPECT_CALL(*m_mockConfig, getPersistentStorage(_)).Times(1).WillOnce(Invoke([](bool& persistentStorage) { + persistentStorage = true; + return true; + })); + + for (auto& typeOfSpeaker : GetParam()) { + auto group = std::make_shared>(typeOfSpeaker); + group->DelegateToReal(); + + // There should be one call to getSpeakerSettings for the first speaker of each type. + if (uniqueTypes.find(typeOfSpeaker) == uniqueTypes.end()) { + EXPECT_CALL(*group, getSpeakerSettings(_)).Times(AtLeast(1)); + uniqueTypes.insert(typeOfSpeaker); + } + + groupVec.push_back(group); + } + + m_mockStorage->setFailureMode(); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); + m_speakerManager->addSpeakerManagerObserver(m_observer); + + for (auto speaker : groupVec) { + // SpeakerManager attempts to cache speaker settings initially. No getSpeakerSettings() call should be made to + // each speaker. + auto mockSpeaker = std::dynamic_pointer_cast>(speaker); + ASSERT_TRUE(mockSpeaker); + EXPECT_CALL(*mockSpeaker, getSpeakerSettings(_)).Times(0); + } + + for (auto type : uniqueTypes) { + SpeakerInterface::SpeakerSettings settings; + // Query SpeakerMananger for speaker settings, value should be cached and not queried from each speaker. + std::future future = m_speakerManager->getSpeakerSettings(type, &settings); + ASSERT_TRUE(future.get()); + + switch (type) { + case avsCommon::sdkInterfaces::ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME: + EXPECT_EQ(settings.volume, alexaClientSDK::avsCommon::avs::speakerConstants::DEFAULT_SPEAKER_VOLUME); + break; + case avsCommon::sdkInterfaces::ChannelVolumeInterface::Type::AVS_ALERTS_VOLUME: + EXPECT_EQ(settings.volume, alexaClientSDK::avsCommon::avs::speakerConstants::DEFAULT_ALERTS_VOLUME); + break; + } + ASSERT_EQ(settings.mute, false); + } +} + +/** + * Parameterized test for getSpeakerSettings. Operation should succeed with speaker settings from storage. + */ +TEST_P(SpeakerManagerTest, test_getSpeakerConfigFromStorage) { + std::vector> groupVec; + std::set uniqueTypes; + + for (auto& typeOfSpeaker : GetParam()) { + auto group = std::make_shared>(typeOfSpeaker); + group->DelegateToReal(); + + // There should be one call to getSpeakerSettings for the first speaker of each type. + if (uniqueTypes.find(typeOfSpeaker) == uniqueTypes.end()) { + EXPECT_CALL(*group, getSpeakerSettings(_)).Times(AtLeast(1)); + uniqueTypes.insert(typeOfSpeaker); + } + + groupVec.push_back(group); + } + + m_mockStorage->setDefaults(); + + m_speakerManager = SpeakerManager::create( + m_mockConfig, + m_mockStorage, + groupVec, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + + EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); + m_speakerManager->addSpeakerManagerObserver(m_observer); + + for (auto speaker : groupVec) { + // SpeakerManager attempts to cache speaker settings initially. No getSpeakerSettings() call should be made to + // each speaker. + auto mockSpeaker = std::dynamic_pointer_cast>(speaker); + ASSERT_TRUE(mockSpeaker); + EXPECT_CALL(*mockSpeaker, getSpeakerSettings(_)).Times(0); + } + + for (auto type : uniqueTypes) { + SpeakerInterface::SpeakerSettings settings; + // Query SpeakerMananger for speaker settings, value should be cached and not queried from each speaker. + std::future future = m_speakerManager->getSpeakerSettings(type, &settings); + ASSERT_TRUE(future.get()); + + EXPECT_EQ(settings.volume, AVS_SET_VOLUME_MIN); + EXPECT_EQ(settings.mute, false); + } +} + +} // namespace test +} // namespace speakerManager +} // namespace alexaClientSDK diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/test/SpeakerManagerTest.dox b/CapabilityAgents/SpeakerManager/SpeakerManager/test/SpeakerManagerTest.dox new file mode 100644 index 0000000000..961c63dd42 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/test/SpeakerManagerTest.dox @@ -0,0 +1,34 @@ +/* + * 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. + */ + +/** + * @defgroup Test_acsdkSpeakerManager Unit tests for Speaker API Capability Agent. + * @brief Capability Agent unit tests. + * + * @see alexaClientSDK::speakerManager + * @see alexaClientSDK::speakerManager::test + * + * @ingroup Lib_acsdkSpeakerManager + */ + +/** + * @defgroup Lib_acsdkSpeakerManagerTestLib Test mocks for speaker manager interfaces. + * @brief Capability Agent unit tests. + * + * @see alexaClientSDK::speakerManager + * @see alexaClientSDK::speakerManager::test + * + * @ingroup Lib_acsdkSpeakerManager + */ diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/test/include/acsdk/SpeakerManager/test/MockSpeakerManagerConfig.h b/CapabilityAgents/SpeakerManager/SpeakerManager/test/include/acsdk/SpeakerManager/test/MockSpeakerManagerConfig.h new file mode 100644 index 0000000000..9901fab15d --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/test/include/acsdk/SpeakerManager/test/MockSpeakerManagerConfig.h @@ -0,0 +1,44 @@ +/* + * 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. + */ + +#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_TEST_INCLUDE_ACSDK_SPEAKERMANAGER_TEST_MOCKSPEAKERMANAGERCONFIG_H_ +#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_TEST_INCLUDE_ACSDK_SPEAKERMANAGER_TEST_MOCKSPEAKERMANAGERCONFIG_H_ + +#include + +namespace alexaClientSDK { +namespace speakerManager { +namespace test { + +/** + * @brief Mock object for @a SpeakerManagerConfigInterface. + * + * @see SpeakerManagerConfigInterface + * @ingroup Lib_acsdkSpeakerManagerTestLib + */ +class MockSpeakerManagerConfig : public SpeakerManagerConfigInterface { +public: + MOCK_NOEXCEPT_METHOD1(getPersistentStorage, bool(bool&)); + MOCK_NOEXCEPT_METHOD1(getMinUnmuteVolume, bool(std::uint8_t&)); + MOCK_NOEXCEPT_METHOD1(getRestoreMuteState, bool(bool&)); + MOCK_NOEXCEPT_METHOD1(getDefaultSpeakerVolume, bool(std::uint8_t&)); + MOCK_NOEXCEPT_METHOD1(getDefaultAlertsVolume, bool(std::uint8_t&)); +}; + +} // namespace test +} // namespace speakerManager +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_TEST_INCLUDE_ACSDK_SPEAKERMANAGER_TEST_MOCKSPEAKERMANAGERCONFIG_H_ diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/test/include/acsdk/SpeakerManager/test/MockSpeakerManagerObserver.h b/CapabilityAgents/SpeakerManager/SpeakerManager/test/include/acsdk/SpeakerManager/test/MockSpeakerManagerObserver.h new file mode 100644 index 0000000000..c30213f867 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/test/include/acsdk/SpeakerManager/test/MockSpeakerManagerObserver.h @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_TEST_INCLUDE_ACSDK_SPEAKERMANAGER_TEST_MOCKSPEAKERMANAGEROBSERVER_H_ +#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_TEST_INCLUDE_ACSDK_SPEAKERMANAGER_TEST_MOCKSPEAKERMANAGEROBSERVER_H_ + +#include +#include + +namespace alexaClientSDK { +namespace speakerManager { +namespace test { + +/** + * @brief Mock object for @a SpeakerManagerObserverInterface. + * + * @see avsCommon::sdkInterfaces::SpeakerManagerObserverInterface + * @ingroup Lib_acsdkSpeakerManagerTestLib + */ +class MockSpeakerManagerObserver : public avsCommon::sdkInterfaces::SpeakerManagerObserverInterface { +public: + MOCK_METHOD3( + onSpeakerSettingsChanged, + void( + const Source&, + const avsCommon::sdkInterfaces::ChannelVolumeInterface::Type&, + const avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings&)); +}; + +} // namespace test +} // namespace speakerManager +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_TEST_INCLUDE_ACSDK_SPEAKERMANAGER_TEST_MOCKSPEAKERMANAGEROBSERVER_H_ diff --git a/CapabilityAgents/SpeakerManager/SpeakerManager/test/include/acsdk/SpeakerManager/test/MockSpeakerManagerStorage.h b/CapabilityAgents/SpeakerManager/SpeakerManager/test/include/acsdk/SpeakerManager/test/MockSpeakerManagerStorage.h new file mode 100644 index 0000000000..b770141df1 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManager/test/include/acsdk/SpeakerManager/test/MockSpeakerManagerStorage.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_TEST_INCLUDE_ACSDK_SPEAKERMANAGER_TEST_MOCKSPEAKERMANAGERSTORAGE_H_ +#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_TEST_INCLUDE_ACSDK_SPEAKERMANAGER_TEST_MOCKSPEAKERMANAGERSTORAGE_H_ + +#include + +namespace alexaClientSDK { +namespace speakerManager { +namespace test { + +/** + * @brief Mock object for @a SpeakerManagerStorageInterface. + * + * @see SpeakerManagerStorageInterface + * @ingroup Lib_acsdkSpeakerManagerTestLib + */ +class MockSpeakerManagerStorage : public SpeakerManagerStorageInterface { +public: + MOCK_NOEXCEPT_METHOD1(loadState, bool(SpeakerManagerStorageState&)); + MOCK_NOEXCEPT_METHOD1(saveState, bool(const SpeakerManagerStorageState&)); +}; + +} // namespace test +} // namespace speakerManager +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGER_TEST_INCLUDE_ACSDK_SPEAKERMANAGER_TEST_MOCKSPEAKERMANAGERSTORAGE_H_ diff --git a/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/CMakeLists.txt b/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/CMakeLists.txt new file mode 100644 index 0000000000..89b9b5e934 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.0) +project(acsdkSpeakerManagerComponent LANGUAGES CXX) + +add_subdirectory("src") diff --git a/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/doc/Namespaces.dox b/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/doc/Namespaces.dox new file mode 100644 index 0000000000..5b243b21c3 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/doc/Namespaces.dox @@ -0,0 +1,25 @@ +/* + * 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. + */ + +/** + * @module Lib_acsdkSpeakerManager Speaker Manager Capability Agent Components + * + * @namespace alexaClientSDK::speakerManagerComponent + * @brief Speaker Manager Capability Agent Components + * + * This namespace contains factory methods for Speaker Manager components. + * + * @ingroup Lib_acsdkSpeakerManagerComponent + */ diff --git a/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/doc/SpeakerManagerComponent.dox b/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/doc/SpeakerManagerComponent.dox new file mode 100644 index 0000000000..b750a0bfd4 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/doc/SpeakerManagerComponent.dox @@ -0,0 +1,27 @@ +/* + * 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. + */ + +/** + * @defgroup Lib_acsdkSpeakerManagerComponent Speaker Capability Agent Components + * @brief Manufactory support for Speaker capability agent components. + * + * Speaker manager contains the following components: + * - Speaker manager capability agent + * - Speaker manager configuration integration + * - Speaker manager storage integration + * - Channel volume factory + * + * @ingroup alexaClientSDK::speakerManager + */ diff --git a/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/include/acsdk/SpeakerManager/ChannelVolumeFactoryComponent.h b/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/include/acsdk/SpeakerManager/ChannelVolumeFactoryComponent.h new file mode 100644 index 0000000000..50d92f87af --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/include/acsdk/SpeakerManager/ChannelVolumeFactoryComponent.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + +#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGERCOMPONENT_INCLUDE_ACSDK_SPEAKERMANAGER_CHANNELVOLUMEFACTORYCOMPONENT_H_ +#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGERCOMPONENT_INCLUDE_ACSDK_SPEAKERMANAGER_CHANNELVOLUMEFACTORYCOMPONENT_H_ + +#include +#include + +#include +#include + +namespace alexaClientSDK { +namespace speakerManagerComponent { + +/// @addtogroup Lib_acsdkSpeakerManagerComponent +/// @{ + +/** + * @brief Component for ChannelVolumeFactoryInterface. + * + * Definition of a Manufactory component for the ChannelVolumeFactoryInterface. + */ +using ChannelVolumeFactoryComponent = + acsdkManufactory::Component>; + +/** + * @brief Create component for ChannelVolumeFactoryInterface. + * + * Creates an manufactory component that exports a shared pointer to an implementation of @c + * ChannelVolumeFactoryInterface. + * + * @return A component. + */ +ChannelVolumeFactoryComponent getChannelVolumeFactoryComponent(); + +/// @} + +} // namespace speakerManagerComponent +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGERCOMPONENT_INCLUDE_ACSDK_SPEAKERMANAGER_CHANNELVOLUMEFACTORYCOMPONENT_H_ diff --git a/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/include/acsdk/SpeakerManager/SpeakerManagerComponent.h b/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/include/acsdk/SpeakerManager/SpeakerManagerComponent.h new file mode 100644 index 0000000000..a082cf2952 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/include/acsdk/SpeakerManager/SpeakerManagerComponent.h @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGERCOMPONENT_INCLUDE_ACSDK_SPEAKERMANAGER_SPEAKERMANAGERCOMPONENT_H_ +#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGERCOMPONENT_INCLUDE_ACSDK_SPEAKERMANAGER_SPEAKERMANAGERCOMPONENT_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace alexaClientSDK { +namespace speakerManagerComponent { + +/// @addtogroup Lib_acsdkSpeakerManagerComponent +/// @{ + +/** + * @brief Component for @c SpeakerManagerInterface. + * + * Definition of a Manufactory component for the avsCommon::sdkInterfaces::SpeakerManagerInterface. + */ +using SpeakerManagerComponent = acsdkManufactory::Component< + std::shared_ptr, + acsdkManufactory::Import>, + acsdkManufactory::Import>, + acsdkManufactory::Import>, + acsdkManufactory::Import>, + acsdkManufactory::Import>, + acsdkManufactory::Import>, + acsdkManufactory::Import>>; + +/** + * @brief Create component for @c SpeakerManagerInterface. + * + * Creates an manufactory component that exports a shared pointer to an implementation of @c SpeakerManagerInterface. + * + * @return A component. + */ +SpeakerManagerComponent getSpeakerManagerComponent() noexcept; + +/// @} + +} // namespace speakerManagerComponent +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_SPEAKERMANAGERCOMPONENT_INCLUDE_ACSDK_SPEAKERMANAGER_SPEAKERMANAGERCOMPONENT_H_ diff --git a/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/src/CMakeLists.txt b/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/src/CMakeLists.txt new file mode 100644 index 0000000000..6d26f9a7d3 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/src/CMakeLists.txt @@ -0,0 +1,10 @@ +add_definitions("-DACSDK_LOG_MODULE=acsdkSpeakerManagerComponent") + +add_library(acsdkSpeakerManagerComponent ChannelVolumeFactoryComponent.cpp SpeakerManagerComponent.cpp) + +target_include_directories(acsdkSpeakerManagerComponent PUBLIC "${acsdkSpeakerManagerComponent_SOURCE_DIR}/include") + +target_link_libraries(acsdkSpeakerManagerComponent acsdkSpeakerManager acsdkManufactory) + +# install target +asdk_install() diff --git a/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/src/ChannelVolumeFactoryComponent.cpp b/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/src/ChannelVolumeFactoryComponent.cpp new file mode 100644 index 0000000000..1220c0fec1 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/src/ChannelVolumeFactoryComponent.cpp @@ -0,0 +1,32 @@ +/* + * 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 + +namespace alexaClientSDK { +namespace speakerManagerComponent { + +using namespace acsdkManufactory; +using namespace speakerManager; + +ChannelVolumeFactoryComponent getChannelVolumeFactoryComponent() { + return ComponentAccumulator<>().addRetainedFactory(createChannelVolumeFactory); +} + +} // namespace speakerManagerComponent +} // namespace alexaClientSDK diff --git a/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/src/SpeakerManagerComponent.cpp b/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/src/SpeakerManagerComponent.cpp new file mode 100644 index 0000000000..daf760c0e6 --- /dev/null +++ b/CapabilityAgents/SpeakerManager/SpeakerManagerComponent/src/SpeakerManagerComponent.cpp @@ -0,0 +1,80 @@ +/* + * 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 + +namespace alexaClientSDK { +namespace speakerManagerComponent { + +using namespace acsdkManufactory; +using namespace speakerManager; + +/** + * @brief Helper for manufactory. + * + * This method uses annotated types for correct interface lookup in manufactory. + * + * @param[in] storage A @c MiscStorageInterface to access persistent configuration. + * @param[in] contextManager A @c ContextManagerInterface to manage the context. + * @param[in] messageSender A @c MessageSenderInterface to send messages to AVS. + * @param[in] exceptionEncounteredSender An @c ExceptionEncounteredSenderInterface to send directive processing + * exceptions to AVS. + * @param[in] shutdownNotifier A @c ShutdownNotifierInterface to notify the SpeakerManager when it's time + * to shut down. + * @param[in] endpointCapabilitiesRegistrar The @c EndpointCapabilitiesRegistrarInterface for the default endpoint + * (annotated with DefaultEndpointAnnotation), so that the SpeakerManager can register itself as a capability + * with the default endpoint. + * @param[in] metricRecorder The metric recorder. + * + * @return Reference to SpeakerManager CA or nullptr on error. + * + * @see createSpeakerManagerCapabilityAgent() + * @private + * @ingroup Lib_acsdkSpeakerManagerComponent + */ +static std::shared_ptr createSpeakerManagerCA( + std::shared_ptr config, + std::shared_ptr storage, + std::shared_ptr contextManager, + std::shared_ptr messageSender, + std::shared_ptr exceptionEncounteredSender, + const std::shared_ptr& shutdownNotifier, + const Annotated< + avsCommon::sdkInterfaces::endpoints::DefaultEndpointAnnotation, + avsCommon::sdkInterfaces::endpoints::EndpointCapabilitiesRegistrarInterface>& endpointCapabilitiesRegistrar, + std::shared_ptr metricRecorder) noexcept { + return createSpeakerManagerCapabilityAgent( + std::move(config), + std::move(storage), + std::move(contextManager), + std::move(messageSender), + std::move(exceptionEncounteredSender), + std::move(metricRecorder), + shutdownNotifier, + endpointCapabilitiesRegistrar); +} + +SpeakerManagerComponent getSpeakerManagerComponent() noexcept { + return ComponentAccumulator<>() + .addRequiredFactory(createSpeakerManagerCA) + .addRequiredFactory(createSpeakerManagerStorage) + .addRequiredFactory(createSpeakerManagerConfig); +} + +} // namespace speakerManagerComponent +} // namespace alexaClientSDK diff --git a/CapabilityAgents/SpeakerManager/include/SpeakerManager/DefaultChannelVolumeFactory.h b/CapabilityAgents/SpeakerManager/include/SpeakerManager/DefaultChannelVolumeFactory.h deleted file mode 100644 index aae9897390..0000000000 --- a/CapabilityAgents/SpeakerManager/include/SpeakerManager/DefaultChannelVolumeFactory.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_DEFAULTCHANNELVOLUMEFACTORY_H_ -#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_DEFAULTCHANNELVOLUMEFACTORY_H_ - -#include -#include "SpeakerManager/ChannelVolumeManager.h" - -namespace alexaClientSDK { -namespace capabilityAgents { -namespace speakerManager { - -/** - * The @c DefaultChannelVolumeFactory provides a default implementation of @c ChannelVolumeFactoryInterface - * using the @c ChannelVolumeManager. - */ -class DefaultChannelVolumeFactory : public alexaClientSDK::avsCommon::sdkInterfaces::ChannelVolumeFactoryInterface { -public: - /** - * Creates a new @c ChannelVolumeFactoryInterface implementation. - * - * @return A shared ptr to a new @c ChannelVolumeFactoryInterface. - */ - static std::shared_ptr - createChannelVolumeFactoryInterface(); - - /// ChannelVolumeFactoryInterface Functions. - /// @{ - virtual std::shared_ptr - createChannelVolumeInterface( - std::shared_ptr speaker, - alexaClientSDK::avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, - std::function volumeCurve) override; - /// @} -}; -} // namespace speakerManager -} // namespace capabilityAgents -} // namespace alexaClientSDK -#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_DEFAULTCHANNELVOLUMEFACTORY_H_ diff --git a/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManagerComponent.h b/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManagerComponent.h deleted file mode 100644 index 6321b07c58..0000000000 --- a/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManagerComponent.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGERCOMPONENT_H_ -#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGERCOMPONENT_H_ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace alexaClientSDK { -namespace capabilityAgents { -namespace speakerManager { - -/** - * Definition of a Manufactory component for the SpeakerManager. - */ -using SpeakerManagerComponent = acsdkManufactory::Component< - std::shared_ptr, - acsdkManufactory::Import>, - acsdkManufactory::Import>, - acsdkManufactory::Import>, - acsdkManufactory::Import>, - acsdkManufactory::Import>, - acsdkManufactory::Import>, - acsdkManufactory::Import>>; - -/** - * Creates an manufactory component that exports a shared pointer to an implementation of @c SpeakerManagerInterface. - * - * @return A component. - */ -SpeakerManagerComponent getComponent(); - -} // namespace speakerManager -} // namespace capabilityAgents -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGERCOMPONENT_H_ diff --git a/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManagerConfigHelper.h b/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManagerConfigHelper.h deleted file mode 100644 index 2561933635..0000000000 --- a/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManagerConfigHelper.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGERCONFIGHELPER_H_ -#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGERCONFIGHELPER_H_ - -#include -#include - -namespace alexaClientSDK { -namespace capabilityAgents { -namespace speakerManager { - -/** - * Helper class to manage configuration operations for SpeakerManager CA. - * - * This class implements all configuration operations and merges logic of accessing different configuration sources. - * SpeakerManager get configuration values from three sources: hardcoded values, platform configuration, and persistent - * storage. - */ -class SpeakerManagerConfigHelper { -public: - /** - * Creates object. - * @param[in] storage Storage interface. - */ - SpeakerManagerConfigHelper(const std::shared_ptr& storage); - - /** - * Load configuration. - * - * This method always succeeds (assuming @a state is not @a nullptr), as it first tries to load configuration - * from config storage, then from platform configuration files, and falls back to hardcoded values. - * - * @param[out] state Pointer to configuration container to fill with config values. - */ - void loadState(SpeakerManagerStorageState& state); - - /** - * Saves configuration to to config storage. - * - * @param[in] state Configuration data to persist. - * - * @return Boolean that indicates operation success. - */ - bool saveState(const SpeakerManagerStorageState& state); - - /** - * Loads minimum unmute volume level from platform configuration. The method tries to load the unmute value from - * platform configuration files, and if it fails, it returns a hardcoded value. - * - * @return Minimum volume level to unmute speakers. - */ - int getMinUnmuteVolume() const; - - /** - * Loads mute state handling from configuration. By default the speaker manager sets the mute status to the value - * prior to reboot, but this behaviour can be overridden by configuration. - * - * @return Returns configured value, where true indicates that mute status is configured according to the last - * saved state, and "false" indicates a default shall be kept. - */ - bool getRestoreMuteState() const; - -private: - /** - * Load channels settings from hardcoded defaults. - * - * @param[out] state Destination container for configuration data. - */ - void loadHardcodedState(SpeakerManagerStorageState& state); - - /** - * Load channels settings from platform configuration. - * - * @param[out] state Destination container for configuration data. - * - * @return A bool indicating success. - */ - bool loadStateFromConfig(SpeakerManagerStorageState& state); - - /** - * Default values that are used when no other configuration sources are available. - */ - static const SpeakerManagerStorageState c_defaults; - - /// Reference to configuration storage interface. - std::shared_ptr m_storage; -}; - -} // namespace speakerManager -} // namespace capabilityAgents -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGERCONFIGHELPER_H_ diff --git a/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManagerMiscStorage.h b/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManagerMiscStorage.h deleted file mode 100644 index 33562cba1a..0000000000 --- a/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManagerMiscStorage.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGERMISCSTORAGE_H_ -#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGERMISCSTORAGE_H_ - -#include -#include -#include - -namespace alexaClientSDK { -namespace capabilityAgents { -namespace speakerManager { - -/** - * Configuration interface for SpeakerManager. - */ -class SpeakerManagerMiscStorage : public SpeakerManagerStorageInterface { -public: - /** - * Creates an instance of the @c SpeakerManagerMiscStorage. - * - * @param miscStorage The underlying miscellaneous storage to store @c SpeakerManager data. - * @return A unique pointer to the instance of the newly created @c SpeakerManagerMiscStorage. - */ - static std::shared_ptr create( - const std::shared_ptr& miscStorage); - - /// @name SpeakerManagerStorageInterface Functions - /// @{ - bool loadState(SpeakerManagerStorageState& state) override; - bool saveState(const SpeakerManagerStorageState& state) override; - /// @} - -private: - /// Helper to convert structure to JSON string. - static std::string convertToStateString(const SpeakerManagerStorageState& state); - static std::string convertToStateString(const SpeakerManagerStorageState::ChannelState& state); - - /** - * Constructor. - * - * @param miscStorage The underlying miscellaneous storage used to store component data. - */ - SpeakerManagerMiscStorage( - const std::shared_ptr& miscStorage); - - /** - * Method to initialize the object. - * - * This method connects to underlying storage and performs necessary actions. - * - * @return Boolean status, indicating operation success. - */ - bool init(); - - /** - * Helper to convert JSON string to structure. - * - * @param stateString JSON string describing @a SpeakerManagerStorageState data. - * @param state Pointer to storage for parsed values. - * - * @return Boolean status, indicating operation success. - */ - bool convertFromStateString(const std::string& stateString, SpeakerManagerStorageState& state); - - /** - * Helper to convert JSON string to structure. - * - * @param stateString JSON string describing @a SpeakerManagerStorageState::ChannelState data. - * @param state Pointer to storage for parsed values. - * - * @return Boolean status, indicating operation success. - */ - bool convertFromStateString(const std::string& stateString, SpeakerManagerStorageState::ChannelState& state); - - /// The Misc storage. - std::shared_ptr m_miscStorage; -}; - -} // namespace speakerManager -} // namespace capabilityAgents -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGERMISCSTORAGE_H_ diff --git a/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManagerStorageInterface.h b/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManagerStorageInterface.h deleted file mode 100644 index cd100bb911..0000000000 --- a/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManagerStorageInterface.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGERSTORAGEINTERFACE_H_ -#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGERSTORAGEINTERFACE_H_ - -namespace alexaClientSDK { -namespace capabilityAgents { -namespace speakerManager { - -struct SpeakerManagerStorageState; - -/** - * Storage interface for SpeakerManager. - */ -struct SpeakerManagerStorageInterface { - /** - * Virtual destructor to assure proper cleanup of derived types. - */ - virtual ~SpeakerManagerStorageInterface() = default; - - /** - * Loads state from underlying storage. - * @param[out] state Pointer to state structure for loaded values. - * @return Boolean flag if the operation is successful. - */ - virtual bool loadState(SpeakerManagerStorageState& state) = 0; - - /** - * Stores state to underlying storage. - * @param[in] state Reference of state structure for values to store. - * @return Boolean flag if the operation is successful. - */ - virtual bool saveState(const SpeakerManagerStorageState& state) = 0; -}; - -} // namespace speakerManager -} // namespace capabilityAgents -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGERSTORAGEINTERFACE_H_ diff --git a/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManagerStorageState.h b/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManagerStorageState.h deleted file mode 100644 index 0037b1cbeb..0000000000 --- a/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManagerStorageState.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGERSTORAGESTATE_H_ -#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGERSTORAGESTATE_H_ - -#include - -namespace alexaClientSDK { -namespace capabilityAgents { -namespace speakerManager { - -/** - * Storage state for SpeakerManager. SpeakerManager configuration includes configuration for two channel types: speaker - * and alerts. There can be any number of channels for each of types, but all of them share the same configuraiton. - */ -struct SpeakerManagerStorageState { - /** - * SpeakerManager channel type configuration state. - */ - struct ChannelState { - /// Channel volume. - uint8_t channelVolume; - /// Channel mute status. - bool channelMuteStatus; - }; - /// Configuration for speaker channels. - ChannelState speakerChannelState; - /// Configuration for alerts channels. - ChannelState alertsChannelState; -}; - -} // namespace speakerManager -} // namespace capabilityAgents -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_SPEAKERMANAGER_INCLUDE_SPEAKERMANAGER_SPEAKERMANAGERSTORAGESTATE_H_ diff --git a/CapabilityAgents/SpeakerManager/src/CMakeLists.txt b/CapabilityAgents/SpeakerManager/src/CMakeLists.txt deleted file mode 100644 index 565e2341b0..0000000000 --- a/CapabilityAgents/SpeakerManager/src/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -add_definitions("-DACSDK_LOG_MODULE=speakerManager") - -add_library(SpeakerManager SpeakerManager.cpp - ChannelVolumeManager.cpp - DefaultChannelVolumeFactory.cpp - SpeakerManagerComponent.cpp - SpeakerManagerMiscStorage.cpp - SpeakerManagerConfigHelper.cpp) - -target_include_directories(SpeakerManager PUBLIC - "${ContextManager_INCLUDE_DIRS}" - "${SpeakerManager_SOURCE_DIR}/include") - -target_link_libraries(SpeakerManager AVSCommon acsdkManufactory acsdkShutdownManagerInterfaces Endpoints) - -# install target -asdk_install() diff --git a/CapabilityAgents/SpeakerManager/src/DefaultChannelVolumeFactory.cpp b/CapabilityAgents/SpeakerManager/src/DefaultChannelVolumeFactory.cpp deleted file mode 100644 index 5299b5aa90..0000000000 --- a/CapabilityAgents/SpeakerManager/src/DefaultChannelVolumeFactory.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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 "SpeakerManager/DefaultChannelVolumeFactory.h" - -namespace alexaClientSDK { -namespace capabilityAgents { -namespace speakerManager { - -std::shared_ptr DefaultChannelVolumeFactory:: - createChannelVolumeFactoryInterface() { - return std::make_shared(); -} - -std::shared_ptr DefaultChannelVolumeFactory:: - createChannelVolumeInterface( - std::shared_ptr speaker, - alexaClientSDK::avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, - std::function volumeCurve) { - return alexaClientSDK::capabilityAgents::speakerManager::ChannelVolumeManager::create(speaker, type, volumeCurve); -} - -} // namespace speakerManager -} // namespace capabilityAgents -} // namespace alexaClientSDK diff --git a/CapabilityAgents/SpeakerManager/src/SpeakerManagerComponent.cpp b/CapabilityAgents/SpeakerManager/src/SpeakerManagerComponent.cpp deleted file mode 100644 index 12b851c9f8..0000000000 --- a/CapabilityAgents/SpeakerManager/src/SpeakerManagerComponent.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 "SpeakerManager/SpeakerManager.h" -#include "SpeakerManager/SpeakerManagerComponent.h" - -namespace alexaClientSDK { -namespace capabilityAgents { -namespace speakerManager { - -using namespace acsdkManufactory; -using namespace capabilityAgents::speakerManager; - -SpeakerManagerComponent getComponent() { - return ComponentAccumulator<>().addRequiredFactory(SpeakerManager::createSpeakerManagerCapabilityAgent); -} - -} // namespace speakerManager -} // namespace capabilityAgents -} // namespace alexaClientSDK diff --git a/CapabilityAgents/SpeakerManager/src/SpeakerManagerConfigHelper.cpp b/CapabilityAgents/SpeakerManager/src/SpeakerManagerConfigHelper.cpp deleted file mode 100644 index 56e400e48a..0000000000 --- a/CapabilityAgents/SpeakerManager/src/SpeakerManagerConfigHelper.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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 - -using namespace alexaClientSDK::avsCommon::sdkInterfaces; -using namespace alexaClientSDK::avsCommon::sdkInterfaces::storage; -using namespace alexaClientSDK::avsCommon::avs::speakerConstants; -using namespace alexaClientSDK::capabilityAgents::speakerManager; -using namespace alexaClientSDK::avsCommon::utils::configuration; - -/// String to identify log entries originating from this file. -static const std::string TAG{"SpeakerManagerConfigHelper"}; - -/** - * Create a LogEntry using this file's TAG and the specified event string. - * - * @param TAG Component tag. - * @param evemt The event string for this @c LogEntry. - */ -#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) - -/// The key in our config file to find the root of speaker manager configuration. -static const std::string SPEAKERMANAGER_CONFIGURATION_ROOT_KEY = "speakerManagerCapabilityAgent"; -/// The key in our config file to find the minUnmuteVolume value. -static const std::string SPEAKERMANAGER_MIN_UNMUTE_VOLUME_KEY = "minUnmuteVolume"; -/// The key in our config file to find the defaultSpeakerVolume value. -static const std::string SPEAKERMANAGER_DEFAULT_SPEAKER_VOLUME_KEY = "defaultSpeakerVolume"; -/// The key in our config file to find the defaultAlertsVolume value. -static const std::string SPEAKERMANAGER_DEFAULT_ALERTS_VOLUME_KEY = "defaultAlertsVolume"; -/// The key in our config file to find mute status keep flag -static const std::string SPEAKERMANAGER_RESTORE_MUTE_STATE_KEY = "restoreMuteState"; - -const SpeakerManagerStorageState SpeakerManagerConfigHelper::c_defaults = {{DEFAULT_SPEAKER_VOLUME, false}, - {DEFAULT_ALERTS_VOLUME, false}}; - -SpeakerManagerConfigHelper::SpeakerManagerConfigHelper(const std::shared_ptr& storage) : - m_storage(storage) { -} - -int SpeakerManagerConfigHelper::getMinUnmuteVolume() const { - int minUnmuteVolume = MIN_UNMUTE_VOLUME; - - auto node = ConfigurationNode::getRoot()[SPEAKERMANAGER_CONFIGURATION_ROOT_KEY]; - // If key is present, then read and initialize the value from config or set to default. - node.getInt(SPEAKERMANAGER_MIN_UNMUTE_VOLUME_KEY, &minUnmuteVolume, MIN_UNMUTE_VOLUME); - - return minUnmuteVolume; -} - -void SpeakerManagerConfigHelper::loadState(SpeakerManagerStorageState& state) { - if (!m_storage->loadState(state) && !loadStateFromConfig(state)) { - loadHardcodedState(state); - } -} - -bool SpeakerManagerConfigHelper::saveState(const SpeakerManagerStorageState& state) { - return m_storage->saveState(state); -} - -bool SpeakerManagerConfigHelper::loadStateFromConfig(SpeakerManagerStorageState& state) { - int speakerVolume = 0; - int alertsVolume = 0; - - auto node = ConfigurationNode::getRoot()[SPEAKERMANAGER_CONFIGURATION_ROOT_KEY]; - - if (node.getInt(SPEAKERMANAGER_DEFAULT_SPEAKER_VOLUME_KEY, &speakerVolume) && - node.getInt(SPEAKERMANAGER_DEFAULT_ALERTS_VOLUME_KEY, &alertsVolume)) { - state.speakerChannelState.channelMuteStatus = false; - state.speakerChannelState.channelVolume = speakerVolume; - state.alertsChannelState.channelMuteStatus = false; - state.alertsChannelState.channelVolume = alertsVolume; - - return true; - } - return false; -} - -void SpeakerManagerConfigHelper::loadHardcodedState(SpeakerManagerStorageState& state) { - state = c_defaults; -} - -bool SpeakerManagerConfigHelper::getRestoreMuteState() const { - auto node = ConfigurationNode::getRoot()[SPEAKERMANAGER_CONFIGURATION_ROOT_KEY]; - bool result = false; - if (node.getBool(SPEAKERMANAGER_RESTORE_MUTE_STATE_KEY, &result)) { - return result; - } else { - return true; - } -} diff --git a/CapabilityAgents/SpeakerManager/test/CMakeLists.txt b/CapabilityAgents/SpeakerManager/test/CMakeLists.txt deleted file mode 100644 index 6ab01b8b35..0000000000 --- a/CapabilityAgents/SpeakerManager/test/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -cmake_minimum_required(VERSION 3.1 FATAL_ERROR) - -set(INCLUDE_PATH - "${SpeakerManager_INCLUDE_DIRS}" - "${AVSCommon_SOURCE_DIR}/AVS/test") - -discover_unit_tests("${INCLUDE_PATH}" "SpeakerManager;UtilsCommonTestLib;SDKInterfacesTests") diff --git a/CapabilityAgents/SpeakerManager/test/SpeakerManagerConfigHelperTest.cpp b/CapabilityAgents/SpeakerManager/test/SpeakerManagerConfigHelperTest.cpp deleted file mode 100644 index b35b9b7f28..0000000000 --- a/CapabilityAgents/SpeakerManager/test/SpeakerManagerConfigHelperTest.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/* - * 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 "SpeakerManager/SpeakerManagerConfigHelper.h" -#include "SpeakerManager/SpeakerManagerStorageState.h" - -namespace alexaClientSDK { -namespace capabilityAgents { -namespace speakerManager { -namespace test { - -using namespace avsCommon::avs; -using namespace avsCommon::avs::speakerConstants; -using namespace avsCommon::sdkInterfaces; -using namespace avsCommon::utils::configuration; -using namespace avsCommon::utils::memory; -using namespace rapidjson; -using namespace ::testing; -using namespace alexaClientSDK::capabilityAgents::speakerManager; - -static const std::string JSON_TEST_CONFIG = - "{\"speakerManagerCapabilityAgent\":{\"minUnmuteVolume\":3,\"defaultSpeakerVolume\":5,\"defaultAlertsVolume\":6," - "\"restoreMuteState\":true}}"; -static const std::string JSON_TEST_CONFIG_NO_MUTE = - "{\"speakerManagerCapabilityAgent\":{\"minUnmuteVolume\":3,\"defaultSpeakerVolume\":5,\"defaultAlertsVolume\":6," - "\"restoreMuteState\":false}}"; - -class MockSpeakerManagerStorageInterface : public SpeakerManagerStorageInterface { -public: - MOCK_METHOD1(loadState, bool(SpeakerManagerStorageState&)); - MOCK_METHOD1(saveState, bool(const SpeakerManagerStorageState&)); -}; - -class SpeakerManagerConfigHelperTest : public Test { -public: - SpeakerManagerConfigHelperTest(); - -protected: - /// SetUp before each test. - void SetUp() override; - - /// TearDown after each test. - void TearDown() override; - - // Upstream interface mock - std::shared_ptr> m_stubStorage; -}; - -SpeakerManagerConfigHelperTest::SpeakerManagerConfigHelperTest() : m_stubStorage() { -} - -void SpeakerManagerConfigHelperTest::SetUp() { - m_stubStorage = std::make_shared>(); -} - -void SpeakerManagerConfigHelperTest::TearDown() { - m_stubStorage.reset(); -} - -TEST_F(SpeakerManagerConfigHelperTest, test_initDoesntCallLoadSave) { - EXPECT_CALL(*m_stubStorage, loadState(_)).Times(0); - EXPECT_CALL(*m_stubStorage, saveState(_)).Times(0); - - SpeakerManagerConfigHelper helper(m_stubStorage); -} - -TEST_F(SpeakerManagerConfigHelperTest, test_getMinUnmuteVolumeFromConfiguration) { - EXPECT_CALL(*m_stubStorage, loadState(_)).Times(0); - EXPECT_CALL(*m_stubStorage, saveState(_)).Times(0); - - // Provide a valid configuration. - std::shared_ptr istr(new std::istringstream(JSON_TEST_CONFIG)); - ConfigurationNode::uninitialize(); - ASSERT_TRUE(ConfigurationNode::initialize({istr})); - - SpeakerManagerConfigHelper helper(m_stubStorage); - ASSERT_EQ(3, helper.getMinUnmuteVolume()); -} - -TEST_F(SpeakerManagerConfigHelperTest, test_getMinUnmuteVolumeReturnsDefaults) { - EXPECT_CALL(*m_stubStorage, loadState(_)).Times(0); - EXPECT_CALL(*m_stubStorage, saveState(_)).Times(0); - - // Provide an empty configuration. - ConfigurationNode::uninitialize(); - ASSERT_TRUE(ConfigurationNode::initialize({})); - - SpeakerManagerConfigHelper helper(m_stubStorage); - ASSERT_EQ(MIN_UNMUTE_VOLUME, helper.getMinUnmuteVolume()); -} - -TEST_F(SpeakerManagerConfigHelperTest, test_getRestoreMuteStateReturnsDefaults) { - // Provide an empty configuration. - ConfigurationNode::uninitialize(); - ASSERT_TRUE(ConfigurationNode::initialize({})); - - SpeakerManagerConfigHelper helper(m_stubStorage); - ASSERT_TRUE(helper.getRestoreMuteState()); -} - -TEST_F(SpeakerManagerConfigHelperTest, test_getRestoreMuteStateReturnsTrue) { - ConfigurationNode::uninitialize(); - std::shared_ptr istr(new std::istringstream(JSON_TEST_CONFIG)); - ASSERT_TRUE(ConfigurationNode::initialize({istr})); - - SpeakerManagerConfigHelper helper(m_stubStorage); - ASSERT_TRUE(helper.getRestoreMuteState()); -} - -TEST_F(SpeakerManagerConfigHelperTest, test_getRestoreMuteStateReturnsFalse) { - ConfigurationNode::uninitialize(); - std::shared_ptr istr(new std::istringstream(JSON_TEST_CONFIG_NO_MUTE)); - ASSERT_TRUE(ConfigurationNode::initialize({istr})); - - SpeakerManagerConfigHelper helper(m_stubStorage); - ASSERT_FALSE(helper.getRestoreMuteState()); -} - -TEST_F(SpeakerManagerConfigHelperTest, test_loadStateDelegate) { - EXPECT_CALL(*m_stubStorage, loadState(_)).Times(1); - EXPECT_CALL(*m_stubStorage, saveState(_)).Times(0); - - SpeakerManagerConfigHelper helper(m_stubStorage); - - ON_CALL(*m_stubStorage, loadState(_)).WillByDefault(Invoke([](SpeakerManagerStorageState& state) { - state.speakerChannelState.channelVolume = 10; - state.speakerChannelState.channelMuteStatus = true; - state.alertsChannelState.channelMuteStatus = false; - state.alertsChannelState.channelVolume = 20; - return true; - })); - - SpeakerManagerStorageState state = {{255, false}, {255, false}}; - helper.loadState(state); - - EXPECT_EQ(10, state.speakerChannelState.channelVolume); - EXPECT_TRUE(state.speakerChannelState.channelMuteStatus); - EXPECT_EQ(20, state.alertsChannelState.channelVolume); - EXPECT_FALSE(state.alertsChannelState.channelMuteStatus); -} - -TEST_F(SpeakerManagerConfigHelperTest, test_loadStateFromConfig) { - EXPECT_CALL(*m_stubStorage, loadState(_)).Times(1); - EXPECT_CALL(*m_stubStorage, saveState(_)).Times(0); - - std::shared_ptr istr(new std::istringstream(JSON_TEST_CONFIG)); - ConfigurationNode::uninitialize(); - ASSERT_TRUE(ConfigurationNode::initialize({istr})); - - SpeakerManagerConfigHelper helper(m_stubStorage); - - ON_CALL(*m_stubStorage, loadState(_)).WillByDefault(Return(false)); - - SpeakerManagerStorageState state = {{255, true}, {255, true}}; - helper.loadState(state); - - EXPECT_EQ(5, state.speakerChannelState.channelVolume); - EXPECT_FALSE(state.speakerChannelState.channelMuteStatus); - EXPECT_EQ(6, state.alertsChannelState.channelVolume); - EXPECT_FALSE(state.alertsChannelState.channelMuteStatus); -} - -TEST_F(SpeakerManagerConfigHelperTest, test_loadStateDefaults) { - EXPECT_CALL(*m_stubStorage, loadState(_)).Times(1); - EXPECT_CALL(*m_stubStorage, saveState(_)).Times(0); - - ConfigurationNode::uninitialize(); - ASSERT_TRUE(ConfigurationNode::initialize({})); - - SpeakerManagerConfigHelper helper(m_stubStorage); - - ON_CALL(*m_stubStorage, loadState(_)).WillByDefault(Return(false)); - - SpeakerManagerStorageState state = {{255, false}, {255, false}}; - helper.loadState(state); - - EXPECT_EQ(DEFAULT_SPEAKER_VOLUME, state.speakerChannelState.channelVolume); - EXPECT_FALSE(state.speakerChannelState.channelMuteStatus); - EXPECT_EQ(DEFAULT_ALERTS_VOLUME, state.alertsChannelState.channelVolume); - EXPECT_FALSE(state.alertsChannelState.channelMuteStatus); -} - -TEST_F(SpeakerManagerConfigHelperTest, test_saveState) { - EXPECT_CALL(*m_stubStorage, loadState(_)).Times(0); - EXPECT_CALL(*m_stubStorage, saveState(_)).Times(1); - - SpeakerManagerConfigHelper helper(m_stubStorage); - SpeakerManagerStorageState saved = {{0, true}, {0, true}}; - - ON_CALL(*m_stubStorage, saveState(_)).WillByDefault(Invoke([&saved](const SpeakerManagerStorageState& state) { - saved = state; - return true; - })); - - SpeakerManagerStorageState state = {{255, false}, {255, false}}; - ASSERT_TRUE(helper.saveState(state)); - - ASSERT_EQ(255, saved.speakerChannelState.channelVolume); - ASSERT_FALSE(saved.speakerChannelState.channelMuteStatus); - ASSERT_EQ(255, saved.alertsChannelState.channelVolume); - ASSERT_FALSE(saved.alertsChannelState.channelMuteStatus); -} - -} // namespace test -} // namespace speakerManager -} // namespace capabilityAgents -} // namespace alexaClientSDK diff --git a/CapabilityAgents/SpeakerManager/test/SpeakerManagerTest.cpp b/CapabilityAgents/SpeakerManager/test/SpeakerManagerTest.cpp deleted file mode 100644 index 1fb1a8799a..0000000000 --- a/CapabilityAgents/SpeakerManager/test/SpeakerManagerTest.cpp +++ /dev/null @@ -1,1592 +0,0 @@ -/* - * 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 "SpeakerManager/SpeakerManagerStorageInterface.h" -#include "SpeakerManager/SpeakerManager.h" - -namespace alexaClientSDK { -namespace capabilityAgents { -namespace speakerManager { -namespace test { - -using namespace avsCommon::avs; -using namespace avsCommon::avs::attachment::test; -using namespace avsCommon::avs::speakerConstants; -using namespace avsCommon::sdkInterfaces; -using namespace avsCommon::sdkInterfaces::test; -using namespace avsCommon::utils::memory; -using namespace rapidjson; -using namespace ::testing; - -/// Timeout when waiting for futures to be set. -static std::chrono::milliseconds TIMEOUT(1000); - -/// The @c MessageId identifer. -static const std::string MESSAGE_ID("messageId"); - -/// A @c SetVolume/AdjustVolume payload. -static const std::string VOLUME_PAYLOAD = - "{" - "\"volume\":" + - std::to_string(AVS_SET_VOLUME_MAX) + - "" - "}"; - -/// A @c SetMute payload. -static const std::string MUTE_PAYLOAD = - "{" - "\"mute\":" + - MUTE_STRING + - "" - "}"; - -/// A @c SetMute payload to unmute. -static const std::string UNMUTE_PAYLOAD = - "{" - "\"mute\":" + - UNMUTE_STRING + - "" - "}"; - -#ifdef ENABLE_MAXVOLUME_SETTING -/// A valid value to be used as maximum volume limit. -static const int8_t VALID_MAXIMUM_VOLUME_LIMIT = AVS_SET_VOLUME_MAX - 10; - -/// An invalid maximum volume limit value -static const int8_t INVALID_MAXIMUM_VOLUME_LIMIT = AVS_SET_VOLUME_MAX + 10; -#endif - -/// A valid delta to adjust the volume. -static const int8_t VALID_VOLUME_ADJUSTMENT = 10; - -/** - * A mock object to test that the observer is being correctly notified. - */ -class MockObserver : public SpeakerManagerObserverInterface { -public: - MOCK_METHOD3( - onSpeakerSettingsChanged, - void(const Source&, const ChannelVolumeInterface::Type&, const SpeakerInterface::SpeakerSettings&)); -}; - -class MockSpeakerManagerStorage : public SpeakerManagerStorageInterface { -public: - MOCK_METHOD1(loadState, bool(SpeakerManagerStorageState&)); - MOCK_METHOD1(saveState, bool(const SpeakerManagerStorageState&)); - - MockSpeakerManagerStorage() : - m_state{.speakerChannelState = {.channelVolume = AVS_SET_VOLUME_MIN, .channelMuteStatus = UNMUTE}, - .alertsChannelState = {.channelVolume = AVS_SET_VOLUME_MIN, .channelMuteStatus = UNMUTE}} { - ON_CALL(*this, loadState(_)).WillByDefault(Invoke([this](SpeakerManagerStorageState& state) { - state = this->m_state; - return true; - })); - ON_CALL(*this, saveState(_)).WillByDefault(Invoke([this](const SpeakerManagerStorageState& state) { - this->m_state = state; - return true; - })); - } - - void setDefaults() { - m_state = {.speakerChannelState = {.channelVolume = AVS_SET_VOLUME_MIN, .channelMuteStatus = UNMUTE}, - .alertsChannelState = {.channelVolume = AVS_SET_VOLUME_MIN, .channelMuteStatus = UNMUTE}}; - } - - void setFailureMode() { - ON_CALL(*this, loadState(_)).WillByDefault(Return(false)); - ON_CALL(*this, saveState(_)).WillByDefault(Return(false)); - } - - SpeakerManagerStorageState m_state; -}; - -class SpeakerManagerTest : public ::testing::TestWithParam> { -public: - /// SetUp before each test. - void SetUp(); - - /// TearDown after each test. - void TearDown(); - - /// CleanUp and reset the SpeakerManager. - void cleanUp(); - - /// Function to wait for @c m_wakeSetCompleteFuture to be set. - void wakeOnSetCompleted(); - - /// Helper function to get unique @c Type. - std::set getUniqueTypes(std::vector>& groups); - -#ifdef ENABLE_MAXVOLUME_SETTING - /** - * Helper function for create and sent a directive - * - * @param directiveName The directive name. One of SetVolume or AdjustVolume. - * @param volume The value of the volume files within the directive. - */ - void createAndSendVolumeDirective(const std::string directiveName, const int8_t volume); -#endif - - std::vector> createChannelVolumeInterfaces() { - auto channelVolumeInterface = std::make_shared>(); - channelVolumeInterface->DelegateToReal(); - return {channelVolumeInterface}; - } - - /// A constructor which initializes the promises and futures needed for the test class. - SpeakerManagerTest() : - 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; - - /// The metric recorder. - std::shared_ptr m_metricRecorder; - - /* - * Set this to a nice mock. The only instance of the mock being called is the setStateProvider member, which we - * explicitly test. - */ - std::shared_ptr> m_mockContextManager; - - std::shared_ptr m_mockStorage; - - /// A strict mock that allows the test to strictly monitor the messages sent. - std::shared_ptr> m_mockMessageSender; - - /// A strict mock that allows the test to strictly monitor the exceptions being sent. - std::shared_ptr> m_mockExceptionSender; - - /// A strict mock that allows the test to strictly monitor the handling of directives. - std::unique_ptr> m_mockDirectiveHandlerResult; - - /// A mock to allow testing of the observer callback behavior. - std::shared_ptr> m_observer; - - /// A pointer to an instance of the SpeakerManager that will be instantiated per test. - std::shared_ptr m_speakerManager; -}; - -void SpeakerManagerTest::SetUp() { - m_mockStorage = std::make_shared>(); - - m_metricRecorder = std::make_shared>(); - m_mockContextManager = std::make_shared>(); - m_mockMessageSender = std::make_shared>(); - m_mockExceptionSender = std::make_shared>(); - m_mockDirectiveHandlerResult = make_unique>(); - m_observer = std::make_shared>(); -} - -void SpeakerManagerTest::TearDown() { - if (m_speakerManager) { - m_speakerManager->shutdown(); - m_speakerManager.reset(); - } -} - -void SpeakerManagerTest::wakeOnSetCompleted() { - m_wakeSetCompletedPromise.set_value(); -} - -/** - * Helper function to get unique @c Type from a vector of speakers. - */ -std::set SpeakerManagerTest::getUniqueTypes( - std::vector>& groups) { - std::set types; - for (auto item : groups) { - types.insert(item->getSpeakerType()); - } - return types; -} - -#ifdef ENABLE_MAXVOLUME_SETTING -void SpeakerManagerTest::createAndSendVolumeDirective(const std::string directiveName, const int8_t volume) { - EXPECT_CALL(*(m_mockDirectiveHandlerResult.get()), setCompleted()) - .Times(1) - .WillOnce(InvokeWithoutArgs(this, &SpeakerManagerTest::wakeOnSetCompleted)); - - static int id = 1; - const std::string messageId = MESSAGE_ID + std::to_string(id++); - std::string payload = - "{" - "\"volume\":" + - std::to_string(volume) + "}"; - - // Create Directive. - auto attachmentManager = std::make_shared>(); - auto avsMessageHeader = std::make_shared(SET_VOLUME.nameSpace, directiveName, messageId); - - std::shared_ptr directive = - AVSDirective::create("", avsMessageHeader, payload, attachmentManager, ""); - - m_speakerManager->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); - m_speakerManager->CapabilityAgent::handleDirective(messageId); - m_wakeSetCompletedFuture.wait_for(TIMEOUT); -} - -static int8_t getSpeakerVolume(std::shared_ptr channelVolumeInterface) { - SpeakerInterface::SpeakerSettings speakerSettings; - channelVolumeInterface->getSpeakerSettings(&speakerSettings); - return speakerSettings.volume; -} -#endif - -/// Helper function to generate the VolumeState in JSON for the ContextManager. -std::string generateVolumeStateJson(SpeakerInterface::SpeakerSettings settings) { - rapidjson::Document state(rapidjson::kObjectType); - state.AddMember(VOLUME_KEY, settings.volume, state.GetAllocator()); - state.AddMember(MUTED_KEY, settings.mute, state.GetAllocator()); - - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - if (!state.Accept(writer)) { - return ""; - } - - return buffer.GetString(); -} - -/** - * Tests creating the SpeakerManager with a null contextManager. - */ -TEST_F(SpeakerManagerTest, test_nullContextManager) { - auto channelVolumeInterfaces = createChannelVolumeInterfaces(); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, channelVolumeInterfaces, nullptr, m_mockMessageSender, m_mockExceptionSender, m_metricRecorder); - - ASSERT_EQ(m_speakerManager, nullptr); -} - -/** - * Tests creating the SpeakerManager with a null messageSender. - */ -TEST_F(SpeakerManagerTest, test_nullMessageSender) { - auto channelVolumeInterfaces = createChannelVolumeInterfaces(); - m_speakerManager = SpeakerManager::create( - m_mockStorage, channelVolumeInterfaces, m_mockContextManager, nullptr, m_mockExceptionSender, m_metricRecorder); - - ASSERT_EQ(m_speakerManager, nullptr); -} - -/** - * Tests creating the SpeakerManager with a null exceptionSender. - */ -TEST_F(SpeakerManagerTest, test_nullExceptionSender) { - auto channelVolumeInterfaces = createChannelVolumeInterfaces(); - m_speakerManager = SpeakerManager::create( - m_mockStorage, channelVolumeInterfaces, m_mockContextManager, m_mockMessageSender, nullptr, m_metricRecorder); - - ASSERT_EQ(m_speakerManager, nullptr); -} - -/** - * Tests creating the SpeakerManager with no channelVolumeInterfaces. - */ -TEST_F(SpeakerManagerTest, test_noChannelVolumeInterfaces) { - m_speakerManager = SpeakerManager::create( - m_mockStorage, {}, m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_metricRecorder); - - ASSERT_NE(m_speakerManager, nullptr); -} - -/** - * Tests that the SpeakerManager initially provides the state at constructor time. - */ -TEST_F(SpeakerManagerTest, test_contextManagerSetStateConstructor) { - EXPECT_CALL( - *m_mockContextManager, - setState(VOLUME_STATE, generateVolumeStateJson(DEFAULT_SETTINGS), StateRefreshPolicy::NEVER, _)) - .Times(Exactly(1)); - auto groups = createChannelVolumeInterfaces(); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, groups, m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_metricRecorder); -} - -/* - * Test setVolume with a value that's under the bounds. The operation should fail. - */ -TEST_F(SpeakerManagerTest, test_setVolumeUnderBounds) { - auto channelVolumeInterface = std::make_shared>(); - channelVolumeInterface->DelegateToReal(); - - // Expect call on initialization - EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(1)); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, - {channelVolumeInterface}, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - - // Expect no more calls - EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(0)); - EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); - EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); - - m_speakerManager->addSpeakerManagerObserver(m_observer); - SpeakerManagerInterface::NotificationProperties properties; - std::future future = m_speakerManager->setVolume( - ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_SET_VOLUME_MIN - 1, properties); - ASSERT_FALSE(future.get()); -} - -/* - * Test setVolume with a value that's over the bounds. The operation should fail. - */ -TEST_F(SpeakerManagerTest, test_setVolumeOverBounds) { - auto channelVolumeInterface = std::make_shared>(); - channelVolumeInterface->DelegateToReal(); - - // Expect call on initialization. - EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(1)); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, - {channelVolumeInterface}, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - - // Expect no more calls. - EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(0)); - EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); - EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); - m_speakerManager->addSpeakerManagerObserver(m_observer); - SpeakerManagerInterface::NotificationProperties properties; - std::future future = m_speakerManager->setVolume( - ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_SET_VOLUME_MAX + 1, properties); - ASSERT_FALSE(future.get()); -} - -/* - * Test adjustVolume with a value that's under the bounds. The operation should fail. - */ -TEST_F(SpeakerManagerTest, test_adjustVolumeUnderBounds) { - auto channelVolumeInterface = std::make_shared>(); - channelVolumeInterface->DelegateToReal(); - - // Expect call on initialization. - EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(1)); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, - {channelVolumeInterface}, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - - // Expect no more calls. - EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(0)); - EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); - EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); - m_speakerManager->addSpeakerManagerObserver(m_observer); - - SpeakerManagerInterface::NotificationProperties properties; - std::future future = m_speakerManager->adjustVolume( - ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_ADJUST_VOLUME_MIN - 1, properties); - ASSERT_FALSE(future.get()); -} - -/* - * Test adjustVolume with a value that's over the bounds. The operation should fail. - */ -TEST_F(SpeakerManagerTest, test_adjustVolumeOverBounds) { - auto channelVolumeInterface = std::make_shared>(); - channelVolumeInterface->DelegateToReal(); - // Expect call on initialization. - EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(1)); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, - {channelVolumeInterface}, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - - // Expect no more calls. - EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(0)); - EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); - EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); - m_speakerManager->addSpeakerManagerObserver(m_observer); - SpeakerManagerInterface::NotificationProperties properties; - std::future future = m_speakerManager->adjustVolume( - ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_ADJUST_VOLUME_MAX + 1, properties); - ASSERT_FALSE(future.get()); -} - -/* - * Test if one speaker is out of sync, getSpeakingSettings should return the cached value correctly. - */ -TEST_F(SpeakerManagerTest, test_getCachedSettings) { - // Prepare two speakers with the same type AVS_SPEAKER_VOLUME - auto channelVolumeInterface1 = std::make_shared>(); - auto channelVolumeInterface2 = std::make_shared>(); - channelVolumeInterface1->DelegateToReal(); - channelVolumeInterface2->DelegateToReal(); - // Get speaker settings from the first speaker of each type during initialization. - EXPECT_CALL(*channelVolumeInterface1, getSpeakerSettings(_)).Times(Exactly(1)); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, - {channelVolumeInterface1, channelVolumeInterface2}, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - - // If a speaker changes its volume and is out of sync with the rest speakers of the same type, querying speaker - // settings from SpeakerManager should return the cached volume correctly. - channelVolumeInterface2->setUnduckedVolume(AVS_SET_VOLUME_MAX); - channelVolumeInterface2->setMute(MUTE); - SpeakerInterface::SpeakerSettings settings; - std::future future = - m_speakerManager->getSpeakerSettings(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, &settings); - ASSERT_TRUE(future.get()); - ASSERT_EQ(settings.volume, DEFAULT_SETTINGS.volume); - ASSERT_EQ(settings.mute, DEFAULT_SETTINGS.mute); - - ASSERT_TRUE(channelVolumeInterface2->getSpeakerSettings(&settings)); - ASSERT_EQ(settings.volume, AVS_SET_VOLUME_MAX); - ASSERT_EQ(settings.mute, MUTE); -} - -/* - * Test adjustVolume when the adjusted volume is unchanged. Should not send an event. - */ -TEST_F(SpeakerManagerTest, test_eventNotSentWhenAdjustVolumeUnchanged) { - auto channelVolumeInterface = std::make_shared>(); - channelVolumeInterface->DelegateToReal(); - - auto groupVec = std::vector>{channelVolumeInterface}; - - m_speakerManager = SpeakerManager::create( - m_mockStorage, groupVec, m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_metricRecorder); - - // The test adjusts the volume by AVS_ADJUST_VOLUME_MIN, which results in the lowest volume possible. - SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MIN, UNMUTE}; - m_speakerManager->addSpeakerManagerObserver(m_observer); - SpeakerManagerInterface::NotificationProperties properties; - - for (auto type : getUniqueTypes(groupVec)) { - EXPECT_CALL( - *m_observer, - onSpeakerSettingsChanged(SpeakerManagerObserverInterface::Source::LOCAL_API, type, expectedSettings)) - .Times(Exactly(1)); - if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { - EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); - EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(0)); - EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)) - .Times(AnyNumber()); - EXPECT_CALL( - *m_mockContextManager, - setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) - .Times(Exactly(1)); - } - - std::future future = m_speakerManager->adjustVolume(type, AVS_ADJUST_VOLUME_MIN, properties); - ASSERT_TRUE(future.get()); - } -} - -/* - * Test setVolume when the new volume is unchanged. Should not send an event. - */ -TEST_F(SpeakerManagerTest, test_eventNotSentWhenSetVolumeUnchanged) { - auto channelVolumeInterface = std::make_shared>(); - channelVolumeInterface->DelegateToReal(); - EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(AVS_SET_VOLUME_MIN)).Times(Exactly(2)); - - auto groupVec = std::vector>{channelVolumeInterface}; - m_speakerManager = SpeakerManager::create( - m_mockStorage, groupVec, m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_metricRecorder); - - SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MIN, UNMUTE}; - m_speakerManager->addSpeakerManagerObserver(m_observer); - SpeakerManagerInterface::NotificationProperties properties; - - for (auto type : getUniqueTypes(groupVec)) { - EXPECT_CALL( - *m_observer, - onSpeakerSettingsChanged(SpeakerManagerObserverInterface::Source::LOCAL_API, type, expectedSettings)) - .Times(Exactly(1)); - EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(0)); - if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { - EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(0)); - EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)) - .Times(AnyNumber()); - EXPECT_CALL( - *m_mockContextManager, - setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) - .Times(Exactly(1)); - } - - std::future future = m_speakerManager->setVolume(type, AVS_SET_VOLUME_MIN, properties); - ASSERT_TRUE(future.get()); - } -} - -/** - * Test getConfiguration and ensure that all directives are handled. - */ -TEST_F(SpeakerManagerTest, test_getConfiguration) { - auto channelVolumeInterfaceVec = createChannelVolumeInterfaces(); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, - channelVolumeInterfaceVec, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - - auto configuration = m_speakerManager->getConfiguration(); - auto neitherNonBlockingPolicy = BlockingPolicy(BlockingPolicy::MEDIUMS_NONE, false); - ASSERT_EQ(configuration[SET_VOLUME], neitherNonBlockingPolicy); - ASSERT_EQ(configuration[ADJUST_VOLUME], neitherNonBlockingPolicy); - ASSERT_EQ(configuration[SET_MUTE], neitherNonBlockingPolicy); -} - -/** - * Test that adding duplicated ChannelVolumeInterface instances in the SpeakerManager works correctly. - */ -TEST_F(SpeakerManagerTest, test_addDuplicatedChannelVolumeInterfaces) { - auto channelVolumeInterface = std::make_shared>(); - channelVolumeInterface->DelegateToReal(); - std::vector> channelVolumeInterfaceVec = {channelVolumeInterface, - channelVolumeInterface}; - m_speakerManager = SpeakerManager::create( - m_mockStorage, - channelVolumeInterfaceVec, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(1)); - EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); - SpeakerManagerInterface::NotificationProperties properties; - std::future future = m_speakerManager->adjustVolume( - ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_ADJUST_VOLUME_MAX, properties); - ASSERT_TRUE(future.get()); -} - -/** - * Test that adding a null observer does not cause any errors in the SpeakerManager. - */ -TEST_F(SpeakerManagerTest, test_addNullObserver) { - auto channelVolumeInterfaceVec = createChannelVolumeInterfaces(); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, - channelVolumeInterfaceVec, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - m_speakerManager->addSpeakerManagerObserver(nullptr); - EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(2)); - SpeakerManagerInterface::NotificationProperties properties; - - m_speakerManager->setVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_SET_VOLUME_MAX, properties) - .wait(); - m_speakerManager->adjustVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_ADJUST_VOLUME_MAX, properties) - .wait(); - m_speakerManager->setMute(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, MUTE, properties).wait(); -} - -/** - * Test that removing an observer works correctly. - */ -TEST_F(SpeakerManagerTest, test_removeSpeakerManagerObserver) { - auto channelVolumeInterfaceVec = createChannelVolumeInterfaces(); - - EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); - EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(2)); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, - channelVolumeInterfaceVec, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - m_speakerManager->addSpeakerManagerObserver(m_observer); - m_speakerManager->removeSpeakerManagerObserver(m_observer); - SpeakerManagerInterface::NotificationProperties properties; - - m_speakerManager->setVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_SET_VOLUME_MAX, properties) - .wait(); - m_speakerManager->adjustVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_ADJUST_VOLUME_MAX, properties) - .wait(); - m_speakerManager->setMute(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, MUTE, properties).wait(); -} - -/** - * Test that removing a null observer does not cause any errors in the SpeakerManager. - */ -TEST_F(SpeakerManagerTest, test_removeNullObserver) { - auto channelVolumeInterfaceVec = createChannelVolumeInterfaces(); - - EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(2)); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, - channelVolumeInterfaceVec, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - m_speakerManager->removeSpeakerManagerObserver(nullptr); - SpeakerManagerInterface::NotificationProperties properties; - - m_speakerManager->setVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_SET_VOLUME_MAX, properties) - .wait(); - m_speakerManager->adjustVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_ADJUST_VOLUME_MAX, properties) - .wait(); - m_speakerManager->setMute(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, MUTE, properties).wait(); -} - -/* - * Test retry logic for SetVolume on speaker type AVS_SPEAKER_VOLUME. Returning false once for speaker->setVolume() - * triggers retry and when successful returns the future of value true. - */ -TEST_F(SpeakerManagerTest, test_retryAndApplySettingsForSetVolume) { - auto channelVolumeInterface = std::make_shared>(); - channelVolumeInterface->DelegateToReal(); - m_speakerManager = SpeakerManager::create( - m_mockStorage, - {channelVolumeInterface}, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - - auto retryTimes = 0; - EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).WillRepeatedly(InvokeWithoutArgs([&retryTimes] { - return retryTimes++ > 0; - })); - - SpeakerManagerInterface::NotificationProperties properties; - std::future future = - m_speakerManager->setVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_SET_VOLUME_MIN, properties); - ASSERT_TRUE(future.get()); -} - -/* - * Test retry logic for AdjustVolume on speakers of type AVS_SPEAKER_VOLUME. Return false once for the second speaker - * during adjustVolume() to trigger a retry. The delta should not be applied again to the first speaker during retry. - */ -TEST_F(SpeakerManagerTest, test_retryAndApplySettingsForAdjustVolume) { - auto channelVolumeInterface1 = std::make_shared>(); - auto channelVolumeInterface2 = std::make_shared>(); - channelVolumeInterface1->DelegateToReal(); - channelVolumeInterface2->DelegateToReal(); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, - {channelVolumeInterface1, channelVolumeInterface2}, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - - auto retryTimes = 0; - EXPECT_CALL(*channelVolumeInterface2, setUnduckedVolume(_)).WillRepeatedly(InvokeWithoutArgs([&retryTimes] { - return retryTimes++ > 0; - })); - - // Expect volumeChanged event. - EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); - std::future future = m_speakerManager->adjustVolume( - ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, - VALID_VOLUME_ADJUSTMENT, - SpeakerManagerInterface::NotificationProperties()); - ASSERT_TRUE(future.get()); - - SpeakerInterface::SpeakerSettings settings1; - ASSERT_TRUE(channelVolumeInterface1->getSpeakerSettings(&settings1)); - ASSERT_EQ(settings1.volume, DEFAULT_SETTINGS.volume + VALID_VOLUME_ADJUSTMENT); - - SpeakerInterface::SpeakerSettings speakerSettings; - std::future settingsFuture = - m_speakerManager->getSpeakerSettings(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, &speakerSettings); - ASSERT_TRUE(settingsFuture.get()); - ASSERT_EQ(speakerSettings.volume, DEFAULT_SETTINGS.volume + VALID_VOLUME_ADJUSTMENT); -} - -/* - * Test retry logic for SetMute on speaker type AVS_SPEAKER_VOLUME. Returning false once for speaker->setMute() - * triggers retry and when successful returns the future of value true. - */ -TEST_F(SpeakerManagerTest, test_retryAndApplySettingsForSetMute) { - auto channelVolumeInterface = std::make_shared>(); - channelVolumeInterface->DelegateToReal(); - m_speakerManager = SpeakerManager::create( - m_mockStorage, - {channelVolumeInterface}, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - - auto retryTimes = 0; - EXPECT_CALL(*channelVolumeInterface, setMute(_)).WillRepeatedly(InvokeWithoutArgs([&retryTimes] { - return retryTimes++ > 0; - })); - - EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); - SpeakerManagerInterface::NotificationProperties properties; - - std::future future = - m_speakerManager->setMute(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, MUTE, properties); - ASSERT_TRUE(future.get()); -} - -/* - * Test retryAndApplySettings() failure for setVolume, adjustVolume and setMute on speaker type AVS_SPEAKER_VOLUME. - * Repeatedly returning false for adjustVolume() and setMute() to trigger retries. After retrying maximum times, - * returning the future of false. - */ -TEST_F(SpeakerManagerTest, test_retryAndApplySettingsFails) { - auto channelVolumeInterface = std::make_shared>(); - channelVolumeInterface->DelegateToReal(); - m_speakerManager = SpeakerManager::create( - m_mockStorage, - {channelVolumeInterface}, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - - EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).WillRepeatedly(Return(false)); - EXPECT_CALL(*channelVolumeInterface, setMute(_)).WillRepeatedly(Return(false)); - EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(0); - - std::future setVolumeResult = m_speakerManager->setVolume( - ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, - AVS_SET_VOLUME_MIN, - SpeakerManagerInterface::NotificationProperties()); - ASSERT_FALSE(setVolumeResult.get()); - - std::future adjustVolumeResult = m_speakerManager->adjustVolume( - ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, - VALID_VOLUME_ADJUSTMENT, - SpeakerManagerInterface::NotificationProperties()); - ASSERT_FALSE(adjustVolumeResult.get()); - - std::future setMuteResult = m_speakerManager->setMute( - ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, MUTE, SpeakerManagerInterface::NotificationProperties()); - ASSERT_FALSE(setMuteResult.get()); - - SpeakerInterface::SpeakerSettings speakerSettings; - std::future settingsFuture = - m_speakerManager->getSpeakerSettings(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, &speakerSettings); - ASSERT_TRUE(settingsFuture.get()); - ASSERT_EQ(speakerSettings.volume, DEFAULT_SETTINGS.volume); - ASSERT_EQ(speakerSettings.mute, DEFAULT_SETTINGS.mute); -} - -#ifdef ENABLE_MAXVOLUME_SETTING -/** - * Test that setting a maximum volume limit succeeds and a local call to setVolume or adjustVolume will - * completely fail. - */ - -TEST_F(SpeakerManagerTest, test_setMaximumVolumeLimit) { - auto avsChannelVolumeInterface = - std::make_shared>(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME); - avsChannelVolumeInterface->DelegateToReal(); - auto alertsChannelVolumeInterface = - std::make_shared>(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME); - alertsChannelVolumeInterface->DelegateToReal(); - - avsChannelVolumeInterface->setUnduckedVolume(VALID_MAXIMUM_VOLUME_LIMIT - 1); - alertsChannelVolumeInterface->setUnduckedVolume(VALID_MAXIMUM_VOLUME_LIMIT - 1); - - // Expect volumeChanged event. - EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); - EXPECT_CALL(*avsChannelVolumeInterface, setUnduckedVolume(_)).Times(AtLeast(1)); - EXPECT_CALL(*alertsChannelVolumeInterface, setUnduckedVolume(_)).Times(AtLeast(1)); - EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(0); - EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(1)); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, - {avsChannelVolumeInterface, alertsChannelVolumeInterface}, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - SpeakerManagerInterface::NotificationProperties properties; - - EXPECT_TRUE(m_speakerManager->setMaximumVolumeLimit(VALID_MAXIMUM_VOLUME_LIMIT).get()); - - // Local change either with setVolume will set to limit but with adjustVolume will fail - EXPECT_TRUE( - m_speakerManager - ->setVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, VALID_MAXIMUM_VOLUME_LIMIT + 1, properties) - .get()); - EXPECT_FALSE( - m_speakerManager - ->adjustVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, VALID_MAXIMUM_VOLUME_LIMIT + 1, properties) - .get()); - - // The volume went to upper limit. - EXPECT_EQ(getSpeakerVolume(avsChannelVolumeInterface), VALID_MAXIMUM_VOLUME_LIMIT); - EXPECT_EQ(getSpeakerVolume(alertsChannelVolumeInterface), VALID_MAXIMUM_VOLUME_LIMIT); - - // Increase the volume by 2, so end result will exceed the limit. - EXPECT_TRUE(m_speakerManager->adjustVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, 2, properties).get()); - - // Following the 2nd adjustVolume, the volume will change to the limit. - EXPECT_EQ(getSpeakerVolume(alertsChannelVolumeInterface), VALID_MAXIMUM_VOLUME_LIMIT); -} - -/** - * Test that if a new limit was set while the volume was higher than the new limit, operation will succeed and the - * volume will be decreased. - */ - -TEST_F(SpeakerManagerTest, testSetMaximumVolumeLimitWhileVolumeIsHigher) { - auto avsChannelVolumeInterface = - std::make_shared>(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME); - auto alertsChannelVolumeInterface = - std::make_shared>(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME); - - avsChannelVolumeInterface->DelegateToReal(); - alertsChannelVolumeInterface->DelegateToReal(); - - EXPECT_TRUE(avsChannelVolumeInterface->setUnduckedVolume(VALID_MAXIMUM_VOLUME_LIMIT + 1)); - EXPECT_TRUE(alertsChannelVolumeInterface->setUnduckedVolume(VALID_MAXIMUM_VOLUME_LIMIT + 1)); - - EXPECT_CALL(*avsChannelVolumeInterface, setUnduckedVolume(VALID_MAXIMUM_VOLUME_LIMIT)).Times(1); - EXPECT_CALL(*alertsChannelVolumeInterface, setUnduckedVolume(VALID_MAXIMUM_VOLUME_LIMIT)).Times(1); - - // Expect volumeChanged event. - EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); - - EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(1)); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, - {avsChannelVolumeInterface, alertsChannelVolumeInterface}, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - - EXPECT_TRUE(m_speakerManager->setMaximumVolumeLimit(VALID_MAXIMUM_VOLUME_LIMIT).get()); - - EXPECT_EQ(getSpeakerVolume(avsChannelVolumeInterface), VALID_MAXIMUM_VOLUME_LIMIT); - EXPECT_EQ(getSpeakerVolume(alertsChannelVolumeInterface), VALID_MAXIMUM_VOLUME_LIMIT); -} - -/** - * Test that SetVolume directive with volume > limit should set the volume to the limit - */ - -TEST_F(SpeakerManagerTest, testAVSSetVolumeHigherThanLimit) { - avsCommon::utils::logger::getConsoleLogger()->setLevel(avsCommon::utils::logger::Level::DEBUG9); - auto avsChannelVolumeInterface = - std::make_shared>(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME); - auto alertsChannelVolumeInterface = - std::make_shared>(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME); - - avsChannelVolumeInterface->DelegateToReal(); - alertsChannelVolumeInterface->DelegateToReal(); - - EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(1)); - EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); - - EXPECT_TRUE(avsChannelVolumeInterface->setUnduckedVolume(VALID_MAXIMUM_VOLUME_LIMIT - 1)); - EXPECT_TRUE(alertsChannelVolumeInterface->setUnduckedVolume(VALID_MAXIMUM_VOLUME_LIMIT - 1)); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, - {avsChannelVolumeInterface, alertsChannelVolumeInterface}, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - - EXPECT_TRUE(m_speakerManager->setMaximumVolumeLimit(VALID_MAXIMUM_VOLUME_LIMIT).get()); - - createAndSendVolumeDirective(SET_VOLUME.name, VALID_MAXIMUM_VOLUME_LIMIT + 1); - - ASSERT_EQ(getSpeakerVolume(avsChannelVolumeInterface), VALID_MAXIMUM_VOLUME_LIMIT); - ASSERT_EQ(getSpeakerVolume(alertsChannelVolumeInterface), VALID_MAXIMUM_VOLUME_LIMIT); -} - -/** - * Test that a call to @c setMaximumVolumeLimit with invalid value fails. - */ -TEST_F(SpeakerManagerTest, testSetMaximumVolumeLimitWithInvalidValue) { - auto avsChannelVolumeInterface = createChannelVolumeInterfaces(); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, - avsChannelVolumeInterface, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - - EXPECT_FALSE(m_speakerManager->setMaximumVolumeLimit(INVALID_MAXIMUM_VOLUME_LIMIT).get()); -} -#endif - -/** - * Create different combinations of @c Type for parameterized tests (TEST_P). - */ -INSTANTIATE_TEST_CASE_P( - Parameterized, - SpeakerManagerTest, - // clang-format off - ::testing::Values( - std::vector{ - ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME - }, - std::vector{ - ChannelVolumeInterface::Type::AVS_ALERTS_VOLUME - }, - std::vector{ - ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, - ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME - }, - std::vector{ - ChannelVolumeInterface::Type::AVS_ALERTS_VOLUME, - ChannelVolumeInterface::Type::AVS_ALERTS_VOLUME, - }, - std::vector{ - ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, - ChannelVolumeInterface::Type::AVS_ALERTS_VOLUME, - ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, - ChannelVolumeInterface::Type::AVS_ALERTS_VOLUME - })); -// clang-format on - -/** - * Parameterized test for setVolume. One event should be sent if an AVS_SPEAKER_VOLUME typed speaker is modified. - */ -TEST_P(SpeakerManagerTest, test_setVolume) { - std::vector> groupVec; - - for (auto& typeOfSpeaker : GetParam()) { - auto group = std::make_shared>(typeOfSpeaker); - group->DelegateToReal(); - EXPECT_CALL(*group, setUnduckedVolume(_)).Times(Exactly(1)); - EXPECT_CALL(*group, setUnduckedVolume(AVS_SET_VOLUME_MAX)).Times(Exactly(1)); - groupVec.push_back(group); - } - - m_speakerManager = SpeakerManager::create( - m_mockStorage, groupVec, m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_metricRecorder); - - SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MAX, UNMUTE}; - m_speakerManager->addSpeakerManagerObserver(m_observer); - SpeakerManagerInterface::NotificationProperties properties(SpeakerManagerObserverInterface::Source::DIRECTIVE); - - for (auto type : getUniqueTypes(groupVec)) { - EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(1)); - EXPECT_CALL( - *m_observer, - onSpeakerSettingsChanged(SpeakerManagerObserverInterface::Source::DIRECTIVE, type, expectedSettings)) - .Times(Exactly(1)); - if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { - EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); - EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)) - .Times(AnyNumber()); - EXPECT_CALL( - *m_mockContextManager, - setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) - .Times(Exactly(1)); - } - - std::future future = m_speakerManager->setVolume(type, AVS_SET_VOLUME_MAX, properties); - ASSERT_TRUE(future.get()); - } -} - -/** - * Parameterized test for adjustVolume. One event should be sent if an AVS_SPEAKER_VOLUME typed speaker is modified. - */ -TEST_P(SpeakerManagerTest, test_adjustVolume) { - std::vector> groupVec; - - for (auto& typeOfSpeaker : GetParam()) { - auto group = std::make_shared>(typeOfSpeaker); - group->DelegateToReal(); - groupVec.push_back(group); - } - - m_speakerManager = SpeakerManager::create( - m_mockStorage, groupVec, m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_metricRecorder); - - // The test adjusts the volume by AVS_ADJUST_VOLUME_MAX, which results in the lowest volume possible. - SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MAX, UNMUTE}; - m_speakerManager->addSpeakerManagerObserver(m_observer); - SpeakerManagerInterface::NotificationProperties properties(SpeakerManagerObserverInterface::Source::DIRECTIVE); - - for (auto type : getUniqueTypes(groupVec)) { - EXPECT_CALL(*m_mockStorage, saveState(_)).Times(Exactly(1)); - EXPECT_CALL( - *m_observer, - onSpeakerSettingsChanged(SpeakerManagerObserverInterface::Source::DIRECTIVE, type, expectedSettings)) - .Times(Exactly(1)); - if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { - EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); - EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)) - .Times(AnyNumber()); - EXPECT_CALL( - *m_mockContextManager, - setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) - .Times(Exactly(1)); - } - - std::future future = m_speakerManager->adjustVolume(type, AVS_ADJUST_VOLUME_MAX, properties); - ASSERT_TRUE(future.get()); - } -} - -/** - * Parameterized test for setMute. One event should be sent if an AVS_SPEAKER_VOLUME typed speaker is modified. - */ -TEST_P(SpeakerManagerTest, test_setMute) { - std::vector> groupVec; - - for (auto& typeOfSpeaker : GetParam()) { - auto group = std::make_shared>(typeOfSpeaker); - group->DelegateToReal(); - EXPECT_CALL(*group, setMute(_)).Times(Exactly(1)); - EXPECT_CALL(*group, setMute(MUTE)).Times(Exactly(1)); - groupVec.push_back(group); - } - - m_speakerManager = SpeakerManager::create( - m_mockStorage, groupVec, m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_metricRecorder); - - SpeakerInterface::SpeakerSettings expectedSettings{DEFAULT_SETTINGS.volume, MUTE}; - m_speakerManager->addSpeakerManagerObserver(m_observer); - SpeakerManagerInterface::NotificationProperties properties(SpeakerManagerObserverInterface::Source::DIRECTIVE); - - for (auto type : getUniqueTypes(groupVec)) { - EXPECT_CALL( - *m_observer, - onSpeakerSettingsChanged(SpeakerManagerObserverInterface::Source::DIRECTIVE, type, expectedSettings)) - .Times(Exactly(1)); - if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { - EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); - EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)) - .Times(AnyNumber()); - EXPECT_CALL( - *m_mockContextManager, - setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) - .Times(Exactly(1)); - } - - std::future future = m_speakerManager->setMute(type, MUTE, properties); - ASSERT_TRUE(future.get()); - } -} - -/** - * Parameterized test for getSpeakerSettings. Operation should succeed with default speaker settings. - */ -TEST_P(SpeakerManagerTest, test_getSpeakerSettings) { - std::vector> groupVec; - std::set uniqueTypes; - - for (auto& typeOfSpeaker : GetParam()) { - auto group = std::make_shared>(typeOfSpeaker); - group->DelegateToReal(); - - // There should be one call to getSpeakerSettings for the first speaker of each type. - if (uniqueTypes.find(typeOfSpeaker) == uniqueTypes.end()) { - EXPECT_CALL(*group, getSpeakerSettings(_)).Times(AtLeast(1)); - uniqueTypes.insert(typeOfSpeaker); - } - - groupVec.push_back(group); - } - - m_speakerManager = SpeakerManager::create( - m_mockStorage, groupVec, m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_metricRecorder); - - EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); - m_speakerManager->addSpeakerManagerObserver(m_observer); - - for (auto speaker : groupVec) { - // SpeakerManager attempts to cache speaker settings initially. No getSpeakerSettings() call should be made to - // each speaker. - auto mockSpeaker = std::dynamic_pointer_cast>(speaker); - ASSERT_TRUE(mockSpeaker); - EXPECT_CALL(*mockSpeaker, getSpeakerSettings(_)).Times(0); - } - - for (auto type : uniqueTypes) { - SpeakerInterface::SpeakerSettings settings; - // Query SpeakerMananger for speaker settings, value should be cached and not queried from each speaker. - std::future future = m_speakerManager->getSpeakerSettings(type, &settings); - ASSERT_TRUE(future.get()); - ASSERT_EQ(settings.volume, DEFAULT_SETTINGS.volume); - ASSERT_EQ(settings.mute, DEFAULT_SETTINGS.mute); - } -} - -/** - * Tests SetVolume Directive. Expect that the volume is unmuted and set, as well at most one - * event is sent. In the event there are no AVS_SPEAKER_VOLUME speakers registered, no event will be sent. - * In addition, only AVS_SPEAKER_VOLUME speakers should be affected. - */ -TEST_P(SpeakerManagerTest, test_setVolumeDirective) { - std::vector> groupVec; - int eventsSent = 0; - SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MAX, UNMUTE}; - - // Create Speaker objects. - for (auto& typeOfSpeaker : GetParam()) { - auto group = std::make_shared>(typeOfSpeaker); - group->DelegateToReal(); - int timesCalled = 0; - if (typeOfSpeaker == ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME) { - timesCalled = 1; - } - - SpeakerInterface::SpeakerSettings temp; - group->getSpeakerSettings(&temp); - if (temp.mute) { - EXPECT_CALL(*group, setMute(_)).Times(Exactly(1)); - EXPECT_CALL(*group, setMute(UNMUTE)).Times(Exactly(timesCalled)); - } - EXPECT_CALL(*group, setUnduckedVolume(_)).Times(Exactly(1)); - EXPECT_CALL(*group, setUnduckedVolume(AVS_SET_VOLUME_MAX)).Times(Exactly(timesCalled)); - - groupVec.push_back(group); - } - - auto uniqueTypes = getUniqueTypes(groupVec); - - // Creation expectations based on type. - if (uniqueTypes.count(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME)) { - eventsSent = 1; - - EXPECT_CALL( - *m_observer, - onSpeakerSettingsChanged( - SpeakerManagerObserverInterface::Source::DIRECTIVE, - ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, - expectedSettings)) - .Times(Exactly(1)); - EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(AnyNumber()); - EXPECT_CALL( - *m_mockContextManager, - setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) - .Times(Exactly(1)); - } else { - EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); - EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(Exactly(0)); - } - EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(eventsSent)); - EXPECT_CALL(*(m_mockDirectiveHandlerResult.get()), setCompleted()) - .Times(1) - .WillOnce(InvokeWithoutArgs(this, &SpeakerManagerTest::wakeOnSetCompleted)); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, groupVec, m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_metricRecorder); - m_speakerManager->addSpeakerManagerObserver(m_observer); - - // Create Directive. - auto attachmentManager = std::make_shared>(); - auto avsMessageHeader = std::make_shared(SET_VOLUME.nameSpace, SET_VOLUME.name, MESSAGE_ID); - std::shared_ptr directive = - AVSDirective::create("", avsMessageHeader, VOLUME_PAYLOAD, attachmentManager, ""); - - m_speakerManager->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); - m_speakerManager->CapabilityAgent::handleDirective(MESSAGE_ID); - m_wakeSetCompletedFuture.wait_for(TIMEOUT); -} - -/** - * Tests AdjustVolume Directive. Expect that the volume is unmuted and adjusted, as well at most one - * event is sent. In the event there are no AVS_SPEAKER_VOLUME speakers registered, no event will be sent. - * In addition, only AVS_SPEAKER_VOLUME speakers should be affected. - */ -TEST_P(SpeakerManagerTest, test_adjustVolumeDirective) { - std::vector> groupVec; - int eventsSent = 0; - SpeakerInterface::SpeakerSettings expectedSettings{AVS_SET_VOLUME_MAX, UNMUTE}; - - // Create Speaker objects. - for (auto& typeOfSpeaker : GetParam()) { - auto group = std::make_shared>(typeOfSpeaker); - group->DelegateToReal(); - int timesCalled = 0; - if (typeOfSpeaker == ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME) { - timesCalled = 1; - } - - SpeakerInterface::SpeakerSettings temp; - group->getSpeakerSettings(&temp); - if (temp.mute) { - EXPECT_CALL(*group, setMute(_)).Times(Exactly(1)); - EXPECT_CALL(*group, setMute(UNMUTE)).Times(Exactly(timesCalled)); - } - EXPECT_CALL(*group, setUnduckedVolume(_)).Times(Exactly(1)); - EXPECT_CALL(*group, setUnduckedVolume(AVS_SET_VOLUME_MAX)).Times(Exactly(timesCalled)); - - groupVec.push_back(group); - } - - auto uniqueTypes = getUniqueTypes(groupVec); - - // Creation expectations based on type. - if (uniqueTypes.count(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME)) { - eventsSent = 1; - - EXPECT_CALL( - *m_observer, - onSpeakerSettingsChanged( - SpeakerManagerObserverInterface::Source::DIRECTIVE, - ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, - expectedSettings)) - .Times(Exactly(1)); - EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(AnyNumber()); - EXPECT_CALL( - *m_mockContextManager, - setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) - .Times(Exactly(1)); - } else { - EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); - EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(Exactly(0)); - } - EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(eventsSent)); - EXPECT_CALL(*(m_mockDirectiveHandlerResult.get()), setCompleted()) - .Times(1) - .WillOnce(InvokeWithoutArgs(this, &SpeakerManagerTest::wakeOnSetCompleted)); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, groupVec, m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_metricRecorder); - m_speakerManager->addSpeakerManagerObserver(m_observer); - - // Create Directive. - auto attachmentManager = std::make_shared>(); - auto avsMessageHeader = std::make_shared(ADJUST_VOLUME.nameSpace, ADJUST_VOLUME.name, MESSAGE_ID); - std::shared_ptr directive = - AVSDirective::create("", avsMessageHeader, VOLUME_PAYLOAD, attachmentManager, ""); - - m_speakerManager->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); - m_speakerManager->CapabilityAgent::handleDirective(MESSAGE_ID); - m_wakeSetCompletedFuture.wait_for(TIMEOUT); -} - -/** - * Tests SetMute Directive. Expect that the volume is muted, as well at most one - * event is sent. In the event there are no AVS_SPEAKER_VOLUME speakers registered, no event will be sent. - * In addition, only AVS_SPEAKER_VOLUME speakers should be affected. - */ -TEST_P(SpeakerManagerTest, test_setMuteDirective) { - std::vector> groupVec; - int eventsSent = 0; - SpeakerInterface::SpeakerSettings expectedSettings{DEFAULT_SETTINGS.volume, MUTE}; - - // Create Speaker objects. - for (auto& typeOfSpeaker : GetParam()) { - auto group = std::make_shared>(typeOfSpeaker); - group->DelegateToReal(); - int timesCalled = 0; - if (typeOfSpeaker == ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME) { - timesCalled = 1; - } - - EXPECT_CALL(*group, setMute(_)).Times(Exactly(1)); - EXPECT_CALL(*group, setMute(MUTE)).Times(Exactly(timesCalled)); - - groupVec.push_back(group); - } - - auto uniqueTypes = getUniqueTypes(groupVec); - - // Creation expectations based on type. - if (uniqueTypes.count(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME)) { - eventsSent = 1; - - EXPECT_CALL( - *m_observer, - onSpeakerSettingsChanged( - SpeakerManagerObserverInterface::Source::DIRECTIVE, - ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, - expectedSettings)) - .Times(Exactly(1)); - EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(AnyNumber()); - EXPECT_CALL( - *m_mockContextManager, - setState(VOLUME_STATE, generateVolumeStateJson(expectedSettings), StateRefreshPolicy::NEVER, _)) - .Times(Exactly(1)); - } else { - EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); - EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(Exactly(0)); - } - - EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(eventsSent)); - EXPECT_CALL(*(m_mockDirectiveHandlerResult.get()), setCompleted()) - .Times(1) - .WillOnce(InvokeWithoutArgs(this, &SpeakerManagerTest::wakeOnSetCompleted)); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, groupVec, m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_metricRecorder); - m_speakerManager->addSpeakerManagerObserver(m_observer); - - // Create Directive. - auto attachmentManager = std::make_shared>(); - auto avsMessageHeader = std::make_shared(SET_MUTE.nameSpace, SET_MUTE.name, MESSAGE_ID); - std::shared_ptr directive = - AVSDirective::create("", avsMessageHeader, MUTE_PAYLOAD, attachmentManager, ""); - - m_speakerManager->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); - m_speakerManager->CapabilityAgent::handleDirective(MESSAGE_ID); - m_wakeSetCompletedFuture.wait_for(TIMEOUT); -} - -/** - * Test setVolume when unmute Directive sent. Setup test by setting volume to 0 and mute to true. - * Expect that the volume is unmuted and set to MIN_UNMUTE_VOLUME, as well at most one - * event is sent. In the event there are no AVS_SPEAKER_VOLUME speakers registered, no event will be sent. - * In addition, only AVS_SPEAKER_VOLUME speakers should be affected. - */ -TEST_P(SpeakerManagerTest, test_setVolumeDirectiveWhenMuted) { - std::vector> groupVec; - - for (auto& typeOfSpeaker : GetParam()) { - auto group = std::make_shared>(typeOfSpeaker); - group->DelegateToReal(); - groupVec.push_back(group); - } - - m_speakerManager = SpeakerManager::create( - m_mockStorage, groupVec, m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_metricRecorder); - - for (auto& group : groupVec) { - auto mockGroup = std::dynamic_pointer_cast>(group); - EXPECT_CALL(*mockGroup, setUnduckedVolume(AVS_SET_VOLUME_MIN)).Times(1); - EXPECT_CALL(*mockGroup, setMute(MUTE)).Times(1); - auto typeOfSpeaker = mockGroup->getSpeakerType(); - if (typeOfSpeaker == ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME) { - EXPECT_CALL(*mockGroup, setMute(UNMUTE)).Times(1); - EXPECT_CALL(*mockGroup, setUnduckedVolume(MIN_UNMUTE_VOLUME)).Times(1); - } - } - - m_speakerManager->addSpeakerManagerObserver(m_observer); - SpeakerManagerInterface::NotificationProperties properties( - SpeakerManagerObserverInterface::Source::LOCAL_API, false, false); - - for (auto type : getUniqueTypes(groupVec)) { - m_speakerManager->setVolume(type, AVS_SET_VOLUME_MIN, properties); - } - - for (auto type : getUniqueTypes(groupVec)) { - std::future future = m_speakerManager->setMute(type, MUTE, properties); - } - - // Check to see if AVS_SPEAKER_VOLUME speakers exist and set EXPECT_CALL accordingly - auto uniqueTypes = getUniqueTypes(groupVec); - int eventsSent = 0; - SpeakerInterface::SpeakerSettings unMuteSettings{MIN_UNMUTE_VOLUME, UNMUTE}; - - if (uniqueTypes.count(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME)) { - // 2 events : {MIN_UNMUTE_VOLUME, MUTE} followed by {MIN_UNMUTE_VOLUME, UNMUTE} - eventsSent = 2; - - EXPECT_CALL( - *m_observer, - onSpeakerSettingsChanged( - SpeakerManagerObserverInterface::Source::DIRECTIVE, - ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, - SpeakerInterface::SpeakerSettings{MIN_UNMUTE_VOLUME, MUTE})) - .Times(Exactly(1)); - EXPECT_CALL( - *m_observer, - onSpeakerSettingsChanged( - SpeakerManagerObserverInterface::Source::DIRECTIVE, - ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, - unMuteSettings)) - .Times(Exactly(1)); - EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(AnyNumber()); - EXPECT_CALL( - *m_mockContextManager, - setState(VOLUME_STATE, generateVolumeStateJson(unMuteSettings), StateRefreshPolicy::NEVER, _)) - .Times(Exactly(1)); - } else { - EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(0); - EXPECT_CALL(*m_mockContextManager, setState(VOLUME_STATE, _, StateRefreshPolicy::NEVER, _)).Times(0); - } - - EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(eventsSent); - EXPECT_CALL(*(m_mockDirectiveHandlerResult.get()), setCompleted()) - .Times(1) - .WillOnce(InvokeWithoutArgs(this, &SpeakerManagerTest::wakeOnSetCompleted)); - - // Create Directive to unmute the device. - auto attachmentManager = std::make_shared>(); - auto avsMessageHeader = std::make_shared(SET_MUTE.nameSpace, SET_MUTE.name, MESSAGE_ID); - std::shared_ptr directive = - AVSDirective::create("", avsMessageHeader, UNMUTE_PAYLOAD, attachmentManager, ""); - - m_speakerManager->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); - m_speakerManager->CapabilityAgent::handleDirective(MESSAGE_ID); - m_wakeSetCompletedFuture.wait_for(TIMEOUT); -} - -/** - * Parameterized test for getSpeakerSettings. Operation should succeed with default speaker settings. - */ -TEST_P(SpeakerManagerTest, test_getSpeakerConfigDefaults) { - std::vector> groupVec; - std::set uniqueTypes; - - for (auto& typeOfSpeaker : GetParam()) { - auto group = std::make_shared>(typeOfSpeaker); - group->DelegateToReal(); - - // There should be one call to getSpeakerSettings for the first speaker of each type. - if (uniqueTypes.find(typeOfSpeaker) == uniqueTypes.end()) { - EXPECT_CALL(*group, getSpeakerSettings(_)).Times(AtLeast(1)); - uniqueTypes.insert(typeOfSpeaker); - } - - groupVec.push_back(group); - } - - m_mockStorage->setFailureMode(); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, groupVec, m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_metricRecorder); - - EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); - m_speakerManager->addSpeakerManagerObserver(m_observer); - - for (auto speaker : groupVec) { - // SpeakerManager attempts to cache speaker settings initially. No getSpeakerSettings() call should be made to - // each speaker. - auto mockSpeaker = std::dynamic_pointer_cast>(speaker); - ASSERT_TRUE(mockSpeaker); - EXPECT_CALL(*mockSpeaker, getSpeakerSettings(_)).Times(0); - } - - for (auto type : uniqueTypes) { - SpeakerInterface::SpeakerSettings settings; - // Query SpeakerMananger for speaker settings, value should be cached and not queried from each speaker. - std::future future = m_speakerManager->getSpeakerSettings(type, &settings); - ASSERT_TRUE(future.get()); - - switch (type) { - case avsCommon::sdkInterfaces::ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME: - EXPECT_EQ(settings.volume, alexaClientSDK::avsCommon::avs::speakerConstants::DEFAULT_SPEAKER_VOLUME); - break; - case avsCommon::sdkInterfaces::ChannelVolumeInterface::Type::AVS_ALERTS_VOLUME: - EXPECT_EQ(settings.volume, alexaClientSDK::avsCommon::avs::speakerConstants::DEFAULT_ALERTS_VOLUME); - break; - } - ASSERT_EQ(settings.mute, false); - } -} - -/** - * Parameterized test for getSpeakerSettings. Operation should succeed with speaker settings from storage. - */ -TEST_P(SpeakerManagerTest, test_getSpeakerConfigFromStorage) { - std::vector> groupVec; - std::set uniqueTypes; - - for (auto& typeOfSpeaker : GetParam()) { - auto group = std::make_shared>(typeOfSpeaker); - group->DelegateToReal(); - - // There should be one call to getSpeakerSettings for the first speaker of each type. - if (uniqueTypes.find(typeOfSpeaker) == uniqueTypes.end()) { - EXPECT_CALL(*group, getSpeakerSettings(_)).Times(AtLeast(1)); - uniqueTypes.insert(typeOfSpeaker); - } - - groupVec.push_back(group); - } - - m_mockStorage->setDefaults(); - - m_speakerManager = SpeakerManager::create( - m_mockStorage, groupVec, m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_metricRecorder); - - EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); - m_speakerManager->addSpeakerManagerObserver(m_observer); - - for (auto speaker : groupVec) { - // SpeakerManager attempts to cache speaker settings initially. No getSpeakerSettings() call should be made to - // each speaker. - auto mockSpeaker = std::dynamic_pointer_cast>(speaker); - ASSERT_TRUE(mockSpeaker); - EXPECT_CALL(*mockSpeaker, getSpeakerSettings(_)).Times(0); - } - - for (auto type : uniqueTypes) { - SpeakerInterface::SpeakerSettings settings; - // Query SpeakerMananger for speaker settings, value should be cached and not queried from each speaker. - std::future future = m_speakerManager->getSpeakerSettings(type, &settings); - ASSERT_TRUE(future.get()); - - EXPECT_EQ(settings.volume, AVS_SET_VOLUME_MIN); - EXPECT_EQ(settings.mute, false); - } -} - -} // namespace test -} // namespace speakerManager -} // namespace capabilityAgents -} // namespace alexaClientSDK \ No newline at end of file diff --git a/CapabilityAgents/SpeechSynthesizer/src/SpeechSynthesizer.cpp b/CapabilityAgents/SpeechSynthesizer/src/SpeechSynthesizer.cpp index dfb53ab5cf..bb0ee35b78 100644 --- a/CapabilityAgents/SpeechSynthesizer/src/SpeechSynthesizer.cpp +++ b/CapabilityAgents/SpeechSynthesizer/src/SpeechSynthesizer.cpp @@ -52,7 +52,7 @@ static const std::string SPEECHSYNTHESIZER_CAPABILITY_INTERFACE_NAME = "SpeechSy static const std::string SPEECHSYNTHESIZER_CAPABILITY_INTERFACE_VERSION = "1.3"; /// String to identify log entries originating from this file. -static const std::string TAG{"SpeechSynthesizer"}; +#define TAG "SpeechSynthesizer" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -261,7 +261,7 @@ avsCommon::avs::DirectiveHandlerConfiguration SpeechSynthesizer::getConfiguratio void SpeechSynthesizer::addObserver(std::shared_ptr observer) { ACSDK_DEBUG9(LX("addObserver").d("observer", observer.get())); - m_executor.submit([this, observer]() { m_observers.insert(observer); }); + m_executor.execute([this, observer]() { m_observers.insert(observer); }); } void SpeechSynthesizer::removeObserver(std::shared_ptr observer) { @@ -277,7 +277,7 @@ void SpeechSynthesizer::onDeregistered() { void SpeechSynthesizer::handleDirectiveImmediately(std::shared_ptr directive) { ACSDK_DEBUG9(LX("handleDirectiveImmediately").d("messageId", directive->getMessageId())); auto info = createDirectiveInfo(directive, nullptr); - m_executor.submit([this, info]() { executeHandleImmediately(info); }); + m_executor.execute([this, info]() { executeHandleImmediately(info); }); } void SpeechSynthesizer::preHandleDirective(std::shared_ptr info) { @@ -287,7 +287,7 @@ void SpeechSynthesizer::preHandleDirective(std::shared_ptr info) } ACSDK_DEBUG9(LX("preHandleDirective").d("messageId", info->directive->getMessageId())); - m_executor.submit([this, info]() { executePreHandle(info); }); + m_executor.execute([this, info]() { executePreHandle(info); }); } void SpeechSynthesizer::handleDirective(std::shared_ptr info) { @@ -300,13 +300,13 @@ void SpeechSynthesizer::handleDirective(std::shared_ptr info) { if (info->directive->getName() == "Speak") { ACSDK_METRIC_MSG(TAG, info->directive, Metrics::Location::SPEECH_SYNTHESIZER_RECEIVE); } - m_executor.submit([this, info]() { executeHandle(info); }); + m_executor.execute([this, info]() { executeHandle(info); }); } void SpeechSynthesizer::cancelDirective(std::shared_ptr info) { if (info && info->directive) { ACSDK_DEBUG9(LX("cancelDirective").d("messageId", info->directive->getMessageId())); - m_executor.submit([this, info]() { executeCancel(info); }); + m_executor.execute([this, info]() { executeCancel(info); }); } else { ACSDK_WARN(LX("cancelDirective").d("reason", "infoNotAvailable")); } @@ -340,7 +340,7 @@ void SpeechSynthesizer::onFocusChanged(FocusState newFocus, MixingBehavior behav } auto currentInfo = std::make_shared>(nullptr); - m_executor.submit([this, desiredState, currentInfo]() { + m_executor.execute([this, desiredState, currentInfo]() { *currentInfo = m_currentInfo; executeStateChange(desiredState); }); @@ -354,7 +354,7 @@ void SpeechSynthesizer::onFocusChanged(FocusState newFocus, MixingBehavior behav .d("initialDesiredState", desiredState) .d("desiredState", m_desiredState) .d("currentState", m_currentState)); - m_executor.submit([this, currentInfo]() { + m_executor.execute([this, currentInfo]() { ACSDK_DEBUG9( LX("onFocusChangedLambda") .d("currentInfo", @@ -377,7 +377,7 @@ void SpeechSynthesizer::provideState( const avsCommon::avs::NamespaceAndName& stateProviderName, const unsigned int stateRequestToken) { ACSDK_DEBUG9(LX("provideState").d("token", stateRequestToken)); - m_executor.submit([this, stateRequestToken]() { + m_executor.execute([this, stateRequestToken]() { std::lock_guard lock(m_mutex); executeProvideStateLocked(stateRequestToken); }); @@ -404,7 +404,7 @@ void SpeechSynthesizer::onPlaybackStarted(SourceId id, const MediaPlayerState&) ACSDK_DEBUG9(LX("onPlaybackStarted").d("callbackSourceId", id)); ACSDK_METRIC_IDS(TAG, "SpeechStarted", "", "", Metrics::Location::SPEECH_SYNTHESIZER_RECEIVE); - m_executor.submit([this, id] { + m_executor.execute([this, id] { if (id != m_mediaSourceId) { ACSDK_ERROR(LX("queueingExecutePlaybackStartedFailed") .d("reason", "mismatchSourceId") @@ -429,7 +429,7 @@ void SpeechSynthesizer::onPlaybackFinished(SourceId id, const MediaPlayerState&) ACSDK_DEBUG9(LX("onPlaybackFinished").d("callbackSourceId", id)); ACSDK_METRIC_IDS(TAG, "SpeechFinished", "", "", Metrics::Location::SPEECH_SYNTHESIZER_RECEIVE); - m_executor.submit([this, id] { + m_executor.execute([this, id] { if (id != m_mediaSourceId) { ACSDK_ERROR(LX("queueingExecutePlaybackFinishedFailed") .d("reason", "mismatchSourceId") @@ -456,13 +456,13 @@ void SpeechSynthesizer::onPlaybackError( std::string error, const MediaPlayerState&) { ACSDK_DEBUG9(LX("onPlaybackError").d("callbackSourceId", id)); - m_executor.submit([this, type, error]() { executePlaybackError(type, error); }); + m_executor.execute([this, type, error]() { executePlaybackError(type, error); }); } void SpeechSynthesizer::onPlaybackStopped(SourceId id, const MediaPlayerState&) { ACSDK_DEBUG9(LX("onPlaybackStopped").d("callbackSourceId", id)); - m_executor.submit([this, id]() { executePlaybackStopped(id); }); + m_executor.execute([this, id]() { executePlaybackStopped(id); }); } void SpeechSynthesizer::onBufferUnderrun(SourceId id, const MediaPlayerState&) { @@ -658,6 +658,7 @@ void SpeechSynthesizer::executePreHandleAfterValidation(std::shared_ptr, AVSDirective::ParseStatus> directivePair = + AVSDirective::create(UNPARSED_DIRECTIVE_TEST, nullptr, CONTEXT_ID_TEST); + + std::shared_ptr directiveOne = std::move(directivePair.first); + { + SCOPED_TRACE("Empty attachment manager directive."); + EXPECT_CALL(*(m_mockFocusManager.get()), acquireChannel(CHANNEL_NAME, _)) + .Times(0) + .WillOnce(InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnAcquireChannel)); + EXPECT_CALL( + *(m_mockSpeechPlayer.get()), + attachmentSetSource(A>(), nullptr)) + .Times(0); + EXPECT_CALL(*(m_mockSpeechPlayer.get()), play(_)).Times(0); + + m_speechSynthesizer->handleDirectiveImmediately(directiveOne); + } + + auto avsMessageHeader = std::make_shared( + NAMESPACE_SPEECH_SYNTHESIZER, NAME_SPEAK, MESSAGE_ID_TEST_2, DIALOG_REQUEST_ID_TEST); + std::shared_ptr directiveTwo = + AVSDirective::create("", avsMessageHeader, PAYLOAD_TEST, m_attachmentManager, CONTEXT_ID_TEST); + { + SCOPED_TRACE("Check success after failed."); + EXPECT_CALL(*(m_mockFocusManager.get()), acquireChannel(CHANNEL_NAME, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnAcquireChannel)); + EXPECT_CALL( + *(m_mockSpeechPlayer.get()), + attachmentSetSource(A>(), nullptr)) + .Times(AtLeast(1)); + EXPECT_CALL(*(m_mockSpeechPlayer.get()), play(_)).Times(AtLeast(1)); + EXPECT_CALL(*(m_mockSpeechPlayer.get()), getOffset(_)) + .Times(1) + .WillOnce(Return(OFFSET_IN_CHRONO_MILLISECONDS_TEST)); + EXPECT_CALL(*(m_mockSpeechPlayer.get()), getMediaPlayerState(_)).Times(AtLeast(2)); + EXPECT_CALL( + *(m_mockContextManager.get()), + setState(NAMESPACE_AND_NAME_SPEECH_STATE, PLAYING_STATE_TEST, StateRefreshPolicy::ALWAYS, 0)) + .Times(AtLeast(1)) + .WillOnce(InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnSetState)); + EXPECT_CALL(*(m_mockMessageSender.get()), sendMessage(_)) + .Times(AtLeast(1)) + .WillRepeatedly(InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnSendMessage)); + EXPECT_CALL(*m_mockCaptionManager, isEnabled()).WillRepeatedly(Return(true)); + EXPECT_CALL(*m_mockCaptionManager, onCaption(_, _)).Times(1); + EXPECT_CALL(*m_mockPowerResourceManager, acquire(_, _)).Times(AtLeast(1)); + + std::vector data; + EXPECT_CALL( + *m_mockSpeechSynthesizerObserver, + onStateChanged(SpeechSynthesizerObserverInterface::SpeechSynthesizerState::GAINING_FOCUS, _, _, _)) + .Times(1); + EXPECT_CALL( + *m_mockSpeechSynthesizerObserver, + onStateChanged(SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING, _, _, Eq(data))) + .Times(1); + + m_speechSynthesizer->addObserver(m_mockSpeechSynthesizerObserver); + m_speechSynthesizer->handleDirectiveImmediately(directiveTwo); + ASSERT_TRUE(std::future_status::ready == m_wakeAcquireChannelFuture.wait_for(MY_WAIT_TIMEOUT)); + m_speechSynthesizer->onFocusChanged(FocusState::FOREGROUND, MixingBehavior::PRIMARY); + ASSERT_TRUE(std::future_status::ready == m_wakeSetStateFuture.wait_for(MY_WAIT_TIMEOUT)); + m_wakeSetStatePromise = std::promise(); + m_wakeSetStateFuture = m_wakeSetStatePromise.get_future(); + ASSERT_TRUE(m_mockSpeechPlayer->waitUntilPlaybackStarted()); + } +} + } // namespace test } // namespace speechSynthesizer } // namespace capabilityAgents -} // namespace alexaClientSDK +} // namespace alexaClientSDK \ No newline at end of file diff --git a/CapabilityAgents/System/src/LocaleHandler.cpp b/CapabilityAgents/System/src/LocaleHandler.cpp index a850e8dcf4..42b378e85e 100644 --- a/CapabilityAgents/System/src/LocaleHandler.cpp +++ b/CapabilityAgents/System/src/LocaleHandler.cpp @@ -36,7 +36,7 @@ using namespace avsCommon::utils::logger; using namespace settings; /// String to identify log entries originating from this file. -static const std::string TAG("LocaleHandler"); +#define TAG "LocaleHandler" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -115,7 +115,7 @@ void LocaleHandler::handleDirective(std::shared_ptr info) { diff --git a/CapabilityAgents/System/src/ReportStateHandler.cpp b/CapabilityAgents/System/src/ReportStateHandler.cpp index e361dad64c..974d6590f0 100644 --- a/CapabilityAgents/System/src/ReportStateHandler.cpp +++ b/CapabilityAgents/System/src/ReportStateHandler.cpp @@ -31,7 +31,7 @@ using namespace avsCommon::utils::error; using namespace settings; /// String to identify log entries originating from this file. -static const std::string TAG("ReportStateHandler"); +#define TAG "ReportStateHandler" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -171,7 +171,7 @@ void ReportStateHandler::handleDirectiveImmediately(std::shared_ptr info) { @@ -185,7 +185,7 @@ void ReportStateHandler::handleDirective(std::shared_ptrdirective)); if (info->result) { if (ok) { @@ -241,7 +241,7 @@ void ReportStateHandler::initialize() { m_connectionObserver = SettingConnectionObserver::create([this](bool isConnected) { if (isConnected) { std::lock_guard lock(m_stateMutex); - m_executor.submit([this] { sendReportState(); }); + m_executor.execute([this] { sendReportState(); }); } }); m_connectionManager->addConnectionStatusObserver(m_connectionObserver); diff --git a/CapabilityAgents/System/src/RevokeAuthorizationHandler.cpp b/CapabilityAgents/System/src/RevokeAuthorizationHandler.cpp index c45de104e3..0686d5fada 100644 --- a/CapabilityAgents/System/src/RevokeAuthorizationHandler.cpp +++ b/CapabilityAgents/System/src/RevokeAuthorizationHandler.cpp @@ -31,7 +31,7 @@ using namespace avsCommon::utils::json; using namespace rapidjson; /// String to identify log entries originating from this file. -static const std::string TAG{"RevokeAuthorizationHandler"}; +#define TAG "RevokeAuthorizationHandler" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/CapabilityAgents/System/src/SoftwareInfoSendRequest.cpp b/CapabilityAgents/System/src/SoftwareInfoSendRequest.cpp index f06c1b43d9..21ca43378e 100644 --- a/CapabilityAgents/System/src/SoftwareInfoSendRequest.cpp +++ b/CapabilityAgents/System/src/SoftwareInfoSendRequest.cpp @@ -40,7 +40,7 @@ using namespace avsCommon::utils::json; using namespace rapidjson; /// String to identify log entries originating from this file. -static const std::string TAG{"SoftwareInfoSendRequest"}; +#define TAG "SoftwareInfoSendRequest" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/CapabilityAgents/System/src/SoftwareInfoSender.cpp b/CapabilityAgents/System/src/SoftwareInfoSender.cpp index 82ea66aa28..4589cdd6f6 100644 --- a/CapabilityAgents/System/src/SoftwareInfoSender.cpp +++ b/CapabilityAgents/System/src/SoftwareInfoSender.cpp @@ -34,7 +34,7 @@ using namespace avsCommon::utils::json; using namespace rapidjson; /// String to identify log entries originating from this file. -static const std::string TAG{"SoftwareInfoSender"}; +#define TAG "SoftwareInfoSender" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/CapabilityAgents/System/src/StateReportGenerator.cpp b/CapabilityAgents/System/src/StateReportGenerator.cpp index 9d96882579..66f408b0c0 100644 --- a/CapabilityAgents/System/src/StateReportGenerator.cpp +++ b/CapabilityAgents/System/src/StateReportGenerator.cpp @@ -27,7 +27,7 @@ using namespace avsCommon::utils; using namespace avsCommon::utils::json; /// String to identify log entries originating from this file. -static const std::string TAG("StateReportGenerator"); +#define TAG "StateReportGenerator" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/CapabilityAgents/System/src/SystemCapabilityProvider.cpp b/CapabilityAgents/System/src/SystemCapabilityProvider.cpp index 9748c8a857..f5a3611721 100644 --- a/CapabilityAgents/System/src/SystemCapabilityProvider.cpp +++ b/CapabilityAgents/System/src/SystemCapabilityProvider.cpp @@ -29,7 +29,7 @@ namespace system { using namespace avsCommon::avs; /// String to identify log entries originating from this file. -static const std::string TAG{"SystemCapabilityProvider"}; +#define TAG "SystemCapabilityProvider" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/CapabilityAgents/System/src/TimeZoneHandler.cpp b/CapabilityAgents/System/src/TimeZoneHandler.cpp index 73c398d911..a21302208a 100644 --- a/CapabilityAgents/System/src/TimeZoneHandler.cpp +++ b/CapabilityAgents/System/src/TimeZoneHandler.cpp @@ -34,7 +34,7 @@ using namespace avsCommon::utils::logger; using namespace settings; /// String to identify log entries originating from this file. -static const std::string TAG("TimeZoneHandler"); +#define TAG "TimeZoneHandler" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -85,7 +85,7 @@ void TimeZoneHandler::handleDirectiveImmediately(std::shared_ptr d return; } auto info = createDirectiveInfo(directive, nullptr); - m_executor.submit([this, info]() { executeHandleDirectiveImmediately(info); }); + m_executor.execute([this, info]() { executeHandleDirectiveImmediately(info); }); } void TimeZoneHandler::preHandleDirective(std::shared_ptr info) { // intentional no-op @@ -95,7 +95,7 @@ void TimeZoneHandler::handleDirective(std::shared_ptr info) { // intentional no-op diff --git a/CapabilityAgents/System/src/UserInactivityMonitor.cpp b/CapabilityAgents/System/src/UserInactivityMonitor.cpp index 3d8ca625bf..3e052f33ad 100644 --- a/CapabilityAgents/System/src/UserInactivityMonitor.cpp +++ b/CapabilityAgents/System/src/UserInactivityMonitor.cpp @@ -34,7 +34,7 @@ using namespace avsCommon::utils::json; using namespace rapidjson; /// String to identify log entries originating from this file. -static const std::string TAG("UserInactivityMonitor"); +#define TAG "UserInactivityMonitor" /// Number of seconds in one hour. static const int SECONDS_IN_HOUR = 3600; @@ -147,7 +147,7 @@ void UserInactivityMonitor::sendInactivityReport() { return; } Document inactivityPayload(kObjectType); - SizeType payloadKeySize = INACTIVITY_EVENT_PAYLOAD_KEY.length(); + SizeType payloadKeySize = static_cast(INACTIVITY_EVENT_PAYLOAD_KEY.length()); const Pointer::Token payloadKey[] = {{INACTIVITY_EVENT_PAYLOAD_KEY.c_str(), payloadKeySize, kPointerInvalidIndex}}; // AVS requires inactivity time to be a multiple of 3600. diff --git a/CapabilityAgents/TemplateRuntime/CMakeLists.txt b/CapabilityAgents/TemplateRuntime/CMakeLists.txt deleted file mode 100644 index f637b4bf9e..0000000000 --- a/CapabilityAgents/TemplateRuntime/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -cmake_minimum_required(VERSION 3.1 FATAL_ERROR) -project(TemplateRuntime LANGUAGES CXX) - -add_subdirectory("src") -add_subdirectory("test") diff --git a/CapabilityAgents/TemplateRuntime/include/TemplateRuntime/RenderPlayerInfoCardsProviderRegistrar.h b/CapabilityAgents/TemplateRuntime/include/TemplateRuntime/RenderPlayerInfoCardsProviderRegistrar.h deleted file mode 100644 index 5d2d1edbf2..0000000000 --- a/CapabilityAgents/TemplateRuntime/include/TemplateRuntime/RenderPlayerInfoCardsProviderRegistrar.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_TEMPLATERUNTIME_INCLUDE_TEMPLATERUNTIME_RENDERPLAYERINFOCARDSPROVIDERREGISTRAR_H_ -#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_TEMPLATERUNTIME_INCLUDE_TEMPLATERUNTIME_RENDERPLAYERINFOCARDSPROVIDERREGISTRAR_H_ - -#include - -#include -#include - -namespace alexaClientSDK { -namespace capabilityAgents { -namespace templateRuntime { - -/** - * Class to accumulate the set of @c RenderPlayerInfoCardsProvider instances for creating the TemplateRuntime CA. - */ -class RenderPlayerInfoCardsProviderRegistrar - : public avsCommon::sdkInterfaces::RenderPlayerInfoCardsProviderRegistrarInterface { -public: - /** - * Create a new instance of @c RenderPlayerInfoCardsProviderRegistrar. - * - * @return A new instance of @c PostConnectOperationProviderRegistrar. - */ - static std::shared_ptr - createRenderPlayerInfoCardsProviderRegistrarInterface(); - - /// @name RenderPlayerInfoCardsProviderRegistrarInterface methods. - /// @{ - bool registerProvider( - const std::shared_ptr& provider) override; - - std::unordered_set> getProviders() - override; - /// @} - -private: - /** - * Constructor. - */ - RenderPlayerInfoCardsProviderRegistrar(); - - /// Serializes access to @c m_providers. - std::mutex m_mutex; - - /// The @c RenderPlayerInfoCardsProviderInterface instances to use for the TemplateRuntime CA. - std::unordered_set> m_providers; -}; - -} // namespace templateRuntime -} // namespace capabilityAgents -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_TEMPLATERUNTIME_INCLUDE_TEMPLATERUNTIME_RENDERPLAYERINFOCARDSPROVIDERREGISTRAR_H_ diff --git a/CapabilityAgents/TemplateRuntime/include/TemplateRuntime/TemplateRuntime.h b/CapabilityAgents/TemplateRuntime/include/TemplateRuntime/TemplateRuntime.h deleted file mode 100644 index 81d4eafd29..0000000000 --- a/CapabilityAgents/TemplateRuntime/include/TemplateRuntime/TemplateRuntime.h +++ /dev/null @@ -1,436 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_TEMPLATERUNTIME_INCLUDE_TEMPLATERUNTIME_TEMPLATERUNTIME_H_ -#define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_TEMPLATERUNTIME_INCLUDE_TEMPLATERUNTIME_TEMPLATERUNTIME_H_ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace alexaClientSDK { -namespace capabilityAgents { -namespace templateRuntime { - -/** - * This class implements a @c CapabilityAgent that handles the AVS @c TemplateRuntime API. The - * @c TemplateRuntime CA is responsible for handling the directives with the TemplateRuntime namespace. Due - * to the fact that the @c RenderPlayerInfo directives are closely related to the @c AudioPlayer, the @c TemplateRuntime - * CA is an observer to the AudioPlayer and will be synchronizing the @c RenderPlayerInfo directives with the - * corresponding @c AudioItem being handled in the @c AudioPlayer. - * - * The @c TemplateRuntime CA is also an observer to the @c DialogUXState to determine the end of a interaction so - * that it would know when to clear a @c RenderTemplate displayCard. - * - * The clients who are interested in any TemplateRuntime directives can subscribe themselves as an observer, and the - * clients will be notified via the TemplateRuntimeObserverInterface. - */ -class TemplateRuntime - : public avsCommon::avs::CapabilityAgent - , public avsCommon::utils::RequiresShutdown - , public avsCommon::sdkInterfaces::RenderPlayerInfoCardsObserverInterface - , public avsCommon::sdkInterfaces::CapabilityConfigurationInterface - , public avsCommon::sdkInterfaces::DialogUXStateObserverInterface - , public std::enable_shared_from_this { -public: - /** - * Create an instance of @c TemplateRuntime. - * - * @param renderPlayerInfoCardsProviderRegistrar The registrar containing the set of @c - * RenderPlayerInfoCardsProviders. - * @param exceptionSender The object to use for sending AVS Exception messages. - * @return @c nullptr if the inputs are not defined, else a new instance of @c TemplateRuntime. - */ - static std::shared_ptr createTemplateRuntime( - const std::shared_ptr& - renderPlayerInfoCardsInterfaces, - std::shared_ptr focusManager, - std::shared_ptr exceptionSender); - - /** - * Create an instance of @c TemplateRuntime. - * - * @param renderPlayerInfoCardsInterfaces A set of objects to use for subscribing @c TemplateRuntime as an - * observer of changes for RenderPlayerInfoCards. - * @param exceptionSender The object to use for sending AVS Exception messages. - * @return @c nullptr if the inputs are not defined, else a new instance of @c TemplateRuntime. - */ - static std::shared_ptr create( - const std::unordered_set>& - renderPlayerInfoCardsInterfaces, - std::shared_ptr focusManager, - std::shared_ptr exceptionSender); - - /** - * Destructor. - */ - virtual ~TemplateRuntime() = default; - - /// @name CapabilityAgent/DirectiveHandlerInterface Functions - /// @{ - void handleDirectiveImmediately(std::shared_ptr directive) override; - void preHandleDirective(std::shared_ptr info) override; - void handleDirective(std::shared_ptr info) override; - void cancelDirective(std::shared_ptr info) override; - avsCommon::avs::DirectiveHandlerConfiguration getConfiguration() const override; - /// @} - - /// @name ChannelObserverInterface Functions - /// @{ - void onFocusChanged(avsCommon::avs::FocusState newFocus, avsCommon::avs::MixingBehavior behavior) override; - /// @} - - /// @name RenderPlayerInfoCardsObserverInterface Functions - /// @{ - void onRenderPlayerCardsInfoChanged(avsCommon::avs::PlayerActivity state, const Context& context) override; - /// @} - - /// @name DialogUXStateObserverInterface Functions - /// @{ - void onDialogUXStateChanged( - avsCommon::sdkInterfaces::DialogUXStateObserverInterface::DialogUXState newState) override; - /// @} - - /** - * This function adds an observer to @c TemplateRuntime so that it will get notified for renderTemplateCard or - * renderPlayerInfoCard. - * - * @param observer The @c TemplateRuntimeObserverInterface - */ - void addObserver(std::shared_ptr observer); - - /** - * This function removes an observer from @c TemplateRuntime so that it will no longer be notified of - * renderTemplateCard or renderPlayerInfoCard callbacks. - * - * @param observer The @c TemplateRuntimeObserverInterface - */ - void removeObserver(std::shared_ptr observer); - - /** - * This function notifies the @c TemplateRuntime that a displayCard has been cleared from the screen. Upon getting - * this notification, the @c TemplateRuntime will release the visual channel. - */ - void displayCardCleared(); - - /// @name CapabilityConfigurationInterface Functions - /// @{ - std::unordered_set> getCapabilityConfigurations() override; - /// @} - - /** - * This function adds a @RenderPlayerInfoCardsProviderInterface for a client to subscribe @c TemplateRuntime as an - * observer of changes for RenderPlayerInfoCards. - * - * @param cardsProvider The @c RenderPlayerInfoCardsProviderInterface - */ - void addRenderPlayerInfoCardsProvider( - std::shared_ptr cardsProvider); - -private: - /** - * This enum provides the state of the @c TemplateRuntime. - */ - enum class State { - /// The @c TemplateRuntime is idle. - IDLE, - - /* - * The @c TemplateRuntime has received a displayCard event is acquiring the visual channel from @c - * FocusManager. - */ - ACQUIRING, - - /* - * The @c TemplateRuntime has focus, either background or foreground, of the channel and has - * notified its observers of a displayCard. @TemplateRuntime will remain in this state until there is a - * timeout, clearCard, or focusChanged(NONE) event. - */ - DISPLAYING, - - /* - * The @c TemplateRuntime has received a timeout or a clearCard event and is releasing the - * channel and has notified its observers to clear the display. - */ - RELEASING, - - /* - * The @c TemplateRuntime has received a displayCard event during releasing of the channel and is trying to - * acquire the visual channel again. - */ - REACQUIRING - }; - - /** - * Utility structure to correspond a directive with its audioItemId. - */ - struct AudioItemPair { - /** - * Default Constructor. - */ - AudioItemPair() = default; - - /** - * Constructor. - * - * @param itemId The ID for the @c AudioItem. - * @param renderPlayerInfoDirective The @c RenderPlayerInfo directive that corresponds to the audioItemId. - */ - AudioItemPair( - std::string itemId, - std::shared_ptr renderPlayerInfoDirective) : - audioItemId{itemId}, - directive{renderPlayerInfoDirective} {}; - - /// The ID of the @c AudioItem. - std::string audioItemId; - - /// The directive corresponding to the audioItemId. - std::shared_ptr directive; - }; - - /** - * Constructor. - * - * @param renderPlayerInfoCardsInterfaces A set of objects to use for subscribing @c TemplateRuntime as an - * observer of changes for RenderPlayerInfoCards. - * @param exceptionSender The object to use for sending AVS Exception messages. - */ - TemplateRuntime( - const std::unordered_set>& - renderPlayerInfoCardsInterfaces, - std::shared_ptr focusManager, - std::shared_ptr exceptionSender); - - // @name RequiresShutdown Functions - /// @{ - void doShutdown() override; - /// @} - - /** - * Initializes the object by reading the values from configuration. - */ - bool initialize(); - - /** - * Remove a directive from the map of message IDs to DirectiveInfo instances. - * - * @param info The @c DirectiveInfo containing the @c AVSDirective whose message ID is to be removed. - */ - void removeDirective(std::shared_ptr info); - - /** - * Send the handling completed notification and clean up the resources. - * - * @param info The @c DirectiveInfo containing the @c AVSDirective and the @c DirectiveHandlerResultInterface. - */ - void setHandlingCompleted(std::shared_ptr info); - - /** - * This function handles a @c RenderTemplate directive. - * - * @param info The @c DirectiveInfo containing the @c AVSDirective and the @c DirectiveHandlerResultInterface. - */ - void handleRenderTemplateDirective(std::shared_ptr info); - - /** - * This function handles a @c RenderPlayerInfo directive. - * - * @param info The @c DirectiveInfo containing the @c AVSDirective and the @c DirectiveHandlerResultInterface. - */ - void handleRenderPlayerInfoDirective(std::shared_ptr info); - - /** - * This function handles any unknown directives received by @c TemplateRuntime CA. - * - * @param info The @c DirectiveInfo containing the @c AVSDirective and the @c DirectiveHandlerResultInterface. - */ - void handleUnknownDirective(std::shared_ptr info); - - /** - * This is an internal function that handles updating the @c m_audioItemInExecution when the @c AudioPlayer - * notifies the @c TemplateRuntime CA of any changes in the @c AudioPlayer audio state. This function is - * intended to be used in the context of @c m_executor worker thread. - * - * @param state The @c PlayerActivity of the @c AudioPlayer. - * @param context The @c Context of the @c AudioPlayer at the time of the notification. - */ - void executeAudioPlayerInfoUpdates(avsCommon::avs::PlayerActivity state, const Context& context); - - /** - * This is an internal function that start or stop the @c m_clearDisplayTimer based on the @c PlayerActivity - * reported by the @c AudioPlayer. - * - * @param state The @c PlayerActivity of the @c AudioPlayer. - */ - void executeAudioPlayerStartTimer(avsCommon::avs::PlayerActivity state); - - /** - * This function handles the notification of the renderPlayerInfoCard callbacks to all the observers. This function - * is intended to be used in the context of @c m_executor worker thread. - */ - void executeRenderPlayerInfoCallbacks(bool isClearCard); - - /** - * This function handles the notification of the renderTemplateCard callbacks to all the observers. This function - * is intended to be used in the context of @c m_executor worker thread. - */ - void executeRenderTemplateCallbacks(bool isClearCard); - - /** - * This is an internal function that is called when the state machine is ready to notify the @TemplateRuntime - * observers to display a card. - */ - void executeDisplayCard(); - - /** - * This is an internal function that is called when the state machine is ready to notify the @TemplateRuntime - * observers to clear a card. - */ - void executeClearCard(); - - /** - * This is an internal function to start the @c m_clearDisplayTimer. - * - * @param timeout The period of the timer. - */ - void executeStartTimer(std::chrono::milliseconds timeout); - - /** - * This is an internal function to stop the @c m_clearDisplayTimer. - */ - void executeStopTimer(); - - /** - * This is an internal function to convert the @c State to a string. - */ - std::string stateToString(const TemplateRuntime::State state); - - /** - * This is a state machine function to handle the timer event. - */ - void executeTimerEvent(); - - /** - * This is a state machine function to handle the focus change event. - */ - void executeOnFocusChangedEvent(avsCommon::avs::FocusState newFocus); - - /** - * This is a state machine function to handle the displayCard event. - */ - void executeDisplayCardEvent( - const std::shared_ptr info); - - /** - * This is a state machine function to handle the clearCard event. - */ - void executeCardClearedEvent(); - - /// Timer that is responsible for clearing the display. - avsCommon::utils::timing::Timer m_clearDisplayTimer; - - /** - * @name Executor Thread Variables - * - * These member variables are only accessed by functions in the @c m_executor worker thread, and do not require any - * synchronization. - */ - /// @{ - /// A set of observers to be notified when a @c RenderTemplate or @c RenderPlayerInfo directive is received - std::unordered_set> m_observers; - - /* - * This is a map that is used to store the current executing @c AudioItem based on the callbacks from the - * @c RenderPlayerInfoCardsProviderInterface. - */ - std::unordered_map, AudioItemPair> - m_audioItemsInExecution; - - /// The current active RenderPlayerInfoCards provider that has the matching audioItemId. - std::shared_ptr m_activeRenderPlayerInfoCardsProvider; - - /* - * This queue is for storing the @c RenderPlayerInfo directives when its audioItemId does not match the audioItemId - * in execution in the @c AudioPlayer. - */ - std::deque m_audioItems; - - /// This map is to store the @c AudioPlayerInfo to be passed to the observers in the renderPlayerInfoCard callback. - std::unordered_map< - std::shared_ptr, - avsCommon::sdkInterfaces::TemplateRuntimeObserverInterface::AudioPlayerInfo> - m_audioPlayerInfo; - - /// The directive corresponding to the RenderTemplate directive. - std::shared_ptr m_lastDisplayedDirective; - - /// A flag to check if @c RenderTemplate is the last directive received. - bool m_isRenderTemplateLastReceived; - - /// The current focus state of the @c TemplateRuntime on the visual channel. - avsCommon::avs::FocusState m_focus; - - /// The state of the @c TemplateRuntime state machine. - State m_state; - /// @} - - /* - * This is a set of interfaces to the @c RenderPlayerInfoCardsProviderInterface. The @c TemplateRuntime CA - * used this interface to add and remove itself as an observer. - */ - std::unordered_set> - m_renderPlayerInfoCardsInterfaces; - - /// The @c FocusManager used to manage usage of the visual channel. - std::shared_ptr m_focusManager; - - /// Set of capability configurations that will get published using the Capabilities API - std::unordered_set> m_capabilityConfigurations; - - /// This is the worker thread for the @c TemplateRuntime CA. - avsCommon::utils::threading::Executor m_executor; - - /// The timeout value in ms for clearing the diplay card when TTS is FINSIHED - std::chrono::milliseconds m_ttsFinishedTimeout; - - /// The timeout value in ms for clearing the diplay card when AudioPlay is FINSIHED - std::chrono::milliseconds m_audioPlaybackFinishedTimeout; - - /// The timeout value in ms for clearing the diplay card when AudioPlay is STOPPED or PAUSED - std::chrono::milliseconds m_audioPlaybackStoppedPausedTimeout; -}; - -} // namespace templateRuntime -} // namespace capabilityAgents -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_CAPABILITYAGENTS_TEMPLATERUNTIME_INCLUDE_TEMPLATERUNTIME_TEMPLATERUNTIME_H_ diff --git a/CapabilityAgents/TemplateRuntime/src/CMakeLists.txt b/CapabilityAgents/TemplateRuntime/src/CMakeLists.txt deleted file mode 100644 index 8755e32d20..0000000000 --- a/CapabilityAgents/TemplateRuntime/src/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -add_definitions("-DACSDK_LOG_MODULE=templateRuntime") - -add_library(TemplateRuntime - "${CMAKE_CURRENT_LIST_DIR}/TemplateRuntime.cpp" - "${CMAKE_CURRENT_LIST_DIR}/RenderPlayerInfoCardsProviderRegistrar.cpp") - -target_include_directories(TemplateRuntime - PUBLIC "${TemplateRuntime_SOURCE_DIR}/include") - -target_link_libraries(TemplateRuntime AVSCommon) - -# install target -asdk_install() \ No newline at end of file diff --git a/CapabilityAgents/TemplateRuntime/src/TemplateRuntime.cpp b/CapabilityAgents/TemplateRuntime/src/TemplateRuntime.cpp deleted file mode 100644 index 5a19ef6994..0000000000 --- a/CapabilityAgents/TemplateRuntime/src/TemplateRuntime.cpp +++ /dev/null @@ -1,820 +0,0 @@ -/* - * 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 "TemplateRuntime/TemplateRuntime.h" - -namespace alexaClientSDK { -namespace capabilityAgents { -namespace templateRuntime { - -using namespace avsCommon::avs; -using namespace avsCommon::sdkInterfaces; -using namespace avsCommon::utils; -using namespace avsCommon::utils::configuration; -using namespace avsCommon::utils::json; - -/// TemplateRuntime capability constants -/// TemplateRuntime interface type -static const std::string TEMPLATERUNTIME_CAPABILITY_INTERFACE_TYPE = "AlexaInterface"; -/// TemplateRuntime interface name -static const std::string TEMPLATERUNTIME_CAPABILITY_INTERFACE_NAME = "TemplateRuntime"; -/// TemplateRuntime interface version -static const std::string TEMPLATERUNTIME_CAPABILITY_INTERFACE_VERSION = "1.1"; - -/// String to identify log entries originating from this file. -static const std::string TAG{"TemplateRuntime"}; - -/// The key in our config file to find the root of template runtime configuration. -static const std::string TEMPLATERUNTIME_CONFIGURATION_ROOT_KEY = "templateRuntimeCapabilityAgent"; -/// The key in our config file to set the display card timeout value when TTS is in FINISHED state -static const std::string TEMPLATERUNTIME_TTS_FINISHED_KEY = "displayCardTTSFinishedTimeout"; -/// The key in our config file to set the display card timeout value when AudioPlayer is in FINISHED state -static const std::string TEMPLATERUNTIME_AUDIOPLAYBACK_FINISHED_KEY = "displayCardAudioPlaybackFinishedTimeout"; -/// The key in our config file to set the display card timeout value when AudioPlayer is in STOPPED or PAUSE state -static const std::string TEMPLATERUNTIME_AUDIOPLAYBACK_STOPPED_PAUSED_KEY = - "displayCardAudioPlaybackStoppedPausedTimeout"; - -/** - * 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) - -/// The name of the @c FocusManager channel used by @c TemplateRuntime. -static const std::string CHANNEL_NAME = avsCommon::sdkInterfaces::FocusManagerInterface::VISUAL_CHANNEL_NAME; - -/// The namespace for this capability agent. -static const std::string NAMESPACE{"TemplateRuntime"}; - -/// The name for RenderTemplate directive. -static const std::string RENDER_TEMPLATE{"RenderTemplate"}; - -/// The name for RenderPlayerInfo directive. -static const std::string RENDER_PLAYER_INFO{"RenderPlayerInfo"}; - -/// The RenderTemplate directive signature. -static const NamespaceAndName TEMPLATE{NAMESPACE, RENDER_TEMPLATE}; - -/// The RenderPlayerInfo directive signature. -static const NamespaceAndName PLAYER_INFO{NAMESPACE, RENDER_PLAYER_INFO}; - -/// Tag for find the AudioItemId in the payload of the RenderPlayerInfo directive -static const std::string AUDIO_ITEM_ID_TAG{"audioItemId"}; - -/// Maximum queue size allowed for m_audioItems. -static const size_t MAXIMUM_QUEUE_SIZE{100}; - -/// Default timeout for clearing the RenderTemplate display card when SpeechSynthesizer is in FINISHED state. -static const std::chrono::milliseconds DEFAULT_TTS_FINISHED_TIMEOUT_MS{2000}; - -/// Default timeout for clearing the RenderPlayerInfo display card when AudioPlayer is in FINISHED state. -static const std::chrono::milliseconds DEFAULT_AUDIO_FINISHED_TIMEOUT_MS{2000}; - -/// Default timeout for clearing the RenderPlayerInfo display card when AudioPlayer is in STOPPED/PAUSED state. -static const std::chrono::milliseconds DEFAULT_AUDIO_STOPPED_PAUSED_TIMEOUT_MS{60000}; - -/** - * Creates the TemplateRuntime capability configuration. - * - * @return The TemplateRuntime capability configuration. - */ -static std::shared_ptr getTemplateRuntimeCapabilityConfiguration(); - -std::shared_ptr TemplateRuntime::createTemplateRuntime( - const std::shared_ptr& - renderPlayerInfoCardsProviderRegistrar, - std::shared_ptr focusManager, - std::shared_ptr exceptionSender) { - if (!renderPlayerInfoCardsProviderRegistrar || !focusManager || !exceptionSender) { - ACSDK_ERROR(LX("createFailed") - .d("isRenderPlayerInfoCardsProviderRegistrarNull", !renderPlayerInfoCardsProviderRegistrar) - .d("isFocusManagerNull", !focusManager) - .d("isExceptionSenderNull", !exceptionSender)); - return nullptr; - } - - auto providers = renderPlayerInfoCardsProviderRegistrar->getProviders(); - return TemplateRuntime::create(providers, focusManager, exceptionSender); -} - -std::shared_ptr TemplateRuntime::create( - const std::unordered_set>& - renderPlayerInfoCardInterface, - std::shared_ptr focusManager, - std::shared_ptr exceptionSender) { - if (!focusManager) { - ACSDK_ERROR(LX("createFailed").d("reason", "nullFocusManager")); - return nullptr; - } - - if (!exceptionSender) { - ACSDK_ERROR(LX("createFailed").d("reason", "nullExceptionSender")); - return nullptr; - } - std::shared_ptr templateRuntime( - new TemplateRuntime(renderPlayerInfoCardInterface, focusManager, exceptionSender)); - - if (!templateRuntime->initialize()) { - ACSDK_ERROR(LX("createFailed").d("reason", "Initialization error.")); - return nullptr; - } - - for (const auto& renderPlayerInfoCardProvider : renderPlayerInfoCardInterface) { - if (!renderPlayerInfoCardProvider) { - ACSDK_ERROR(LX("createFailed").d("reason", "nullRenderPlayerInfoCardInterface")); - return nullptr; - } - renderPlayerInfoCardProvider->setObserver(templateRuntime); - } - - return templateRuntime; -} - -/** - * Initializes the object by reading the values from configuration. - */ -bool TemplateRuntime::initialize() { - auto configurationRoot = ConfigurationNode::getRoot()[TEMPLATERUNTIME_CONFIGURATION_ROOT_KEY]; - - // If key is present, then read and initilize the value from config or set to default. - configurationRoot.getDuration( - TEMPLATERUNTIME_TTS_FINISHED_KEY, &m_ttsFinishedTimeout, DEFAULT_TTS_FINISHED_TIMEOUT_MS); - - // If key is present, then read and initilize the value from config or set to default. - configurationRoot.getDuration( - TEMPLATERUNTIME_AUDIOPLAYBACK_FINISHED_KEY, &m_audioPlaybackFinishedTimeout, DEFAULT_AUDIO_FINISHED_TIMEOUT_MS); - - // If key is present, then read and initilize the value from config or set to default. - configurationRoot.getDuration( - TEMPLATERUNTIME_AUDIOPLAYBACK_STOPPED_PAUSED_KEY, - &m_audioPlaybackStoppedPausedTimeout, - DEFAULT_AUDIO_STOPPED_PAUSED_TIMEOUT_MS); - - return true; -} - -void TemplateRuntime::handleDirectiveImmediately(std::shared_ptr directive) { - ACSDK_DEBUG5(LX("handleDirectiveImmediately")); - handleDirective(std::make_shared(directive, nullptr)); -} - -void TemplateRuntime::preHandleDirective(std::shared_ptr info) { - ACSDK_DEBUG5(LX("preHandleDirective")); - // do nothing. -} - -void TemplateRuntime::handleDirective(std::shared_ptr info) { - ACSDK_DEBUG5(LX("handleDirective")); - if (!info || !info->directive) { - ACSDK_ERROR(LX("preHandleDirectiveFailed").d("reason", "nullDirectiveInfo")); - return; - } - if (info->directive->getName() == TEMPLATE.name) { - handleRenderTemplateDirective(info); - } else if (info->directive->getName() == PLAYER_INFO.name) { - handleRenderPlayerInfoDirective(info); - } else { - handleUnknownDirective(info); - } -} - -void TemplateRuntime::cancelDirective(std::shared_ptr info) { - removeDirective(info); -} - -DirectiveHandlerConfiguration TemplateRuntime::getConfiguration() const { - ACSDK_DEBUG5(LX("getConfiguration")); - DirectiveHandlerConfiguration configuration; - auto visualNonBlockingPolicy = BlockingPolicy(BlockingPolicy::MEDIUM_VISUAL, false); - - configuration[TEMPLATE] = visualNonBlockingPolicy; - configuration[PLAYER_INFO] = visualNonBlockingPolicy; - return configuration; -} - -void TemplateRuntime::onFocusChanged(avsCommon::avs::FocusState newFocus, MixingBehavior) { - m_executor.submit([this, newFocus]() { executeOnFocusChangedEvent(newFocus); }); -} - -void TemplateRuntime::onRenderPlayerCardsInfoChanged(avsCommon::avs::PlayerActivity state, const Context& context) { - ACSDK_DEBUG5(LX("onRenderPlayerCardsInfoChanged")); - m_executor.submit([this, state, context]() { - ACSDK_DEBUG5(LX("onPlayerActivityChangedInExecutor")); - executeAudioPlayerInfoUpdates(state, context); - }); -} - -void TemplateRuntime::onDialogUXStateChanged( - avsCommon::sdkInterfaces::DialogUXStateObserverInterface::DialogUXState newState) { - ACSDK_DEBUG5(LX("onDialogUXStateChanged").d("state", newState)); - m_executor.submit([this, newState]() { - if (TemplateRuntime::State::DISPLAYING == m_state && m_lastDisplayedDirective && - m_lastDisplayedDirective->directive->getName() == RENDER_TEMPLATE) { - if (avsCommon::sdkInterfaces::DialogUXStateObserverInterface::DialogUXState::IDLE == newState) { - executeStartTimer(m_ttsFinishedTimeout); - } else if ( - avsCommon::sdkInterfaces::DialogUXStateObserverInterface::DialogUXState::EXPECTING == newState || - avsCommon::sdkInterfaces::DialogUXStateObserverInterface::DialogUXState::SPEAKING == newState) { - executeStopTimer(); - } - } - }); -} - -void TemplateRuntime::addObserver( - std::shared_ptr observer) { - ACSDK_DEBUG5(LX("addObserver")); - if (!observer) { - ACSDK_ERROR(LX("addObserver").m("Observer is null.")); - return; - } - m_executor.submit([this, observer]() { - ACSDK_DEBUG5(LX("addObserverInExecutor")); - if (!m_observers.insert(observer).second) { - ACSDK_ERROR(LX("addObserverInExecutor").m("Duplicate observer.")); - } - }); -} - -void TemplateRuntime::removeObserver( - std::shared_ptr observer) { - ACSDK_DEBUG5(LX("removeObserver")); - if (!observer) { - ACSDK_ERROR(LX("removeObserver").m("Observer is null.")); - return; - } - m_executor.submit([this, observer]() { - ACSDK_DEBUG5(LX("removeObserverInExecutor")); - if (m_observers.erase(observer) == 0) { - ACSDK_WARN(LX("removeObserverInExecutor").m("Nonexistent observer.")); - } - }); -} - -TemplateRuntime::TemplateRuntime( - const std::unordered_set>& - renderPlayerInfoCardsInterfaces, - std::shared_ptr focusManager, - std::shared_ptr exceptionSender) : - CapabilityAgent{NAMESPACE, exceptionSender}, - RequiresShutdown{"TemplateRuntime"}, - m_isRenderTemplateLastReceived{false}, - m_focus{FocusState::NONE}, - m_state{TemplateRuntime::State::IDLE}, - m_renderPlayerInfoCardsInterfaces{renderPlayerInfoCardsInterfaces}, - m_focusManager{focusManager} { - m_capabilityConfigurations.insert(getTemplateRuntimeCapabilityConfiguration()); -} - -std::shared_ptr getTemplateRuntimeCapabilityConfiguration() { - std::unordered_map configMap; - configMap.insert({CAPABILITY_INTERFACE_TYPE_KEY, TEMPLATERUNTIME_CAPABILITY_INTERFACE_TYPE}); - configMap.insert({CAPABILITY_INTERFACE_NAME_KEY, TEMPLATERUNTIME_CAPABILITY_INTERFACE_NAME}); - configMap.insert({CAPABILITY_INTERFACE_VERSION_KEY, TEMPLATERUNTIME_CAPABILITY_INTERFACE_VERSION}); - - return std::make_shared(configMap); -} - -void TemplateRuntime::doShutdown() { - m_executor.shutdown(); - m_focusManager.reset(); - m_observers.clear(); - m_activeRenderPlayerInfoCardsProvider.reset(); - m_audioItemsInExecution.clear(); - m_audioPlayerInfo.clear(); - for (const auto& renderPlayerInfoCardsInterface : m_renderPlayerInfoCardsInterfaces) { - renderPlayerInfoCardsInterface->setObserver(nullptr); - } - m_renderPlayerInfoCardsInterfaces.clear(); -} - -void TemplateRuntime::removeDirective(std::shared_ptr info) { - /* - * Check result too, to catch cases where DirectiveInfo was created locally, without a nullptr result. - * In those cases there is no messageId to remove because no result was expected. - */ - if (info->directive && info->result) { - CapabilityAgent::removeDirective(info->directive->getMessageId()); - } -} - -void TemplateRuntime::displayCardCleared() { - m_executor.submit([this]() { executeCardClearedEvent(); }); -} - -void TemplateRuntime::setHandlingCompleted(std::shared_ptr info) { - if (info && info->result) { - info->result->setCompleted(); - } - removeDirective(info); -} - -void TemplateRuntime::handleRenderTemplateDirective(std::shared_ptr info) { - ACSDK_DEBUG5(LX("handleRenderTemplateDirective")); - - m_executor.submit([this, info]() { - ACSDK_DEBUG5(LX("handleRenderTemplateDirectiveInExecutor")); - m_isRenderTemplateLastReceived = true; - executeDisplayCardEvent(info); - setHandlingCompleted(info); - }); -} - -void TemplateRuntime::handleRenderPlayerInfoDirective(std::shared_ptr info) { - ACSDK_DEBUG5(LX("handleRenderPlayerInfoDirective")); - - m_executor.submit([this, info]() { - ACSDK_DEBUG5(LX("handleRenderPlayerInfoDirectiveInExecutor")); - m_isRenderTemplateLastReceived = false; - - rapidjson::Document payload; - rapidjson::ParseResult result = payload.Parse(info->directive->getPayload()); - if (!result) { - ACSDK_ERROR(LX("handleRenderPlayerInfoDirectiveInExecutorParseFailed") - .d("reason", rapidjson::GetParseError_En(result.Code())) - .d("offset", result.Offset()) - .d("messageId", info->directive->getMessageId())); - sendExceptionEncounteredAndReportFailed( - info, "Unable to parse payload", ExceptionErrorType::UNEXPECTED_INFORMATION_RECEIVED); - return; - } - - std::string audioItemId; - if (!jsonUtils::retrieveValue(payload, AUDIO_ITEM_ID_TAG, &audioItemId)) { - ACSDK_ERROR(LX("handleRenderPlayerInfoDirective") - .d("reason", "missingAudioItemId") - .d("messageId", info->directive->getMessageId())); - sendExceptionEncounteredAndReportFailed(info, "missing audioItemId"); - return; - } - - size_t found = std::string::npos; - for (auto& executionMap : m_audioItemsInExecution) { - if (!executionMap.second.audioItemId.empty()) { - found = audioItemId.find(executionMap.second.audioItemId); - } - if (found != std::string::npos) { - ACSDK_DEBUG3(LX("handleRenderPlayerInfoDirectiveInExecutor") - .d("audioItemId", audioItemId) - .m("Matching audioItemId in execution.")); - executionMap.second.directive = info; - m_activeRenderPlayerInfoCardsProvider = executionMap.first; - m_audioPlayerInfo[m_activeRenderPlayerInfoCardsProvider].offset = - executionMap.first->getAudioItemOffset(); - executeStopTimer(); - executeDisplayCardEvent(info); - // Since there'a match, we can safely empty m_audioItems. - m_audioItems.clear(); - break; - } - } - - if (std::string::npos == found) { - ACSDK_DEBUG3(LX("handleRenderPlayerInfoDirectiveInExecutor") - .d("audioItemId", audioItemId) - .m("Not matching audioItemId in execution.")); - - AudioItemPair itemPair{audioItemId, info}; - if (m_audioItems.size() == MAXIMUM_QUEUE_SIZE) { - // Something is wrong, so we pop the back of the queue and log an error. - auto discardedAudioItem = m_audioItems.back(); - m_audioItems.pop_back(); - ACSDK_ERROR(LX("handleRenderPlayerInfoDirective") - .d("reason", "queueIsFull") - .d("discardedAudioItemId", discardedAudioItem.audioItemId)); - } - m_audioItems.push_front(itemPair); - } - - setHandlingCompleted(info); - }); -} - -void TemplateRuntime::handleUnknownDirective(std::shared_ptr info) { - ACSDK_ERROR(LX("handleDirectiveFailed") - .d("reason", "unknownDirective") - .d("namespace", info->directive->getNamespace()) - .d("name", info->directive->getName())); - - m_executor.submit([this, info] { - const std::string exceptionMessage = - "unexpected directive " + info->directive->getNamespace() + ":" + info->directive->getName(); - - sendExceptionEncounteredAndReportFailed( - info, exceptionMessage, ExceptionErrorType::UNEXPECTED_INFORMATION_RECEIVED); - }); -} - -void TemplateRuntime::executeAudioPlayerInfoUpdates(avsCommon::avs::PlayerActivity state, const Context& context) { - ACSDK_DEBUG5(LX("executeAudioPlayerInfoUpdates") - .d("audioItemId", context.audioItemId) - .d("offset", context.offset.count()) - .d("audioPlayerState", state) - .d("isRenderTemplatelastReceived", m_isRenderTemplateLastReceived)); - - if (avsCommon::avs::PlayerActivity::IDLE == state || avsCommon::avs::PlayerActivity::BUFFER_UNDERRUN == state) { - /* - * The TemplateRuntime Capability Agent is not interested in the IDLE nor BUFFER_UNDERRUN state, so we just - * ignore the callback. - */ - return; - } - - if (!context.mediaProperties) { - ACSDK_ERROR(LX("executeAudioPlayerInfoUpdatesFailed").d("reason", "nullRenderPlayerInfoCardsInterface")); - return; - } - - const auto& currentRenderPlayerInfoCardsProvider = context.mediaProperties; - if (m_audioPlayerInfo[currentRenderPlayerInfoCardsProvider].audioPlayerState == state && - m_audioItemsInExecution[currentRenderPlayerInfoCardsProvider].audioItemId == context.audioItemId) { - /* - * The AudioPlayer notification is chatty during audio playback as it will frequently toggle between - * BUFFER_UNDERRUN and PLAYER state. So we filter out the callbacks if the notification are with the - * same state and audioItemId. - */ - return; - } - - auto isStateUpdated = (m_audioPlayerInfo[currentRenderPlayerInfoCardsProvider].audioPlayerState != state); - m_audioPlayerInfo[currentRenderPlayerInfoCardsProvider].audioPlayerState = state; - m_audioPlayerInfo[currentRenderPlayerInfoCardsProvider].offset = context.offset; - if (m_audioItemsInExecution[currentRenderPlayerInfoCardsProvider].audioItemId != context.audioItemId) { - m_audioItemsInExecution[currentRenderPlayerInfoCardsProvider].audioItemId = context.audioItemId; - m_audioItemsInExecution[currentRenderPlayerInfoCardsProvider].directive.reset(); - // iterate from front to back (front is most recent) - for (auto it = m_audioItems.begin(); it != m_audioItems.end(); ++it) { - auto found = it->audioItemId.find(context.audioItemId); - if (std::string::npos != found) { - ACSDK_DEBUG3(LX("executeAudioPlayerInfoUpdates") - .d("audioItemId", context.audioItemId) - .m("Found matching audioItemId in queue.")); - m_audioItemsInExecution[currentRenderPlayerInfoCardsProvider].directive = it->directive; - m_activeRenderPlayerInfoCardsProvider = currentRenderPlayerInfoCardsProvider; - // We are erasing items older than the current found, as well as the current item. - m_audioItems.erase(it, m_audioItems.end()); - break; - } - } - } - if (m_isRenderTemplateLastReceived && state != avsCommon::avs::PlayerActivity::PLAYING) { - /* - * If RenderTemplate is the last directive received and the AudioPlayer is not notifying a PLAY, - * we shouldn't be notifying the observer to render a PlayerInfo display card. - */ - return; - } - m_isRenderTemplateLastReceived = false; - - /* - * If the AudioPlayer notifies a PLAYING state before the RenderPlayerInfo with the corresponding - * audioItemId is received, this function will also be called but the m_audioItemsInExecution.directive - * will be set to nullptr. So we need to do a nullptr check here to make sure there is a RenderPlayerInfo - * displayCard to display.. - */ - if (m_audioItemsInExecution[currentRenderPlayerInfoCardsProvider].directive) { - if (isStateUpdated) { - executeAudioPlayerStartTimer(state); - } - executeDisplayCardEvent(m_audioItemsInExecution[currentRenderPlayerInfoCardsProvider].directive); - } else { - // The RenderTemplateCard is cleared before it's displayed, so we should release the focus. - if (TemplateRuntime::State::ACQUIRING == m_state) { - m_state = TemplateRuntime::State::RELEASING; - } - } -} - -void TemplateRuntime::executeAudioPlayerStartTimer(avsCommon::avs::PlayerActivity state) { - if (avsCommon::avs::PlayerActivity::PLAYING == state) { - executeStopTimer(); - } else if (avsCommon::avs::PlayerActivity::PAUSED == state || avsCommon::avs::PlayerActivity::STOPPED == state) { - executeStartTimer(m_audioPlaybackStoppedPausedTimeout); - } else if (avsCommon::avs::PlayerActivity::FINISHED == state) { - executeStartTimer(m_audioPlaybackFinishedTimeout); - } -} - -void TemplateRuntime::executeRenderPlayerInfoCallbacks(bool isClearCard) { - ACSDK_DEBUG3(LX("executeRenderPlayerInfoCallbacks").d("isClearCard", isClearCard ? "True" : "False")); - if (isClearCard) { - for (auto& observer : m_observers) { - observer->clearPlayerInfoCard(); - } - } else { - if (!m_activeRenderPlayerInfoCardsProvider) { - ACSDK_ERROR( - LX("executeRenderPlayerInfoCallbacksFailed").d("reason", "nullActiveRenderPlayerInfoCardsProvider")); - return; - } - if (!m_audioItemsInExecution[m_activeRenderPlayerInfoCardsProvider].directive) { - ACSDK_ERROR(LX("executeRenderPlayerInfoCallbacksFailed").d("reason", "nullAudioItemInExecution")); - return; - } - auto payload = - m_audioItemsInExecution[m_activeRenderPlayerInfoCardsProvider].directive->directive->getPayload(); - for (auto& observer : m_observers) { - observer->renderPlayerInfoCard(payload, m_audioPlayerInfo[m_activeRenderPlayerInfoCardsProvider], m_focus); - } - } -} - -void TemplateRuntime::executeRenderTemplateCallbacks(bool isClearCard) { - ACSDK_DEBUG3(LX("executeRenderTemplateCallbacks").d("isClear", isClearCard ? "True" : "False")); - for (auto& observer : m_observers) { - if (isClearCard) { - observer->clearTemplateCard(); - } else { - observer->renderTemplateCard(m_lastDisplayedDirective->directive->getPayload(), m_focus); - } - } -} - -void TemplateRuntime::executeDisplayCard() { - if (m_lastDisplayedDirective) { - if (m_lastDisplayedDirective->directive->getName() == RENDER_TEMPLATE) { - executeStopTimer(); - executeRenderTemplateCallbacks(false); - } else { - executeRenderPlayerInfoCallbacks(false); - } - } -} - -void TemplateRuntime::executeClearCard() { - if (m_lastDisplayedDirective) { - if (m_lastDisplayedDirective->directive->getName() == RENDER_TEMPLATE) { - executeRenderTemplateCallbacks(true); - } else { - executeRenderPlayerInfoCallbacks(true); - } - } -} - -void TemplateRuntime::executeStartTimer(std::chrono::milliseconds timeout) { - if (TemplateRuntime::State::DISPLAYING == m_state) { - ACSDK_DEBUG3(LX("executeStartTimer").d("timeoutInMilliseconds", timeout.count())); - m_clearDisplayTimer.start(timeout, [this] { m_executor.submit([this] { executeTimerEvent(); }); }); - } -} - -void TemplateRuntime::executeStopTimer() { - ACSDK_DEBUG3(LX("executeStopTimer")); - m_clearDisplayTimer.stop(); -} - -/* - * A state machine is used to acquire and release the visual channel from the visual @c FocusManager. The state machine - * has five @c State, and four events as listed below: - * - * displayCard - This event happens when the TempateRuntime is ready to notify its observers to display a - * displayCard. - * - * focusChanged - This event happens when the @c FocusManager notifies a change in @c FocusState in the visual - * channel. - * - * timer - This event happens when m_clearDisplayTimer expires and needs to notify its observers to clear the - * displayCard. - * - * cardCleared - This event happens when @c displayCardCleared() is called to notify @c TemplateRuntime the device has - * cleared the screen. - * - * Each state transition may result in one or more of the following actions: - * (A) Acquire channel - * (B) Release channel - * (C) Notify observers to display displayCard - * (D) Notify observers to clear displayCard - * (E) Log error about unexpected focusChanged event. - * - * Below is the state table illustrating the state transition and its action. NC means no change in state. - * - * E V E N T S - * ----------------------------------------------------------------------------------------- - * Current State | displayCard | timer | focusChanged::NONE | focusChanged::FG/BG | cardCleared - * -------------------------------------------------------------------------------------------------------- - * | IDLE | ACQUIRING(A) | NC | NC | RELEASING(B&E) | NC - * | ACQUIRING | NC | NC | IDLE(E) | DISPLAYING(C) | NC - * | DISPLAYING | NC(C) | RELEASING(B&D) | IDLE(D) | DISPLAYING(C) | RELEASING(B) - * | RELEASING | REACQUIRING | NC | IDLE | NC(B&E) | NC - * | REACQUIRING | NC | NC | ACQUIRING(A) | RELEASING(B&E) | NC - * -------------------------------------------------------------------------------------------------------- - * - */ - -std::string TemplateRuntime::stateToString(const TemplateRuntime::State state) { - switch (state) { - case TemplateRuntime::State::IDLE: - return "IDLE"; - case TemplateRuntime::State::ACQUIRING: - return "ACQUIRING"; - case TemplateRuntime::State::DISPLAYING: - return "DISPLAYING"; - case TemplateRuntime::State::RELEASING: - return "RELEASING"; - case TemplateRuntime::State::REACQUIRING: - return "REACQUIRING"; - } - return "UNKNOWN"; -} - -void TemplateRuntime::executeTimerEvent() { - State nextState = m_state; - - switch (m_state) { - case TemplateRuntime::State::DISPLAYING: - executeClearCard(); - m_focusManager->releaseChannel(CHANNEL_NAME, shared_from_this()); - nextState = TemplateRuntime::State::RELEASING; - break; - - case TemplateRuntime::State::IDLE: - case TemplateRuntime::State::ACQUIRING: - case TemplateRuntime::State::RELEASING: - case TemplateRuntime::State::REACQUIRING: - // Do Nothing. - break; - } - ACSDK_DEBUG3( - LX("executeTimerEvent").d("prevState", stateToString(m_state)).d("nextState", stateToString(nextState))); - m_state = nextState; -} - -void TemplateRuntime::executeOnFocusChangedEvent(avsCommon::avs::FocusState newFocus) { - ACSDK_DEBUG5(LX("executeOnFocusChangedEvent").d("prevFocus", m_focus).d("newFocus", newFocus)); - - bool weirdFocusState = false; - State nextState = m_state; - m_focus = newFocus; - - switch (m_state) { - case TemplateRuntime::State::IDLE: - // This is weird. We shouldn't be getting any focus updates in Idle. - switch (newFocus) { - case FocusState::FOREGROUND: - case FocusState::BACKGROUND: - weirdFocusState = true; - break; - case FocusState::NONE: - // Do nothing. - break; - } - break; - case TemplateRuntime::State::ACQUIRING: - switch (newFocus) { - case FocusState::FOREGROUND: - case FocusState::BACKGROUND: - executeDisplayCard(); - nextState = TemplateRuntime::State::DISPLAYING; - break; - case FocusState::NONE: - ACSDK_ERROR(LX("executeOnFocusChangedEvent") - .d("prevState", stateToString(m_state)) - .d("nextFocus", newFocus) - .m("Unexpected focus state event.")); - nextState = TemplateRuntime::State::IDLE; - break; - } - break; - case TemplateRuntime::State::DISPLAYING: - switch (newFocus) { - case FocusState::FOREGROUND: - case FocusState::BACKGROUND: - executeDisplayCard(); - break; - case FocusState::NONE: - executeClearCard(); - nextState = TemplateRuntime::State::IDLE; - break; - } - break; - case TemplateRuntime::State::RELEASING: - switch (newFocus) { - case FocusState::FOREGROUND: - case FocusState::BACKGROUND: - m_focusManager->releaseChannel(CHANNEL_NAME, shared_from_this()); - nextState = TemplateRuntime::State::RELEASING; - break; - case FocusState::NONE: - nextState = TemplateRuntime::State::IDLE; - break; - } - break; - case TemplateRuntime::State::REACQUIRING: - switch (newFocus) { - case FocusState::FOREGROUND: - case FocusState::BACKGROUND: - weirdFocusState = true; - break; - case FocusState::NONE: - m_focusManager->acquireChannel(CHANNEL_NAME, shared_from_this(), NAMESPACE); - nextState = TemplateRuntime::State::ACQUIRING; - break; - } - break; - } - if (weirdFocusState) { - ACSDK_ERROR(LX("executeOnFocusChangedEvent") - .d("prevState", stateToString(m_state)) - .d("nextFocus", newFocus) - .m("Unexpected focus state event.")); - m_focusManager->releaseChannel(CHANNEL_NAME, shared_from_this()); - nextState = TemplateRuntime::State::RELEASING; - } - ACSDK_DEBUG3(LX("executeOnFocusChangedEvent") - .d("prevState", stateToString(m_state)) - .d("nextState", stateToString(nextState))); - m_state = nextState; -} - -void TemplateRuntime::executeDisplayCardEvent( - const std::shared_ptr info) { - State nextState = m_state; - m_lastDisplayedDirective = info; - - switch (m_state) { - case TemplateRuntime::State::IDLE: - m_focusManager->acquireChannel(CHANNEL_NAME, shared_from_this(), NAMESPACE); - nextState = TemplateRuntime::State::ACQUIRING; - break; - case TemplateRuntime::State::ACQUIRING: - // Do Nothing. - break; - case TemplateRuntime::State::DISPLAYING: - executeDisplayCard(); - nextState = TemplateRuntime::State::DISPLAYING; - break; - case TemplateRuntime::State::RELEASING: - nextState = TemplateRuntime::State::REACQUIRING; - break; - case TemplateRuntime::State::REACQUIRING: - // Do Nothing. - break; - } - ACSDK_DEBUG3( - LX("executeDisplayCardEvent").d("prevState", stateToString(m_state)).d("nextState", stateToString(nextState))); - m_state = nextState; -} - -void TemplateRuntime::executeCardClearedEvent() { - State nextState = m_state; - switch (m_state) { - case TemplateRuntime::State::IDLE: - case TemplateRuntime::State::ACQUIRING: - // Do Nothing. - break; - case TemplateRuntime::State::DISPLAYING: - m_focusManager->releaseChannel(CHANNEL_NAME, shared_from_this()); - nextState = TemplateRuntime::State::RELEASING; - break; - case TemplateRuntime::State::RELEASING: - case TemplateRuntime::State::REACQUIRING: - // Do Nothing. - break; - } - ACSDK_DEBUG3( - LX("executeCardClearedEvent").d("prevState", stateToString(m_state)).d("nextState", stateToString(nextState))); - m_state = nextState; -} - -std::unordered_set> TemplateRuntime:: - getCapabilityConfigurations() { - return m_capabilityConfigurations; -} - -void TemplateRuntime::addRenderPlayerInfoCardsProvider( - std::shared_ptr cardsProvider) { - ACSDK_DEBUG5(LX("addRenderPlayerInfoCardsProvider")); - - if (!cardsProvider) { - ACSDK_ERROR( - LX("addRenderPlayerInfoCardsProviderFailed").d("reason", "nullRenderPlayerInfoCardsProviderInterface")); - return; - } - cardsProvider->setObserver(shared_from_this()); - m_renderPlayerInfoCardsInterfaces.insert(cardsProvider); -} - -} // namespace templateRuntime -} // namespace capabilityAgents -} // namespace alexaClientSDK diff --git a/CapabilityAgents/TemplateRuntime/test/CMakeLists.txt b/CapabilityAgents/TemplateRuntime/test/CMakeLists.txt deleted file mode 100644 index 8529d325e6..0000000000 --- a/CapabilityAgents/TemplateRuntime/test/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -cmake_minimum_required(VERSION 3.1 FATAL_ERROR) - -set(INCLUDE_PATH - "${TemplateRuntime_INCLUDE_DIR}" - "${RAPIDJSON_INCLUDE_DIR}" - "${AVSCommon_SOURCE_DIR}/AVS/test") - -discover_unit_tests("${INCLUDE_PATH}" "TemplateRuntime;SDKInterfacesTests") diff --git a/CapabilityAgents/TemplateRuntime/test/RenderPlayerInfoCardsProviderRegistrarTest.cpp b/CapabilityAgents/TemplateRuntime/test/RenderPlayerInfoCardsProviderRegistrarTest.cpp deleted file mode 100644 index f89e2d9edf..0000000000 --- a/CapabilityAgents/TemplateRuntime/test/RenderPlayerInfoCardsProviderRegistrarTest.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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. - */ - -/// @file RenderPlayerInfoCardsProviderRegistrarTest.cpp - -#include - -#include -#include - -#include - -#include "TemplateRuntime/RenderPlayerInfoCardsProviderRegistrar.h" - -namespace alexaClientSDK { -namespace capabilityAgents { -namespace templateRuntime { -namespace test { - -using namespace ::testing; -using namespace avsCommon::sdkInterfaces; - -class MockRenderPlayerInfoCardProvider : public RenderPlayerInfoCardsProviderInterface { -public: - MOCK_METHOD1(setObserver, void(std::shared_ptr)); -}; - -/** - * Verify getProviders() returns the set of registered providers. - */ -TEST(RenderPlayerInfoCardsProviderTest, test_getProviders) { - auto provider1 = std::make_shared>(); - auto provider2 = std::make_shared>(); - auto registrar = RenderPlayerInfoCardsProviderRegistrar::createRenderPlayerInfoCardsProviderRegistrarInterface(); - - ASSERT_TRUE(registrar); - - ASSERT_TRUE(registrar->registerProvider(provider1)); - ASSERT_TRUE(registrar->registerProvider(provider2)); - - std::unordered_set> providers = - registrar->getProviders(); - ASSERT_THAT(providers, Contains(provider1)); - ASSERT_THAT(providers, Contains(provider2)); -} - -/** - * Verify registering a provider fails if it is a duplicate. - */ -TEST(PostConnectOperationProviderRegistrarTest, test_registerDuplicateProviderFails) { - auto provider1 = std::make_shared>(); - auto registrar = RenderPlayerInfoCardsProviderRegistrar::createRenderPlayerInfoCardsProviderRegistrarInterface(); - - ASSERT_TRUE(registrar); - - ASSERT_TRUE(registrar->registerProvider(provider1)); - ASSERT_FALSE(registrar->registerProvider(provider1)); - - std::unordered_set> providers = - registrar->getProviders(); - ASSERT_TRUE(providers.size() == 1); - ASSERT_THAT(providers, Contains(provider1)); -} - -/** - * Verify registering a null provider fails. - */ -TEST(PostConnectOperationProviderRegistrarTest, test_registerNullProviderFails) { - auto registrar = RenderPlayerInfoCardsProviderRegistrar::createRenderPlayerInfoCardsProviderRegistrarInterface(); - - ASSERT_TRUE(registrar); - - ASSERT_FALSE(registrar->registerProvider(nullptr)); - - std::unordered_set> providers = - registrar->getProviders(); - ASSERT_TRUE(providers.size() == 0); -} - -} // namespace test -} // namespace templateRuntime -} // namespace capabilityAgents -} // namespace alexaClientSDK diff --git a/CapabilityAgents/TemplateRuntime/test/TemplateRuntimeTest.cpp b/CapabilityAgents/TemplateRuntime/test/TemplateRuntimeTest.cpp deleted file mode 100644 index 9edbc350af..0000000000 --- a/CapabilityAgents/TemplateRuntime/test/TemplateRuntimeTest.cpp +++ /dev/null @@ -1,971 +0,0 @@ -/* - * 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. - */ - -/// @file TemplateRuntimeTest -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "TemplateRuntime/TemplateRuntime.h" - -namespace alexaClientSDK { -namespace capabilityAgents { -namespace templateRuntime { -namespace test { - -using namespace avsCommon::avs; -using namespace avsCommon::avs::attachment::test; -using namespace avsCommon::sdkInterfaces; -using namespace avsCommon::sdkInterfaces::test; -using namespace avsCommon::utils::memory; -using namespace rapidjson; -using namespace ::testing; - -/// Timeout when waiting for futures to be set. -static std::chrono::milliseconds TIMEOUT(1000); - -/// Timeout when waiting for clearTemplateCard. -static std::chrono::milliseconds TEMPLATE_TIMEOUT(5000); - -/// Timeout when waiting for clearTemplateCard not called. -static std::chrono::milliseconds TEMPLATE_NOT_CLEAR_TIMEOUT(2500); - -/// Timeout when waiting for clearTemplateCard. -static std::chrono::milliseconds PLAYER_FINISHED_TIMEOUT(5000); - -/// The namespace for this capability agent. -static const std::string NAMESPACE{"TemplateRuntime"}; - -/// An unknown directive signature. -static const std::string UNKNOWN_DIRECTIVE{"Unknown"}; - -/// The RenderTemplate directive signature. -static const NamespaceAndName TEMPLATE{NAMESPACE, "RenderTemplate"}; - -/// The RenderPlayerInfo directive signature. -static const NamespaceAndName PLAYER_INFO{NAMESPACE, "RenderPlayerInfo"}; - -/// The @c MessageId identifer. -static const std::string MESSAGE_ID("messageId"); - -/// An audioItemId for the RenderPlayerInfo directive. -static const std::string AUDIO_ITEM_ID("AudioItemId abcdefgh"); - -/// An audioItemId without a corresponding RenderPlayerInfo directive. -static const std::string AUDIO_ITEM_ID_1("AudioItemId 12345678"); - -/// A RenderTemplate directive payload. -// clang-format off -static const std::string TEMPLATE_PAYLOAD = "{" - "\"token\":\"TOKEN1\"," - "\"type\":\"BodyTemplate1\"," - "\"title\":{" - "\"mainTitle\":\"MAIN_TITLE\"," - "\"subTitle\":\"SUB_TITLE\"" - "}" -"}"; -// clang-format on - -/// A RenderPlayerInfo directive payload. -// clang-format off -static const std::string PLAYERINFO_PAYLOAD = "{" - "\"audioItemId\":\"" + AUDIO_ITEM_ID + "\"," - "\"content\":{" - "\"title\":\"TITLE\"," - "\"header\":\"HEADER\"" - "}" -"}"; -// clang-format on - -/// A malformed RenderPlayerInfo directive payload. -// clang-format off -static const std::string MALFORM_PLAYERINFO_PAYLOAD = "{" - "\"audioItemId\"::::\"" + AUDIO_ITEM_ID + "\"," - "\"content\":{{{{" - "\"title\":\"TITLE\"," - "\"header\":\"HEADER\"" - "}" -"}"; -// clang-format on - -class MockMediaPropertiesFetcher : public MediaPropertiesInterface { -public: - MOCK_METHOD0(getAudioItemOffset, std::chrono::milliseconds()); - MOCK_METHOD0(getAudioItemDuration, std::chrono::milliseconds()); -}; - -class MockRenderInfoCardsPlayer : public RenderPlayerInfoCardsProviderInterface { -public: - MOCK_METHOD1( - setObserver, - void(std::shared_ptr observer)); -}; - -class MockRenderInfoCardsPlayerRegistrar : public RenderPlayerInfoCardsProviderRegistrarInterface { -public: - MOCK_METHOD1( - registerProvider, - bool(const std::shared_ptr& provider)); - MOCK_METHOD0( - getProviders, - std::unordered_set>()); -}; - -class MockGui : public TemplateRuntimeObserverInterface { -public: - MOCK_METHOD2(renderTemplateCard, void(const std::string& jsonPayload, avsCommon::avs::FocusState focusState)); - MOCK_METHOD0(clearTemplateCard, void()); - MOCK_METHOD3( - renderPlayerInfoCard, - void( - const std::string& jsonPayload, - TemplateRuntimeObserverInterface::AudioPlayerInfo audioPlayerInfo, - avsCommon::avs::FocusState focusState)); - MOCK_METHOD0(clearPlayerInfoCard, void()); -}; - -/// Test harness for @c TemplateRuntime class. -class TemplateRuntimeTest : public ::testing::Test { -public: - /// Set up the test harness for running a test. - void SetUp() override; - - /// Clean up the test harness after running a test. - void TearDown() override; - - /// Function to set the promise and wake @c m_wakeSetCompleteFuture. - void wakeOnSetCompleted(); - - /// Function to set the promise and wake @c m_wakeRenderTemplateCardFuture. - void wakeOnRenderTemplateCard(); - - /// Function to set the promise and wake @c m_wakeRenderPlayerInfoCardFuture. - void wakeOnRenderPlayerInfoCard(); - - /// Function to set the promise and wake @c m_wakeClearTemplateCardFuture. - void wakeOnClearTemplateCard(); - - /// Function to set the promise and wake @c m_wakeClearPlayerInfoCardFuture. - void wakeOnClearPlayerInfoCard(); - - /// Function to set the promise and wake @c m_wakeReleaseChannelFuture. - void wakeOnReleaseChannel(); - - /// A constructor which initializes the promises and futures needed for the test class. - TemplateRuntimeTest() : - m_wakeSetCompletedPromise{}, - m_wakeSetCompletedFuture{m_wakeSetCompletedPromise.get_future()}, - m_wakeRenderTemplateCardPromise{}, - m_wakeRenderTemplateCardFuture{m_wakeRenderTemplateCardPromise.get_future()}, - m_wakeRenderPlayerInfoCardPromise{}, - m_wakeRenderPlayerInfoCardFuture{m_wakeRenderPlayerInfoCardPromise.get_future()}, - m_wakeClearTemplateCardPromise{}, - m_wakeClearTemplateCardFuture{m_wakeClearTemplateCardPromise.get_future()}, - m_wakeClearPlayerInfoCardPromise{}, - m_wakeClearPlayerInfoCardFuture{m_wakeClearPlayerInfoCardPromise.get_future()}, - m_wakeReleaseChannelPromise{}, - m_wakeReleaseChannelFuture{m_wakeReleaseChannelPromise.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; - - /// Promise to synchronize directive handling with RenderTemplateCard callback. - std::promise m_wakeRenderTemplateCardPromise; - - /// Future to synchronize directive handling with RenderTemplateCard callback. - std::future m_wakeRenderTemplateCardFuture; - - /// Promise to synchronize directive handling with RenderPlayerInfoCard callback. - std::promise m_wakeRenderPlayerInfoCardPromise; - - /// Future to synchronize directive handling with RenderPlayerInfoCard callback. - std::future m_wakeRenderPlayerInfoCardFuture; - - /// Promise to synchronize ClearTemplateCard callback. - std::promise m_wakeClearTemplateCardPromise; - - /// Future to synchronize ClearTemplateCard callback. - std::future m_wakeClearTemplateCardFuture; - - /// Promise to synchronize ClearPlayerInfoCard callback. - std::promise m_wakeClearPlayerInfoCardPromise; - - /// Future to synchronize ClearPlayerInfoCard callback. - std::future m_wakeClearPlayerInfoCardFuture; - - /// Promise to synchronize releaseChannel calls. - std::promise m_wakeReleaseChannelPromise; - - /// Future to synchronize releaseChannel calls. - std::future m_wakeReleaseChannelFuture; - - /// A nice mock for the RenderInfoCardsInterface calls. - std::shared_ptr> m_mockRenderPlayerInfoCardsProvider; - - /// A nice mock for the MediaPropertiesInterface calls. - std::shared_ptr m_mediaPropertiesFetcher; - - /// A strict mock that allows the test to strictly monitor the exceptions being sent. - std::shared_ptr> m_mockExceptionSender; - - /// A strict mock that allows the test to strictly monitor the handling of directives. - std::unique_ptr> m_mockDirectiveHandlerResult; - - /// @c FocusManager to request focus to the Visual channel. - std::shared_ptr m_mockFocusManager; - - /// A strict mock to allow testing of the observer callback. - std::shared_ptr> m_mockGui; - - /// A pointer to an instance of the TemplateRuntime that will be instantiated per test. - std::shared_ptr m_templateRuntime; -}; - -void TemplateRuntimeTest::SetUp() { - m_mockExceptionSender = std::make_shared>(); - m_mockDirectiveHandlerResult = make_unique>(); - m_mockFocusManager = std::make_shared>(); - m_mediaPropertiesFetcher = std::make_shared>(); - m_mockRenderPlayerInfoCardsProvider = std::make_shared>(); - m_mockGui = std::make_shared>(); - m_templateRuntime = - TemplateRuntime::create({m_mockRenderPlayerInfoCardsProvider}, m_mockFocusManager, m_mockExceptionSender); - m_templateRuntime->addObserver(m_mockGui); - - ON_CALL(*m_mockFocusManager, acquireChannel(_, _, _)).WillByDefault(InvokeWithoutArgs([this] { - m_templateRuntime->onFocusChanged(avsCommon::avs::FocusState::FOREGROUND, MixingBehavior::PRIMARY); - return true; - })); - - ON_CALL(*m_mockFocusManager, releaseChannel(_, _)).WillByDefault(InvokeWithoutArgs([this] { - auto releaseChannelSuccess = std::make_shared>(); - std::future returnValue = releaseChannelSuccess->get_future(); - m_templateRuntime->onFocusChanged(avsCommon::avs::FocusState::NONE, MixingBehavior::MUST_STOP); - releaseChannelSuccess->set_value(true); - return returnValue; - })); -} - -void TemplateRuntimeTest::TearDown() { - if (m_templateRuntime) { - m_templateRuntime->shutdown(); - m_templateRuntime.reset(); - } -} - -void TemplateRuntimeTest::wakeOnSetCompleted() { - m_wakeSetCompletedPromise.set_value(); -} - -void TemplateRuntimeTest::wakeOnRenderTemplateCard() { - m_wakeRenderTemplateCardPromise.set_value(); -} - -void TemplateRuntimeTest::wakeOnRenderPlayerInfoCard() { - m_wakeRenderPlayerInfoCardPromise.set_value(); -} - -void TemplateRuntimeTest::wakeOnClearTemplateCard() { - m_wakeClearTemplateCardPromise.set_value(); -} - -void TemplateRuntimeTest::wakeOnClearPlayerInfoCard() { - m_wakeClearPlayerInfoCardPromise.set_value(); -} - -void TemplateRuntimeTest::wakeOnReleaseChannel() { - m_wakeReleaseChannelPromise.set_value(); -} - -/** - * Tests creating the TemplateRuntime with a null audioPlayerInterface. - */ -TEST_F(TemplateRuntimeTest, test_nullAudioPlayerInterface) { - std::unordered_set> providers = { - nullptr}; - auto templateRuntime = TemplateRuntime::create(providers, m_mockFocusManager, m_mockExceptionSender); - ASSERT_EQ(templateRuntime, nullptr); -} - -/** - * Tests creating the TemplateRuntime with a null provider registrar. - */ -TEST_F(TemplateRuntimeTest, test_nullProviderRegistrar) { - std::shared_ptr providerRegistrar = - nullptr; - auto templateRuntime = - TemplateRuntime::createTemplateRuntime(providerRegistrar, m_mockFocusManager, m_mockExceptionSender); - ASSERT_EQ(templateRuntime, nullptr); -} - -/** - * Tests creating the TemplateRuntime with a null focusManagerInterface. - */ -TEST_F(TemplateRuntimeTest, test_nullFocusManagerInterface) { - auto templateRuntime = - TemplateRuntime::create({m_mockRenderPlayerInfoCardsProvider}, nullptr, m_mockExceptionSender); - ASSERT_EQ(templateRuntime, nullptr); -} - -/** - * Tests creating the TemplateRuntime with a null exceptionSender. - */ -TEST_F(TemplateRuntimeTest, test_nullExceptionSender) { - auto templateRuntime = TemplateRuntime::create({m_mockRenderPlayerInfoCardsProvider}, m_mockFocusManager, nullptr); - ASSERT_EQ(templateRuntime, nullptr); -} - -/** - * Tests that the TemplateRuntime will add itself to the providers registered with the RenderInfoCardsPlayerRegistrar at - * constructor time, and remove itself from them during shutdown. - */ -TEST_F(TemplateRuntimeTest, test_renderInfoCardsPlayersFromRegistrarAddRemoveObserver) { - auto mockRenderInfoCardsProvider1 = std::make_shared>(); - auto mockRenderInfoCardsProvider2 = std::make_shared>(); - - auto mockRenderInfoCardsPlayerRegistrar = std::make_shared>(); - EXPECT_CALL(*mockRenderInfoCardsPlayerRegistrar, getProviders()) - .WillOnce(Invoke([mockRenderInfoCardsProvider1, mockRenderInfoCardsProvider2]() { - std::unordered_set> providers = { - mockRenderInfoCardsProvider1, mockRenderInfoCardsProvider2}; - return providers; - })); - - auto mockExceptionSender = std::make_shared>(); - auto mockFocusManager = std::make_shared>(); - - Expectation setObserver1 = EXPECT_CALL(*mockRenderInfoCardsProvider1, setObserver(NotNull())).Times(Exactly(1)); - EXPECT_CALL(*mockRenderInfoCardsProvider1, setObserver(IsNull())).Times(Exactly(1)).After(setObserver1); - Expectation setObserver2 = EXPECT_CALL(*mockRenderInfoCardsProvider2, setObserver(NotNull())).Times(Exactly(1)); - EXPECT_CALL(*mockRenderInfoCardsProvider2, setObserver(IsNull())).Times(Exactly(1)).After(setObserver2); - - auto templateRuntime = TemplateRuntime::createTemplateRuntime( - mockRenderInfoCardsPlayerRegistrar, mockFocusManager, mockExceptionSender); - templateRuntime->shutdown(); -} - -/** - * Tests that the TemplateRuntime successfully add itself with the RenderInfoCardsPlayers at constructor time, and - * successfully remove itself with the RenderPlayerInfoCardsPlayers during shutdown. - */ -TEST_F(TemplateRuntimeTest, test_renderInfoCardsPlayersAddRemoveObserver) { - auto mockRenderInfoCardsProvider1 = std::make_shared>(); - auto mockRenderInfoCardsProvider2 = std::make_shared>(); - auto mockExceptionSender = std::make_shared>(); - auto mockFocusManager = std::make_shared>(); - - Expectation setObserver1 = EXPECT_CALL(*mockRenderInfoCardsProvider1, setObserver(NotNull())).Times(Exactly(1)); - EXPECT_CALL(*mockRenderInfoCardsProvider1, setObserver(IsNull())).Times(Exactly(1)).After(setObserver1); - Expectation setObserver2 = EXPECT_CALL(*mockRenderInfoCardsProvider2, setObserver(NotNull())).Times(Exactly(1)); - EXPECT_CALL(*mockRenderInfoCardsProvider2, setObserver(IsNull())).Times(Exactly(1)).After(setObserver2); - - auto templateRuntime = TemplateRuntime::create( - {mockRenderInfoCardsProvider1, mockRenderInfoCardsProvider2}, mockFocusManager, mockExceptionSender); - templateRuntime->shutdown(); -} - -/** - * Tests unknown Directive. Expect that the sendExceptionEncountered and setFailed will be called. - */ -TEST_F(TemplateRuntimeTest, test_unknownDirective) { - // Create Directive. - auto attachmentManager = std::make_shared>(); - auto avsMessageHeader = std::make_shared(NAMESPACE, UNKNOWN_DIRECTIVE, MESSAGE_ID); - std::shared_ptr directive = AVSDirective::create("", avsMessageHeader, "", attachmentManager, ""); - - EXPECT_CALL(*m_mockExceptionSender, sendExceptionEncountered(_, _, _)).Times(Exactly(1)); - EXPECT_CALL(*m_mockDirectiveHandlerResult, setFailed(_)) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnSetCompleted)); - - m_templateRuntime->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); - m_templateRuntime->CapabilityAgent::handleDirective(MESSAGE_ID); - m_wakeSetCompletedFuture.wait_for(TIMEOUT); -} - -/** - * Tests RenderTemplate Directive. Expect that the renderTemplateCard callback will be called and clearTemplateCard will - * be called after 2s after DialogUXState is changed to IDLE state. - */ -TEST_F(TemplateRuntimeTest, testSlow_renderTemplateDirective) { - // Create Directive. - auto attachmentManager = std::make_shared>(); - auto avsMessageHeader = std::make_shared(TEMPLATE.nameSpace, TEMPLATE.name, MESSAGE_ID); - std::shared_ptr directive = - AVSDirective::create("", avsMessageHeader, TEMPLATE_PAYLOAD, attachmentManager, ""); - - EXPECT_CALL(*m_mockGui, renderTemplateCard(TEMPLATE_PAYLOAD, _)) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnRenderTemplateCard)); - EXPECT_CALL(*m_mockDirectiveHandlerResult, setCompleted()) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnSetCompleted)); - EXPECT_CALL(*m_mockGui, clearTemplateCard()) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnClearTemplateCard)); - - m_templateRuntime->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); - m_templateRuntime->CapabilityAgent::handleDirective(MESSAGE_ID); - m_wakeSetCompletedFuture.wait_for(TIMEOUT); - m_wakeRenderTemplateCardFuture.wait_for(TIMEOUT); - m_templateRuntime->onDialogUXStateChanged( - avsCommon::sdkInterfaces::DialogUXStateObserverInterface::DialogUXState::IDLE); - m_wakeClearTemplateCardFuture.wait_for(TEMPLATE_TIMEOUT); -} - -/** - * Tests RenderTemplate Directive. Expect that the renderTemplateCard callback will be called and clearTemplateCard will - * not be called if DialogUXState goes to IDLE state and then goes EXPECTING and SPEAKING state. - */ -TEST_F( - TemplateRuntimeTest, - testRenderTemplateDirectiveWillNotClearCardAfterGoingToExpectingStateAfterGoingToIDLESlowTest) { - // Create Directive. - auto attachmentManager = std::make_shared>(); - auto avsMessageHeader = std::make_shared(TEMPLATE.nameSpace, TEMPLATE.name, MESSAGE_ID); - std::shared_ptr directive = - AVSDirective::create("", avsMessageHeader, TEMPLATE_PAYLOAD, attachmentManager, ""); - - EXPECT_CALL(*m_mockGui, renderTemplateCard(TEMPLATE_PAYLOAD, _)) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnRenderTemplateCard)); - EXPECT_CALL(*m_mockDirectiveHandlerResult, setCompleted()) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnSetCompleted)); - EXPECT_CALL(*m_mockGui, clearTemplateCard()).Times(Exactly(0)); - - m_templateRuntime->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); - m_templateRuntime->CapabilityAgent::handleDirective(MESSAGE_ID); - m_wakeSetCompletedFuture.wait_for(TIMEOUT); - m_wakeRenderTemplateCardFuture.wait_for(TIMEOUT); - - // first test IDLE->EXPECTING transition - m_templateRuntime->onDialogUXStateChanged( - avsCommon::sdkInterfaces::DialogUXStateObserverInterface::DialogUXState::IDLE); - m_templateRuntime->onDialogUXStateChanged( - avsCommon::sdkInterfaces::DialogUXStateObserverInterface::DialogUXState::EXPECTING); - EXPECT_EQ(m_wakeClearTemplateCardFuture.wait_for(TEMPLATE_NOT_CLEAR_TIMEOUT), std::future_status::timeout); - - // now test IDLE->SPEAKING transition - m_templateRuntime->onDialogUXStateChanged( - avsCommon::sdkInterfaces::DialogUXStateObserverInterface::DialogUXState::IDLE); - m_templateRuntime->onDialogUXStateChanged( - avsCommon::sdkInterfaces::DialogUXStateObserverInterface::DialogUXState::SPEAKING); - EXPECT_EQ(m_wakeClearTemplateCardFuture.wait_for(TEMPLATE_NOT_CLEAR_TIMEOUT), std::future_status::timeout); -} - -/** - * Tests RenderTemplate Directive using the handleDirectiveImmediately. Expect that the renderTemplateCard - * callback will be called. - */ -TEST_F(TemplateRuntimeTest, test_handleDirectiveImmediately) { - // Create Directive. - auto attachmentManager = std::make_shared>(); - auto avsMessageHeader = std::make_shared(TEMPLATE.nameSpace, TEMPLATE.name, MESSAGE_ID); - std::shared_ptr directive = - AVSDirective::create("", avsMessageHeader, TEMPLATE_PAYLOAD, attachmentManager, ""); - - EXPECT_CALL(*m_mockGui, renderTemplateCard(TEMPLATE_PAYLOAD, _)) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnRenderTemplateCard)); - - m_templateRuntime->handleDirectiveImmediately(directive); - m_wakeRenderTemplateCardFuture.wait_for(TIMEOUT); -} - -/** - * Tests RenderTemplate Directive received before the corresponding AudioPlayer call. Expect - * that the renderTemplateCard callback will be called and clearPlayerInfoCard will be called after 2s after Audio State - * is changed to FINISHED state. - */ -TEST_F(TemplateRuntimeTest, testSlow_renderPlayerInfoDirectiveBefore) { - // Create Directive. - auto attachmentManager = std::make_shared>(); - auto avsMessageHeader = std::make_shared(PLAYER_INFO.nameSpace, PLAYER_INFO.name, MESSAGE_ID); - std::shared_ptr directive = - AVSDirective::create("", avsMessageHeader, PLAYERINFO_PAYLOAD, attachmentManager, ""); - - ::testing::InSequence s; - EXPECT_CALL(*m_mockDirectiveHandlerResult, setCompleted()) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnSetCompleted)); - EXPECT_CALL(*m_mockGui, renderTemplateCard(_, _)).Times(Exactly(0)); - - // do not expect renderPlayerInfo card call until AudioPlayer notify with the correct audioItemId - EXPECT_CALL(*m_mockGui, renderPlayerInfoCard(_, _, _)).Times(Exactly(0)); - - m_templateRuntime->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); - m_templateRuntime->CapabilityAgent::handleDirective(MESSAGE_ID); - m_wakeSetCompletedFuture.wait_for(TIMEOUT); - - EXPECT_CALL(*m_mockGui, renderPlayerInfoCard(PLAYERINFO_PAYLOAD, _, _)) - .Times(Exactly(2)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnRenderPlayerInfoCard)) - .WillOnce(InvokeWithoutArgs([] {})); - - RenderPlayerInfoCardsObserverInterface::Context context; - context.mediaProperties = m_mediaPropertiesFetcher; - context.audioItemId = AUDIO_ITEM_ID; - context.offset = TIMEOUT; - m_templateRuntime->onRenderPlayerCardsInfoChanged(avsCommon::avs::PlayerActivity::PLAYING, context); - - m_wakeRenderPlayerInfoCardFuture.wait_for(TIMEOUT); - - EXPECT_CALL(*m_mockGui, clearPlayerInfoCard()) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnClearPlayerInfoCard)); - - m_templateRuntime->onRenderPlayerCardsInfoChanged(avsCommon::avs::PlayerActivity::FINISHED, context); - m_wakeClearPlayerInfoCardFuture.wait_for(PLAYER_FINISHED_TIMEOUT); -} - -/** - * Tests RenderTemplate Directive received after the corresponding AudioPlayer call. Expect - * that the renderTemplateCard callback will be called. - */ -TEST_F(TemplateRuntimeTest, test_renderPlayerInfoDirectiveAfter) { - // Create Directive. - auto attachmentManager = std::make_shared>(); - auto avsMessageHeader = std::make_shared(PLAYER_INFO.nameSpace, PLAYER_INFO.name, MESSAGE_ID); - std::shared_ptr directive = - AVSDirective::create("", avsMessageHeader, PLAYERINFO_PAYLOAD, attachmentManager, ""); - - EXPECT_CALL(*m_mockGui, renderPlayerInfoCard(PLAYERINFO_PAYLOAD, _, _)) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnRenderPlayerInfoCard)); - EXPECT_CALL(*m_mockDirectiveHandlerResult, setCompleted()) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnSetCompleted)); - - RenderPlayerInfoCardsObserverInterface::Context context; - context.mediaProperties = m_mediaPropertiesFetcher; - context.audioItemId = AUDIO_ITEM_ID; - context.offset = TIMEOUT; - m_templateRuntime->onRenderPlayerCardsInfoChanged(avsCommon::avs::PlayerActivity::PLAYING, context); - m_templateRuntime->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); - m_templateRuntime->CapabilityAgent::handleDirective(MESSAGE_ID); - - m_wakeRenderPlayerInfoCardFuture.wait_for(TIMEOUT); - m_wakeSetCompletedFuture.wait_for(TIMEOUT); -} - -/** - * Tests RenderTemplate Directive received without an audioItemId. Expect that the - * sendExceptionEncountered and setFailed will be called. - */ -TEST_F(TemplateRuntimeTest, test_renderPlayerInfoDirectiveWithoutAudioItemId) { - // Create Directive. - auto attachmentManager = std::make_shared>(); - auto avsMessageHeader = std::make_shared(PLAYER_INFO.nameSpace, PLAYER_INFO.name, MESSAGE_ID); - std::shared_ptr directive = - AVSDirective::create("", avsMessageHeader, TEMPLATE_PAYLOAD, attachmentManager, ""); - - EXPECT_CALL(*m_mockExceptionSender, sendExceptionEncountered(_, _, _)).Times(Exactly(1)); - EXPECT_CALL(*m_mockDirectiveHandlerResult, setFailed(_)) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnSetCompleted)); - - m_templateRuntime->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); - m_templateRuntime->CapabilityAgent::handleDirective(MESSAGE_ID); - m_wakeSetCompletedFuture.wait_for(TIMEOUT); -} - -/** - * Tests when a malformed RenderTemplate Directive is received. Expect that the - * sendExceptionEncountered and setFailed will be called. - */ -TEST_F(TemplateRuntimeTest, test_malformedRenderPlayerInfoDirective) { - // Create Directive. - auto attachmentManager = std::make_shared>(); - auto avsMessageHeader = std::make_shared(PLAYER_INFO.nameSpace, PLAYER_INFO.name, MESSAGE_ID); - std::shared_ptr directive = - AVSDirective::create("", avsMessageHeader, MALFORM_PLAYERINFO_PAYLOAD, attachmentManager, ""); - - EXPECT_CALL(*m_mockExceptionSender, sendExceptionEncountered(_, _, _)).Times(Exactly(1)); - EXPECT_CALL(*m_mockDirectiveHandlerResult, setFailed(_)) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnSetCompleted)); - - m_templateRuntime->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); - m_templateRuntime->CapabilityAgent::handleDirective(MESSAGE_ID); - m_wakeSetCompletedFuture.wait_for(TIMEOUT); -} - -/** - * Tests AudioPlayer notified the handling of AUDIO_ITEM_ID_1, and then RenderTemplate Directive with - * AUDIO_ITEM_ID is received. Expect that the renderTemplateCard callback will not be called until - * the AudioPlayer notified the handling of AUDIO_ITEM_ID later. - */ -TEST_F(TemplateRuntimeTest, test_renderPlayerInfoDirectiveDifferentAudioItemId) { - // Create Directive. - auto attachmentManager = std::make_shared>(); - auto avsMessageHeader = std::make_shared(PLAYER_INFO.nameSpace, PLAYER_INFO.name, MESSAGE_ID); - std::shared_ptr directive = - AVSDirective::create("", avsMessageHeader, PLAYERINFO_PAYLOAD, attachmentManager, ""); - - EXPECT_CALL(*m_mockGui, renderPlayerInfoCard(PLAYERINFO_PAYLOAD, _, _)).Times(Exactly(0)); - EXPECT_CALL(*m_mockDirectiveHandlerResult, setCompleted()) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnSetCompleted)); - - RenderPlayerInfoCardsObserverInterface::Context context; - context.mediaProperties = m_mediaPropertiesFetcher; - context.audioItemId = AUDIO_ITEM_ID_1; - context.offset = TIMEOUT; - m_templateRuntime->onRenderPlayerCardsInfoChanged(avsCommon::avs::PlayerActivity::PLAYING, context); - m_templateRuntime->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); - m_templateRuntime->CapabilityAgent::handleDirective(MESSAGE_ID); - m_wakeSetCompletedFuture.wait_for(TIMEOUT); - - EXPECT_CALL(*m_mockGui, renderPlayerInfoCard(PLAYERINFO_PAYLOAD, _, _)) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnRenderPlayerInfoCard)); - - context.audioItemId = AUDIO_ITEM_ID; - m_templateRuntime->onRenderPlayerCardsInfoChanged(avsCommon::avs::PlayerActivity::PLAYING, context); - - m_wakeRenderPlayerInfoCardFuture.wait_for(TIMEOUT); -} - -/** - * Tests Provider notified the handling of AUDIO_ITEM_ID_1, and another provider notified the handling of - * AUDIO_ITEM_ID, and then RenderTemplate Directive with AUDIO_ITEM_ID is received. Expect that the - * renderTemplateCard callback will be called and the correct getAudioItemOffset is called. - */ -TEST_F(TemplateRuntimeTest, test_renderPlayerInfoDirectiveWithTwoProviders) { - auto anotherMediaPropertiesFetcher = std::make_shared>(); - - // Create Directive. - auto attachmentManager = std::make_shared>(); - auto avsMessageHeader = std::make_shared(PLAYER_INFO.nameSpace, PLAYER_INFO.name, MESSAGE_ID); - std::shared_ptr directive = - AVSDirective::create("", avsMessageHeader, PLAYERINFO_PAYLOAD, attachmentManager, ""); - - EXPECT_CALL(*m_mockGui, renderPlayerInfoCard(PLAYERINFO_PAYLOAD, _, _)) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnRenderPlayerInfoCard)); - EXPECT_CALL(*m_mockDirectiveHandlerResult, setCompleted()) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnSetCompleted)); - - EXPECT_CALL(*anotherMediaPropertiesFetcher, getAudioItemOffset()) - .Times(Exactly(1)) - .WillOnce(Return(std::chrono::milliseconds::zero())); - EXPECT_CALL(*m_mediaPropertiesFetcher, getAudioItemOffset()).Times(Exactly(0)); - - RenderPlayerInfoCardsObserverInterface::Context context; - context.mediaProperties = m_mediaPropertiesFetcher; - context.audioItemId = AUDIO_ITEM_ID_1; - context.offset = TIMEOUT; - m_templateRuntime->onRenderPlayerCardsInfoChanged(avsCommon::avs::PlayerActivity::PLAYING, context); - - RenderPlayerInfoCardsObserverInterface::Context context1; - context1.mediaProperties = anotherMediaPropertiesFetcher; - context1.audioItemId = AUDIO_ITEM_ID; - context1.offset = TIMEOUT; - m_templateRuntime->onRenderPlayerCardsInfoChanged(avsCommon::avs::PlayerActivity::PLAYING, context1); - - m_templateRuntime->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); - m_templateRuntime->CapabilityAgent::handleDirective(MESSAGE_ID); - - m_wakeRenderPlayerInfoCardFuture.wait_for(TIMEOUT); - m_wakeSetCompletedFuture.wait_for(TIMEOUT); -} - -/** - * Tests AudioPlayer callbacks will trigger the correct renderPlayerInfoCard callbacks. Expect - * the payload, audioPlayerState and offset to match to the ones passed in by the - * RenderPlayerInfoCardsObserverInterface. - */ -TEST_F(TemplateRuntimeTest, test_renderPlayerInfoDirectiveAudioStateUpdate) { - // Create Directive. - auto attachmentManager = std::make_shared>(); - auto avsMessageHeader = std::make_shared(PLAYER_INFO.nameSpace, PLAYER_INFO.name, MESSAGE_ID); - std::shared_ptr directive = - AVSDirective::create("", avsMessageHeader, PLAYERINFO_PAYLOAD, attachmentManager, ""); - - ::testing::InSequence s; - // Send a directive first to TemplateRuntime - EXPECT_CALL(*m_mockDirectiveHandlerResult, setCompleted()) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnSetCompleted)); - m_templateRuntime->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); - m_templateRuntime->CapabilityAgent::handleDirective(MESSAGE_ID); - m_wakeSetCompletedFuture.wait_for(TIMEOUT); - - RenderPlayerInfoCardsObserverInterface::Context context; - context.mediaProperties = m_mediaPropertiesFetcher; - context.audioItemId = AUDIO_ITEM_ID; - - // Test onAudioPlayed() callback with 100ms offset - std::promise wakePlayPromise; - std::future wakePlayFuture = wakePlayPromise.get_future(); - context.offset = std::chrono::milliseconds(100); - EXPECT_CALL(*m_mockGui, renderPlayerInfoCard(PLAYERINFO_PAYLOAD, _, _)) - .Times(Exactly(1)) - .WillOnce(Invoke([&wakePlayPromise, context]( - const std::string& jsonPayload, - TemplateRuntimeObserverInterface::AudioPlayerInfo audioPlayerInfo, - avsCommon::avs::FocusState focusState) { - EXPECT_EQ(audioPlayerInfo.audioPlayerState, avsCommon::avs::PlayerActivity::PLAYING); - EXPECT_EQ(audioPlayerInfo.offset, context.offset); - wakePlayPromise.set_value(); - })); - m_templateRuntime->onRenderPlayerCardsInfoChanged(avsCommon::avs::PlayerActivity::PLAYING, context); - wakePlayFuture.wait_for(TIMEOUT); - - // Test onAudioPaused() callback with 200ms offset - std::promise wakePausePromise; - std::future wakePauseFuture = wakePausePromise.get_future(); - context.offset = std::chrono::milliseconds(200); - EXPECT_CALL(*m_mockGui, renderPlayerInfoCard(PLAYERINFO_PAYLOAD, _, _)) - .Times(Exactly(1)) - .WillOnce(Invoke([&wakePausePromise, context]( - const std::string& jsonPayload, - TemplateRuntimeObserverInterface::AudioPlayerInfo audioPlayerInfo, - avsCommon::avs::FocusState focusState) { - EXPECT_EQ(audioPlayerInfo.audioPlayerState, avsCommon::avs::PlayerActivity::PAUSED); - EXPECT_EQ(audioPlayerInfo.offset, context.offset); - wakePausePromise.set_value(); - })); - m_templateRuntime->onRenderPlayerCardsInfoChanged(avsCommon::avs::PlayerActivity::PAUSED, context); - wakePauseFuture.wait_for(TIMEOUT); - - // Test onAudioStopped() callback with 300ms offset - std::promise wakeStopPromise; - std::future wakeStopFuture = wakeStopPromise.get_future(); - context.offset = std::chrono::milliseconds(300); - EXPECT_CALL(*m_mockGui, renderPlayerInfoCard(PLAYERINFO_PAYLOAD, _, _)) - .Times(Exactly(1)) - .WillOnce(Invoke([&wakeStopPromise, context]( - const std::string& jsonPayload, - TemplateRuntimeObserverInterface::AudioPlayerInfo audioPlayerInfo, - avsCommon::avs::FocusState focusState) { - EXPECT_EQ(audioPlayerInfo.audioPlayerState, avsCommon::avs::PlayerActivity::STOPPED); - EXPECT_EQ(audioPlayerInfo.offset, context.offset); - wakeStopPromise.set_value(); - })); - m_templateRuntime->onRenderPlayerCardsInfoChanged(avsCommon::avs::PlayerActivity::STOPPED, context); - wakeStopFuture.wait_for(TIMEOUT); - - // Test onAudioFinished() callback with 400ms offset - std::promise wakeFinishPromise; - std::future wakeFinishFuture = wakeFinishPromise.get_future(); - context.offset = std::chrono::milliseconds(400); - EXPECT_CALL(*m_mockGui, renderPlayerInfoCard(PLAYERINFO_PAYLOAD, _, _)) - .Times(Exactly(1)) - .WillOnce(Invoke([&wakeFinishPromise, context]( - const std::string& jsonPayload, - TemplateRuntimeObserverInterface::AudioPlayerInfo audioPlayerInfo, - avsCommon::avs::FocusState focusState) { - EXPECT_EQ(audioPlayerInfo.audioPlayerState, avsCommon::avs::PlayerActivity::FINISHED); - EXPECT_EQ(audioPlayerInfo.offset, context.offset); - wakeFinishPromise.set_value(); - })); - m_templateRuntime->onRenderPlayerCardsInfoChanged(avsCommon::avs::PlayerActivity::FINISHED, context); - wakeFinishFuture.wait_for(TIMEOUT); -} - -/** - * Tests that if focus is changed to none, the clearTemplateCard() will be called. - */ -TEST_F(TemplateRuntimeTest, test_focusNone) { - // Create Directive. - auto attachmentManager = std::make_shared>(); - auto avsMessageHeader = std::make_shared(TEMPLATE.nameSpace, TEMPLATE.name, MESSAGE_ID); - std::shared_ptr directive = - AVSDirective::create("", avsMessageHeader, TEMPLATE_PAYLOAD, attachmentManager, ""); - - EXPECT_CALL(*m_mockGui, renderTemplateCard(TEMPLATE_PAYLOAD, _)) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnRenderTemplateCard)); - EXPECT_CALL(*m_mockDirectiveHandlerResult, setCompleted()) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnSetCompleted)); - EXPECT_CALL(*m_mockGui, clearTemplateCard()) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnClearTemplateCard)); - - m_templateRuntime->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); - m_templateRuntime->CapabilityAgent::handleDirective(MESSAGE_ID); - m_wakeSetCompletedFuture.wait_for(TIMEOUT); - m_wakeRenderTemplateCardFuture.wait_for(TIMEOUT); - m_templateRuntime->onFocusChanged(FocusState::NONE, MixingBehavior::MUST_STOP); - m_wakeClearTemplateCardFuture.wait_for(TIMEOUT); -} - -/** - * Tests that if displayCardCleared() is called, the clearTemplateCard() will not be called. - */ -TEST_F(TemplateRuntimeTest, test_displayCardCleared) { - // Create Directive. - auto attachmentManager = std::make_shared>(); - auto avsMessageHeader = std::make_shared(TEMPLATE.nameSpace, TEMPLATE.name, MESSAGE_ID); - std::shared_ptr directive = - AVSDirective::create("", avsMessageHeader, TEMPLATE_PAYLOAD, attachmentManager, ""); - - EXPECT_CALL(*m_mockGui, renderTemplateCard(TEMPLATE_PAYLOAD, _)) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnRenderTemplateCard)); - EXPECT_CALL(*m_mockDirectiveHandlerResult, setCompleted()) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnSetCompleted)); - EXPECT_CALL(*m_mockGui, clearTemplateCard()).Times(Exactly(0)); - EXPECT_CALL(*m_mockFocusManager, releaseChannel(_, _)).Times(Exactly(1)).WillOnce(InvokeWithoutArgs([this] { - auto releaseChannelSuccess = std::make_shared>(); - std::future returnValue = releaseChannelSuccess->get_future(); - m_templateRuntime->onFocusChanged(avsCommon::avs::FocusState::NONE, MixingBehavior::MUST_STOP); - releaseChannelSuccess->set_value(true); - wakeOnReleaseChannel(); - return returnValue; - })); - - m_templateRuntime->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); - m_templateRuntime->CapabilityAgent::handleDirective(MESSAGE_ID); - m_wakeSetCompletedFuture.wait_for(TIMEOUT); - m_wakeRenderTemplateCardFuture.wait_for(TIMEOUT); - m_templateRuntime->displayCardCleared(); - m_wakeReleaseChannelFuture.wait_for(TIMEOUT); -} - -/** - * Tests that if another displayCard event is sent before channel's focus is set to none, the state machine would - * transition to REACQUIRING state and acquireChannel again to display the card. - */ -TEST_F(TemplateRuntimeTest, test_reacquireChannel) { - // Create RenderPlayerInfo Directive and wait until PlayerInfo card is displayed. - auto attachmentManager = std::make_shared>(); - auto avsMessageHeader = std::make_shared(PLAYER_INFO.nameSpace, PLAYER_INFO.name, MESSAGE_ID); - std::shared_ptr directive = - AVSDirective::create("", avsMessageHeader, PLAYERINFO_PAYLOAD, attachmentManager, ""); - - EXPECT_CALL(*m_mockGui, renderPlayerInfoCard(PLAYERINFO_PAYLOAD, _, _)) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnRenderPlayerInfoCard)); - - RenderPlayerInfoCardsObserverInterface::Context context; - context.mediaProperties = m_mediaPropertiesFetcher; - context.audioItemId = AUDIO_ITEM_ID; - context.offset = TIMEOUT; - m_templateRuntime->onRenderPlayerCardsInfoChanged(avsCommon::avs::PlayerActivity::PLAYING, context); - m_templateRuntime->handleDirectiveImmediately(directive); - m_wakeRenderPlayerInfoCardFuture.wait_for(TIMEOUT); - - // Send displayCardCleared() to clear card, before setting focus to NONE, send another TemplateCard. - EXPECT_CALL(*m_mockFocusManager, releaseChannel(_, _)).Times(Exactly(1)).WillOnce(InvokeWithoutArgs([this] { - auto releaseChannelSuccess = std::make_shared>(); - std::future returnValue = releaseChannelSuccess->get_future(); - releaseChannelSuccess->set_value(true); - wakeOnReleaseChannel(); - return returnValue; - })); - m_templateRuntime->displayCardCleared(); - m_wakeReleaseChannelFuture.wait_for(TIMEOUT); - - // Create RenderTemplate Directive and see if channel is reacquire correctly. - auto avsMessageHeader1 = std::make_shared(TEMPLATE.nameSpace, TEMPLATE.name, MESSAGE_ID); - std::shared_ptr directive1 = - AVSDirective::create("", avsMessageHeader1, TEMPLATE_PAYLOAD, attachmentManager, ""); - - EXPECT_CALL(*m_mockGui, renderTemplateCard(TEMPLATE_PAYLOAD, _)) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnRenderTemplateCard)); - - m_templateRuntime->handleDirectiveImmediately(directive1); - m_templateRuntime->onFocusChanged(avsCommon::avs::FocusState::NONE, MixingBehavior::MUST_STOP); - m_wakeRenderTemplateCardFuture.wait_for(TIMEOUT); -} - -/** - * Test that we should skip rendering a player info card if the audio has already changed. - */ -TEST_F(TemplateRuntimeTest, testTimer_RenderPlayerInfoAfterPlayerActivityChanged) { - // Create Directive1. - const std::string messageId1{"messageId1"}; - auto attachmentManager = std::make_shared>(); - auto avsMessageHeader1 = std::make_shared(PLAYER_INFO.nameSpace, PLAYER_INFO.name, messageId1); - std::shared_ptr directive1 = - AVSDirective::create("", avsMessageHeader1, PLAYERINFO_PAYLOAD, attachmentManager, ""); - - RenderPlayerInfoCardsObserverInterface::Context context; - context.mediaProperties = m_mediaPropertiesFetcher; - context.audioItemId = AUDIO_ITEM_ID; - m_templateRuntime->onRenderPlayerCardsInfoChanged(avsCommon::avs::PlayerActivity::PLAYING, context); - - ::testing::InSequence s; - EXPECT_CALL(*m_mockFocusManager, acquireChannel(_, _, _)).WillOnce(Return(true)); - // Send a directive first to TemplateRuntime - EXPECT_CALL(*m_mockDirectiveHandlerResult, setCompleted()) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &TemplateRuntimeTest::wakeOnSetCompleted)); - m_templateRuntime->CapabilityAgent::preHandleDirective(directive1, std::move(m_mockDirectiveHandlerResult)); - m_templateRuntime->CapabilityAgent::handleDirective(messageId1); - m_wakeSetCompletedFuture.wait_for(TIMEOUT); - - // Test onAudioPlayed() callback with 100ms offset - std::promise wakePlayPromise; - std::future wakePlayFuture = wakePlayPromise.get_future(); - context.offset = std::chrono::milliseconds(100); - EXPECT_CALL(*m_mockGui, renderPlayerInfoCard(PLAYERINFO_PAYLOAD, _, _)).Times(0); - EXPECT_CALL(*m_mockFocusManager, releaseChannel(_, _)).Times(Exactly(1)).WillOnce(InvokeWithoutArgs([this] { - auto releaseChannelSuccess = std::make_shared>(); - std::future returnValue = releaseChannelSuccess->get_future(); - m_templateRuntime->onFocusChanged(avsCommon::avs::FocusState::NONE, MixingBehavior::MUST_STOP); - releaseChannelSuccess->set_value(true); - wakeOnReleaseChannel(); - return returnValue; - })); - - // Create Directive2. - const std::string messageId2{"messageId2"}; - auto avsMessageHeader2 = std::make_shared(PLAYER_INFO.nameSpace, PLAYER_INFO.name, messageId2); - auto mockDirectiveHandlerResult1 = make_unique>(); - std::shared_ptr directive2 = - AVSDirective::create("", avsMessageHeader1, PLAYERINFO_PAYLOAD, attachmentManager, ""); - m_templateRuntime->CapabilityAgent::preHandleDirective(directive2, std::move(m_mockDirectiveHandlerResult)); - m_templateRuntime->CapabilityAgent::handleDirective(messageId2); - m_wakeSetCompletedFuture.wait_for(TIMEOUT); - m_wakeRenderTemplateCardFuture.wait_for(TIMEOUT); - m_templateRuntime->displayCardCleared(); - m_wakeReleaseChannelFuture.wait_for(TIMEOUT); - context.audioItemId = AUDIO_ITEM_ID_1; - m_templateRuntime->onRenderPlayerCardsInfoChanged(avsCommon::avs::PlayerActivity::PLAYING, context); - m_templateRuntime->onFocusChanged(avsCommon::avs::FocusState::FOREGROUND, MixingBehavior::PRIMARY); - m_templateRuntime->displayCardCleared(); - m_wakeReleaseChannelFuture.wait_for(TIMEOUT); -} - -} // namespace test -} // namespace templateRuntime -} // namespace capabilityAgents -} // namespace alexaClientSDK diff --git a/CapabilityAgents/ToggleController/src/ToggleControllerAttributeBuilder.cpp b/CapabilityAgents/ToggleController/src/ToggleControllerAttributeBuilder.cpp index 53d367266e..1fd0540cc4 100644 --- a/CapabilityAgents/ToggleController/src/ToggleControllerAttributeBuilder.cpp +++ b/CapabilityAgents/ToggleController/src/ToggleControllerAttributeBuilder.cpp @@ -27,7 +27,7 @@ using namespace avsCommon::sdkInterfaces::toggleController; using namespace avsCommon::utils; /// String to identify log entries originating from this file. -static const std::string TAG{"ToggleControllerAttributeBuilder"}; +#define TAG "ToggleControllerAttributeBuilder" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/CapabilityAgents/ToggleController/src/ToggleControllerCapabilityAgent.cpp b/CapabilityAgents/ToggleController/src/ToggleControllerCapabilityAgent.cpp index 5d9de1abce..3b3cd91491 100644 --- a/CapabilityAgents/ToggleController/src/ToggleControllerCapabilityAgent.cpp +++ b/CapabilityAgents/ToggleController/src/ToggleControllerCapabilityAgent.cpp @@ -29,7 +29,7 @@ using namespace avsCommon::utils; using namespace avsCommon::utils::configuration; /// String to identify log entries originating from this file. -static const std::string TAG{"ToggleControllerCapabilityAgent"}; +#define TAG "ToggleControllerCapabilityAgent" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -202,7 +202,7 @@ void ToggleControllerCapabilityAgent::handleDirective(std::shared_ptrdirective->getName(); if (!info->directive->getEndpoint().hasValue() || @@ -242,7 +242,7 @@ void ToggleControllerCapabilityAgent::provideState( ACSDK_DEBUG5( LX(__func__).d("contextRequestToken", contextRequestToken).sensitive("stateProviderName", stateProviderName)); - m_executor.submit([this, stateProviderName, contextRequestToken] { + m_executor.execute([this, stateProviderName, contextRequestToken] { ACSDK_DEBUG5(LX("provideStateInExecutor")); executeProvideState(stateProviderName, contextRequestToken); }); @@ -316,7 +316,7 @@ void ToggleControllerCapabilityAgent::onToggleStateChanged( return; } - m_executor.submit([this, toggleState, cause] { + m_executor.execute([this, toggleState, cause] { m_contextManager->reportStateChange( CapabilityTag(NAMESPACE, TOGGLESTATE_PROPERTY_NAME, m_endpointId, m_instance), buildCapabilityState(toggleState), diff --git a/Captions/Component/src/CaptionsComponent.cpp b/Captions/Component/src/CaptionsComponent.cpp index d942de1485..e413ce1488 100644 --- a/Captions/Component/src/CaptionsComponent.cpp +++ b/Captions/Component/src/CaptionsComponent.cpp @@ -30,7 +30,7 @@ using namespace acsdkManufactory; using namespace acsdkShutdownManagerInterfaces; /// String to identify log entries originating from this file. -static const std::string TAG("CaptionsComponent"); +#define TAG "CaptionsComponent" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/Captions/Implementation/src/CaptionManager.cpp b/Captions/Implementation/src/CaptionManager.cpp index ab59778983..9982d8e77c 100644 --- a/Captions/Implementation/src/CaptionManager.cpp +++ b/Captions/Implementation/src/CaptionManager.cpp @@ -243,7 +243,7 @@ void CaptionManager::onParsed(const CaptionFrame& captionFrame) { // Attempt to split at the first available break between words before the requested wrap point. for (size_t i = wrapIndex; i > 0; i--) { if (' ' == lineText[i]) { - wrapIndex = i; + wrapIndex = static_cast(i); break; } } diff --git a/Captions/Implementation/src/LibwebvttParserAdapter.cpp b/Captions/Implementation/src/LibwebvttParserAdapter.cpp index 3ebf5fedde..a16b24a1aa 100644 --- a/Captions/Implementation/src/LibwebvttParserAdapter.cpp +++ b/Captions/Implementation/src/LibwebvttParserAdapter.cpp @@ -31,7 +31,7 @@ namespace captions { using namespace alexaClientSDK::avsCommon::utils::logger; /// String to identify log entries originating from this file. -static const std::string TAG("LibwebvttParserAdapter"); +#define TAG "LibwebvttParserAdapter" /// Return value indicating an error occurred during parsing. static const int WEBVTT_CALLBACK_ERROR = -1; diff --git a/Captions/Interface/src/CaptionLine.cpp b/Captions/Interface/src/CaptionLine.cpp index eae9ec339d..72d89623eb 100644 --- a/Captions/Interface/src/CaptionLine.cpp +++ b/Captions/Interface/src/CaptionLine.cpp @@ -29,7 +29,7 @@ using namespace alexaClientSDK::avsCommon; using namespace alexaClientSDK::avsCommon::utils::logger; /// String to identify log entries originating from this file. -static const std::string TAG("CaptionLine"); +#define TAG "CaptionLine" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/CertifiedSender/src/CertifiedSender.cpp b/CertifiedSender/src/CertifiedSender.cpp index 840d314729..e1972327b2 100644 --- a/CertifiedSender/src/CertifiedSender.cpp +++ b/CertifiedSender/src/CertifiedSender.cpp @@ -33,7 +33,7 @@ using namespace avsCommon::utils::configuration; using namespace avsCommon::utils::power; /// String to identify log entries originating from this file. -static const std::string TAG("CertifiedSender"); +#define TAG "CertifiedSender" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/CertifiedSender/src/SQLiteMessageStorage.cpp b/CertifiedSender/src/SQLiteMessageStorage.cpp index 697c571107..3bd3f3a377 100644 --- a/CertifiedSender/src/SQLiteMessageStorage.cpp +++ b/CertifiedSender/src/SQLiteMessageStorage.cpp @@ -31,7 +31,7 @@ using namespace avsCommon::utils::logger; using namespace alexaClientSDK::storage::sqliteStorage; /// String to identify log entries originating from this file. -static const std::string TAG("SQLiteMessageStorage"); +#define TAG "SQLiteMessageStorage" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/CertifiedSender/test/CertifiedSenderTest.cpp b/CertifiedSender/test/CertifiedSenderTest.cpp index 4dd454e707..99464d7df5 100644 --- a/CertifiedSender/test/CertifiedSenderTest.cpp +++ b/CertifiedSender/test/CertifiedSenderTest.cpp @@ -325,7 +325,7 @@ TEST_F(CertifiedSenderTest, testTimer_SendMessageWithURI) { /** * Tests if messages are re-submitted when the response is a re-tryable response. */ -TEST_F(CertifiedSenderTest, testSlow_retryableResponsesAreRetried) { +TEST_F(CertifiedSenderTest, testTimer_retryableResponsesAreRetried) { std::static_pointer_cast(m_certifiedSender) ->onConnectionStatusChanged( avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status::CONNECTED, @@ -376,7 +376,7 @@ TEST_F(CertifiedSenderTest, testSlow_retryableResponsesAreRetried) { /** * Tests if messages are discarded when the response is a non-retryable response. */ -TEST_F(CertifiedSenderTest, testSlow_nonRetryableResponsesAreNotRetried) { +TEST_F(CertifiedSenderTest, testTimer_nonRetryableResponsesAreNotRetried) { std::static_pointer_cast(m_certifiedSender) ->onConnectionStatusChanged( avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status::CONNECTED, diff --git a/CertifiedSender/test/Common/MockCertifiedSender.cpp b/CertifiedSender/test/Common/MockCertifiedSender.cpp index 0e49410fe2..373a248250 100644 --- a/CertifiedSender/test/Common/MockCertifiedSender.cpp +++ b/CertifiedSender/test/Common/MockCertifiedSender.cpp @@ -25,7 +25,6 @@ using namespace avsCommon::sdkInterfaces; using namespace avsCommon::sdkInterfaces::test; using namespace avsCommon::utils; using namespace certifiedSender; -using namespace certifiedSender::test; using namespace registrationManager; using namespace ::testing; diff --git a/ContextManager/include/ContextManager/ContextManager.h b/ContextManager/include/ContextManager/ContextManager.h index a00d0592f0..2fdb8d97f9 100644 --- a/ContextManager/include/ContextManager/ContextManager.h +++ b/ContextManager/include/ContextManager/ContextManager.h @@ -186,16 +186,11 @@ class ContextManager : public avsCommon::sdkInterfaces::ContextManagerInterface */ struct RequestTracker { /// The token returned by the @c MultiTimer. - avsCommon::utils::timing::MultiTimer::Token timerToken; + const avsCommon::utils::timing::MultiTimer::Token timerToken; /// The context requester. - std::shared_ptr contextRequester; + const std::shared_ptr contextRequester; /// If Reportable Properties should be skipped for this request. - bool skipReportableStateProperties; - - /** - * Default Constructor. - */ - RequestTracker(); + const bool skipReportableStateProperties; /** * Constructor. diff --git a/ContextManager/src/ContextManager.cpp b/ContextManager/src/ContextManager.cpp index 6631034a55..3abb219644 100644 --- a/ContextManager/src/ContextManager.cpp +++ b/ContextManager/src/ContextManager.cpp @@ -33,7 +33,7 @@ using namespace avsCommon::utils; using namespace avsCommon::utils::metrics; /// String to identify log entries originating from this file. -static const std::string TAG("ContextManager"); +#define TAG "ContextManager" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -87,12 +87,6 @@ static void NoopCallback() { // No-op } -ContextManager::RequestTracker::RequestTracker() : - timerToken{0}, - contextRequester{nullptr}, - skipReportableStateProperties{false} { -} - ContextManager::RequestTracker::RequestTracker( avsCommon::utils::timing::MultiTimer::Token timerToken, std::shared_ptr contextRequester, @@ -140,10 +134,10 @@ SetStateResult ContextManager::setState( const std::string& jsonState, const StateRefreshPolicy& refreshPolicy, const ContextRequestToken stateRequestToken) { - ACSDK_DEBUG5(LX(__func__).sensitive("capability", capabilityIdentifier)); + ACSDK_DEBUG5(LX(__func__).d("token", stateRequestToken).sensitive("capability", capabilityIdentifier)); if (EMPTY_TOKEN == stateRequestToken) { - m_executor.submit([this, capabilityIdentifier, jsonState, refreshPolicy] { + m_executor.execute([this, capabilityIdentifier, jsonState, refreshPolicy] { updateCapabilityState(capabilityIdentifier, jsonState, refreshPolicy); }); return SetStateResult::SUCCESS; @@ -169,7 +163,7 @@ SetStateResult ContextManager::setState( return SetStateResult::STATE_PROVIDER_NOT_REGISTERED; } - m_executor.submit([this, capabilityIdentifier, jsonState, refreshPolicy, stateRequestToken] { + m_executor.execute([this, capabilityIdentifier, jsonState, refreshPolicy, stateRequestToken] { updateCapabilityState(capabilityIdentifier, jsonState, refreshPolicy); if (jsonState.empty() && (StateRefreshPolicy::ALWAYS == refreshPolicy)) { ACSDK_ERROR(LX("setStateFailed") @@ -218,7 +212,7 @@ void ContextManager::reportStateChange( AlexaStateChangeCauseType cause) { ACSDK_DEBUG5(LX(__func__).sensitive("capability", capabilityIdentifier)); - m_executor.submit([this, capabilityIdentifier, capabilityState, cause] { + m_executor.execute([this, capabilityIdentifier, capabilityState, cause] { updateCapabilityState(capabilityIdentifier, capabilityState); std::lock_guard observerMutex{m_observerMutex}; for (auto& observer : m_observers) { @@ -233,7 +227,7 @@ void ContextManager::provideStateResponse( ContextRequestToken stateRequestToken) { ACSDK_DEBUG5(LX(__func__).sensitive("capability", capabilityIdentifier)); - m_executor.submit([this, capabilityIdentifier, capabilityState, stateRequestToken] { + m_executor.execute([this, capabilityIdentifier, capabilityState, stateRequestToken] { std::function contextAvailableCallback = NoopCallback; { std::lock_guard requestsLock{m_requestsMutex}; @@ -274,7 +268,7 @@ void ContextManager::provideStateUnavailableResponse( bool isEndpointUnreachable) { ACSDK_DEBUG5(LX(__func__).sensitive("capability", capabilityIdentifier)); - m_executor.submit([this, capabilityIdentifier, stateRequestToken, isEndpointUnreachable] { + m_executor.execute([this, capabilityIdentifier, stateRequestToken, isEndpointUnreachable] { std::function contextAvailableCallback = NoopCallback; std::function contextFailureCallback = NoopCallback; { @@ -359,59 +353,75 @@ ContextRequestToken ContextManager::getContextInternal( const std::string& endpointId, const std::chrono::milliseconds& timeout, bool bSkipReportableStateProperties) { - ACSDK_DEBUG5(LX(__func__).sensitive("endpointId", endpointId)); auto token = generateToken(); - m_executor.submit([this, contextRequester, endpointId, token, timeout, bSkipReportableStateProperties] { - auto timerToken = m_multiTimer->submitTask(timeout, [this, token] { - // Cancel request after timeout. - m_executor.submit([this, token] { - std::function contextFailureCallback = NoopCallback; - { - std::lock_guard lock{m_requestsMutex}; - contextFailureCallback = - getContextFailureCallbackLocked(token, ContextRequestError::STATE_PROVIDER_TIMEDOUT); - } - contextFailureCallback(); - }); + ACSDK_DEBUG5(LX(__func__) + .d("token", token) + .d("timeout(ms)", timeout.count()) + .d("skipReportableStateProperties", bSkipReportableStateProperties) + .sensitive("endpointId", endpointId)); + + auto timerToken = m_multiTimer->submitTask(timeout, [this, token] { + // Cancel request after timeout. + m_executor.execute([this, token] { + std::function contextFailureCallback = NoopCallback; + { + std::lock_guard lock{m_requestsMutex}; + contextFailureCallback = + getContextFailureCallbackLocked(token, ContextRequestError::STATE_PROVIDER_TIMEDOUT); + } + contextFailureCallback(); }); + }); - std::lock_guard requestsLock{m_requestsMutex}; - auto& requestEndpointId = endpointId.empty() ? m_defaultEndpointId : endpointId; - m_pendingRequests.emplace(token, RequestTracker(timerToken, contextRequester, bSkipReportableStateProperties)); - + m_executor.execute([this, contextRequester, endpointId, token, timerToken, bSkipReportableStateProperties] { std::function contextAvailableCallback = NoopCallback; { - std::lock_guard statesLock{m_endpointsStateMutex}; - - for (auto& capability : m_endpointsState[requestEndpointId]) { - auto stateInfo = capability.second; - auto stateProvider = capability.second.stateProvider; - - if (stateProvider) { - bool requestState = false; - if (stateInfo.legacyCapability && stateInfo.refreshPolicy != StateRefreshPolicy::NEVER) { - requestState = true; - } else if ( - !stateInfo.legacyCapability && stateProvider->canStateBeRetrieved() && - stateProvider->shouldQueryState()) { - if (stateProvider->hasReportableStateProperties()) { - /// Check if the reportable state properties should be skipped. - if (!bSkipReportableStateProperties) { + std::lock_guard requestsLock{m_requestsMutex}; + auto& requestEndpointId = endpointId.empty() ? m_defaultEndpointId : endpointId; + m_pendingRequests.emplace( + token, RequestTracker(timerToken, contextRequester, bSkipReportableStateProperties)); + + { + std::lock_guard statesLock{m_endpointsStateMutex}; + + auto endpointsStateIt = m_endpointsState.find(requestEndpointId); + if (m_endpointsState.end() == endpointsStateIt) { + ACSDK_WARN(LX(__func__) + .d("reason", "requestEndpointIdNotFound") + .d("token", token) + .sensitive("endpointId", requestEndpointId)); + } else { + for (auto& capability : endpointsStateIt->second) { + auto stateInfo = capability.second; + auto stateProvider = capability.second.stateProvider; + + if (stateProvider) { + bool requestState = false; + if (stateInfo.legacyCapability && stateInfo.refreshPolicy != StateRefreshPolicy::NEVER) { requestState = true; + } else if ( + !stateInfo.legacyCapability && stateProvider->canStateBeRetrieved() && + stateProvider->shouldQueryState()) { + if (stateProvider->hasReportableStateProperties()) { + /// Check if the reportable state properties should be skipped. + if (!bSkipReportableStateProperties) { + requestState = true; + } + } else { + requestState = true; + } } - } else { - requestState = true; - } - } - if (requestState) { - stateProvider->provideState(capability.first, token); - m_pendingStateRequest[token].emplace(capability.first); + if (requestState) { + m_pendingStateRequest[token].emplace(capability.first); + stateProvider->provideState(capability.first, token); + } + } } } - } - contextAvailableCallback = getContextAvailableCallbackIfReadyLocked(token, requestEndpointId); + contextAvailableCallback = getContextAvailableCallbackIfReadyLocked(token, requestEndpointId); + } } /// Callback method should be called outside the lock. contextAvailableCallback(); @@ -434,27 +444,36 @@ std::function ContextManager::getContextFailureCallbackLocked( m_pendingStateRequest.erase(requestToken); }}; - auto& request = m_pendingRequests[requestToken]; - if (!request.contextRequester) { + auto requestIt = m_pendingRequests.find(requestToken); + if (m_pendingRequests.end() == requestIt) { + ACSDK_ERROR(LX(__func__).d("result", "tokenNotFound").d("token", requestToken)); + return NoopCallback; + } + auto& request = requestIt->second; + std::shared_ptr contextRequester = request.contextRequester; + if (!contextRequester) { ACSDK_DEBUG0(LX(__func__).d("result", "nullRequester").d("token", requestToken)); return NoopCallback; } - for (auto& pendingState : m_pendingStateRequest[requestToken]) { - ACSDK_ERROR(LX(__func__) - .d("pendingStateProviderName", pendingState.name) - .d("pendingStateProviderNamespace", pendingState.nameSpace)); - auto metricName = STATE_PROVIDER_TIMEOUT_METRIC_PREFIX + pendingState.nameSpace; - recordMetric( - m_metricRecorder, - MetricEventBuilder{} - .setActivityName("CONTEXT_MANAGER-" + metricName) - .addDataPoint(DataPointCounterBuilder{}.setName(metricName).increment(1).build()) - .build()); + auto stateRequestIt = m_pendingStateRequest.find(requestToken); + if (m_pendingStateRequest.end() != stateRequestIt) { + for (auto& pendingState : stateRequestIt->second) { + ACSDK_ERROR(LX(__func__) + .d("pendingStateProviderName", pendingState.name) + .d("pendingStateProviderNamespace", pendingState.nameSpace)); + auto metricName = STATE_PROVIDER_TIMEOUT_METRIC_PREFIX + pendingState.nameSpace; + recordMetric( + m_metricRecorder, + MetricEventBuilder{} + .setActivityName("CONTEXT_MANAGER-" + metricName) + .addDataPoint(DataPointCounterBuilder{}.setName(metricName).increment(1).build()) + .build()); + } } - auto contextRequester = request.contextRequester; return [contextRequester, error, requestToken]() { if (contextRequester) { + ACSDK_ERROR(LX(__func__).d("reason", "invokeOnContextFailure").d("token", requestToken)); contextRequester->onContextFailure(error, requestToken); } }; @@ -463,10 +482,16 @@ std::function ContextManager::getContextFailureCallbackLocked( std::function ContextManager::getContextAvailableCallbackIfReadyLocked( unsigned int requestToken, const EndpointIdentifier& endpointId) { - auto& pendingStates = m_pendingStateRequest[requestToken]; - if (!pendingStates.empty()) { - ACSDK_DEBUG5(LX(__func__).d("result", "stateNotAvailableYet").d("pendingStates", pendingStates.size())); - return NoopCallback; + auto stateRequestIt = m_pendingStateRequest.find(requestToken); + if (m_pendingStateRequest.end() != stateRequestIt) { + auto& pendingStates = stateRequestIt->second; + if (!pendingStates.empty()) { + ACSDK_DEBUG5(LX(__func__) + .d("result", "stateNotAvailableYet") + .d("token", requestToken) + .d("pendingStates", pendingStates.size())); + return NoopCallback; + } } ACSDK_DEBUG5(LX(__func__).sensitive("endpointId", endpointId).d("token", requestToken)); @@ -480,8 +505,14 @@ std::function ContextManager::getContextAvailableCallbackIfReadyLocked( m_pendingStateRequest.erase(requestToken); }}; - auto& request = m_pendingRequests[requestToken]; - if (!request.contextRequester) { + auto requestIt = m_pendingRequests.find(requestToken); + if (m_pendingRequests.end() == requestIt) { + ACSDK_ERROR(LX(__func__).d("result", "tokenNotFound").d("token", requestToken)); + return NoopCallback; + } + auto& request = requestIt->second; + std::shared_ptr contextRequester = request.contextRequester; + if (!contextRequester) { ACSDK_ERROR( LX("getContextAvailableCallbackIfReadyLockedFailed").d("reason", "nullRequester").d("token", requestToken)); return NoopCallback; @@ -489,40 +520,44 @@ std::function ContextManager::getContextAvailableCallbackIfReadyLocked( AVSContext context; auto& requestEndpointId = endpointId.empty() ? m_defaultEndpointId : endpointId; - for (auto& capability : m_endpointsState[requestEndpointId]) { - auto stateProvider = capability.second.stateProvider; - auto stateInfo = capability.second; - bool addState = false; - - if (stateInfo.legacyCapability) { - // Ignore if the state is not available for legacy SOMETIMES refresh policy. - if ((stateInfo.refreshPolicy == StateRefreshPolicy::SOMETIMES) && !stateInfo.capabilityState.hasValue()) { - ACSDK_DEBUG5(LX(__func__).d("skipping state for legacy capabilityIdentifier", capability.first)); + auto endpointStateIt = m_endpointsState.find(requestEndpointId); + if (endpointStateIt != m_endpointsState.end()) { + for (auto& capability : endpointStateIt->second) { + auto stateProvider = capability.second.stateProvider; + auto stateInfo = capability.second; + bool addState = false; + + if (stateInfo.legacyCapability) { + // Ignore if the state is not available for legacy SOMETIMES refresh policy. + if ((stateInfo.refreshPolicy == StateRefreshPolicy::SOMETIMES) && + !stateInfo.capabilityState.hasValue()) { + ACSDK_DEBUG5(LX(__func__).d("skipping state for legacy capabilityIdentifier", capability.first)); + } else { + addState = true; + } } else { - addState = true; - } - } else { - if (stateProvider && stateProvider->canStateBeRetrieved()) { - /// Check if the reportable state properties should be skipped. - if (stateProvider->hasReportableStateProperties()) { - if (!request.skipReportableStateProperties) { + if (stateProvider && stateProvider->canStateBeRetrieved()) { + /// Check if the reportable state properties should be skipped. + if (stateProvider->hasReportableStateProperties()) { + if (!request.skipReportableStateProperties) { + addState = true; + } + } else { addState = true; } - } else { - addState = true; } } - } - if (addState) { - ACSDK_DEBUG5(LX(__func__).sensitive("addState", capability.first)); - context.addState(capability.first, stateInfo.capabilityState.value()); + if (addState) { + ACSDK_DEBUG5(LX(__func__).sensitive("addState", capability.first)); + context.addState(capability.first, stateInfo.capabilityState.value()); + } } } - auto contextRequester = request.contextRequester; return [contextRequester, context, endpointId, requestToken]() { if (contextRequester) { + ACSDK_DEBUG5(LX(__func__).d("reason", "invokeOnContextAvailable").d("token", requestToken)); contextRequester->onContextAvailable(endpointId, context, requestToken); } }; diff --git a/ContextManager/test/ContextManagerTest.cpp b/ContextManager/test/ContextManagerTest.cpp index 54a6b4b1da..de987cd5b8 100644 --- a/ContextManager/test/ContextManagerTest.cpp +++ b/ContextManager/test/ContextManagerTest.cpp @@ -32,7 +32,7 @@ using namespace avsCommon::avs; using namespace avsCommon::sdkInterfaces; /// String to identify log entries originating from this file. -static const std::string TAG("ContextManagerTest"); +#define TAG "ContextManagerTest" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/Diagnostics/src/AudioInjectorMicrophone.cpp b/Diagnostics/src/AudioInjectorMicrophone.cpp index 2da9ed25bb..d330c2e32c 100644 --- a/Diagnostics/src/AudioInjectorMicrophone.cpp +++ b/Diagnostics/src/AudioInjectorMicrophone.cpp @@ -24,7 +24,7 @@ using avsCommon::avs::AudioInputStream; using namespace avsCommon::utils::timing; /// String to identify log entries originating from this file. -static const std::string TAG("AudioInjectorMicrophone"); +#define TAG "AudioInjectorMicrophone" /// The timeout to use for writing to the SharedDataStream. static const std::chrono::milliseconds TIMEOUT_FOR_WRITING{500}; diff --git a/Diagnostics/src/DevicePropertyAggregator.cpp b/Diagnostics/src/DevicePropertyAggregator.cpp index f6a2001092..099a69622f 100644 --- a/Diagnostics/src/DevicePropertyAggregator.cpp +++ b/Diagnostics/src/DevicePropertyAggregator.cpp @@ -25,7 +25,7 @@ using namespace alexaClientSDK::avsCommon::sdkInterfaces; using namespace alexaClientSDK::avsCommon::utils; /// String to identify log entries originating from this file. -static const std::string TAG{"DevicePropertyAggregator"}; +#define TAG "DevicePropertyAggregator" /// String to identify invalid property value. static const std::string INVALID_VALUE{"INVALID"}; @@ -127,7 +127,7 @@ void DevicePropertyAggregator::onAuthStateChange( break; } - m_executor.submit([this, registered]() { + m_executor.execute([this, registered]() { m_asyncPropertyMap[DevicePropertyAggregatorInterface::REGISTRATION_STATUS] = toString(registered); }); } @@ -260,7 +260,7 @@ Optional DevicePropertyAggregator::getDeviceContextJson() { void DevicePropertyAggregator::onAlertStateChange(const AlertObserverInterface::AlertInfo& alertInfo) { ACSDK_DEBUG5(LX(__func__)); - m_executor.submit([this, alertInfo]() { + m_executor.execute([this, alertInfo]() { std::stringstream ss; ss << alertInfo.type << ":" << alertInfo.state; m_asyncPropertyMap[DevicePropertyAggregatorInterface::ALERT_TYPE_AND_STATE] = ss.str(); @@ -271,7 +271,7 @@ void DevicePropertyAggregator::onPlayerActivityChanged( PlayerActivity state, const AudioPlayerObserverInterface::Context& context) { ACSDK_DEBUG5(LX(__func__)); - m_executor.submit([this, state, context]() { + m_executor.execute([this, state, context]() { std::string playerActivityState = avsCommon::avs::playerActivityToString(state); m_asyncPropertyMap[DevicePropertyAggregatorInterface::AUDIO_PLAYER_STATE] = playerActivityState; m_asyncPropertyMap[DevicePropertyAggregatorInterface::CONTENT_ID] = context.audioItemId; @@ -280,7 +280,7 @@ void DevicePropertyAggregator::onPlayerActivityChanged( void DevicePropertyAggregator::onConnectionStatusChanged(const Status status, const ChangedReason reason) { ACSDK_DEBUG5(LX(__func__)); - m_executor.submit([this, status]() { + m_executor.execute([this, status]() { std::stringstream ss; ss << status; m_asyncPropertyMap[DevicePropertyAggregatorInterface::CONNECTION_STATE] = ss.str(); @@ -289,7 +289,7 @@ void DevicePropertyAggregator::onConnectionStatusChanged(const Status status, co void DevicePropertyAggregator::onSetIndicator(IndicatorState state) { ACSDK_DEBUG5(LX(__func__)); - m_executor.submit([this, state]() { + m_executor.execute([this, state]() { std::stringstream ss; ss << state; m_asyncPropertyMap[DevicePropertyAggregatorInterface::NOTIFICATION_INDICATOR] = ss.str(); @@ -302,7 +302,7 @@ void DevicePropertyAggregator::onNotificationReceived() { void DevicePropertyAggregator::onDialogUXStateChanged(DialogUXStateObserverInterface::DialogUXState newState) { ACSDK_DEBUG5(LX(__func__)); - m_executor.submit([this, newState]() { + m_executor.execute([this, newState]() { m_asyncPropertyMap[DevicePropertyAggregatorInterface::TTS_PLAYER_STATE] = DialogUXStateObserverInterface::stateToString(newState); }); @@ -313,7 +313,7 @@ void DevicePropertyAggregator::onSpeakerSettingsChanged( const ChannelVolumeInterface::Type& type, const SpeakerInterface::SpeakerSettings& settings) { ACSDK_DEBUG5(LX(__func__)); - m_executor.submit([this, type, settings]() { updateSpeakerSettingsInPropertyMap(type, settings); }); + m_executor.execute([this, type, settings]() { updateSpeakerSettingsInPropertyMap(type, settings); }); } void DevicePropertyAggregator::updateSpeakerSettingsInPropertyMap( @@ -338,7 +338,7 @@ void DevicePropertyAggregator::updateSpeakerSettingsInPropertyMap( void DevicePropertyAggregator::onRangeChanged(const RangeState& rangeState, const AlexaStateChangeCauseType cause) { ACSDK_DEBUG5(LX(__func__).d("range value", rangeState.value)); - m_executor.submit([this, rangeState]() { + m_executor.execute([this, rangeState]() { std::stringstream ss; ss << rangeState.value; m_asyncPropertyMap[DevicePropertyAggregatorInterface::RANGE_CONTROLLER_STATUS] = ss.str(); @@ -349,7 +349,7 @@ void DevicePropertyAggregator::onPowerStateChanged( const PowerState& powerState, const AlexaStateChangeCauseType cause) { ACSDK_DEBUG5(LX(__func__).d("power state", powerState.powerState)); - m_executor.submit([this, powerState]() { + m_executor.execute([this, powerState]() { std::stringstream ss; ss << powerState.powerState; m_asyncPropertyMap[DevicePropertyAggregatorInterface::POWER_CONTROLLER_STATUS] = ss.str(); diff --git a/Diagnostics/src/DeviceProtocolTracer.cpp b/Diagnostics/src/DeviceProtocolTracer.cpp index 4d84df6886..ab2500c335 100644 --- a/Diagnostics/src/DeviceProtocolTracer.cpp +++ b/Diagnostics/src/DeviceProtocolTracer.cpp @@ -30,7 +30,7 @@ using namespace avsCommon::utils::json; using namespace rapidjson; /// String to identify log entries originating from this file. -static const std::string TAG("DeviceProtocolTracer"); +#define TAG "DeviceProtocolTracer" /// Maximum number of trace messages stored in the device protocol tracer. static const unsigned int DEFAULT_MAX_MESSAGES = 1; diff --git a/Diagnostics/src/DiagnosticsUtils.cpp b/Diagnostics/src/DiagnosticsUtils.cpp index 8d6375f63f..0057e5dab1 100644 --- a/Diagnostics/src/DiagnosticsUtils.cpp +++ b/Diagnostics/src/DiagnosticsUtils.cpp @@ -23,7 +23,7 @@ namespace diagnostics { namespace utils { /// String to identify log entries originating from this file. -static const std::string TAG("DiagnosticsUtils"); +#define TAG "DiagnosticsUtils" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/Diagnostics/src/FileBasedAudioInjector.cpp b/Diagnostics/src/FileBasedAudioInjector.cpp index 8202c49d78..134c63f7d0 100644 --- a/Diagnostics/src/FileBasedAudioInjector.cpp +++ b/Diagnostics/src/FileBasedAudioInjector.cpp @@ -25,7 +25,7 @@ namespace alexaClientSDK { namespace diagnostics { /// String to identify log entries originating from this file. -static const std::string TAG("FileBasedAudioInjector"); +#define TAG "FileBasedAudioInjector" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/Endpoints/src/DefaultEndpointBuilder.cpp b/Endpoints/src/DefaultEndpointBuilder.cpp index 7f56656529..27ea6d8188 100644 --- a/Endpoints/src/DefaultEndpointBuilder.cpp +++ b/Endpoints/src/DefaultEndpointBuilder.cpp @@ -31,7 +31,7 @@ using namespace avsCommon::sdkInterfaces::endpoints; using namespace avsCommon::utils; /// String to identify log entries originating from this file. -static const std::string TAG("DefaultEndpointBuilder"); +#define TAG "DefaultEndpointBuilder" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/Endpoints/src/Endpoint.cpp b/Endpoints/src/Endpoint.cpp index 79a896e6ba..302be3294c 100644 --- a/Endpoints/src/Endpoint.cpp +++ b/Endpoints/src/Endpoint.cpp @@ -26,7 +26,7 @@ using namespace avsCommon::sdkInterfaces::endpoints; using namespace avsCommon::utils; /// String to identify log entries originating from this file. -static const std::string TAG("Endpoint"); +#define TAG "Endpoint" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/Endpoints/src/EndpointBuilder.cpp b/Endpoints/src/EndpointBuilder.cpp index ca2e6e29b2..f2e4eb182f 100644 --- a/Endpoints/src/EndpointBuilder.cpp +++ b/Endpoints/src/EndpointBuilder.cpp @@ -44,7 +44,7 @@ using namespace avsCommon::sdkInterfaces::endpoints; using namespace avsCommon::utils; /// String to identify log entries originating from this file. -static const std::string TAG("EndpointBuilder"); +#define TAG "EndpointBuilder" /** * Create a LogEntry using this file's TAG and the specified event string. diff --git a/Endpoints/src/EndpointRegistrationManager.cpp b/Endpoints/src/EndpointRegistrationManager.cpp index ba02676377..28ee17b4bc 100644 --- a/Endpoints/src/EndpointRegistrationManager.cpp +++ b/Endpoints/src/EndpointRegistrationManager.cpp @@ -25,7 +25,7 @@ namespace alexaClientSDK { namespace endpoints { /// String to identify log entries originating from this file. -static const std::string TAG("EndpointRegistrationManager"); +#define TAG "EndpointRegistrationManager" /** * Create a LogEntry using this file's TAG and the specified event string. @@ -117,7 +117,7 @@ std::future EndpointRegistratio return promise.get_future(); } - m_executor.submit([this, endpoint]() { executeRegisterEndpoint(endpoint); }); + m_executor.execute([this, endpoint]() { executeRegisterEndpoint(endpoint); }); auto& pending = m_pendingRegistrations[endpointId]; pending.first = std::move(endpoint); @@ -160,7 +160,7 @@ std::future EndpointRegistrationManag } auto endpoint = m_endpoints[endpointId]; - m_executor.submit( + m_executor.execute( [this, endpoint, endpointModificationData]() { executeUpdateEndpoint(endpoint, endpointModificationData); }); auto& pending = m_pendingUpdates[endpointId]; pending.first = std::move(endpoint); @@ -219,7 +219,7 @@ std::future EndpointRegistrat auto endpoint = m_endpoints[endpointId]; - m_executor.submit([this, endpoint]() { executeDeregisterEndpoint(endpoint); }); + m_executor.execute([this, endpoint]() { executeDeregisterEndpoint(endpoint); }); auto& pending = m_pendingDeregistrations[endpointId]; pending.first = std::move(endpoint); @@ -472,7 +472,7 @@ void EndpointRegistrationManager::onCapabilityRegistrationStatusChanged( const std::pair>& addedOrUpdatedEndpoints, const std::pair>& deletedEndpoints) { ACSDK_DEBUG5(LX(__func__)); - m_executor.submit([this, addedOrUpdatedEndpoints, deletedEndpoints] { + m_executor.execute([this, addedOrUpdatedEndpoints, deletedEndpoints] { updateAddedOrUpdatedEndpoints(addedOrUpdatedEndpoints); removeDeletedEndpoints(deletedEndpoints); }); diff --git a/Endpoints/test/EndpointRegistrationManagerTest.cpp b/Endpoints/test/EndpointRegistrationManagerTest.cpp index c2b0a35e7e..df8a8a85e3 100644 --- a/Endpoints/test/EndpointRegistrationManagerTest.cpp +++ b/Endpoints/test/EndpointRegistrationManagerTest.cpp @@ -113,6 +113,8 @@ void EndpointRegistrationManagerTest::SetUp() { void EndpointRegistrationManagerTest::TearDown() { m_manager.reset(); m_capabilitiesDelegate.reset(); + m_capabilitiesObserver.reset(); + m_registrationObserver.reset(); m_sequencer->shutdown(); m_sequencer.reset(); @@ -214,14 +216,14 @@ TEST_F(EndpointRegistrationManagerTest, test_registerEndpointSucceeds) { EXPECT_CALL(*m_sequencer, addDirectiveHandler(handler)).WillOnce(Return(true)); EXPECT_CALL(*m_capabilitiesDelegate, addOrUpdateEndpoint(_, configurations)).WillOnce(Return(true)); - // Check that register endpoint was enqueued. - auto result = m_manager->registerEndpoint(endpoint); - ASSERT_EQ(result.wait_for(std::chrono::milliseconds::zero()), std::future_status::timeout); - // Expect that the observer will be notified that the endpoint was registered. EXPECT_CALL(*m_registrationObserver, onEndpointRegistration(endpointId, _, RegistrationResult::SUCCEEDED)); EXPECT_CALL(*m_registrationObserver, onPendingEndpointRegistrationOrUpdate(endpointId, _, _)); + // Check that register endpoint was enqueued. + auto result = m_manager->registerEndpoint(endpoint); + ASSERT_EQ(result.wait_for(std::chrono::milliseconds::zero()), std::future_status::timeout); + m_capabilitiesObserver->onCapabilitiesStateChange( CapabilitiesDelegateObserverInterface::State::SUCCESS, CapabilitiesDelegateObserverInterface::Error::SUCCESS, @@ -251,11 +253,13 @@ TEST_F(EndpointRegistrationManagerTest, test_deregisterEndpointSucceeds) { EXPECT_CALL(*m_capabilitiesDelegate, addOrUpdateEndpoint(_, configurations)).WillOnce(Return(true)); EXPECT_CALL(*m_capabilitiesDelegate, deleteEndpoint(_, configurations)).WillOnce(Return(true)); + // set expectation + EXPECT_CALL(*m_registrationObserver, onEndpointRegistration(endpointId, _, RegistrationResult::SUCCEEDED)); + EXPECT_CALL(*m_registrationObserver, onPendingEndpointRegistrationOrUpdate(endpointId, _, _)); + // Add an endpoint so we can test delete. auto addResult = m_manager->registerEndpoint(endpoint); - EXPECT_CALL(*m_registrationObserver, onEndpointRegistration(endpointId, _, RegistrationResult::SUCCEEDED)); - EXPECT_CALL(*m_registrationObserver, onPendingEndpointRegistrationOrUpdate(endpointId, _, _)); m_capabilitiesObserver->onCapabilitiesStateChange( CapabilitiesDelegateObserverInterface::State::SUCCESS, CapabilitiesDelegateObserverInterface::Error::SUCCESS, @@ -312,11 +316,13 @@ TEST_F(EndpointRegistrationManagerTest, test_updateEndpointSucceeds) { EXPECT_CALL(*m_sequencer, addDirectiveHandler(handler1)).WillOnce(Return(true)); EXPECT_CALL(*m_sequencer, addDirectiveHandler(handler2)).WillOnce(Return(true)); EXPECT_CALL(*m_capabilitiesDelegate, addOrUpdateEndpoint(_, _)).WillRepeatedly(Return(true)); - // Add an endpoint so we can test update - auto addResult = m_manager->registerEndpoint(endpoint); EXPECT_CALL(*m_registrationObserver, onEndpointRegistration(endpointId, _, RegistrationResult::SUCCEEDED)); EXPECT_CALL(*m_registrationObserver, onPendingEndpointRegistrationOrUpdate(endpointId, _, _)); + + // Add an endpoint so we can test update + auto addResult = m_manager->registerEndpoint(endpoint); + m_capabilitiesObserver->onCapabilitiesStateChange( CapabilitiesDelegateObserverInterface::State::SUCCESS, CapabilitiesDelegateObserverInterface::Error::SUCCESS, @@ -330,9 +336,9 @@ TEST_F(EndpointRegistrationManagerTest, test_updateEndpointSucceeds) { EXPECT_CALL(*m_sequencer, addDirectiveHandler(addedHandler)).WillOnce(Return(true)); EXPECT_CALL(*m_sequencer, removeDirectiveHandler(handler2)).WillOnce(Return(true)); EXPECT_CALL(*endpoint, update(_)).WillOnce(Return(true)); - auto updateResult = m_manager->updateEndpoint(endpointId, std::make_shared(updatedData)); EXPECT_CALL(*m_registrationObserver, onEndpointUpdate(endpointId, _, UpdateResult::SUCCEEDED)); EXPECT_CALL(*m_registrationObserver, onPendingEndpointRegistrationOrUpdate(endpointId, _, _)); + auto updateResult = m_manager->updateEndpoint(endpointId, std::make_shared(updatedData)); m_capabilitiesObserver->onCapabilitiesStateChange( CapabilitiesDelegateObserverInterface::State::SUCCESS, CapabilitiesDelegateObserverInterface::Error::SUCCESS, @@ -417,15 +423,15 @@ TEST_F(EndpointRegistrationManagerTest, test_registerEndpointWhenCapabilityRegis EXPECT_CALL(*m_sequencer, removeDirectiveHandler(handler)).WillOnce(Return(true)); EXPECT_CALL(*m_capabilitiesDelegate, addOrUpdateEndpoint(_, configurations)).WillOnce(Return(true)); - // Check that register endpoint was enqueued. - auto result = m_manager->registerEndpoint(endpoint); - ASSERT_EQ(result.wait_for(std::chrono::milliseconds::zero()), std::future_status::timeout); - // Expect that the observer will be notified that the endpoint was registered. EXPECT_CALL( *m_registrationObserver, onEndpointRegistration(endpointId, _, RegistrationResult::CONFIGURATION_ERROR)); EXPECT_CALL(*m_registrationObserver, onPendingEndpointRegistrationOrUpdate(endpointId, _, _)); + // Check that register endpoint was enqueued. + auto result = m_manager->registerEndpoint(endpoint); + ASSERT_EQ(result.wait_for(std::chrono::milliseconds::zero()), std::future_status::timeout); + m_capabilitiesObserver->onCapabilitiesStateChange( CapabilitiesDelegateObserverInterface::State::FATAL_ERROR, CapabilitiesDelegateObserverInterface::Error::UNKNOWN_ERROR, @@ -716,9 +722,9 @@ TEST_F(EndpointRegistrationManagerTest, test_deregisterEndpointWhileUpdateInProg ASSERT_EQ(result.get(), RegistrationResult::SUCCEEDED); // Check that update endpoint enqueued. - auto updateResult = m_manager->updateEndpoint(endpointId, updatedData); EXPECT_CALL(*m_registrationObserver, onEndpointUpdate(endpointId, _, _)).Times(1); EXPECT_CALL(*m_registrationObserver, onPendingEndpointRegistrationOrUpdate(endpointId, _, _)).Times(1); + auto updateResult = m_manager->updateEndpoint(endpointId, updatedData); ASSERT_EQ(updateResult.wait_for(std::chrono::milliseconds::zero()), std::future_status::timeout); // Check that the deregistration fails. @@ -852,9 +858,9 @@ TEST_F(EndpointRegistrationManagerTest, test_updateEndpointWhileUpdateInProgress ASSERT_EQ(result.get(), RegistrationResult::SUCCEEDED); // Check that update endpoint enqueued. - auto deleteResult = m_manager->updateEndpoint(endpointId, updatedData); EXPECT_CALL(*m_registrationObserver, onEndpointUpdate(endpointId, _, _)).Times(1); EXPECT_CALL(*m_registrationObserver, onPendingEndpointRegistrationOrUpdate(endpointId, _, _)).Times(1); + auto deleteResult = m_manager->updateEndpoint(endpointId, updatedData); ASSERT_EQ(deleteResult.wait_for(std::chrono::milliseconds::zero()), std::future_status::timeout); // Check that the redundant update fails. diff --git a/Integration/AlexaClientSDKConfig.json b/Integration/AlexaClientSDKConfig.json index da213f9dde..d64f42a441 100644 --- a/Integration/AlexaClientSDKConfig.json +++ b/Integration/AlexaClientSDKConfig.json @@ -4,9 +4,6 @@ /// See https://developer.amazon.com/en-US/docs/alexa/avs-device-sdk/alexa-client-sdk-config-json.html /// to learn more about configuration options and default behaviours. { - "cblAuthDelegate":{ - "databaseFilePath":"${SDK_CBL_AUTH_DELEGATE_DATABASE_FILE_PATH}" - }, "deviceInfo":{ "deviceSerialNumber":"${SDK_CONFIG_DEVICE_SERIAL_NUMBER}", "clientId":"${SDK_CONFIG_CLIENT_ID}", @@ -27,6 +24,7 @@ "databaseFilePath":"${SDK_LWA_AUTHORIZATION_ADAPTER_DATABASE_FILE_PATH}" }, "speakerManagerCapabilityAgent":{ + "persistentStorage":true, "defaultSpeakerVolume":40, "defaultAlertsVolume":40, "restoreMuteState":true @@ -42,6 +40,12 @@ "locales":["en-US","en-GB","de-DE","en-IN","en-CA","ja-JP","en-AU","fr-FR","it-IT","es-ES","es-MX","fr-CA", "es-US", "hi-IN", "pt-BR", "ar-SA"], "defaultLocale":"en-US", + "davs": { + "artifactDirectory": "${SDK_DAVS_ARTIFACT_DIR}", + "segmentId": "${SDK_DAVS_SEGMENT_ID}", + "artifactBudgetSizeInMB": 100, + "pollingIntervalInSeconds": 0 + }, "localeCombinations":[ ["en-US", "es-US"], ["es-US", "en-US"], @@ -58,7 +62,21 @@ ["en-IN", "hi-IN"], ["hi-IN", "en-IN"], ["fr-CA", "en-CA"], - ["en-CA", "fr-CA"] + ["en-CA", "fr-CA"], + ["en-GB", "fr-FR"], + ["fr-FR", "en-GB"], + ["en-GB", "es-ES"], + ["es-ES", "en-GB"], + ["en-GB", "de-DE"], + ["de-DE", "en-GB"], + ["en-GB", "it-IT"], + ["it-IT", "en-GB"], + ["hi-IN", "en-US"], + ["en-US", "hi-IN"], + ["pt-BR", "en-US"], + ["en-US", "pt-BR"], + ["es-MX", "en-US"], + ["en-US", "es-MX"] ], "defaultTimezone":"America/Vancouver" }, @@ -75,9 +93,6 @@ "agentString": "CQCAFYNYDC" }, "sampleApp": { - "displayCardsSupported":true, - "sensory": { - "modelFilePath": "${SDK_SENSORY_MODEL_FILE_PATH}" - } + "displayCardsSupported":true } } diff --git a/Integration/CMakeLists.txt b/Integration/CMakeLists.txt deleted file mode 100644 index 78d41e5378..0000000000 --- a/Integration/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -cmake_minimum_required(VERSION 3.1 FATAL_ERROR) -project(Integration LANGUAGES CXX) - -include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) - -add_subdirectory("src") -add_subdirectory("test") diff --git a/Integration/include/Integration/ACLTestContext.h b/Integration/include/Integration/ACLTestContext.h deleted file mode 100644 index c01ec0165b..0000000000 --- a/Integration/include/Integration/ACLTestContext.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_ACLTESTCONTEXT_H_ -#define ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_ACLTESTCONTEXT_H_ - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "Integration/AuthDelegateTestContext.h" -#include "Integration/ConnectionStatusObserver.h" -#include "Integration/SDKTestContext.h" - -namespace alexaClientSDK { -namespace integration { -namespace test { - -/** - * Class providing lifecycle management of resources needed for testing ACL or functionality that - * requires ACL for testing. - */ -class ACLTestContext { -public: - /** - * Create an ACLTestContext. - * - * @note Only one instance of this class should exist at a time - but it is okay (and expected) - * that multiple instances of this class will be created (and destroyed) during one execution - * of the application using this class. - * - * Creating an instance of this class provides: - *
  • Initialization of the @c Alexa @c Client @c SDK (includes @ libcurl and @c ConfigurationNode.
  • - *
  • A @c CustomerDataManager instance.
  • - *
  • An @c AuthDelegateInterface instance.
  • - *
  • An @c AttachmentManager instance.
  • - *
  • A @c ConnectionStatusObserver instance.
  • - *
  • A @c ContextManager instance.
  • - *
  • Initialization of @c PostConnect.
  • - * - * @param filePath The path to a config file. - * @param overlay A @c JSON string containing values to overlay on the contents of the configuration file. - * @return An ACLTestContext instance or nullptr if the operation failed. - */ - static std::unique_ptr create(const std::string& filePath, const std::string& overlay = ""); - - /** - * Destructor. de-initializes all resources acquired during construction. - */ - ~ACLTestContext(); - - /** - * Get the instance of @c AuthDelegateInterface to use for the test. - * - * @return The instance of @c AuthDelegateInterface to use for the test. - */ - std::shared_ptr getAuthDelegate() const; - - /** - * Get the instance of @c CustomerDataManager to use for the test. - * - * @return The instance of @c CustomerDataManager to use for the test. - */ - std::shared_ptr getCustomerDataManager() const; - - /** - * Get the instance of @c AttachmentManager to use for the test. - * - * @return The instance of @c AttachmentManager to use for the test. - */ - std::shared_ptr getAttachmentManager() const; - - /** - * Get the @c MessageRouter instance to use for the test. - * - * @return The @c MessageRouter instance to use for the test. - */ - std::shared_ptr getMessageRouter() const; - - /** - * Get the @c ConnectionStatusObserver instance to use for the test. - * - * @return The @c ConnectionStatusObserver instance to use for the test. - */ - std::shared_ptr getConnectionStatusObserver() const; - - /** - * Get the @c ContextManager instance to use for the test. - * - * @return The @c ContextManager instance to use for the test. - */ - std::shared_ptr getContextManager() const; - - /** - * Wait for the @c ConnectionStatusObserver to be notified that the client has successfully connected to @c AVS. - */ - void waitForConnected(); - - /** - * Wait for the @c ConnectionStatusObserver to be notified that the client has successfully disconnected - * from @c AVS. - */ - void waitForDisconnected(); - -private: - /** - * Constructor. - * - * @param filePath The path to a config file. - * @param overlay A @c JSON string containing values to overlay on the contents of the configuration file. - */ - ACLTestContext(const std::string& filePath, const std::string& overlay = ""); - - /// Provide and AuthDelegate implementation suitable for testing. - std::unique_ptr m_authDelegateTestContext; - - /// The Object to use to manage attachments. - std::shared_ptr m_attachmentManager; - - /// Object that routs messages from @c AVS. - std::shared_ptr m_messageRouter; - - /// Object to monitor the status of the connection with @c AVS. - std::shared_ptr m_connectionStatusObserver; - - /// Object to acquire SDK context. - std::shared_ptr m_contextManager; -}; - -} // namespace test -} // namespace integration -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_ACLTESTCONTEXT_H_ diff --git a/Integration/include/Integration/AipStateObserver.h b/Integration/include/Integration/AipStateObserver.h deleted file mode 100644 index 38378b7ccd..0000000000 --- a/Integration/include/Integration/AipStateObserver.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_AIPSTATEOBSERVER_H_ -#define ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_AIPSTATEOBSERVER_H_ - -#include -#include -#include -#include - -#include - -namespace alexaClientSDK { -namespace integration { - -class AipStateObserver : public avsCommon::sdkInterfaces::AudioInputProcessorObserverInterface { -public: - AipStateObserver(); - void onStateChanged(avsCommon::sdkInterfaces::AudioInputProcessorObserverInterface::State newState) override; - bool checkState( - const avsCommon::sdkInterfaces::AudioInputProcessorObserverInterface::State expectedState, - const std::chrono::seconds duration = std::chrono::seconds(10)); - avsCommon::sdkInterfaces::AudioInputProcessorObserverInterface::State waitForNext( - const std::chrono::seconds duration); - -private: - avsCommon::sdkInterfaces::AudioInputProcessorObserverInterface::State m_state; - std::mutex m_mutex; - std::condition_variable m_wakeTrigger; - std::deque m_queue; -}; - -} // namespace integration -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_AIPSTATEOBSERVER_H_ diff --git a/Integration/include/Integration/AuthDelegateTestContext.h b/Integration/include/Integration/AuthDelegateTestContext.h deleted file mode 100644 index 139c053580..0000000000 --- a/Integration/include/Integration/AuthDelegateTestContext.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_AUTHDELEGATETESTCONTEXT_H_ -#define ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_AUTHDELEGATETESTCONTEXT_H_ - -#include -#include - -#include - -#include -#include -#include -#ifdef ACSDK_ACS_UTILS -#include -#else -#include -#endif -#include -#include - -#include "Integration/ConnectionStatusObserver.h" -#include "Integration/SDKTestContext.h" - -namespace alexaClientSDK { -namespace integration { -namespace test { - -/** - * Class providing lifecycle management of resources needed for testing instances of AuthDelegateInterface or - * functionality that requires such instances for testing. - */ -class AuthDelegateTestContext { -public: - /** - * Create an AuthDelegateTestContext - * - * @note Only one instance of this class should exist at a time - but it is okay (and expected) - * that multiple instances of this class will be created (and destroyed) during one execution - * of the application using this class. - * - * Creating an instance of this class provides: - *
  • A @c CustomerDataManager instance.
  • - *
  • An @c AuthDelegateInterface instance.
  • - * - * @param filePath The path to a config file. - * @param overlay A @c JSON string containing values to overlay on the contents of the configuration file. - */ - static std::unique_ptr create( - const std::string& filePath, - const std::string& overlay = ""); - - /** - * Destructor. de-initializes all resources acquired during construction. - */ - ~AuthDelegateTestContext(); - - /** - * Determine whether or not this instance was properly initialized. - * - * @return Whether or not this instance was properly initialized. - */ - bool isValid() const; - - /** - * Get the instance of @c AuthDelegateInterface to use for the test. - * - * @return The instance of @c AuthDelegateInterface to use for the test. - */ - std::shared_ptr getAuthDelegate() const; - - /** - * Get the instance of @c CustomerDataManager to use for the test. - * - * @return The instance of @c CustomerDataManager to use for the test. - */ - std::shared_ptr getCustomerDataManager() const; - -private: -#ifdef ACSDK_ACS_UTILS - class AuthRequester : public acsdkffsAuthDelegate::FFSAuthRequesterInterface { - public: - void onRequestFFS() override; - }; -#else - /** - * Implementation of @c CBLAuthRequesterInterface used to detect the case where the user - * still needs to authorize access to @c AVS. - */ - class AuthRequester : public authorization::cblAuthDelegate::CBLAuthRequesterInterface { - public: - /// @name CBLAuthRequesterInterface methods - /// @{ - void onRequestAuthorization(const std::string& url, const std::string& code) override; - void onCheckingForAuthorization() override; - /// @} - }; -#endif - /** - * Constructor. - * - * @param filePath The path to a config file. - * @param overlay A @c JSON string containing values to overlay on the contents of the configuration file. - */ - AuthDelegateTestContext(const std::string& filePath, const std::string& overlay = ""); - - /// Provide and SDK initialization suitable for testing. - std::unique_ptr m_sdkTestContext; - - /// The AuthDelegate to use for authorizing with LWA and AVS. - std::shared_ptr m_authDelegate; - - /// Object to manage customer specific data. - std::shared_ptr m_customerDataManager; -}; - -} // namespace test -} // namespace integration -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_AUTHDELEGATETESTCONTEXT_H_ diff --git a/Integration/include/Integration/AuthObserver.h b/Integration/include/Integration/AuthObserver.h deleted file mode 100644 index a0c4ed4e4e..0000000000 --- a/Integration/include/Integration/AuthObserver.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_AUTHOBSERVER_H_ -#define ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_AUTHOBSERVER_H_ - -#include -#include -#include - -#include - -namespace alexaClientSDK { -namespace integration { - -class AuthObserver : public avsCommon::sdkInterfaces::AuthObserverInterface { -public: - AuthObserver(); - void onAuthStateChange( - const avsCommon::sdkInterfaces::AuthObserverInterface::State, - const avsCommon::sdkInterfaces::AuthObserverInterface::Error = - avsCommon::sdkInterfaces::AuthObserverInterface::Error::SUCCESS) override; - AuthObserverInterface::State getAuthState() const; - bool waitFor( - const avsCommon::sdkInterfaces::AuthObserverInterface::State, - const std::chrono::seconds = std::chrono::seconds(20)); - -private: - avsCommon::sdkInterfaces::AuthObserverInterface::State m_authState; - avsCommon::sdkInterfaces::AuthObserverInterface::Error m_authError; - std::mutex m_mutex; - std::condition_variable m_wakeTrigger; -}; - -} // namespace integration -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_AUTHOBSERVER_H_ diff --git a/Integration/include/Integration/ClientMessageHandler.h b/Integration/include/Integration/ClientMessageHandler.h deleted file mode 100644 index 233b7ea186..0000000000 --- a/Integration/include/Integration/ClientMessageHandler.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_CLIENTMESSAGEHANDLER_H_ -#define ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_CLIENTMESSAGEHANDLER_H_ - -#include -#include -#include -#include - -#include - -#include - -namespace alexaClientSDK { -namespace integration { - -using namespace alexaClientSDK::avsCommon::sdkInterfaces; - -/// Minimal implementation of a message observer for integration tests. -class ClientMessageHandler : public MessageObserverInterface { -public: - /// Constructor. - ClientMessageHandler(std::shared_ptr attachmentManager); - - /** - * Implementation of the interface's receive function. - * - * For the purposes of these integration tests, this function simply logs and counts messages. - */ - void receive(const std::string& contextId, const std::string& message) override; - - /** - * Wait for a message to be received. - * - * This function waits for a specified number of seconds for a message to arrive. - * @param duration Number of seconds to wait before giving up. - * @return true if a message was received within the specified duration, else false. - */ - bool waitForNext(const std::chrono::seconds duration = std::chrono::seconds(2)); - -private: - /// Mutex to protect m_count. - std::mutex m_mutex; - /// Trigger to wake up waitForNext calls. - std::condition_variable m_wakeTrigger; - /// Count of received messages that have not been waited on. - unsigned int m_count; - - std::shared_ptr m_attachmentManager; -}; - -} // namespace integration -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_CLIENTMESSAGEHANDLER_H_ diff --git a/Integration/include/Integration/ConnectionStatusObserver.h b/Integration/include/Integration/ConnectionStatusObserver.h deleted file mode 100644 index db81005f0f..0000000000 --- a/Integration/include/Integration/ConnectionStatusObserver.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_CONNECTIONSTATUSOBSERVER_H_ -#define ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_CONNECTIONSTATUSOBSERVER_H_ - -#include -#include -#include -#include -#include - -namespace alexaClientSDK { -namespace integration { - -/** - * The class implements ConnectionStatusObserverInterface for testing. - */ -class ConnectionStatusObserver : public avsCommon::sdkInterfaces::ConnectionStatusObserverInterface { -public: - /** - * ConnectionStatusObserver constructor. - */ - ConnectionStatusObserver(); - - void onConnectionStatusChanged( - const avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status connectionStatus, - const avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::ChangedReason reason) override; - /** - * The utility function to get the connection status. - * @return Status The @c connectionStatus for the connection. - */ - avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status getConnectionStatus() const; - - /** - * Function to allow waiting for an expected status when a connection or disconnection is done. - * @param connectionStatus The expected connection status for which the waiting is done. - * @param duration The maximum time waiting for the expected connectionStatus. - * @return true if expected connectionStatus is received within @c duration else false. - */ - bool waitFor( - const avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status connectionStatus, - const std::chrono::seconds duration = std::chrono::seconds(15)); - - /** - * Function to check if the connection is broken due to Server side Disconnect. - * @return true if the disconnect happens due to SERVER_SIDE_DISCONNECT else false. - */ - bool checkForServerSideDisconnect(); - -private: - /// Mutex used internally to enforce thread safety and serialize read/write access to @c m_statusChanges. - mutable std::mutex m_mutex; - /// The cv used when waiting for a particular status of a connection - std::condition_variable m_wakeTrigger; - /// The queue of values of the pair (Connection status, ChangedReason) throughout the connection. - std::deque> - m_statusChanges; -}; - -} // namespace integration -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_CONNECTIONSTATUSOBSERVER_H_ diff --git a/Integration/include/Integration/JsonHeader.h b/Integration/include/Integration/JsonHeader.h deleted file mode 100644 index ae736d0cb5..0000000000 --- a/Integration/include/Integration/JsonHeader.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_JSONHEADER_H_ -#define ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_JSONHEADER_H_ - -#include - -// Todo ACSDK-443: Move the JSON to text file -/// This is a basic synchronize JSON message which may be used to initiate a connection with AVS. -// clang-format off -static const std::string SYNCHRONIZE_STATE_JSON = - "{" - "\"context\":[{" - "\"header\":{" - "\"name\":\"SpeechState\"," - "\"namespace\":\"SpeechSynthesizer\"" - "}," - "\"payload\":{" - "\"playerActivity\":\"FINISHED\"," - "\"offsetInMilliseconds\":0," - "\"token\":\"\"" - "}" - "}]," - "\"event\":{" - "\"header\":{" - "\"messageId\":\"00000000-0000-0000-0000-000000000000\"," - "\"name\":\"SynchronizeState\"," - "\"namespace\":\"System\"" - "}," - "\"payload\":{" - "}" - "}" - "}"; -// clang-format on - -#endif // ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_JSONHEADER_H_ diff --git a/Integration/include/Integration/ObservableMessageRequest.h b/Integration/include/Integration/ObservableMessageRequest.h deleted file mode 100644 index 8f72cda42b..0000000000 --- a/Integration/include/Integration/ObservableMessageRequest.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_OBSERVABLEMESSAGEREQUEST_H_ -#define ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_OBSERVABLEMESSAGEREQUEST_H_ - -#include -#include -#include -#include - -#include - -namespace alexaClientSDK { -namespace integration { - -class ObservableMessageRequest : public avsCommon::avs::MessageRequest { -public: - /** - * Constructor. - */ - ObservableMessageRequest( - const std::string& jsonContent, - std::shared_ptr attachmentReader = nullptr); - - void sendCompleted(avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status status) override; - - void exceptionReceived(const std::string& exceptionMessage) override; - - /** - * Utility function to get the status once the message has been sent. - */ - avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status getSendMessageStatus() const; - - /** - * Function to allow waiting for a particular status back from the component sending the message to AVS. - */ - bool waitFor( - const avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status, - const std::chrono::seconds = std::chrono::seconds(10)); - /// Function indicating if sendCompleted has been called - bool hasSendCompleted(); - /// Function indicating if exceptionReceived has been called - bool wasExceptionReceived(); - -private: - /// The status of whether the message was sent to AVS ok. - avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status m_sendMessageStatus; - /// Mutex used internally to enforce thread safety. - mutable std::mutex m_mutex; - /// The cv used when waiting for a particular status of a message being sent. - std::condition_variable m_wakeTrigger; - /// The variable that gets set when send is completed. - std::atomic m_sendCompleted; - /// The variable that gets set when exception is received. - std::atomic m_exceptionReceived; -}; - -} // namespace integration -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_OBSERVABLEMESSAGEREQUEST_H_ diff --git a/Integration/include/Integration/SDKTestContext.h b/Integration/include/Integration/SDKTestContext.h deleted file mode 100644 index 013e92bf82..0000000000 --- a/Integration/include/Integration/SDKTestContext.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_SDKTESTCONTEXT_H_ -#define ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_SDKTESTCONTEXT_H_ - -#include -#include - -namespace alexaClientSDK { -namespace integration { -namespace test { - -/** - * Class providing lifecycle management of resources needed for testing the @c Alexa @c Client @c SDK. - */ -class SDKTestContext { -public: - /** - * Create an SDKTestContext - * - * @note Only one instance of this class should exist at a time - but it is okay (and expected) - * that multiple instances of this class will be created (and destroyed) during one execution - * of the application using this class. - * - * Creating an instance of this class provides initialization of the @c Alexa @c Client @c SDK - * (which includes initialization of @ libcurl and @c ConfigurationNode. - * - * @param filePath The path to a config file. - * @param overlay A @c JSON string containing values to overlay on the contents of the configuration file. - * @return An SDKTestContext instance or nullptr if the operation failed. - */ - static std::unique_ptr create(const std::string& filePath, const std::string& overlay = ""); - - /** - * Destructor de-initializes the @c Alexa @c Client @c SDK. - */ - ~SDKTestContext(); - -private: - /** - * Constructor. - * - * @param filePath The path to a config file. - * @param overlay A @c JSON string containing values to overlay on the contents of the configuration file. - */ - SDKTestContext(const std::string& filePath, const std::string& overlay); -}; - -} // namespace test -} // namespace integration -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_SDKTESTCONTEXT_H_ diff --git a/Integration/include/Integration/TestAlertObserver.h b/Integration/include/Integration/TestAlertObserver.h deleted file mode 100644 index b0f9c39d7a..0000000000 --- a/Integration/include/Integration/TestAlertObserver.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_TESTALERTOBSERVER_H_ -#define ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_TESTALERTOBSERVER_H_ - -#include -#include -#include -#include -#include - -#include - -namespace alexaClientSDK { -namespace integration { -namespace test { - -class TestAlertObserver : public acsdkAlertsInterfaces::AlertObserverInterface { -public: - void onAlertStateChange(const AlertObserverInterface::AlertInfo& alertInfo) override; - - class changedAlert { - public: - State state; - }; - - changedAlert waitForNext(const std::chrono::seconds duration); - -private: - std::mutex m_mutex; - std::condition_variable m_wakeTrigger; - std::deque m_queue; - State currentState; -}; - -} // namespace test -} // namespace integration -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_TESTALERTOBSERVER_H_ diff --git a/Integration/include/Integration/TestDirectiveHandler.h b/Integration/include/Integration/TestDirectiveHandler.h deleted file mode 100644 index 604bca98bc..0000000000 --- a/Integration/include/Integration/TestDirectiveHandler.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_TESTDIRECTIVEHANDLER_H_ -#define ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_TESTDIRECTIVEHANDLER_H_ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "AVSCommon/SDKInterfaces/DirectiveHandlerInterface.h" -#include "AVSCommon/AVS/Attachment/AttachmentManager.h" -#include "AVSCommon/Utils/JSON/JSONUtils.h" - -using namespace alexaClientSDK::avsCommon; - -namespace alexaClientSDK { -namespace integration { -namespace test { - -/** - * TestDirectiveHandler is a mock of the @c DirectiveHandlerInterface and allows tests - * to wait for invocations upon those interfaces and inspect the parameters of those invocations. - */ -class TestDirectiveHandler : public avsCommon::sdkInterfaces::DirectiveHandlerInterface { -public: - /** - * Constructor. - * - * @param config The @c avsCommon::avs::DirectiveHandlerConfiguration for the directive handler for registering - * with a directive sequencer. - */ - TestDirectiveHandler(avsCommon::avs::DirectiveHandlerConfiguration config); - - void handleDirectiveImmediately(std::shared_ptr directive) override; - - void preHandleDirective( - std::shared_ptr directive, - std::unique_ptr result) override; - - bool handleDirective(const std::string& messageId) override; - - void cancelDirective(const std::string& messageId) override; - - avsCommon::avs::DirectiveHandlerConfiguration getConfiguration() const override; - - void onDeregistered() override; - - /** - * Class defining the parameters to calls to the mocked interfaces. - */ - class DirectiveParams { - public: - /** - * Constructor. - */ - DirectiveParams(); - - /** - * Return whether this DirectiveParams is of type 'UNSET'. - * - * @return Whether this DirectiveParams is of type 'UNSET'. - */ - bool isUnset() const { - return Type::UNSET == type; - } - - /** - * Return whether this DirectiveParams is of type 'HANDLE_IMMEDIATELY'. - * - * @return Whether this DirectiveParams is of type 'HANDLE_IMMEDIATELY'. - */ - bool isHandleImmediately() const { - return Type::HANDLE_IMMEDIATELY == type; - } - - /** - * Return whether this DirectiveParams is of type 'PREHANDLE'. - * - * @return Whether this DirectiveParams is of type 'PREHANDLE'. - */ - bool isPreHandle() const { - return Type::PREHANDLE == type; - } - - /** - * Return whether this DirectiveParams is of type 'HANDLE'. - * - * @return Whether this DirectiveParams is of type 'HANDLE'. - */ - bool isHandle() const { - return Type::HANDLE == type; - } - - /** - * Return whether this DirectiveParams is of type 'CANCEL'. - * - * @return Whether this DirectiveParams is of type 'CANCEL'. - */ - bool isCancel() const { - return Type::CANCEL == type; - } - - /** - * Return whether this DirectiveParams is of type 'TIMEOUT'. - * - * @return Whether this DirectiveParams is of type 'TIMEOUT'. - */ - bool isTimeout() const { - return Type::TIMEOUT == type; - } - - // Enum for the way the directive was passed to DirectiveHandler. - enum class Type { - // Not yet set. - UNSET, - // Set when handleDirectiveImmediately is called. - HANDLE_IMMEDIATELY, - // Set when preHandleDirective is called. - PREHANDLE, - // Set when handleDirective is called. - HANDLE, - // Set when cancelDirective is called. - CANCEL, - // Set when waitForNext times out waiting for a directive. - TIMEOUT - }; - - // Type of how the directive was passed to DirectiveHandler. - Type type; - // AVSDirective passed from the Directive Sequencer to the DirectiveHandler. - std::shared_ptr directive; - // DirectiveHandlerResult to inform the Directive Sequencer a directive has either successfully or - // unsuccessfully handled. - std::shared_ptr result; - }; - - /** - * Function to retrieve the next DirectiveParams in the test queue or time out if the queue is empty. Takes a - * duration in seconds to wait before timing out. - */ - DirectiveParams waitForNext(const std::chrono::seconds duration); - -private: - /// Mutex to protect m_queue. - std::mutex m_mutex; - /// Trigger to wake up waitForNext calls. - std::condition_variable m_wakeTrigger; - /// Queue of received directives that have not been waited on. - std::deque m_queue; - /// map of message IDs to result handlers. - std::unordered_map> - m_results; - /// map of message IDs to result handlers. - std::unordered_map> m_directives; - /// The @c avsCommon::avs::DirectiveHandlerConfiguration of the handler. - avsCommon::avs::DirectiveHandlerConfiguration m_configuration; -}; -} // namespace test -} // namespace integration -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_TESTDIRECTIVEHANDLER_H_ diff --git a/Integration/include/Integration/TestExceptionEncounteredSender.h b/Integration/include/Integration/TestExceptionEncounteredSender.h deleted file mode 100644 index 0df39f10a3..0000000000 --- a/Integration/include/Integration/TestExceptionEncounteredSender.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_TESTEXCEPTIONENCOUNTEREDSENDER_H_ -#define ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_TESTEXCEPTIONENCOUNTEREDSENDER_H_ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "AVSCommon/SDKInterfaces/DirectiveHandlerInterface.h" -#include "AVSCommon/SDKInterfaces/ExceptionEncounteredSenderInterface.h" -#include "AVSCommon/AVS/Attachment/AttachmentManager.h" -#include "AVSCommon/Utils/JSON/JSONUtils.h" - -using namespace alexaClientSDK::avsCommon; - -namespace alexaClientSDK { -namespace integration { -namespace test { - -/** - * TestExceptionEncounteredSender is a mock of the @c ExceptionEncounteredSenderInterface and allows tests - * to wait for invocations upon those interfaces and inspect the parameters of those invocations. - */ -class TestExceptionEncounteredSender : public avsCommon::sdkInterfaces::ExceptionEncounteredSenderInterface { -public: - void sendExceptionEncountered( - const std::string& unparsedDirective, - avsCommon::avs::ExceptionErrorType error, - const std::string& message) override; - /** - * Parse an @c AVSDirective from a JSON string. - * - * @param rawJSON The JSON to parse. - * @param attachmentManager The @c AttachmentManager to initialize the new @c AVSDirective with. - * @return A new @c AVSDirective, or nullptr if parsing the JSON fails. - */ - std::shared_ptr parseDirective( - const std::string& rawJSON, - std::shared_ptr attachmentManager); - - /** - * Class defining the parameters to calls to the mocked interfaces. - */ - class ExceptionParams { - public: - /** - * Constructor. - */ - ExceptionParams(); - - // Enum for the way the directive was passed. - enum class Type { - // Not yet set. - UNSET, - // Set when sendExceptionEncountered is called. - EXCEPTION, - // Set when waitForNext times out waiting for a directive. - TIMEOUT - }; - - // Type of how the directive was passed. - Type type; - // AVSDirective passed from the Directive Sequencer. - std::shared_ptr directive; - // Unparsed directive string passed to sendExceptionEncountered. - std::string exceptionUnparsedDirective; - // Error type passed to sendExceptionEncountered. - avsCommon::avs::ExceptionErrorType exceptionError; - // Additional information passed to sendExceptionEncountered. - std::string exceptionMessage; - }; - - /** - * Function to retrieve the next DirectiveParams in the test queue or time out if the queue is empty. Takes a - * duration in seconds to wait before timing out. - */ - ExceptionParams waitForNext(const std::chrono::seconds duration); - -private: - /// Mutex to protect m_queue. - std::mutex m_mutex; - /// Trigger to wake up waitForNext calls. - std::condition_variable m_wakeTrigger; - /// Queue of received directives that have not been waited on. - std::deque m_queue; -}; - -} // namespace test -} // namespace integration -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_TESTEXCEPTIONENCOUNTEREDSENDER_H_ diff --git a/Integration/include/Integration/TestMediaPlayer.h b/Integration/include/Integration/TestMediaPlayer.h deleted file mode 100644 index e445af4ff4..0000000000 --- a/Integration/include/Integration/TestMediaPlayer.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_TESTMEDIAPLAYER_H_ -#define ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_TESTMEDIAPLAYER_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "AVSCommon/Utils/MediaType.h" -#include "AVSCommon/Utils/Timing/Timer.h" - -namespace alexaClientSDK { -namespace integration { -namespace test { - -/** - * A Mock MediaPlayer that attempts to alert the observer of playing and stopping without - * actually playing audio. This removes the dependancy on an audio player to run tests with - * SpeechSynthesizer - */ -class TestMediaPlayer : public avsCommon::utils::mediaPlayer::MediaPlayerInterface { -public: - // Destructor. - ~TestMediaPlayer(); - - avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId setSource( - std::shared_ptr attachmentReader, - const avsCommon::utils::AudioFormat* audioFormat = nullptr, - const avsCommon::utils::mediaPlayer::SourceConfig& config = - avsCommon::utils::mediaPlayer::emptySourceConfig()) override; - - avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId setSource( - std::shared_ptr attachmentReader, - std::chrono::milliseconds offsetAdjustment, - const avsCommon::utils::AudioFormat* audioFormat = nullptr, - const avsCommon::utils::mediaPlayer::SourceConfig& config = - avsCommon::utils::mediaPlayer::emptySourceConfig()) override; - - avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId setSource( - std::shared_ptr stream, - bool repeat, - const avsCommon::utils::mediaPlayer::SourceConfig& config = avsCommon::utils::mediaPlayer::emptySourceConfig(), - avsCommon::utils::MediaType format = avsCommon::utils::MediaType::UNKNOWN) override; - - avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId setSource( - const std::string& url, - std::chrono::milliseconds offset = std::chrono::milliseconds::zero(), - const avsCommon::utils::mediaPlayer::SourceConfig& config = avsCommon::utils::mediaPlayer::emptySourceConfig(), - bool repeat = false, - const avsCommon::utils::mediaPlayer::PlaybackContext& playbackContext = - avsCommon::utils::mediaPlayer::PlaybackContext()) override; - - bool play(avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId id) override; - - bool stop(avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId id) override; - - bool pause(avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId id) override; - - bool resume(avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId id) override; - - std::chrono::milliseconds getOffset(avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId id) override; - - void addObserver( - std::shared_ptr playerObserver) override; - - void removeObserver( - std::shared_ptr playerObserver) override; - - avsCommon::utils::Optional getMediaPlayerState( - avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId id) override; - - uint64_t getNumBytesBuffered() override; - -private: - /// Observers to notify of state changes. - std::unordered_set> m_observers; - /// Flag to indicate when a playback finished notification has been sent to the observer. - bool m_playbackFinished = false; - /// The AttachmentReader to read audioData from. - std::shared_ptr m_attachmentReader; - /// Timer to wait to send onPlaybackFinished to the observer. - std::shared_ptr m_timer; - // istream for Alerts. - std::shared_ptr m_istream; -}; -} // namespace test -} // namespace integration -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_TESTMEDIAPLAYER_H_ diff --git a/Integration/include/Integration/TestMessageSender.h b/Integration/include/Integration/TestMessageSender.h deleted file mode 100644 index 66bcc24460..0000000000 --- a/Integration/include/Integration/TestMessageSender.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_TESTMESSAGESENDER_H_ -#define ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_TESTMESSAGESENDER_H_ - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace alexaClientSDK { -namespace integration { -namespace test { - -class TestMessageSender - : public avsCommon::sdkInterfaces::MessageSenderInterface - , public avsCommon::utils::RequiresShutdown { -public: - /// Destructor. - ~TestMessageSender() = default; - - void sendMessage(std::shared_ptr request) override; - - TestMessageSender( - std::shared_ptr messageRouter, - bool isEnabled, - std::shared_ptr connectionStatusObserver, - std::shared_ptr messageObserver); - - class SendParams { - public: - enum class Type { SEND, TIMEOUT }; - Type type; - std::shared_ptr request; - }; - - SendParams waitForNext(const std::chrono::seconds duration); - - /** - * Enable the AVSConnectionManager object to make connections to AVS. Once enabled, the object will attempt to - * create a connection to AVS. If the object is already connected, this function will do nothing. - */ - void enable(); - - /** - * Disable the AVSConnectionManager object. If the object is currently connected to AVS, then calling this - * function will cause the connection to be closed. If the object is not connected, then calling this function - * will do nothing. - */ - void disable(); - - /** - * Returns if the object is enabled for making connections to AVS. - * - * @return Whether this Connection object is enabled to make connections. - */ - bool isEnabled(); - - /** - * This function causes the object, if enabled, to create new connection to AVS. If the object is already - * connected, then that connection will be closed and a new one created. If the object is not connected, but - * perhaps in the process of waiting for its next connection attempt, then its waiting policy will be reset and - * it will attempt to create a new connection immediately. - * If the object is disabled, then this function will do nothing. - */ - void reconnect(); - - /** - * Set the gateway URL for the AVS connection. Calling this function with a new value will cause the - * current active connection to be closed, and a new one opened to the new gateway. - * @param avsGateway The URL for the new AVS gateway. - */ - void setAVSGateway(const std::string& avsGateway); - - void addConnectionStatusObserver( - std::shared_ptr observer); - - /** - * Removes an observer from being notified of connection status changes. - * - * @param observer The observer object to remove. - */ - void removeConnectionStatusObserver( - std::shared_ptr observer); - - /** - * Adds an observer to be notified of message receptions. - * - * @param observer The observer object to add. - */ - void addMessageObserver(std::shared_ptr observer); - - /** - * Removes an observer from being notified of message receptions. - * - * @param observer The observer object to remove. - */ - void removeMessageObserver(std::shared_ptr observer); - - void doShutdown() override; - - std::shared_ptr getConnectionManager() const; - -private: - /// Mutex to protect m_queue. - std::mutex m_mutex; - /// Trigger to wake up waitForNext calls. - std::condition_variable m_wakeTrigger; - /// Queue of received directives that have not been waited on. - std::deque m_queue; - - std::shared_ptr m_connectionManager; -}; - -} // namespace test -} // namespace integration -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_TESTMESSAGESENDER_H_ diff --git a/Integration/include/Integration/TestSpeechSynthesizerObserver.h b/Integration/include/Integration/TestSpeechSynthesizerObserver.h deleted file mode 100644 index 56cb3a5dbc..0000000000 --- a/Integration/include/Integration/TestSpeechSynthesizerObserver.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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. - */ - -#ifndef ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_TESTSPEECHSYNTHESIZEROBSERVER_H_ -#define ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_TESTSPEECHSYNTHESIZEROBSERVER_H_ - -#include -#include -#include -#include - -#include - -namespace alexaClientSDK { -namespace integration { -namespace test { - -/** - * Interface for observing a SpeechSynthesizer. - */ -class TestSpeechSynthesizerObserver : public avsCommon::sdkInterfaces::SpeechSynthesizerObserverInterface { -public: - TestSpeechSynthesizerObserver(); - - ~TestSpeechSynthesizerObserver() = default; - - void onStateChanged( - avsCommon::sdkInterfaces::SpeechSynthesizerObserverInterface::SpeechSynthesizerState state, - const avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId mediaSourceId, - const avsCommon::utils::Optional& mediaPlayerState, - const std::vector& audioAnalyzerState) override; - - bool checkState( - const avsCommon::sdkInterfaces::SpeechSynthesizerObserverInterface::SpeechSynthesizerState expectedState, - const std::chrono::seconds duration); - - avsCommon::sdkInterfaces::SpeechSynthesizerObserverInterface::SpeechSynthesizerState waitForNext( - const std::chrono::seconds duration); - - avsCommon::sdkInterfaces::SpeechSynthesizerObserverInterface::SpeechSynthesizerState getCurrentState(); - -private: - avsCommon::sdkInterfaces::SpeechSynthesizerObserverInterface::SpeechSynthesizerState m_state; - std::mutex m_mutex; - std::condition_variable m_wakeTrigger; - std::deque m_queue; -}; - -} // namespace test -} // namespace integration -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_INTEGRATION_INCLUDE_INTEGRATION_TESTSPEECHSYNTHESIZEROBSERVER_H_ diff --git a/Integration/inputs/alexa_recognize_joke_test.wav b/Integration/inputs/alexa_recognize_joke_test.wav deleted file mode 100755 index 5d67f82b8c..0000000000 Binary files a/Integration/inputs/alexa_recognize_joke_test.wav and /dev/null differ diff --git a/Integration/inputs/alexa_recognize_silence_test.wav b/Integration/inputs/alexa_recognize_silence_test.wav deleted file mode 100755 index 06f49fe699..0000000000 Binary files a/Integration/inputs/alexa_recognize_silence_test.wav and /dev/null differ diff --git a/Integration/inputs/alexa_recognize_wiki_test.wav b/Integration/inputs/alexa_recognize_wiki_test.wav deleted file mode 100755 index 6b976e4afc..0000000000 Binary files a/Integration/inputs/alexa_recognize_wiki_test.wav and /dev/null differ diff --git a/Integration/inputs/recognize_cancel_timer_test.wav b/Integration/inputs/recognize_cancel_timer_test.wav deleted file mode 100755 index 372fe6c52d..0000000000 Binary files a/Integration/inputs/recognize_cancel_timer_test.wav and /dev/null differ diff --git a/Integration/inputs/recognize_flashbriefing_test.wav b/Integration/inputs/recognize_flashbriefing_test.wav deleted file mode 100755 index 2719b0e9b2..0000000000 Binary files a/Integration/inputs/recognize_flashbriefing_test.wav and /dev/null differ diff --git a/Integration/inputs/recognize_joke_test.wav b/Integration/inputs/recognize_joke_test.wav deleted file mode 100755 index 0a046867be..0000000000 Binary files a/Integration/inputs/recognize_joke_test.wav and /dev/null differ diff --git a/Integration/inputs/recognize_lions_test.wav b/Integration/inputs/recognize_lions_test.wav deleted file mode 100755 index 1c42a3b45d..0000000000 Binary files a/Integration/inputs/recognize_lions_test.wav and /dev/null differ diff --git a/Integration/inputs/recognize_long_timer_test.wav b/Integration/inputs/recognize_long_timer_test.wav deleted file mode 100755 index 00e03f297c..0000000000 Binary files a/Integration/inputs/recognize_long_timer_test.wav and /dev/null differ diff --git a/Integration/inputs/recognize_silence_test.wav b/Integration/inputs/recognize_silence_test.wav deleted file mode 100755 index 94498fb4ca..0000000000 Binary files a/Integration/inputs/recognize_silence_test.wav and /dev/null differ diff --git a/Integration/inputs/recognize_sing_song_test.wav b/Integration/inputs/recognize_sing_song_test.wav deleted file mode 100755 index 39505d8a0d..0000000000 Binary files a/Integration/inputs/recognize_sing_song_test.wav and /dev/null differ diff --git a/Integration/inputs/recognize_stop_test.wav b/Integration/inputs/recognize_stop_test.wav deleted file mode 100755 index b9802c952c..0000000000 Binary files a/Integration/inputs/recognize_stop_test.wav and /dev/null differ diff --git a/Integration/inputs/recognize_stop_timer_test.wav b/Integration/inputs/recognize_stop_timer_test.wav deleted file mode 100755 index f278b2f219..0000000000 Binary files a/Integration/inputs/recognize_stop_timer_test.wav and /dev/null differ diff --git a/Integration/inputs/recognize_test.wav b/Integration/inputs/recognize_test.wav deleted file mode 100755 index 9f0462f5ed..0000000000 Binary files a/Integration/inputs/recognize_test.wav and /dev/null differ diff --git a/Integration/inputs/recognize_timer_test.wav b/Integration/inputs/recognize_timer_test.wav deleted file mode 100755 index b7116821cd..0000000000 Binary files a/Integration/inputs/recognize_timer_test.wav and /dev/null differ diff --git a/Integration/inputs/recognize_very_long_timer_test.wav b/Integration/inputs/recognize_very_long_timer_test.wav deleted file mode 100755 index 0deaf82aa4..0000000000 Binary files a/Integration/inputs/recognize_very_long_timer_test.wav and /dev/null differ diff --git a/Integration/inputs/recognize_volume_up_test.wav b/Integration/inputs/recognize_volume_up_test.wav deleted file mode 100755 index 2075dcbab3..0000000000 Binary files a/Integration/inputs/recognize_volume_up_test.wav and /dev/null differ diff --git a/Integration/inputs/recognize_weather_test.wav b/Integration/inputs/recognize_weather_test.wav deleted file mode 100755 index 80adfced19..0000000000 Binary files a/Integration/inputs/recognize_weather_test.wav and /dev/null differ diff --git a/Integration/inputs/recognize_whats_up_test.wav b/Integration/inputs/recognize_whats_up_test.wav deleted file mode 100755 index 42aa2d4cb5..0000000000 Binary files a/Integration/inputs/recognize_whats_up_test.wav and /dev/null differ diff --git a/Integration/inputs/recognize_wiki_test.wav b/Integration/inputs/recognize_wiki_test.wav deleted file mode 100755 index f1edb68c9c..0000000000 Binary files a/Integration/inputs/recognize_wiki_test.wav and /dev/null differ diff --git a/Integration/inputs/silence_test.wav b/Integration/inputs/silence_test.wav deleted file mode 100755 index 464dfe623e..0000000000 Binary files a/Integration/inputs/silence_test.wav and /dev/null differ diff --git a/Integration/inputs/utterance_time_success.opus b/Integration/inputs/utterance_time_success.opus deleted file mode 100755 index 9c1198ba20..0000000000 Binary files a/Integration/inputs/utterance_time_success.opus and /dev/null differ diff --git a/Integration/src/ACLTestContext.cpp b/Integration/src/ACLTestContext.cpp deleted file mode 100644 index 0824d6b7ac..0000000000 --- a/Integration/src/ACLTestContext.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - * 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 "Integration/ACLTestContext.h" - -namespace alexaClientSDK { -namespace integration { -namespace test { - -using namespace acl; -using namespace avsCommon::avs::attachment; -using namespace avsCommon::sdkInterfaces; -using namespace avsCommon::utils::configuration; -using namespace avsCommon::utils::libcurlUtils; -using namespace authorization::cblAuthDelegate; -using namespace contextManager; -using namespace registrationManager; - -std::unique_ptr ACLTestContext::create(const std::string& filePath, const std::string& overlay) { - std::unique_ptr context(new ACLTestContext(filePath, overlay)); - if (context->m_authDelegateTestContext && context->m_attachmentManager && context->m_messageRouter && - context->m_connectionStatusObserver && context->m_contextManager) { - return context; - } - return nullptr; -} - -ACLTestContext::~ACLTestContext() { - m_attachmentManager.reset(); - - if (m_messageRouter) { - m_messageRouter->shutdown(); - } - - m_connectionStatusObserver.reset(); - m_contextManager.reset(); - m_authDelegateTestContext.reset(); -} - -std::shared_ptr ACLTestContext::getAuthDelegate() const { - return m_authDelegateTestContext->getAuthDelegate(); -} - -std::shared_ptr ACLTestContext::getCustomerDataManager() const { - return m_authDelegateTestContext->getCustomerDataManager(); -} - -std::shared_ptr ACLTestContext::getAttachmentManager() const { - return m_attachmentManager; -} - -std::shared_ptr ACLTestContext::getMessageRouter() const { - return m_messageRouter; -} - -std::shared_ptr ACLTestContext::getConnectionStatusObserver() const { - return m_connectionStatusObserver; -} - -std::shared_ptr ACLTestContext::getContextManager() const { - return m_contextManager; -} - -void ACLTestContext::waitForConnected() { - ASSERT_TRUE(m_connectionStatusObserver->waitFor(ConnectionStatusObserverInterface::Status::CONNECTED)) - << "Connecting timed out"; -} - -void ACLTestContext::waitForDisconnected() { - ASSERT_TRUE(m_connectionStatusObserver->waitFor(ConnectionStatusObserverInterface::Status::DISCONNECTED)) - << "Disconnecting timed out"; -} - -/// Default @c AVS gateway to connect to. -static const std::string DEFAULT_AVS_GATEWAY = "https://alexa.na.gateway.devices.a2z.com"; - -ACLTestContext::ACLTestContext(const std::string& filePath, const std::string& overlay) { - m_authDelegateTestContext = AuthDelegateTestContext::create(filePath, overlay); - EXPECT_TRUE(m_authDelegateTestContext); - if (!m_authDelegateTestContext) { - return; - } - - auto config = ConfigurationNode::getRoot(); - EXPECT_TRUE(config); - if (!config) { - return; - } - - auto deviceInfo = - avsCommon::utils::DeviceInfo::createFromConfiguration(std::make_shared(config)); - EXPECT_TRUE(deviceInfo); - if (!deviceInfo) { - return; - } - - m_contextManager = ContextManager::createContextManagerInterface(std::move(deviceInfo)); - EXPECT_TRUE(m_contextManager); - if (!m_contextManager) { - return; - } - - auto synchronizeStateSenderFactory = - synchronizeStateSender::SynchronizeStateSenderFactory::create(m_contextManager); - std::vector> providers; - providers.push_back(synchronizeStateSenderFactory); - auto postConnectFactory = acl::PostConnectSequencerFactory::create(providers); - auto http2ConnectionFactory = std::make_shared(); - auto transportFactory = std::make_shared(http2ConnectionFactory, postConnectFactory); - m_attachmentManager = std::make_shared(AttachmentManager::AttachmentType::IN_PROCESS); - m_messageRouter = - std::make_shared(getAuthDelegate(), m_attachmentManager, transportFactory, DEFAULT_AVS_GATEWAY); - m_connectionStatusObserver = std::make_shared(); -} - -} // namespace test -} // namespace integration -} // namespace alexaClientSDK diff --git a/Integration/src/AipStateObserver.cpp b/Integration/src/AipStateObserver.cpp deleted file mode 100644 index 012a0aa427..0000000000 --- a/Integration/src/AipStateObserver.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 "Integration/AipStateObserver.h" - -namespace alexaClientSDK { -namespace integration { - -using avsCommon::sdkInterfaces::AudioInputProcessorObserverInterface; - -AipStateObserver::AipStateObserver() : m_state(AudioInputProcessorObserverInterface::State::IDLE) { -} - -void AipStateObserver::onStateChanged(AudioInputProcessorObserverInterface::State newState) { - std::unique_lock lock(m_mutex); - m_queue.push_back(newState); - m_state = newState; - m_wakeTrigger.notify_all(); -} - -bool AipStateObserver::checkState( - const AudioInputProcessorObserverInterface::State expectedState, - const std::chrono::seconds duration) { - AudioInputProcessorObserverInterface::State hold = waitForNext(duration); - return hold == expectedState; -} - -AudioInputProcessorObserverInterface::State AipStateObserver::waitForNext(const std::chrono::seconds duration) { - AudioInputProcessorObserverInterface::State ret; - std::unique_lock lock(m_mutex); - if (!m_wakeTrigger.wait_for(lock, duration, [this]() { return !m_queue.empty(); })) { - ret = AudioInputProcessorObserverInterface::State::IDLE; - return ret; - } - ret = m_queue.front(); - m_queue.pop_front(); - return ret; -} - -} // namespace integration -} // namespace alexaClientSDK diff --git a/Integration/src/AuthDelegateTestContext.cpp b/Integration/src/AuthDelegateTestContext.cpp deleted file mode 100644 index 662e36bea4..0000000000 --- a/Integration/src/AuthDelegateTestContext.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - * 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 -#ifdef ACSDK_ACS_UTILS -#include -#include -#else -#include -#include -#endif -#include - -#include "Integration/AuthDelegateTestContext.h" - -namespace alexaClientSDK { -namespace integration { -namespace test { - -using namespace avsCommon::sdkInterfaces; -using namespace avsCommon::utils::configuration; -#ifdef ACSDK_ACS_UTILS -using namespace acsdkffsAuthDelegate; -#else -using namespace authorization::cblAuthDelegate; -#endif -using namespace contextManager; -using namespace registrationManager; -using namespace ::testing; - -std::unique_ptr AuthDelegateTestContext::create( - const std::string& filePath, - const std::string& overlay) { - std::unique_ptr context(new AuthDelegateTestContext(filePath, overlay)); - if (context->m_sdkTestContext && context->m_customerDataManager && context->m_authDelegate) { - return context; - } - return nullptr; -} - -AuthDelegateTestContext::~AuthDelegateTestContext() { - m_authDelegate.reset(); - m_customerDataManager.reset(); - m_sdkTestContext.reset(); -} - -#ifdef ACSDK_ACS_UTILS -void AuthDelegateTestContext::AuthRequester::onRequestFFS() { -} -#else -void AuthDelegateTestContext::AuthRequester::onRequestAuthorization(const std::string& url, const std::string& code) { - ASSERT_FALSE(true) << "FATAL ERROR: Authorization required before running integration test"; -} - -void AuthDelegateTestContext::AuthRequester::onCheckingForAuthorization() { -} -#endif - -bool AuthDelegateTestContext::isValid() const { - return m_customerDataManager && m_authDelegate; -} - -std::shared_ptr AuthDelegateTestContext::getAuthDelegate() const { - return m_authDelegate; -} - -std::shared_ptr AuthDelegateTestContext::getCustomerDataManager() - const { - return m_customerDataManager; -} - -AuthDelegateTestContext::AuthDelegateTestContext(const std::string& filePath, const std::string& overlay) { - m_sdkTestContext = SDKTestContext::create(filePath, overlay); - EXPECT_TRUE(m_sdkTestContext); - if (!m_sdkTestContext) { - return; - } - - auto config = std::make_shared(ConfigurationNode::getRoot()); - EXPECT_TRUE(*config); - if (!*config) { - return; - } - - // TODO: use configuration or parameter here to determine which kind of AuthDelegate to create. - - m_customerDataManager = registrationManager::CustomerDataManagerFactory::createCustomerDataManagerInterface(); - EXPECT_TRUE(m_customerDataManager); - if (!m_customerDataManager) { - return; - } - -#ifdef ACSDK_ACS_UTILS - auto storage = ACSFFSAuthDelegateStorage::createFFSAuthDelegateStorageInterface(); - EXPECT_TRUE(storage); - if (!storage) { - return; - } - - m_authDelegate = FFSAuthDelegate::createFFSAuthDelegateInterface( - config, - m_customerDataManager, - std::move(storage), - std::make_shared(), - avsCommon::utils::libcurlUtils::HttpPost::create(), - avsCommon::utils::DeviceInfo::createFromConfiguration(config)); - - EXPECT_TRUE(m_authDelegate); -#else - auto storage = SQLiteCBLAuthDelegateStorage::createCBLAuthDelegateStorageInterface(config, nullptr, nullptr); - EXPECT_TRUE(storage); - if (!storage) { - return; - } - - m_authDelegate = CBLAuthDelegate::createAuthDelegateInterface( - config, - m_customerDataManager, - std::move(storage), - std::make_shared(), - avsCommon::utils::libcurlUtils::HttpPost::create(), - avsCommon::utils::DeviceInfo::createFromConfiguration(config)); - - EXPECT_TRUE(m_authDelegate); -#endif -} - -} // namespace test -} // namespace integration -} // namespace alexaClientSDK diff --git a/Integration/src/AuthObserver.cpp b/Integration/src/AuthObserver.cpp deleted file mode 100644 index 248a4f481f..0000000000 --- a/Integration/src/AuthObserver.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 "Integration/AuthObserver.h" -#include - -namespace alexaClientSDK { -namespace integration { - -using avsCommon::sdkInterfaces::AuthObserverInterface; - -AuthObserver::AuthObserver() : - m_authState(AuthObserverInterface::State::UNINITIALIZED), - m_authError(AuthObserverInterface::Error::SUCCESS) { -} - -void AuthObserver::onAuthStateChange( - const AuthObserverInterface::State authState, - const AuthObserverInterface::Error authError) { - m_authState = authState; - m_authError = authError; - m_wakeTrigger.notify_all(); -} - -AuthObserverInterface::State AuthObserver::getAuthState() const { - return m_authState; -} - -bool AuthObserver::waitFor(const AuthObserverInterface::State authState, const std::chrono::seconds duration) { - std::unique_lock lock(m_mutex); - return m_wakeTrigger.wait_for(lock, duration, [this, authState]() { return m_authState == authState; }); -} - -} // namespace integration -} // namespace alexaClientSDK diff --git a/Integration/src/CMakeLists.txt b/Integration/src/CMakeLists.txt deleted file mode 100644 index 7aca9a6fb6..0000000000 --- a/Integration/src/CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -if(BUILD_TESTING) - add_definitions("-DACSDK_LOG_MODULE=integration") - file(GLOB_RECURSE INTEGRATION_SRC "*.cpp") - add_library(Integration STATIC "${INTEGRATION_SRC}") - target_include_directories(Integration PUBLIC "${ACL_SOURCE_DIR}/include") - target_include_directories(Integration PUBLIC "${CBLAuthDelegate_SOURCE_DIR}/include") - target_include_directories(Integration PUBLIC "${ContextManager_SOURCE_DIR}/include") - target_include_directories(Integration PUBLIC "${Integration_SOURCE_DIR}/include") - target_include_directories(Integration PUBLIC "${CapabilityAgents_SOURCE_DIR}/include") - target_include_directories(Integration PUBLIC "${AIP_SOURCE_DIR}/include") - target_include_directories(Integration PUBLIC "${SpeechSynthesizer_SOURCE_DIR}/include") - target_include_directories(Integration PUBLIC "${AudioPlayer_SOURCE_DIR}/include") - target_include_directories(Integration PUBLIC "${AVSSystem_SOURCE_DIR}/include") - target_include_directories(Integration PUBLIC "${RegistrationManager_SOURCE_DIR}/include") - target_include_directories(Integration PUBLIC "${SQLiteStorage_SOURCE_DIR}/include") - target_include_directories(Integration PUBLIC "${SynchronizeStateSender_SOURCE_DIR}/include") - - target_link_libraries(Integration - ACL - AudioResources - CBLAuthDelegate - ContextManager - gtest - gmock - RegistrationManager - SynchronizeStateSender - acsdkAlerts) - if(ACS_UTILS) - target_link_libraries(Integration acsdkFFSAuthDelegate) - endif() -endif() \ No newline at end of file diff --git a/Integration/src/ClientMessageHandler.cpp b/Integration/src/ClientMessageHandler.cpp deleted file mode 100644 index c6fd245fab..0000000000 --- a/Integration/src/ClientMessageHandler.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 "Integration/ClientMessageHandler.h" -#include - -namespace alexaClientSDK { -namespace integration { - -using namespace avsCommon::avs::attachment; - -ClientMessageHandler::ClientMessageHandler(std::shared_ptr attachmentManager) : - m_count{0}, - m_attachmentManager{attachmentManager} { -} - -void ClientMessageHandler::receive(const std::string& contextId, const std::string& message) { - std::cout << "ClientMessageHandler::receive: message:" << message << std::endl; - std::unique_lock lock(m_mutex); - ++m_count; - m_wakeTrigger.notify_all(); -} - -bool ClientMessageHandler::waitForNext(const std::chrono::seconds duration) { - std::unique_lock lock(m_mutex); - if (!m_wakeTrigger.wait_for(lock, duration, [this]() { return m_count > 0; })) { - return false; - } - --m_count; - return true; -} - -} // namespace integration -} // namespace alexaClientSDK diff --git a/Integration/src/ConnectionStatusObserver.cpp b/Integration/src/ConnectionStatusObserver.cpp deleted file mode 100644 index 9625883025..0000000000 --- a/Integration/src/ConnectionStatusObserver.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 "Integration/ConnectionStatusObserver.h" - -#include - -namespace alexaClientSDK { -namespace integration { - -using alexaClientSDK::avsCommon::sdkInterfaces::ConnectionStatusObserverInterface; - -ConnectionStatusObserver::ConnectionStatusObserver() : - m_statusChanges({std::make_pair(Status::DISCONNECTED, ChangedReason::ACL_CLIENT_REQUEST)}) { -} - -void ConnectionStatusObserver::onConnectionStatusChanged(const Status connectionStatus, const ChangedReason reason) { - std::lock_guard lock(m_mutex); - m_statusChanges.push_back(std::make_pair(connectionStatus, reason)); - m_wakeTrigger.notify_all(); -} - -bool ConnectionStatusObserver::checkForServerSideDisconnect() { - std::lock_guard lock(m_mutex); - for (auto pairValue : m_statusChanges) { - if (pairValue.first == Status::PENDING && pairValue.second == ChangedReason::SERVER_SIDE_DISCONNECT) { - return true; - } - } - return false; -} - -ConnectionStatusObserverInterface::Status ConnectionStatusObserver::getConnectionStatus() const { - std::lock_guard lock(m_mutex); - return m_statusChanges.back().first; -} - -bool ConnectionStatusObserver::waitFor(const Status connectionStatus, const std::chrono::seconds duration) { - std::unique_lock lock(m_mutex); - return m_wakeTrigger.wait_for( - lock, duration, [this, connectionStatus]() { return m_statusChanges.back().first == connectionStatus; }); -} - -} // namespace integration -} // namespace alexaClientSDK diff --git a/Integration/src/ObservableMessageRequest.cpp b/Integration/src/ObservableMessageRequest.cpp deleted file mode 100644 index fd26425ebd..0000000000 --- a/Integration/src/ObservableMessageRequest.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 "Integration/ObservableMessageRequest.h" - -#include - -namespace alexaClientSDK { -namespace integration { - -/// String to identify log entries originating from this file. -static const std::string TAG("ObservableMessageRequest"); - -/** - * 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) - -using namespace avsCommon::avs; -using namespace avsCommon::avs::attachment; - -/// The field name for the user voice attachment. -static const std::string AUDIO_ATTACHMENT_FIELD_NAME = "audio"; - -ObservableMessageRequest::ObservableMessageRequest( - const std::string& jsonContent, - std::shared_ptr attachmentReader) : - MessageRequest{jsonContent}, - m_sendMessageStatus(avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::PENDING), - m_sendCompleted{false}, - m_exceptionReceived{false} { - if (attachmentReader) { - addAttachmentReader(AUDIO_ATTACHMENT_FIELD_NAME, attachmentReader); - } -} - -void ObservableMessageRequest::sendCompleted( - avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status sendMessageStatus) { - std::lock_guard lock(m_mutex); - ACSDK_DEBUG(LX("onSendCompleted").d("status", sendMessageStatus)); - m_sendMessageStatus = sendMessageStatus; - m_sendCompleted = true; - m_wakeTrigger.notify_all(); -} - -avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status ObservableMessageRequest::getSendMessageStatus() - const { - std::lock_guard lock(m_mutex); - return m_sendMessageStatus; -} - -bool ObservableMessageRequest::waitFor( - const avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status sendMessageStatus, - const std::chrono::seconds duration) { - std::unique_lock lock(m_mutex); - return m_wakeTrigger.wait_for( - lock, duration, [this, sendMessageStatus]() { return sendMessageStatus == m_sendMessageStatus; }); -} - -void ObservableMessageRequest::exceptionReceived(const std::string& exceptionMessage) { - ACSDK_DEBUG(LX("onExceptionReceived").d("status", exceptionMessage)); - m_exceptionReceived = true; -} - -bool ObservableMessageRequest::hasSendCompleted() { - return m_sendCompleted; -} - -bool ObservableMessageRequest::wasExceptionReceived() { - return m_exceptionReceived; -} - -} // namespace integration -} // namespace alexaClientSDK diff --git a/Integration/src/SDKTestContext.cpp b/Integration/src/SDKTestContext.cpp deleted file mode 100644 index b877f17f32..0000000000 --- a/Integration/src/SDKTestContext.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 "Integration/SDKTestContext.h" - -namespace alexaClientSDK { -namespace integration { -namespace test { - -using namespace avsCommon::avs::initialization; - -std::unique_ptr SDKTestContext::create(const std::string& filePath, const std::string& overlay) { - std::unique_ptr context(new SDKTestContext(filePath, overlay)); - if (AlexaClientSDKInit::isInitialized()) { - return context; - } - return nullptr; -} - -SDKTestContext::~SDKTestContext() { - AlexaClientSDKInit::uninitialize(); -} - -SDKTestContext::SDKTestContext(const std::string& filePath, const std::string& overlay) { - std::vector> streams; - - auto infile = std::shared_ptr(new std::ifstream(filePath)); - EXPECT_TRUE(infile->good()); - if (!infile->good()) { - return; - } - streams.push_back(infile); - - auto overlayStream = std::shared_ptr(new std::stringstream()); - if (!overlay.empty()) { - (*overlayStream) << overlay; - streams.push_back(overlayStream); - } - - EXPECT_TRUE(AlexaClientSDKInit::initialize(streams)); -} - -} // namespace test -} // namespace integration -} // namespace alexaClientSDK diff --git a/Integration/src/TestAlertObserver.cpp b/Integration/src/TestAlertObserver.cpp deleted file mode 100644 index 82233cb52b..0000000000 --- a/Integration/src/TestAlertObserver.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 "Integration/TestAlertObserver.h" - -namespace alexaClientSDK { -namespace integration { -namespace test { - -void TestAlertObserver::onAlertStateChange(const AlertObserverInterface::AlertInfo& alertInfo) { - std::unique_lock lock(m_mutex); - TestAlertObserver::changedAlert ca; - ca.state = alertInfo.state; - m_queue.push_back(ca); - m_wakeTrigger.notify_all(); -} - -TestAlertObserver::changedAlert TestAlertObserver::waitForNext(const std::chrono::seconds duration) { - TestAlertObserver::changedAlert ret; - std::unique_lock lock(m_mutex); - if (!m_wakeTrigger.wait_for(lock, duration, [this]() { return !m_queue.empty(); })) { - ret.state = currentState; - return ret; - } - ret = m_queue.front(); - m_queue.pop_front(); - return ret; -} - -} // namespace test -} // namespace integration -} // namespace alexaClientSDK diff --git a/Integration/src/TestDirectiveHandler.cpp b/Integration/src/TestDirectiveHandler.cpp deleted file mode 100644 index 7137a88da0..0000000000 --- a/Integration/src/TestDirectiveHandler.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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 "Integration/TestDirectiveHandler.h" - -namespace alexaClientSDK { -namespace integration { -namespace test { - -TestDirectiveHandler::TestDirectiveHandler(avsCommon::avs::DirectiveHandlerConfiguration config) : - m_configuration{config} { -} - -void TestDirectiveHandler::handleDirectiveImmediately(std::shared_ptr directive) { - std::unique_lock lock(m_mutex); - TestDirectiveHandler::DirectiveParams dp; - dp.type = DirectiveParams::Type::HANDLE_IMMEDIATELY; - dp.directive = directive; - m_queue.push_back(dp); - m_wakeTrigger.notify_all(); -} - -void TestDirectiveHandler::preHandleDirective( - std::shared_ptr directive, - std::unique_ptr result) { - std::unique_lock lock(m_mutex); - TestDirectiveHandler::DirectiveParams dp; - dp.type = TestDirectiveHandler::DirectiveParams::Type::PREHANDLE; - dp.directive = directive; - dp.result = std::move(result); - m_results[directive->getMessageId()] = dp.result; - m_directives[directive->getMessageId()] = directive; - m_queue.push_back(dp); - m_wakeTrigger.notify_all(); -} - -bool TestDirectiveHandler::handleDirective(const std::string& messageId) { - std::unique_lock lock(m_mutex); - TestDirectiveHandler::DirectiveParams dp; - dp.type = DirectiveParams::Type::HANDLE; - auto result = m_results.find(messageId); - if (m_results.end() == result) { - return false; - } - dp.result = result->second; - auto directive = m_directives.find(messageId); - if (m_directives.end() == directive) { - return false; - } - dp.directive = directive->second; - m_queue.push_back(dp); - m_wakeTrigger.notify_all(); - return true; -} - -void TestDirectiveHandler::cancelDirective(const std::string& messageId) { - std::unique_lock lock(m_mutex); - TestDirectiveHandler::DirectiveParams dp; - dp.type = DirectiveParams::Type::CANCEL; - auto result = m_results.find(messageId); - dp.result = result->second; - m_results.erase(result); - auto directive = m_directives.find(messageId); - dp.directive = directive->second; - m_directives.erase(directive); - m_queue.push_back(dp); - m_wakeTrigger.notify_all(); -} - -avsCommon::avs::DirectiveHandlerConfiguration TestDirectiveHandler::getConfiguration() const { - return m_configuration; -} - -void TestDirectiveHandler::onDeregistered() { -} - -TestDirectiveHandler::DirectiveParams TestDirectiveHandler::waitForNext(const std::chrono::seconds duration) { - DirectiveParams ret; - std::unique_lock lock(m_mutex); - if (!m_wakeTrigger.wait_for(lock, duration, [this]() { return !m_queue.empty(); })) { - ret.type = DirectiveParams::Type::TIMEOUT; - return ret; - } - ret = m_queue.front(); - m_queue.pop_front(); - return ret; -} - -TestDirectiveHandler::DirectiveParams::DirectiveParams() : type{Type::UNSET} { -} -} // namespace test -} // namespace integration -} // namespace alexaClientSDK diff --git a/Integration/src/TestExceptionEncounteredSender.cpp b/Integration/src/TestExceptionEncounteredSender.cpp deleted file mode 100644 index e0b558d809..0000000000 --- a/Integration/src/TestExceptionEncounteredSender.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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 "Integration/TestExceptionEncounteredSender.h" - -namespace alexaClientSDK { -namespace integration { -namespace test { - -using namespace avsCommon::utils::json; - -/// JSON key to get the event object of a message. -static const std::string JSON_MESSAGE_EVENT_KEY = "event"; -/// JSON key to get the directive object of a message. -static const std::string JSON_MESSAGE_DIRECTIVE_KEY = "directive"; -/// JSON key to get the header object of a message. -static const std::string JSON_MESSAGE_HEADER_KEY = "header"; -/// JSON key to get the namespace value of a header. -static const std::string JSON_MESSAGE_NAMESPACE_KEY = "namespace"; -/// JSON key to get the name value of a header. -static const std::string JSON_MESSAGE_NAME_KEY = "name"; -/// JSON key to get the messageId value of a header. -static const std::string JSON_MESSAGE_MESSAGE_ID_KEY = "messageId"; -/// JSON key to get the dialogRequestId value of a header. -static const std::string JSON_MESSAGE_DIALOG_REQUEST_ID_KEY = "dialogRequestId"; -/// JSON key to get the payload object of a message. -static const std::string JSON_MESSAGE_PAYLOAD_KEY = "payload"; - -void TestExceptionEncounteredSender::sendExceptionEncountered( - const std::string& unparsedDirective, - avs::ExceptionErrorType error, - const std::string& message) { - std::unique_lock lock(m_mutex); - ExceptionParams dp; - dp.type = ExceptionParams::Type::EXCEPTION; - dp.directive = parseDirective( - unparsedDirective, - std::make_shared( - avsCommon::avs::attachment::AttachmentManager::AttachmentType::IN_PROCESS)); - dp.exceptionUnparsedDirective = unparsedDirective; - dp.exceptionError = error; - dp.exceptionMessage = message; - m_queue.push_back(dp); - m_wakeTrigger.notify_all(); -} - -std::shared_ptr TestExceptionEncounteredSender::parseDirective( - const std::string& rawJSON, - std::shared_ptr attachmentManager) { - std::string directiveJSON; - std::string headerJSON; - std::string payloadJSON; - std::string nameSpace; - std::string name; - std::string messageId; - std::string dialogRequestId; - - if (!jsonUtils::retrieveValue(rawJSON, JSON_MESSAGE_DIRECTIVE_KEY, &directiveJSON) || - !jsonUtils::retrieveValue(directiveJSON, JSON_MESSAGE_HEADER_KEY, &headerJSON) || - !jsonUtils::retrieveValue(directiveJSON, JSON_MESSAGE_PAYLOAD_KEY, &payloadJSON) || - !jsonUtils::retrieveValue(headerJSON, JSON_MESSAGE_NAMESPACE_KEY, &nameSpace) || - !jsonUtils::retrieveValue(headerJSON, JSON_MESSAGE_NAME_KEY, &name) || - !jsonUtils::retrieveValue(headerJSON, JSON_MESSAGE_MESSAGE_ID_KEY, &messageId)) { - return nullptr; - } - - jsonUtils::retrieveValue(headerJSON, JSON_MESSAGE_NAMESPACE_KEY, &dialogRequestId); - - auto header = std::make_shared(nameSpace, name, messageId, dialogRequestId); - return avsCommon::avs::AVSDirective::create(rawJSON, header, payloadJSON, attachmentManager, ""); -} - -TestExceptionEncounteredSender::ExceptionParams TestExceptionEncounteredSender::waitForNext( - const std::chrono::seconds duration) { - ExceptionParams ret; - std::unique_lock lock(m_mutex); - if (!m_wakeTrigger.wait_for(lock, duration, [this]() { return !m_queue.empty(); })) { - ret.type = ExceptionParams::Type::TIMEOUT; - return ret; - } - ret = m_queue.front(); - m_queue.pop_front(); - return ret; -} - -TestExceptionEncounteredSender::ExceptionParams::ExceptionParams() : - type{Type::UNSET}, - exceptionError{avsCommon::avs::ExceptionErrorType::INTERNAL_ERROR} { -} - -} // namespace test -} // namespace integration -} // namespace alexaClientSDK diff --git a/Integration/src/TestMediaPlayer.cpp b/Integration/src/TestMediaPlayer.cpp deleted file mode 100644 index 67521e02d5..0000000000 --- a/Integration/src/TestMediaPlayer.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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 "Integration/TestMediaPlayer.h" - -namespace alexaClientSDK { -namespace integration { -namespace test { - -/// String to identify log entries originating from this file. -static const std::string TAG("TestMediaPlayer"); - -/// Default media player state for reporting all playback eventing -static const avsCommon::utils::mediaPlayer::MediaPlayerState DEFAULT_MEDIA_PLAYER_STATE = { - std::chrono::milliseconds(0)}; - -/// A counter used to increment the source id when a new source is set. -static std::atomic g_sourceId{0}; - -/** - * 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) - -TestMediaPlayer::~TestMediaPlayer() { -} - -avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId TestMediaPlayer::setSource( - std::shared_ptr attachmentReader, - const avsCommon::utils::AudioFormat* audioFormat, - const avsCommon::utils::mediaPlayer::SourceConfig& config) { - m_attachmentReader = std::move(attachmentReader); - return ++g_sourceId; -} - -avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId TestMediaPlayer::setSource( - std::shared_ptr attachmentReader, - std::chrono::milliseconds offsetAdjustment, - const avsCommon::utils::AudioFormat* audioFormat, - const avsCommon::utils::mediaPlayer::SourceConfig& config) { - m_attachmentReader = std::move(attachmentReader); - return ++g_sourceId; -} - -avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId TestMediaPlayer::setSource( - std::shared_ptr stream, - bool repeat, - const avsCommon::utils::mediaPlayer::SourceConfig& config, - avsCommon::utils::MediaType format) { - m_istream = stream; - return ++g_sourceId; -} - -avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId TestMediaPlayer::setSource( - const std::string& url, - std::chrono::milliseconds offset, - const avsCommon::utils::mediaPlayer::SourceConfig& config, - bool repeat, - const avsCommon::utils::mediaPlayer::PlaybackContext& playbackContext) { - return ++g_sourceId; -} - -bool TestMediaPlayer::play(avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId id) { - if (m_observers.empty()) { - return false; - } - for (const auto& observer : m_observers) { - observer->onPlaybackStarted(id, DEFAULT_MEDIA_PLAYER_STATE); - } - m_playbackFinished = true; - m_timer = std::unique_ptr(new avsCommon::utils::timing::Timer); - // Wait 600 milliseconds before sending onPlaybackFinished. - m_timer->start(std::chrono::milliseconds(600), [this, id] { - for (const auto& observer : m_observers) { - if (m_playbackFinished) { - observer->onPlaybackFinished(id, DEFAULT_MEDIA_PLAYER_STATE); - } - } - m_playbackFinished = false; - }); - return true; -} - -bool TestMediaPlayer::stop(avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId id) { - if (!m_playbackFinished || m_observers.empty()) { - return false; - } - for (const auto& observer : m_observers) { - if (m_playbackFinished) { - observer->onPlaybackStopped(id, DEFAULT_MEDIA_PLAYER_STATE); - } - } - m_playbackFinished = false; - return true; -} - -// TODO Add implementation -bool TestMediaPlayer::pause(avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId id) { - return true; -} - -// TODO Add implementation -bool TestMediaPlayer::resume(avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId id) { - return true; -} - -std::chrono::milliseconds TestMediaPlayer::getOffset(avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId id) { - return std::chrono::milliseconds::zero(); -} - -void TestMediaPlayer::addObserver( - std::shared_ptr playerObserver) { - m_observers.insert(playerObserver); -} - -void TestMediaPlayer::removeObserver( - std::shared_ptr playerObserver) { - m_observers.erase(playerObserver); -} - -uint64_t TestMediaPlayer::getNumBytesBuffered() { - return 0; -} - -avsCommon::utils::Optional TestMediaPlayer::getMediaPlayerState( - avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId id) { - auto offset = getOffset(id); - auto state = avsCommon::utils::mediaPlayer::MediaPlayerState(); - state.offset = offset; - return avsCommon::utils::Optional(state); -} - -} // namespace test -} // namespace integration -} // namespace alexaClientSDK diff --git a/Integration/src/TestMessageSender.cpp b/Integration/src/TestMessageSender.cpp deleted file mode 100644 index 37f88e6e23..0000000000 --- a/Integration/src/TestMessageSender.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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 "ACL/AVSConnectionManager.h" -#include "Integration/TestMessageSender.h" - -using namespace alexaClientSDK; -using namespace acl; -using namespace avsCommon::avs; -using namespace avsCommon::sdkInterfaces; -using namespace avsCommon::utils; - -namespace alexaClientSDK { -namespace integration { -namespace test { - -TestMessageSender::TestMessageSender( - std::shared_ptr messageRouter, - bool isEnabled, - std::shared_ptr connectionStatusObserver, - std::shared_ptr messageObserver) : - RequiresShutdown{"TestMessageSender"} { - m_connectionManager = - acl::AVSConnectionManager::create(messageRouter, isEnabled, {connectionStatusObserver}, {messageObserver}); -} - -void TestMessageSender::sendMessage(std::shared_ptr request) { - m_connectionManager->sendMessage(request); - SendParams sendState; - std::unique_lock lock(m_mutex); - sendState.type = SendParams::Type::SEND; - sendState.request = request; - m_queue.push_back(sendState); - m_wakeTrigger.notify_all(); -} - -TestMessageSender::SendParams TestMessageSender::waitForNext(const std::chrono::seconds duration) { - SendParams ret; - std::unique_lock lock(m_mutex); - if (!m_wakeTrigger.wait_for(lock, duration, [this]() { return !m_queue.empty(); })) { - ret.type = SendParams::Type::TIMEOUT; - return ret; - } - ret = m_queue.front(); - m_queue.pop_front(); - return ret; -} - -void TestMessageSender::enable() { - m_connectionManager->enable(); -} - -void TestMessageSender::disable() { - m_connectionManager->disable(); -} - -bool TestMessageSender::isEnabled() { - return m_connectionManager->isEnabled(); -} - -void TestMessageSender::reconnect() { - m_connectionManager->reconnect(); -} - -void TestMessageSender::setAVSGateway(const std::string& avsGateway) { - m_connectionManager->setAVSGateway(avsGateway); -} - -void TestMessageSender::addConnectionStatusObserver( - std::shared_ptr observer) { - m_connectionManager->addConnectionStatusObserver(observer); -} - -void TestMessageSender::removeConnectionStatusObserver( - std::shared_ptr observer) { - m_connectionManager->removeConnectionStatusObserver(observer); -} - -void TestMessageSender::addMessageObserver( - std::shared_ptr observer) { - m_connectionManager->addMessageObserver(observer); -} - -void TestMessageSender::doShutdown() { - m_connectionManager->shutdown(); -} - -void TestMessageSender::removeMessageObserver( - std::shared_ptr observer) { - m_connectionManager->removeMessageObserver(observer); -} - -std::shared_ptr TestMessageSender::getConnectionManager() const { - return m_connectionManager; -} - -} // namespace test -} // namespace integration -} // namespace alexaClientSDK diff --git a/Integration/src/TestSpeechSynthesizerObserver.cpp b/Integration/src/TestSpeechSynthesizerObserver.cpp deleted file mode 100644 index a2efec6dbb..0000000000 --- a/Integration/src/TestSpeechSynthesizerObserver.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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 "Integration/TestSpeechSynthesizerObserver.h" - -namespace alexaClientSDK { -namespace integration { -namespace test { - -using avsCommon::sdkInterfaces::SpeechSynthesizerObserverInterface; - -TestSpeechSynthesizerObserver::TestSpeechSynthesizerObserver() : - m_state(SpeechSynthesizerObserverInterface::SpeechSynthesizerState::FINISHED) { -} - -void TestSpeechSynthesizerObserver::onStateChanged( - SpeechSynthesizerObserverInterface::SpeechSynthesizerState state, - const avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId mediaSourceId, - const avsCommon::utils::Optional& mediaPlayerState, - const std::vector& audioAnalyzerState) { - std::unique_lock lock(m_mutex); - m_state = state; - m_queue.push_back(state); - m_wakeTrigger.notify_all(); -} - -bool TestSpeechSynthesizerObserver::checkState( - const SpeechSynthesizerObserverInterface::SpeechSynthesizerState expectedState, - const std::chrono::seconds duration) { - // Pull the front of the state queue - SpeechSynthesizerObserverInterface::SpeechSynthesizerState hold = waitForNext(duration); - return (hold == expectedState); -} - -SpeechSynthesizerObserverInterface::SpeechSynthesizerState TestSpeechSynthesizerObserver::waitForNext( - const std::chrono::seconds duration) { - SpeechSynthesizerObserverInterface::SpeechSynthesizerState ret; - std::unique_lock lock(m_mutex); - if (!m_wakeTrigger.wait_for(lock, duration, [this]() { return !m_queue.empty(); })) { - return m_state; - } - - ret = m_queue.front(); - m_queue.pop_front(); - return ret; -} - -SpeechSynthesizerObserverInterface::SpeechSynthesizerState TestSpeechSynthesizerObserver::getCurrentState() { - return m_state; -} - -} // namespace test -} // namespace integration -} // namespace alexaClientSDK diff --git a/Integration/test/AlertsIntegrationTest.cpp b/Integration/test/AlertsIntegrationTest.cpp deleted file mode 100644 index b9e276df52..0000000000 --- a/Integration/test/AlertsIntegrationTest.cpp +++ /dev/null @@ -1,1449 +0,0 @@ -/* - * 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. - */ - -/// @file AlertsIntegrationTest.cpp - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include