417 lines
18 KiB
C++
417 lines
18 KiB
C++
/*
|
|
* Copyright 2018-2020 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 <memory>
|
|
|
|
#include <gtest/gtest.h>
|
|
#include <gmock/gmock.h>
|
|
|
|
#include <AVSCommon/SDKInterfaces/CapabilitiesObserverInterface.h>
|
|
#include <CapabilitiesDelegate/CapabilitiesDelegate.h>
|
|
#include <CapabilitiesDelegate/PostConnectCapabilitiesPublisher.h>
|
|
#include <CapabilitiesDelegate/Storage/CapabilitiesDelegateStorageInterface.h>
|
|
#include <CapabilitiesDelegate/Utils/DiscoveryUtils.h>
|
|
|
|
#include "MockAuthDelegate.h"
|
|
#include "MockCapabilitiesObserver.h"
|
|
#include "MockCapabilitiesStorage.h"
|
|
|
|
namespace alexaClientSDK {
|
|
namespace capabilitiesDelegate {
|
|
namespace test {
|
|
|
|
using namespace avsCommon::sdkInterfaces;
|
|
using namespace avsCommon::sdkInterfaces::endpoints;
|
|
using namespace avsCommon::avs;
|
|
using namespace ::testing;
|
|
|
|
/**
|
|
* Mock discovery event sender class.
|
|
*/
|
|
class MockDiscoveryEventSender : public DiscoveryEventSenderInterface {
|
|
public:
|
|
MOCK_METHOD1(addDiscoveryStatusObserver, void(const std::shared_ptr<DiscoveryStatusObserverInterface>&));
|
|
MOCK_METHOD1(removeDiscoveryStatusObserver, void(const std::shared_ptr<DiscoveryStatusObserverInterface>&));
|
|
MOCK_METHOD1(onAlexaEventProcessedReceived, void(const std::string&));
|
|
};
|
|
|
|
/**
|
|
* Create a test @c AVSDiscoveryEndpointAttributes.
|
|
* @return a @c AVSDiscoveryEndpointAttributes structure.
|
|
*/
|
|
AVSDiscoveryEndpointAttributes createEndpointAttributes() {
|
|
AVSDiscoveryEndpointAttributes attributes;
|
|
|
|
attributes.endpointId = "TEST_ENDPOINT_ID";
|
|
attributes.description = "TEST_DESCRIPTION";
|
|
attributes.manufacturerName = "TEST_MANUFACTURER_NAME";
|
|
attributes.displayCategories = {"TEST_DISPLAY_CATEGORY"};
|
|
|
|
return attributes;
|
|
}
|
|
|
|
/**
|
|
* Creates a test @c CapabilityConfiguration.
|
|
* @return a @c CapabilityConfiguration structure.
|
|
*/
|
|
CapabilityConfiguration createCapabilityConfiguration() {
|
|
return CapabilityConfiguration("TEST_TYPE", "TEST_INTERFACE", "TEST_VERSION");
|
|
}
|
|
|
|
/**
|
|
* Test harness for @c CapabilitiesDelegate class.
|
|
*/
|
|
class CapabilitiesDelegateTest : public Test {
|
|
public:
|
|
/**
|
|
* Set up the test harness for running a test.
|
|
*/
|
|
void SetUp() override;
|
|
|
|
/**
|
|
* Tear down the test harness after running a test.
|
|
*/
|
|
void TearDown() override;
|
|
|
|
protected:
|
|
/// The mock Auth Delegate instance.
|
|
std::shared_ptr<MockAuthDelegate> m_mockAuthDelegate;
|
|
|
|
/// The mock Capabilities Storage instance.
|
|
std::shared_ptr<MockCapabilitiesDelegateStorage> m_mockCapabilitiesStorage;
|
|
|
|
/// The mock Capabilities observer instance.
|
|
std::shared_ptr<MockCapabilitiesObserver> m_mockCapabilitiesObserver;
|
|
|
|
/// The data manager required to build the base object
|
|
std::shared_ptr<registrationManager::CustomerDataManager> m_dataManager;
|
|
|
|
/// The instance of the capabilitiesDelegate used in the tests.
|
|
std::shared_ptr<CapabilitiesDelegate> m_capabilitiesDelegate;
|
|
};
|
|
|
|
void CapabilitiesDelegateTest::SetUp() {
|
|
m_mockCapabilitiesStorage = std::make_shared<StrictMock<MockCapabilitiesDelegateStorage>>();
|
|
m_mockAuthDelegate = std::make_shared<StrictMock<MockAuthDelegate>>();
|
|
m_mockCapabilitiesObserver = std::make_shared<StrictMock<MockCapabilitiesObserver>>();
|
|
m_dataManager = std::make_shared<registrationManager::CustomerDataManager>();
|
|
|
|
/// 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.
|
|
m_mockCapabilitiesObserver = std::make_shared<StrictMock<MockCapabilitiesObserver>>();
|
|
EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _))
|
|
.WillOnce(
|
|
Invoke([](CapabilitiesObserverInterface::State newState, CapabilitiesObserverInterface::Error newError) {
|
|
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::UNINITIALIZED);
|
|
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNINITIALIZED);
|
|
}));
|
|
|
|
m_capabilitiesDelegate->addCapabilitiesObserver(m_mockCapabilitiesObserver);
|
|
}
|
|
|
|
void CapabilitiesDelegateTest::TearDown() {
|
|
m_capabilitiesDelegate->shutdown();
|
|
}
|
|
|
|
/**
|
|
* Tests the create method with various configurations.
|
|
*/
|
|
TEST_F(CapabilitiesDelegateTest, test_createMethodWithInvalidParameters) {
|
|
auto instance = CapabilitiesDelegate::create(nullptr, m_mockCapabilitiesStorage, m_dataManager);
|
|
ASSERT_EQ(instance, nullptr);
|
|
|
|
instance = CapabilitiesDelegate::create(m_mockAuthDelegate, nullptr, m_dataManager);
|
|
ASSERT_EQ(instance, nullptr);
|
|
|
|
instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, nullptr);
|
|
ASSERT_EQ(instance, nullptr);
|
|
|
|
EXPECT_CALL(*m_mockCapabilitiesStorage, open()).WillOnce(Return(true));
|
|
instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager);
|
|
ASSERT_NE(instance, nullptr);
|
|
instance->shutdown();
|
|
}
|
|
|
|
/**
|
|
* Tests the init method and if the open(), createDatabase() and load() methods get called on storage.
|
|
*/
|
|
TEST_F(CapabilitiesDelegateTest, test_init) {
|
|
/// Test if creteDatabase fails create method return nullptr.
|
|
EXPECT_CALL(*m_mockCapabilitiesStorage, open()).WillOnce(Return(false));
|
|
EXPECT_CALL(*m_mockCapabilitiesStorage, createDatabase()).Times(1).WillOnce(Return(false));
|
|
auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager);
|
|
ASSERT_EQ(instance, nullptr);
|
|
|
|
/// Happy path.
|
|
EXPECT_CALL(*m_mockCapabilitiesStorage, open()).WillOnce(Return(false));
|
|
EXPECT_CALL(*m_mockCapabilitiesStorage, createDatabase()).WillOnce(Return(true));
|
|
|
|
instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager);
|
|
ASSERT_NE(instance, nullptr);
|
|
instance->shutdown();
|
|
}
|
|
|
|
/**
|
|
* Tests if the invalidateCapabilities method triggers a database cleanup.
|
|
*/
|
|
TEST_F(CapabilitiesDelegateTest, test_invalidateCapabilities) {
|
|
EXPECT_CALL(*m_mockCapabilitiesStorage, clearDatabase()).WillOnce(Return(true));
|
|
m_capabilitiesDelegate->invalidateCapabilities();
|
|
}
|
|
|
|
/**
|
|
* Tests if the clearData method triggers a database cleanup.
|
|
*/
|
|
TEST_F(CapabilitiesDelegateTest, test_clearData) {
|
|
EXPECT_CALL(*m_mockCapabilitiesStorage, clearDatabase()).WillOnce(Return(true));
|
|
m_capabilitiesDelegate->clearData();
|
|
}
|
|
|
|
/**
|
|
* Tests if the addDiscoveryObserver method gets triggered when the addDiscoveryEventSender method is called.
|
|
* Test if the removeDisoveryObserver method gets triggered when the shutdown method is called.
|
|
*/
|
|
TEST_F(CapabilitiesDelegateTest, test_shutdownTriggeresRemoveDiscoveryObserver) {
|
|
auto discoveryEventSender = std::make_shared<StrictMock<MockDiscoveryEventSender>>();
|
|
EXPECT_CALL(*discoveryEventSender, addDiscoveryStatusObserver(_))
|
|
.WillOnce(Invoke([this](const std::shared_ptr<DiscoveryStatusObserverInterface>& observer) {
|
|
EXPECT_EQ(observer, m_capabilitiesDelegate);
|
|
}));
|
|
m_capabilitiesDelegate->addDiscoveryEventSender(discoveryEventSender);
|
|
|
|
EXPECT_CALL(*discoveryEventSender, removeDiscoveryStatusObserver(_))
|
|
.WillOnce(Invoke([this](const std::shared_ptr<DiscoveryStatusObserverInterface>& observer) {
|
|
EXPECT_EQ(observer, m_capabilitiesDelegate);
|
|
}));
|
|
m_capabilitiesDelegate->shutdown();
|
|
}
|
|
|
|
/**
|
|
* Tests the addCapabilitiesObserver() method.
|
|
*/
|
|
TEST_F(CapabilitiesDelegateTest, test_addCapabilitiesObserver) {
|
|
/// Adding null observer doesn't fail.
|
|
m_capabilitiesDelegate->addCapabilitiesObserver(nullptr);
|
|
|
|
/// Add a new observer and it receives notifications of the current capabilities state.
|
|
auto mockObserver = std::make_shared<StrictMock<MockCapabilitiesObserver>>();
|
|
|
|
EXPECT_CALL(*mockObserver, onCapabilitiesStateChange(_, _))
|
|
.WillOnce(
|
|
Invoke([](CapabilitiesObserverInterface::State newState, CapabilitiesObserverInterface::Error newError) {
|
|
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::UNINITIALIZED);
|
|
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNINITIALIZED);
|
|
}));
|
|
m_capabilitiesDelegate->addCapabilitiesObserver(mockObserver);
|
|
|
|
/// Add existing observer and it does not get any notifications (strict mock would catch any extra notifications)
|
|
m_capabilitiesDelegate->addCapabilitiesObserver(mockObserver);
|
|
}
|
|
|
|
/**
|
|
* Tests for onDiscoveryCompleted() method.
|
|
*/
|
|
TEST_F(CapabilitiesDelegateTest, test_onDiscoveryCompleted) {
|
|
std::unordered_map<std::string, std::string> addOrUpdateReportEndpoints = {{"add_1", "1"}, {"update_1", "2"}};
|
|
std::unordered_map<std::string, std::string> deleteReportEndpoints = {{"delete_1", "1"}};
|
|
|
|
EXPECT_CALL(*m_mockCapabilitiesStorage, store(addOrUpdateReportEndpoints)).WillOnce(Return(true));
|
|
EXPECT_CALL(*m_mockCapabilitiesStorage, erase(deleteReportEndpoints)).WillOnce(Return(true));
|
|
|
|
EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _))
|
|
.WillOnce(
|
|
Invoke([](CapabilitiesObserverInterface::State newState, CapabilitiesObserverInterface::Error newError) {
|
|
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::SUCCESS);
|
|
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::SUCCESS);
|
|
}));
|
|
|
|
/// Check if store and erase is triggered and if observer gets notified.
|
|
m_capabilitiesDelegate->onDiscoveryCompleted(addOrUpdateReportEndpoints, deleteReportEndpoints);
|
|
|
|
/// Check removing observer does not send notifications to the observer.
|
|
m_capabilitiesDelegate->removeCapabilitiesObserver(m_mockCapabilitiesObserver);
|
|
|
|
EXPECT_CALL(*m_mockCapabilitiesStorage, store(addOrUpdateReportEndpoints)).WillOnce(Return(true));
|
|
EXPECT_CALL(*m_mockCapabilitiesStorage, erase(deleteReportEndpoints)).WillOnce(Return(true));
|
|
|
|
/// Only store and erase is triggered, observer does not get notified (should fail as we use strict mock for
|
|
/// observer).
|
|
m_capabilitiesDelegate->onDiscoveryCompleted(addOrUpdateReportEndpoints, deleteReportEndpoints);
|
|
}
|
|
|
|
/**
|
|
* Check onDiscoveryCompleted() but storage to device fails.
|
|
*/
|
|
TEST_F(CapabilitiesDelegateTest, test_onDiscoveryCompletedButStorageFails) {
|
|
std::unordered_map<std::string, std::string> addOrUpdateReportEndpoints = {{"add_1", "1"}, {"update_1", "2"}};
|
|
std::unordered_map<std::string, std::string> deleteReportEndpoints = {{"delete_1", "1"}};
|
|
|
|
EXPECT_CALL(*m_mockCapabilitiesStorage, store(addOrUpdateReportEndpoints)).WillOnce(Return(false));
|
|
|
|
EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _))
|
|
.WillOnce(
|
|
Invoke([](CapabilitiesObserverInterface::State newState, CapabilitiesObserverInterface::Error newError) {
|
|
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::FATAL_ERROR);
|
|
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNKNOWN_ERROR);
|
|
}));
|
|
|
|
/// Check if store and erase is triggered and if observer gets notified.
|
|
m_capabilitiesDelegate->onDiscoveryCompleted(addOrUpdateReportEndpoints, deleteReportEndpoints);
|
|
}
|
|
|
|
/**
|
|
* Check notifications when onDiscoveryFailure() method is called.
|
|
*/
|
|
TEST_F(CapabilitiesDelegateTest, test_onDicoveryFailure) {
|
|
/// validate retriable error response
|
|
EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _))
|
|
.WillOnce(
|
|
Invoke([](CapabilitiesObserverInterface::State newState, CapabilitiesObserverInterface::Error newError) {
|
|
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::RETRIABLE_ERROR);
|
|
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::SERVER_INTERNAL_ERROR);
|
|
}));
|
|
|
|
m_capabilitiesDelegate->onDiscoveryFailure(MessageRequestObserverInterface::Status::SERVER_INTERNAL_ERROR_V2);
|
|
|
|
/// validate invalid auth error response
|
|
EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _))
|
|
.WillOnce(
|
|
Invoke([](CapabilitiesObserverInterface::State newState, CapabilitiesObserverInterface::Error newError) {
|
|
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::FATAL_ERROR);
|
|
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::FORBIDDEN);
|
|
}));
|
|
|
|
m_capabilitiesDelegate->onDiscoveryFailure(MessageRequestObserverInterface::Status::INVALID_AUTH);
|
|
|
|
/// validate bad request error response
|
|
EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _))
|
|
.WillOnce(
|
|
Invoke([](CapabilitiesObserverInterface::State newState, CapabilitiesObserverInterface::Error newError) {
|
|
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::FATAL_ERROR);
|
|
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::BAD_REQUEST);
|
|
}));
|
|
|
|
m_capabilitiesDelegate->onDiscoveryFailure(MessageRequestObserverInterface::Status::BAD_REQUEST);
|
|
|
|
/// other responses
|
|
EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _))
|
|
.WillOnce(
|
|
Invoke([](CapabilitiesObserverInterface::State newState, CapabilitiesObserverInterface::Error newError) {
|
|
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::RETRIABLE_ERROR);
|
|
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNKNOWN_ERROR);
|
|
}));
|
|
|
|
m_capabilitiesDelegate->onDiscoveryFailure(MessageRequestObserverInterface::Status::THROTTLED);
|
|
}
|
|
|
|
/**
|
|
* Tests if the registerEndpoint returns true for new endpoints, and false for invalid input or existing endpoint.
|
|
*/
|
|
TEST_F(CapabilitiesDelegateTest, test_registerEndpoint) {
|
|
/// Invalid AVSDiscoveryEndpointAttributes.
|
|
|
|
auto attributes = createEndpointAttributes();
|
|
auto capabilityConfig = createCapabilityConfiguration();
|
|
|
|
/// Empty Capabilities.
|
|
ASSERT_FALSE(m_capabilitiesDelegate->registerEndpoint(attributes, std::vector<CapabilityConfiguration>()));
|
|
|
|
/// Invalid CapabilityConfiguration.
|
|
capabilityConfig.version = "";
|
|
ASSERT_FALSE(m_capabilitiesDelegate->registerEndpoint(attributes, {capabilityConfig}));
|
|
|
|
/// Registering with same endpointAttributes fails.
|
|
ASSERT_TRUE(m_capabilitiesDelegate->registerEndpoint(attributes, {createCapabilityConfiguration()}));
|
|
ASSERT_FALSE(m_capabilitiesDelegate->registerEndpoint(attributes, {createCapabilityConfiguration()}));
|
|
|
|
/// EndpointAttributes does not have endpointID which is required.
|
|
attributes.endpointId = "";
|
|
ASSERT_FALSE(m_capabilitiesDelegate->registerEndpoint(attributes, {capabilityConfig}));
|
|
}
|
|
|
|
/***
|
|
* Tests if the createPostConnectOperation() creates the @c PostConnectCapabilitiesPublisher when registered endpoint
|
|
* configurations are different from the ones in storage.
|
|
*/
|
|
TEST_F(CapabilitiesDelegateTest, test_createPostConnectOperationWithDifferentEndpointConfigs) {
|
|
auto endpointAttributes = createEndpointAttributes();
|
|
auto capabilityConfig = createCapabilityConfiguration();
|
|
|
|
std::string endpointConfig = "TEST_CONFIG";
|
|
EXPECT_CALL(*m_mockCapabilitiesStorage, open()).Times(1).WillOnce(Return(true));
|
|
EXPECT_CALL(*m_mockCapabilitiesStorage, load(_))
|
|
.Times(1)
|
|
.WillOnce(
|
|
Invoke([endpointAttributes, endpointConfig](std::unordered_map<std::string, std::string>* storedEndpoints) {
|
|
storedEndpoints->insert({endpointAttributes.endpointId, endpointConfig});
|
|
return true;
|
|
}));
|
|
|
|
auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager);
|
|
instance->registerEndpoint(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();
|
|
instance->shutdown();
|
|
|
|
ASSERT_NE(publisher, nullptr);
|
|
}
|
|
|
|
/**
|
|
* Tests if the createPostConnectOperation() does not create a new @c PostConnectCapabilitiesPublisher when registered
|
|
* endpoint configurations are same as the ones in storage.
|
|
*/
|
|
TEST_F(CapabilitiesDelegateTest, test_createPostConnectOperationWithSameEndpointConfigs) {
|
|
auto endpointAttributes = createEndpointAttributes();
|
|
auto capabilityConfig = createCapabilityConfiguration();
|
|
std::vector<CapabilityConfiguration> capabilityConfigs = {capabilityConfig};
|
|
|
|
std::string endpointConfig = utils::getEndpointConfigJson(endpointAttributes, capabilityConfigs);
|
|
EXPECT_CALL(*m_mockCapabilitiesStorage, open()).Times(1).WillOnce(Return(true));
|
|
EXPECT_CALL(*m_mockCapabilitiesStorage, load(_))
|
|
.Times(1)
|
|
.WillOnce(
|
|
Invoke([endpointAttributes, endpointConfig](std::unordered_map<std::string, std::string>* storedEndpoints) {
|
|
storedEndpoints->insert({endpointAttributes.endpointId, endpointConfig});
|
|
return true;
|
|
}));
|
|
|
|
auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager);
|
|
instance->registerEndpoint(endpointAttributes, capabilityConfigs);
|
|
|
|
/// Endpoint config is same as the endpoint config created with the test endpoint attributes so a
|
|
/// post connect operation is not created.
|
|
auto publisher = instance->createPostConnectOperation();
|
|
|
|
ASSERT_EQ(publisher, nullptr);
|
|
}
|
|
|
|
/**
|
|
* Test if the CapabilitiesDelegate calls the clearDatabase() method when the onAVSGatewayChanged() method is called.
|
|
*/
|
|
TEST_F(CapabilitiesDelegateTest, test_onAVSGatewayChangedNotification) {
|
|
EXPECT_CALL(*m_mockCapabilitiesStorage, clearDatabase()).WillOnce(Return(true));
|
|
m_capabilitiesDelegate->onAVSGatewayChanged("TEST_GATEWAY");
|
|
}
|
|
|
|
} // namespace test
|
|
} // namespace capabilitiesDelegate
|
|
} // namespace alexaClientSDK
|