diff options
author | Frank Ronneburg <fronneburg@xevo.com> | 2018-04-12 09:23:41 +0900 |
---|---|---|
committer | fronneburg <fronneburg@xevo.com> | 2018-04-11 17:25:49 -0700 |
commit | f9d0a8c9d9117b91deda42c9c9148bd5917e6dd2 (patch) | |
tree | 1d0ad97d3e1edf6f37a11cb5c320f1cfc70404e9 | |
parent | e0caccd349cd66a21508c00bcfab8f288d688d86 (diff) | |
download | sdl_core-f9d0a8c9d9117b91deda42c9c9148bd5917e6dd2.tar.gz |
Merge pull request #241 in NAR/sdl-core from feat/transport_unit_tests to feature/Ford-WiFi
* commit 'ebf724367d56d40d18dde5fb2d8af928b985d18d':
UT: add transport adapter, adapter listener, manager unit tests
Fix NetworkInterfaceListener issues detected by UT
UT: Add NetworkInterfaceListener unit tests
Append htonl() for INADDR_ANY
Fix TcpClientListener issues detected by UT
UT: Add TcpClientListener unit tests
Refactor: create NetworkInterfaceListener interface class
12 files changed, 1534 insertions, 180 deletions
diff --git a/src/components/transport_manager/include/transport_manager/tcp/network_interface_listener.h b/src/components/transport_manager/include/transport_manager/tcp/network_interface_listener.h index dd6e94b2f3..91ddb3839c 100644 --- a/src/components/transport_manager/include/transport_manager/tcp/network_interface_listener.h +++ b/src/components/transport_manager/include/transport_manager/tcp/network_interface_listener.h @@ -33,160 +33,38 @@ #ifndef SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_TCP_NETWORK_INTERFACE_LISTENER_H_ #define SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_TCP_NETWORK_INTERFACE_LISTENER_H_ -#include <map> -#include <string> -#include <vector> - -#include <netinet/in.h> -#include "utils/macro.h" -#include "utils/threads/thread_delegate.h" - -class Thread; -struct ifaddrmsg; - namespace transport_manager { namespace transport_adapter { -class TcpClientListener; - -/** - * @brief Struct to keep network interface's status flags and IP addresses - */ -class InterfaceStatus { - public: - InterfaceStatus() : flags_(0), has_ipv4_(false), has_ipv6_(false) {} - ~InterfaceStatus() {} - - bool IsAvailable() const; - bool IsLoopback() const; - // only for debugging output - unsigned int GetFlags() const { - return flags_; - } - - bool HasIPAddress() const; - std::string GetIPv4Address() const; - std::string GetIPv6Address() const; - - void SetFlags(unsigned int flags) { - flags_ = flags; - } - - // specify NULL to remove existing address - void SetIPv4Address(struct in_addr* addr); - void SetIPv6Address(struct in6_addr* addr); - - private: - unsigned int flags_; - bool has_ipv4_; - bool has_ipv6_; - struct in_addr ipv4_address_; - struct in6_addr ipv6_address_; -}; - -typedef std::map<std::string, InterfaceStatus> InterfaceStatusTable; - /** * @brief Listener to detect various events on network interfaces */ class NetworkInterfaceListener { public: /** - * @brief Constructor - * - * @param tcp_client_listener an instance of TcpClientListener which receives - * status updates - * @param designated_interface if we want to listen only on a specific - * network interface, specify its name - */ - NetworkInterfaceListener(TcpClientListener* tcp_client_listener, - const std::string designated_interface = ""); - - /** * @brief Destructor */ - virtual ~NetworkInterfaceListener(); + virtual ~NetworkInterfaceListener() {} /** * @brief Initialize this listener */ - bool Init(); + virtual bool Init() = 0; /** * @brief Deinitialize this listener */ - void Deinit(); + virtual void Deinit() = 0; /** * @brief Start this listener */ - bool Start(); + virtual bool Start() = 0; /** * @brief Stop this listener */ - bool Stop(); - - private: - // Struct to hold an event on a network interface. - // The event can be either an update on flags or an update on IP address. - struct EventParam { - unsigned int if_index; - unsigned int flags; - struct sockaddr_storage address; - - EventParam(int interface_index, unsigned int interface_flags = 0) - : if_index(interface_index), flags(interface_flags) {} - }; - - // parent class which we will notify the events to - TcpClientListener* tcp_client_listener_; - // if configured, NetworkInterfaceListener will always look into the IP - // addresses of this interface - const std::string designated_interface_; - - // a map to store status of each interface - InterfaceStatusTable status_table_; - // this is the name of the interface we are currently focusing on - std::string selected_interface_; - // previous IP addresses that we have notified - std::string notified_ipv4_addr_; - std::string notified_ipv6_addr_; - - int socket_; - int pipe_fds_[2]; - threads::Thread* thread_; - - void Loop(); - bool StopLoop(); - - // reset status_table_ by fetching current status of each interface - bool InitializeStatus(); - // update status_table_ by applying the events - bool UpdateStatus(uint16_t type, std::vector<EventParam>& params); - // update notified_ipv4_addr_ and notified_ipv6_addr_ then notify the parent - // class of the change if necessary - void NotifyIPAddresses(); - // Select an appropriate network interface that we will get IP addresses. Also - // update selected_interface_. - const std::string SelectInterface(); - // convert ifaddrmsg to a list of EventParam structs - std::vector<EventParam> ParseIFAddrMessage(struct ifaddrmsg* message, - unsigned int size); - // for debugging - void DumpTable() const; - - class ListenerThreadDelegate : public threads::ThreadDelegate { - public: - explicit ListenerThreadDelegate(NetworkInterfaceListener* parent); - virtual void threadMain(); - void exitThreadMain(); - - private: - NetworkInterfaceListener* parent_; - }; - - DISALLOW_COPY_AND_ASSIGN(NetworkInterfaceListener); + virtual bool Stop() = 0; }; } // namespace transport_adapter diff --git a/src/components/transport_manager/include/transport_manager/tcp/network_interface_listener_impl.h b/src/components/transport_manager/include/transport_manager/tcp/network_interface_listener_impl.h new file mode 100644 index 0000000000..972419f791 --- /dev/null +++ b/src/components/transport_manager/include/transport_manager/tcp/network_interface_listener_impl.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2018 Xevo Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * Neither the names of the copyright holders nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_TCP_NETWORK_INTERFACE_LISTENER_IMPL_H_ +#define SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_TCP_NETWORK_INTERFACE_LISTENER_IMPL_H_ + +#include <map> +#include <string> +#include <vector> + +#include <netinet/in.h> +#include "transport_manager/tcp/network_interface_listener.h" +#include "utils/macro.h" +#include "utils/threads/thread_delegate.h" + +class Thread; +struct ifaddrmsg; + +namespace transport_manager { +namespace transport_adapter { + +class TcpClientListener; + +/** + * @brief Struct to keep network interface's status flags and IP addresses + */ +class InterfaceStatus { + public: + InterfaceStatus() : flags_(0), has_ipv4_(false), has_ipv6_(false) {} + ~InterfaceStatus() {} + + bool IsAvailable() const; + bool IsLoopback() const; + // only for debugging output + unsigned int GetFlags() const { + return flags_; + } + + bool HasIPAddress() const; + std::string GetIPv4Address() const; + std::string GetIPv6Address() const; + + void SetFlags(unsigned int flags) { + flags_ = flags; + } + + // specify NULL to remove existing address + void SetIPv4Address(struct in_addr* addr); + void SetIPv6Address(struct in6_addr* addr); + + private: + unsigned int flags_; + bool has_ipv4_; + bool has_ipv6_; + struct in_addr ipv4_address_; + struct in6_addr ipv6_address_; +}; + +typedef std::map<std::string, InterfaceStatus> InterfaceStatusTable; + +/** + * @brief Listener to detect various events on network interfaces + */ +class NetworkInterfaceListenerImpl : public NetworkInterfaceListener { + public: + /** + * @brief Constructor + * + * @param tcp_client_listener an instance of TcpClientListener which receives + * status updates + * @param designated_interface if we want to listen only on a specific + * network interface, specify its name + */ + NetworkInterfaceListenerImpl(TcpClientListener* tcp_client_listener, + const std::string designated_interface = ""); + + /** + * @brief Destructor + */ + virtual ~NetworkInterfaceListenerImpl(); + + /** + * @brief Initialize this listener + */ + bool Init() OVERRIDE; + + /** + * @brief Deinitialize this listener + */ + void Deinit() OVERRIDE; + + /** + * @brief Start this listener + */ + bool Start() OVERRIDE; + + /** + * @brief Stop this listener + */ + bool Stop() OVERRIDE; + +#ifdef BUILD_TESTS + int GetSocket() const { + return socket_; + } + + threads::Thread* GetThread() const { + return thread_; + } + + void OverwriteStatusTable(const InterfaceStatusTable dummy_table) { + status_table_ = dummy_table; + } + + void testCallNotifyIPAddresses() { + NotifyIPAddresses(); + } + + const std::string& GetSelectedInterfaceName() const { + return selected_interface_; + } +#endif // BUILD_TESTS + + private: + // Struct to hold an event on a network interface. + // The event can be either an update on flags or an update on IP address. + struct EventParam { + unsigned int if_index; + unsigned int flags; + struct sockaddr_storage address; + + EventParam(int interface_index, unsigned int interface_flags = 0) + : if_index(interface_index), flags(interface_flags) {} + }; + + // parent class which we will notify the events to + TcpClientListener* tcp_client_listener_; + // if configured, NetworkInterfaceListener will always look into the IP + // addresses of this interface + const std::string designated_interface_; + + // a map to store status of each interface + InterfaceStatusTable status_table_; + // this is the name of the interface we are currently focusing on + std::string selected_interface_; + // previous IP addresses that we have notified + std::string notified_ipv4_addr_; + std::string notified_ipv6_addr_; + + int socket_; + int pipe_fds_[2]; + threads::Thread* thread_; + + void Loop(); + bool StopLoop(); + + // reset status_table_ by fetching current status of each interface + bool InitializeStatus(); + // update status_table_ by applying the events + bool UpdateStatus(uint16_t type, std::vector<EventParam>& params); + // update notified_ipv4_addr_ and notified_ipv6_addr_ then notify the parent + // class of the change if necessary + void NotifyIPAddresses(); + // Select an appropriate network interface that we will get IP addresses. Also + // update selected_interface_. + const std::string SelectInterface(); + // convert ifaddrmsg to a list of EventParam structs + std::vector<EventParam> ParseIFAddrMessage(struct ifaddrmsg* message, + unsigned int size); + // for debugging + void DumpTable() const; + + class ListenerThreadDelegate : public threads::ThreadDelegate { + public: + explicit ListenerThreadDelegate(NetworkInterfaceListenerImpl* parent); + virtual void threadMain(); + void exitThreadMain(); + + private: + NetworkInterfaceListenerImpl* parent_; + }; + + DISALLOW_COPY_AND_ASSIGN(NetworkInterfaceListenerImpl); +}; + +} // namespace transport_adapter +} // namespace transport_manager + +#endif // SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_TCP_NETWORK_INTERFACE_LISTENER_IMPL_H_ diff --git a/src/components/transport_manager/include/transport_manager/tcp/tcp_client_listener.h b/src/components/transport_manager/include/transport_manager/tcp/tcp_client_listener.h index 90af32ec39..a44387a282 100644 --- a/src/components/transport_manager/include/transport_manager/tcp/tcp_client_listener.h +++ b/src/components/transport_manager/include/transport_manager/tcp/tcp_client_listener.h @@ -115,10 +115,14 @@ class TcpClientListener : public ClientConnectionListener { * @brief Called from NetworkInterfaceListener when IP address of the network * interface is changed. */ - void OnIPAddressUpdated(const std::string ipv4_addr, - const std::string ipv6_addr); + virtual void OnIPAddressUpdated(const std::string ipv4_addr, + const std::string ipv6_addr); #ifdef BUILD_TESTS + void set_network_interface_listener(NetworkInterfaceListener* listener) { + interface_listener_ = listener; + } + uint16_t port() const { return port_; } diff --git a/src/components/transport_manager/src/tcp/network_interface_listener.cpp b/src/components/transport_manager/src/tcp/network_interface_listener_impl.cpp index 0bf317dc2e..f78617aa91 100644 --- a/src/components/transport_manager/src/tcp/network_interface_listener.cpp +++ b/src/components/transport_manager/src/tcp/network_interface_listener_impl.cpp @@ -30,7 +30,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "transport_manager/tcp/network_interface_listener.h" +#include "transport_manager/tcp/network_interface_listener_impl.h" #include <arpa/inet.h> #include <asm/types.h> @@ -74,7 +74,7 @@ bool InterfaceStatus::HasIPAddress() const { std::string InterfaceStatus::GetIPv4Address() const { char buf[INET_ADDRSTRLEN] = ""; - if (has_ipv4_) { + if (has_ipv4_ && IsAvailable()) { inet_ntop(AF_INET, &ipv4_address_, buf, sizeof(buf)); } return std::string(buf); @@ -82,7 +82,7 @@ std::string InterfaceStatus::GetIPv4Address() const { std::string InterfaceStatus::GetIPv6Address() const { char buf[INET6_ADDRSTRLEN] = ""; - if (has_ipv6_) { + if (has_ipv6_ && IsAvailable()) { inet_ntop(AF_INET6, &ipv6_address_, buf, sizeof(buf)); } return std::string(buf); @@ -106,7 +106,7 @@ void InterfaceStatus::SetIPv6Address(struct in6_addr* addr) { } } -NetworkInterfaceListener::NetworkInterfaceListener( +NetworkInterfaceListenerImpl::NetworkInterfaceListenerImpl( TcpClientListener* tcp_client_listener, const std::string designated_interface) : tcp_client_listener_(tcp_client_listener) @@ -116,11 +116,11 @@ NetworkInterfaceListener::NetworkInterfaceListener( , notified_ipv6_addr_("") , socket_(-1) { pipe_fds_[0] = pipe_fds_[1] = -1; - thread_ = threads::CreateThread("NetworkInterfaceListener", + thread_ = threads::CreateThread("NetworkInterfaceListenerImpl", new ListenerThreadDelegate(this)); } -NetworkInterfaceListener::~NetworkInterfaceListener() { +NetworkInterfaceListenerImpl::~NetworkInterfaceListenerImpl() { LOG4CXX_AUTO_TRACE(logger_); Stop(); @@ -130,7 +130,7 @@ NetworkInterfaceListener::~NetworkInterfaceListener() { threads::DeleteThread(thread_); } -bool NetworkInterfaceListener::Init() { +bool NetworkInterfaceListenerImpl::Init() { LOG4CXX_AUTO_TRACE(logger_); if (socket_ >= 0) { @@ -176,7 +176,7 @@ bool NetworkInterfaceListener::Init() { return true; } -void NetworkInterfaceListener::Deinit() { +void NetworkInterfaceListenerImpl::Deinit() { LOG4CXX_AUTO_TRACE(logger_); if (socket_ >= 0) { @@ -193,7 +193,7 @@ void NetworkInterfaceListener::Deinit() { } } -bool NetworkInterfaceListener::Start() { +bool NetworkInterfaceListenerImpl::Start() { LOG4CXX_AUTO_TRACE(logger_); if (socket_ < 0) { @@ -215,7 +215,7 @@ bool NetworkInterfaceListener::Start() { return true; } -bool NetworkInterfaceListener::Stop() { +bool NetworkInterfaceListenerImpl::Stop() { LOG4CXX_AUTO_TRACE(logger_); if (!thread_->is_running()) { @@ -229,7 +229,7 @@ bool NetworkInterfaceListener::Stop() { return true; } -void NetworkInterfaceListener::Loop() { +void NetworkInterfaceListenerImpl::Loop() { LOG4CXX_AUTO_TRACE(logger_); InitializeStatus(); @@ -274,6 +274,7 @@ void NetworkInterfaceListener::Loop() { } } +#ifndef BUILD_TESTS // don't enable events from network interface while testing if (FD_ISSET(socket_, &rfds)) { ret = recv(socket_, buf, sizeof(buf), 0); if (ret < 0) { @@ -324,10 +325,11 @@ void NetworkInterfaceListener::Loop() { NotifyIPAddresses(); } +#endif } } -bool NetworkInterfaceListener::StopLoop() { +bool NetworkInterfaceListenerImpl::StopLoop() { LOG4CXX_AUTO_TRACE(logger_); LOG4CXX_INFO(logger_, "Stopping network interface listener"); @@ -348,9 +350,14 @@ bool NetworkInterfaceListener::StopLoop() { return true; } -bool NetworkInterfaceListener::InitializeStatus() { +bool NetworkInterfaceListenerImpl::InitializeStatus() { LOG4CXX_AUTO_TRACE(logger_); +#ifdef BUILD_TESTS + // don't actually call getifaddrs() + return true; +#endif + struct ifaddrs* if_list, *interface; if (getifaddrs(&if_list) != 0) { LOG4CXX_WARN(logger_, @@ -400,8 +407,8 @@ bool NetworkInterfaceListener::InitializeStatus() { return true; } -bool NetworkInterfaceListener::UpdateStatus(uint16_t type, - std::vector<EventParam>& params) { +bool NetworkInterfaceListenerImpl::UpdateStatus( + uint16_t type, std::vector<EventParam>& params) { LOG4CXX_AUTO_TRACE(logger_); for (std::vector<EventParam>::iterator it = params.begin(); @@ -469,7 +476,7 @@ bool NetworkInterfaceListener::UpdateStatus(uint16_t type, return true; } -void NetworkInterfaceListener::NotifyIPAddresses() { +void NetworkInterfaceListenerImpl::NotifyIPAddresses() { LOG4CXX_AUTO_TRACE(logger_); std::string ipv4_addr; @@ -479,9 +486,12 @@ void NetworkInterfaceListener::NotifyIPAddresses() { // note that if interface_name is empty (i.e. no interface is selected), // the IP addresses will be empty if (!interface_name.empty()) { - InterfaceStatus& status = status_table_[interface_name]; - ipv4_addr = status.GetIPv4Address(); - ipv6_addr = status.GetIPv6Address(); + InterfaceStatusTable::iterator it = status_table_.find(interface_name); + if (status_table_.end() != it) { + InterfaceStatus& status = it->second; + ipv4_addr = status.GetIPv4Address(); + ipv6_addr = status.GetIPv6Address(); + } } if (notified_ipv4_addr_ != ipv4_addr || notified_ipv6_addr_ != ipv6_addr) { @@ -499,7 +509,7 @@ void NetworkInterfaceListener::NotifyIPAddresses() { } } -const std::string NetworkInterfaceListener::SelectInterface() { +const std::string NetworkInterfaceListenerImpl::SelectInterface() { LOG4CXX_AUTO_TRACE(logger_); if (!designated_interface_.empty()) { @@ -538,12 +548,13 @@ const std::string NetworkInterfaceListener::SelectInterface() { return selected_interface_; } - return std::string(); + selected_interface_ = ""; + return selected_interface_; } -std::vector<NetworkInterfaceListener::EventParam> -NetworkInterfaceListener::ParseIFAddrMessage(struct ifaddrmsg* message, - unsigned int size) { +std::vector<NetworkInterfaceListenerImpl::EventParam> +NetworkInterfaceListenerImpl::ParseIFAddrMessage(struct ifaddrmsg* message, + unsigned int size) { LOG4CXX_AUTO_TRACE(logger_); std::vector<EventParam> params; @@ -598,7 +609,7 @@ NetworkInterfaceListener::ParseIFAddrMessage(struct ifaddrmsg* message, return params; } -void NetworkInterfaceListener::DumpTable() const { +void NetworkInterfaceListenerImpl::DumpTable() const { LOG4CXX_DEBUG(logger_, "Number of network interfaces: " << status_table_.size()); @@ -616,15 +627,15 @@ void NetworkInterfaceListener::DumpTable() const { } } -NetworkInterfaceListener::ListenerThreadDelegate::ListenerThreadDelegate( - NetworkInterfaceListener* parent) +NetworkInterfaceListenerImpl::ListenerThreadDelegate::ListenerThreadDelegate( + NetworkInterfaceListenerImpl* parent) : parent_(parent) {} -void NetworkInterfaceListener::ListenerThreadDelegate::threadMain() { +void NetworkInterfaceListenerImpl::ListenerThreadDelegate::threadMain() { parent_->Loop(); } -void NetworkInterfaceListener::ListenerThreadDelegate::exitThreadMain() { +void NetworkInterfaceListenerImpl::ListenerThreadDelegate::exitThreadMain() { parent_->StopLoop(); } diff --git a/src/components/transport_manager/src/tcp/tcp_client_listener.cc b/src/components/transport_manager/src/tcp/tcp_client_listener.cc index b6610b77f2..a7d3dfc94c 100644 --- a/src/components/transport_manager/src/tcp/tcp_client_listener.cc +++ b/src/components/transport_manager/src/tcp/tcp_client_listener.cc @@ -62,7 +62,7 @@ #include "utils/make_shared.h" #include "utils/threads/thread.h" #include "transport_manager/transport_adapter/transport_adapter_controller.h" -#include "transport_manager/tcp/network_interface_listener.h" +#include "transport_manager/tcp/network_interface_listener_impl.h" #include "transport_manager/tcp/tcp_device.h" #include "transport_manager/tcp/tcp_socket_connection.h" @@ -90,7 +90,7 @@ TcpClientListener::TcpClientListener(TransportAdapterController* controller, thread_ = threads::CreateThread("TcpClientListener", new ListeningThreadDelegate(this)); interface_listener_ = - new NetworkInterfaceListener(this, designated_interface); + new NetworkInterfaceListenerImpl(this, designated_interface); } TransportAdapter::Error TcpClientListener::Init() { @@ -130,11 +130,11 @@ TransportAdapter::Error TcpClientListener::Init() { void TcpClientListener::Terminate() { LOG4CXX_AUTO_TRACE(logger_); + if (!initialized_) { + return; + } + if (!IsListeningOnSpecificInterface()) { - if (socket_ == -1) { - LOG4CXX_WARN(logger_, "Socket has been closed"); - return; - } DestroyServerSocket(socket_); socket_ = -1; } else { @@ -157,6 +157,7 @@ TcpClientListener::~TcpClientListener() { delete thread_->delegate(); threads::DeleteThread(thread_); Terminate(); + delete interface_listener_; } void SetKeepaliveOptions(const int fd) { @@ -541,7 +542,7 @@ int TcpClientListener::CreateIPv4ServerSocket( struct in_addr ipv4_address; memset(&ipv4_address, 0, sizeof(ipv4_address)); if (interface_name.empty()) { - ipv4_address.s_addr = INADDR_ANY; + ipv4_address.s_addr = htonl(INADDR_ANY); } else if (!GetIPv4Address(interface_name, &ipv4_address)) { return -1; } @@ -597,6 +598,17 @@ bool TcpClientListener::GetIPv4Address(const std::string interface_name, struct in_addr* ip_address) { LOG4CXX_AUTO_TRACE(logger_); +#ifdef BUILD_TESTS + // return a dummy address of INADDR_LOOPBACK + struct in_addr dummy_addr; + dummy_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (ip_address != NULL) { + *ip_address = dummy_addr; + } + return true; +#endif + struct ifaddrs* if_list; if (getifaddrs(&if_list) != 0) { LOG4CXX_WARN(logger_, "getifaddrs failed"); diff --git a/src/components/transport_manager/test/include/transport_manager/tcp/mock_tcp_client_listener.h b/src/components/transport_manager/test/include/transport_manager/tcp/mock_tcp_client_listener.h new file mode 100644 index 0000000000..b7db7c7e64 --- /dev/null +++ b/src/components/transport_manager/test/include/transport_manager/tcp/mock_tcp_client_listener.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018 Xevo Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * Neither the name of the copyright holders nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SRC_COMPONENTS_TRANSPORT_MANAGER_TEST_INCLUDE_TRANSPORT_MANAGER_TCP_MOCK_TCP_CLIENT_LISTENER_H_ +#define SRC_COMPONENTS_TRANSPORT_MANAGER_TEST_INCLUDE_TRANSPORT_MANAGER_TCP_MOCK_TCP_CLIENT_LISTENER_H_ + +#include "gmock/gmock.h" +#include "transport_manager/tcp/tcp_client_listener.h" + +namespace test { +namespace components { +namespace transport_manager_test { + +using namespace ::transport_manager::transport_adapter; + +class MockTcpClientListener : public TcpClientListener { + public: + MockTcpClientListener(TransportAdapterController* controller, + uint16_t port, + bool enable_keepalive, + const std::string designated_interface = "") + : TcpClientListener( + controller, port, enable_keepalive, designated_interface) {} + MOCK_METHOD0(Init, TransportAdapter::Error()); + MOCK_METHOD0(Terminate, void()); + MOCK_CONST_METHOD0(IsInitialised, bool()); + MOCK_METHOD0(StartListening, TransportAdapter::Error()); + MOCK_METHOD0(StopListening, TransportAdapter::Error()); + MOCK_METHOD2(OnIPAddressUpdated, + void(const std::string ipv4_addr, const std::string ipv6_addr)); +}; + +} // namespace transport_manager_test +} // namespace components +} // namespace test + +#endif // SRC_COMPONENTS_TRANSPORT_MANAGER_TEST_INCLUDE_TRANSPORT_MANAGER_TCP_MOCK_TCP_CLIENT_LISTENER_H_ diff --git a/src/components/transport_manager/test/network_interface_listener_test.cc b/src/components/transport_manager/test/network_interface_listener_test.cc new file mode 100644 index 0000000000..8198e42eb2 --- /dev/null +++ b/src/components/transport_manager/test/network_interface_listener_test.cc @@ -0,0 +1,597 @@ +/* + * Copyright (c) 2018 Xevo Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * Neither the name of the copyright holders nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <arpa/inet.h> +#include <net/if.h> +#include <time.h> + +#include "gtest/gtest.h" +#include "transport_manager/tcp/network_interface_listener_impl.h" +#include "transport_manager/tcp/mock_tcp_client_listener.h" +#include "utils/test_async_waiter.h" +#include "utils/threads/thread.h" + +namespace test { +namespace components { +namespace transport_manager_test { + +namespace { +const long kThreadStartWaitMsec = 10; +const uint32_t kStartNotificationTimeoutMsec = 500; +} + +using ::testing::_; +using ::testing::AtLeast; +using ::testing::SaveArg; + +class NetworkInterfaceListenerTest : public ::testing::Test { + public: + NetworkInterfaceListenerTest() + : interface_listener_impl_(NULL) + , mock_tcp_client_listener_(NULL, 0, false, "") {} + + virtual ~NetworkInterfaceListenerTest() {} + + protected: + struct InterfaceEntry { + const char* name; + const char* ipv4_address; + const char* ipv6_address; + unsigned int flags; + }; + + void Init(const std::string interface_name) { + interface_listener_impl_ = new NetworkInterfaceListenerImpl( + &mock_tcp_client_listener_, interface_name); + } + + void Deinit() { + delete interface_listener_impl_; + } + + void SetDummyInterfaceTable(struct InterfaceEntry* entries) { + InterfaceStatusTable dummy_table; + + while (entries->name != NULL) { + InterfaceStatus status; + if (entries->ipv4_address != NULL) { + struct in_addr addr; + ASSERT_EQ(1, inet_pton(AF_INET, entries->ipv4_address, &addr)); + status.SetIPv4Address(&addr); + } + if (entries->ipv6_address != NULL) { + struct in6_addr addr6; + ASSERT_EQ(1, inet_pton(AF_INET6, entries->ipv6_address, &addr6)); + status.SetIPv6Address(&addr6); + } + status.SetFlags(entries->flags); + + dummy_table.insert(std::make_pair(entries->name, status)); + entries++; + } + + interface_listener_impl_->OverwriteStatusTable(dummy_table); + } + + void SleepFor(long msec) const { + if (msec > 0) { + struct timespec ts = {0, msec * 1000 * 1000}; + nanosleep(&ts, NULL); + } + } + + NetworkInterfaceListenerImpl* interface_listener_impl_; + MockTcpClientListener mock_tcp_client_listener_; +}; + +TEST_F(NetworkInterfaceListenerTest, Init) { + Init(""); + + EXPECT_TRUE(interface_listener_impl_->Init()); + EXPECT_TRUE(0 <= interface_listener_impl_->GetSocket()); + + Deinit(); +} + +TEST_F(NetworkInterfaceListenerTest, Deinit) { + Init(""); + EXPECT_TRUE(interface_listener_impl_->Init()); + + interface_listener_impl_->Deinit(); + + EXPECT_EQ(-1, interface_listener_impl_->GetSocket()); + + Deinit(); +} + +TEST_F(NetworkInterfaceListenerTest, Start_success) { + Init(""); + EXPECT_TRUE(interface_listener_impl_->Init()); + + struct InterfaceEntry entries[] = { + {"dummy_int0", "1.2.3.4", NULL, IFF_UP | IFF_RUNNING}, + {NULL, NULL, NULL, 0}}; + SetDummyInterfaceTable(entries); + + // after stated, it is expected that the listener notifies current IP address + // (if it's available) + TestAsyncWaiter waiter; + EXPECT_CALL(mock_tcp_client_listener_, + OnIPAddressUpdated(entries[0].ipv4_address, "")) + .WillOnce(NotifyTestAsyncWaiter(&waiter)); + + EXPECT_TRUE(interface_listener_impl_->Start()); + + // the "isThreadRunning_" flag of the thread will be update slightly later + SleepFor(kThreadStartWaitMsec); + + EXPECT_TRUE(interface_listener_impl_->GetThread()->is_running()); + + EXPECT_TRUE(waiter.WaitFor(1, kStartNotificationTimeoutMsec)); + + Deinit(); +} + +TEST_F(NetworkInterfaceListenerTest, Start_twice) { + Init(""); + EXPECT_TRUE(interface_listener_impl_->Init()); + + // ignore OnIPAddressUpdated call + EXPECT_CALL(mock_tcp_client_listener_, OnIPAddressUpdated(_, _)) + .Times(AtLeast(0)); + + EXPECT_TRUE(interface_listener_impl_->Start()); + SleepFor(kThreadStartWaitMsec); + + EXPECT_FALSE(interface_listener_impl_->Start()); + + Deinit(); +} + +TEST_F(NetworkInterfaceListenerTest, Stop_success) { + Init(""); + EXPECT_TRUE(interface_listener_impl_->Init()); + + // ignore OnIPAddressUpdated call + EXPECT_CALL(mock_tcp_client_listener_, OnIPAddressUpdated(_, _)) + .Times(AtLeast(0)); + + EXPECT_TRUE(interface_listener_impl_->Start()); + SleepFor(kThreadStartWaitMsec); + + EXPECT_TRUE(interface_listener_impl_->Stop()); + SleepFor(kThreadStartWaitMsec); + + EXPECT_FALSE(interface_listener_impl_->GetThread()->is_running()); + + Deinit(); +} + +TEST_F(NetworkInterfaceListenerTest, Stop_twice) { + Init(""); + EXPECT_TRUE(interface_listener_impl_->Init()); + + // ignore OnIPAddressUpdated call + EXPECT_CALL(mock_tcp_client_listener_, OnIPAddressUpdated(_, _)) + .Times(AtLeast(0)); + + EXPECT_TRUE(interface_listener_impl_->Start()); + SleepFor(kThreadStartWaitMsec); + + EXPECT_TRUE(interface_listener_impl_->Stop()); + + EXPECT_FALSE(interface_listener_impl_->Stop()); + + Deinit(); +} + +TEST_F(NetworkInterfaceListenerTest, Stop_without_Start) { + Init(""); + EXPECT_TRUE(interface_listener_impl_->Init()); + + EXPECT_FALSE(interface_listener_impl_->Stop()); + + Deinit(); +} + +TEST_F(NetworkInterfaceListenerTest, DesignatedInterface_IPAddressChanged) { + Init("dummy_int0"); + EXPECT_TRUE(interface_listener_impl_->Init()); + + struct InterfaceEntry entries1[] = { + {"dummy_int0", "1.2.3.4", NULL, IFF_UP | IFF_RUNNING}, + {NULL, NULL, NULL, 0}}; + struct InterfaceEntry entries2[] = { + {"dummy_int0", "5.6.7.8", NULL, IFF_UP | IFF_RUNNING}, + {NULL, NULL, NULL, 0}}; + + SetDummyInterfaceTable(entries1); + + EXPECT_CALL(mock_tcp_client_listener_, + OnIPAddressUpdated(entries1[0].ipv4_address, "")).Times(1); + + // this test case doesn't call Start() - we only check the behavior of + // NotifyIPAddresses() + interface_listener_impl_->testCallNotifyIPAddresses(); + + SetDummyInterfaceTable(entries2); + + EXPECT_CALL(mock_tcp_client_listener_, + OnIPAddressUpdated(entries2[0].ipv4_address, "")).Times(1); + + interface_listener_impl_->testCallNotifyIPAddresses(); + + Deinit(); +} + +TEST_F(NetworkInterfaceListenerTest, DesignatedInterface_IPAddressNotChanged) { + Init("dummy_int0"); + EXPECT_TRUE(interface_listener_impl_->Init()); + + struct InterfaceEntry entries1[] = { + {"dummy_int0", "1.2.3.4", NULL, IFF_UP | IFF_RUNNING}, + {"dummy_int1", "10.10.10.12", NULL, IFF_UP | IFF_RUNNING}, + {NULL, NULL, NULL, 0}}; + struct InterfaceEntry entries2[] = { + {"dummy_int0", "1.2.3.4", NULL, IFF_UP | IFF_RUNNING}, + {"dummy_int1", "172.16.23.30", NULL, IFF_UP | IFF_RUNNING}, + {NULL, NULL, NULL, 0}}; + + SetDummyInterfaceTable(entries1); + + EXPECT_CALL(mock_tcp_client_listener_, + OnIPAddressUpdated(entries1[0].ipv4_address, "")).Times(1); + + interface_listener_impl_->testCallNotifyIPAddresses(); + + SetDummyInterfaceTable(entries2); + + // OnIPAddressUpdated() shouldn't be notified + EXPECT_CALL(mock_tcp_client_listener_, OnIPAddressUpdated(_, _)).Times(0); + + interface_listener_impl_->testCallNotifyIPAddresses(); + + Deinit(); +} + +TEST_F(NetworkInterfaceListenerTest, DesignatedInterface_GoesUnavailable) { + Init("dummy_int0"); + EXPECT_TRUE(interface_listener_impl_->Init()); + + struct InterfaceEntry entries1[] = { + {"dummy_int0", "1.2.3.4", "fdc2:12af:327a::1", IFF_UP | IFF_RUNNING}, + {"dummy_int1", "10.10.10.12", NULL, IFF_UP | IFF_RUNNING}, + {NULL, NULL, NULL, 0}}; + struct InterfaceEntry entries2[] = { + {"dummy_int0", "1.2.3.4", "fdc2:12af:327a::1", IFF_UP}, + {"dummy_int1", "10.10.10.12", NULL, IFF_UP | IFF_RUNNING}, + {NULL, NULL, NULL, 0}}; + + SetDummyInterfaceTable(entries1); + + EXPECT_CALL(mock_tcp_client_listener_, + OnIPAddressUpdated(entries1[0].ipv4_address, + entries1[0].ipv6_address)).Times(1); + + interface_listener_impl_->testCallNotifyIPAddresses(); + + SetDummyInterfaceTable(entries2); + + EXPECT_CALL(mock_tcp_client_listener_, OnIPAddressUpdated("", "")).Times(1); + + interface_listener_impl_->testCallNotifyIPAddresses(); + + Deinit(); +} + +TEST_F(NetworkInterfaceListenerTest, DesignatedInterface_Removed) { + Init("dummy_int0"); + EXPECT_TRUE(interface_listener_impl_->Init()); + + struct InterfaceEntry entries1[] = { + {"dummy_int0", "1.2.3.4", "fdc2:12af:327a::1", IFF_UP | IFF_RUNNING}, + {"dummy_int1", "10.10.10.12", NULL, IFF_UP | IFF_RUNNING}, + {NULL, NULL, NULL, 0}}; + struct InterfaceEntry entries2[] = { + {"dummy_int1", "10.10.10.12", NULL, IFF_UP | IFF_RUNNING}, + {NULL, NULL, NULL, 0}}; + + SetDummyInterfaceTable(entries1); + + EXPECT_CALL(mock_tcp_client_listener_, + OnIPAddressUpdated(entries1[0].ipv4_address, + entries1[0].ipv6_address)).Times(1); + + interface_listener_impl_->testCallNotifyIPAddresses(); + + SetDummyInterfaceTable(entries2); + + EXPECT_CALL(mock_tcp_client_listener_, OnIPAddressUpdated("", "")).Times(1); + + interface_listener_impl_->testCallNotifyIPAddresses(); + + Deinit(); +} + +TEST_F(NetworkInterfaceListenerTest, DesignatedInterface_Added) { + Init("dummy_int0"); + EXPECT_TRUE(interface_listener_impl_->Init()); + + struct InterfaceEntry entries1[] = { + {"dummy_int1", "10.10.10.12", NULL, IFF_UP | IFF_RUNNING}, + {NULL, NULL, NULL, 0}}; + struct InterfaceEntry entries2[] = { + {"dummy_int1", "10.10.10.12", NULL, IFF_UP | IFF_RUNNING}, + {"dummy_int0", "1.2.3.4", NULL, IFF_UP | IFF_RUNNING}, + {NULL, NULL, NULL, 0}}; + + SetDummyInterfaceTable(entries1); + + interface_listener_impl_->testCallNotifyIPAddresses(); + + SetDummyInterfaceTable(entries2); + + EXPECT_CALL(mock_tcp_client_listener_, + OnIPAddressUpdated(entries2[1].ipv4_address, "")).Times(1); + + interface_listener_impl_->testCallNotifyIPAddresses(); + + Deinit(); +} + +TEST_F(NetworkInterfaceListenerTest, AutoSelectInterface_SelectInterface) { + // automatically select network interface + Init(""); + EXPECT_TRUE(interface_listener_impl_->Init()); + + struct InterfaceEntry entries[] = { + {"dummy_int1", "10.10.10.12", NULL, IFF_UP | IFF_RUNNING}, + {"net_dummy2", "192.168.2.3", "fdc2:12af:327a::22", IFF_UP | IFF_RUNNING}, + {NULL, NULL, NULL, 0}}; + + SetDummyInterfaceTable(entries); + + std::string output_ipv4_address; + std::string output_ipv6_address; + EXPECT_CALL(mock_tcp_client_listener_, OnIPAddressUpdated(_, _)) + .WillOnce(DoAll(SaveArg<0>(&output_ipv4_address), + SaveArg<1>(&output_ipv6_address))); + + interface_listener_impl_->testCallNotifyIPAddresses(); + + std::string selected_interface = + interface_listener_impl_->GetSelectedInterfaceName(); + + // the interface listener should pick one of the interfaces + EXPECT_TRUE((selected_interface == entries[0].name && + output_ipv4_address == entries[0].ipv4_address && + output_ipv6_address == "") || + (selected_interface == entries[1].name && + output_ipv4_address == entries[1].ipv4_address && + output_ipv6_address == entries[1].ipv6_address)); + + Deinit(); +} + +TEST_F(NetworkInterfaceListenerTest, + AutoSelectInterface_SkipUnavailableInterface) { + Init(""); + EXPECT_TRUE(interface_listener_impl_->Init()); + + struct InterfaceEntry entries[] = { + {"dummy_int1", "10.10.10.12", NULL, IFF_UP}, + {"net_dummy2", "192.168.2.3", "fdc2:12af:327a::22", IFF_UP | IFF_RUNNING}, + {NULL, NULL, NULL, 0}}; + + SetDummyInterfaceTable(entries); + + // dummy_int1 should not be selected + struct InterfaceEntry* expected = &entries[1]; + EXPECT_CALL(mock_tcp_client_listener_, + OnIPAddressUpdated(expected->ipv4_address, + expected->ipv6_address)).Times(1); + + interface_listener_impl_->testCallNotifyIPAddresses(); + + EXPECT_EQ(expected->name, + interface_listener_impl_->GetSelectedInterfaceName()); + + Deinit(); +} + +TEST_F(NetworkInterfaceListenerTest, AutoSelectInterface_SkipEmptyInterface) { + Init(""); + EXPECT_TRUE(interface_listener_impl_->Init()); + + struct InterfaceEntry entries[] = { + {"dummy_int1", "10.10.10.12", NULL, IFF_UP | IFF_RUNNING}, + {"net_dummy2", NULL, NULL, IFF_UP | IFF_RUNNING}, + {NULL, NULL, NULL, 0}}; + + SetDummyInterfaceTable(entries); + + // net_dummy2 should not be selected + struct InterfaceEntry* expected = &entries[0]; + EXPECT_CALL(mock_tcp_client_listener_, + OnIPAddressUpdated(expected->ipv4_address, "")).Times(1); + + interface_listener_impl_->testCallNotifyIPAddresses(); + + EXPECT_EQ(expected->name, + interface_listener_impl_->GetSelectedInterfaceName()); + + Deinit(); +} + +TEST_F(NetworkInterfaceListenerTest, + AutoSelectInterface_SkipLoopbackInterface) { + Init(""); + EXPECT_TRUE(interface_listener_impl_->Init()); + + struct InterfaceEntry entries[] = { + {"dummy_int1", "10.10.10.12", NULL, IFF_UP | IFF_RUNNING | IFF_LOOPBACK}, + {"net_dummy2", "192.168.2.3", "fdc2:12af:327a::22", IFF_UP | IFF_RUNNING}, + {NULL, NULL, NULL, 0}}; + + // dummy_int1 should not be selected + struct InterfaceEntry* expected = &entries[1]; + EXPECT_CALL(mock_tcp_client_listener_, + OnIPAddressUpdated(expected->ipv4_address, + expected->ipv6_address)).Times(1); + + SetDummyInterfaceTable(entries); + + interface_listener_impl_->testCallNotifyIPAddresses(); + + EXPECT_EQ(expected->name, + interface_listener_impl_->GetSelectedInterfaceName()); + + Deinit(); +} + +TEST_F(NetworkInterfaceListenerTest, AutoSelectInterface_DisableInterface) { + Init(""); + EXPECT_TRUE(interface_listener_impl_->Init()); + + struct InterfaceEntry entries[] = { + {"net_dummy0", "192.168.2.3", "fdc2:12af:327a::22", IFF_UP | IFF_RUNNING}, + {NULL, NULL, NULL, 0}}; + + EXPECT_CALL(mock_tcp_client_listener_, OnIPAddressUpdated(_, _)).Times(1); + SetDummyInterfaceTable(entries); + + interface_listener_impl_->testCallNotifyIPAddresses(); + + // make the interface "not running" + entries[0].flags &= ~IFF_RUNNING; + SetDummyInterfaceTable(entries); + + EXPECT_CALL(mock_tcp_client_listener_, OnIPAddressUpdated("", "")).Times(1); + + interface_listener_impl_->testCallNotifyIPAddresses(); + + EXPECT_EQ("", interface_listener_impl_->GetSelectedInterfaceName()); + + Deinit(); +} + +TEST_F(NetworkInterfaceListenerTest, AutoSelectInterface_EnableInterface) { + Init(""); + EXPECT_TRUE(interface_listener_impl_->Init()); + + struct InterfaceEntry entries[] = { + {"net_dummy0", "192.168.2.3", "fdc2:12af:327a::22", IFF_UP | IFF_RUNNING}, + {NULL, NULL, NULL, 0}}; + + EXPECT_CALL(mock_tcp_client_listener_, OnIPAddressUpdated(_, _)).Times(1); + SetDummyInterfaceTable(entries); + + interface_listener_impl_->testCallNotifyIPAddresses(); + + // make the interface "not running" + entries[0].flags &= ~IFF_RUNNING; + SetDummyInterfaceTable(entries); + + EXPECT_CALL(mock_tcp_client_listener_, OnIPAddressUpdated(_, _)).Times(1); + + interface_listener_impl_->testCallNotifyIPAddresses(); + + // make it running again + entries[0].flags |= IFF_RUNNING; + SetDummyInterfaceTable(entries); + + EXPECT_CALL(mock_tcp_client_listener_, + OnIPAddressUpdated(entries[0].ipv4_address, + entries[0].ipv6_address)).Times(1); + + interface_listener_impl_->testCallNotifyIPAddresses(); + + EXPECT_EQ(entries[0].name, + interface_listener_impl_->GetSelectedInterfaceName()); + + Deinit(); +} + +TEST_F(NetworkInterfaceListenerTest, AutoSelectInterface_SwitchInterface) { + Init(""); + EXPECT_TRUE(interface_listener_impl_->Init()); + + struct InterfaceEntry entries[] = { + {"dummy_int1", + "10.10.10.12", + "fd53:ba79:241d:30c1::78", + IFF_UP | IFF_RUNNING}, + {"net_dummy2", "192.168.2.3", "fdc2:12af:327a::22", IFF_UP | IFF_RUNNING}, + {NULL, NULL, NULL, 0}}; + + EXPECT_CALL(mock_tcp_client_listener_, OnIPAddressUpdated(_, _)).Times(1); + SetDummyInterfaceTable(entries); + + interface_listener_impl_->testCallNotifyIPAddresses(); + + const std::string selected_interface = + interface_listener_impl_->GetSelectedInterfaceName(); + struct InterfaceEntry* selected = &entries[0]; + while (selected->name != NULL) { + if (selected->name == selected_interface) { + break; + } + selected++; + } + ASSERT_TRUE(selected->name != NULL); + + // make the interface "not running" + selected->flags &= ~IFF_RUNNING; + SetDummyInterfaceTable(entries); + + struct InterfaceEntry* switched; + if (selected == &entries[0]) { + switched = &entries[1]; + } else { + switched = &entries[0]; + } + + EXPECT_CALL(mock_tcp_client_listener_, + OnIPAddressUpdated(switched->ipv4_address, + switched->ipv6_address)).Times(1); + + interface_listener_impl_->testCallNotifyIPAddresses(); + + EXPECT_EQ(switched->name, + interface_listener_impl_->GetSelectedInterfaceName()); + + Deinit(); +} + +} // namespace transport_manager_test +} // namespace components +} // namespace test diff --git a/src/components/transport_manager/test/tcp_client_listener_test.cc b/src/components/transport_manager/test/tcp_client_listener_test.cc index ca85b89fa9..0302225b07 100644 --- a/src/components/transport_manager/test/tcp_client_listener_test.cc +++ b/src/components/transport_manager/test/tcp_client_listener_test.cc @@ -2,6 +2,9 @@ * Copyright (c) 2015, Ford Motor Company * All rights reserved. * + * Copyright (c) 2018 Xevo Inc. + * All rights reserved. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * @@ -13,7 +16,7 @@ * disclaimer in the documentation and/or other materials provided with the * distribution. * - * Neither the name of the Ford Motor Company nor the names of its contributors + * Neither the name of the copyright holders nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * @@ -30,21 +33,38 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> +#include <time.h> + #include "gtest/gtest.h" #include "transport_manager/transport_adapter/mock_transport_adapter.h" #include "transport_manager/tcp/tcp_client_listener.h" +#include "transport_manager/tcp/network_interface_listener.h" #include "transport_manager/mock_transport_manager.h" #include "transport_manager/transport_adapter/transport_adapter_controller.h" -#include "transport_manager/transport_adapter/device.h" +#include "transport_manager/transport_adapter/mock_device.h" +#include "utils/make_shared.h" +#include "utils/test_async_waiter.h" +#include "utils/threads/thread.h" namespace test { namespace components { namespace transport_manager_test { +using ::testing::_; +using ::testing::AtLeast; using ::testing::Return; using namespace ::transport_manager; using namespace ::transport_manager::transport_adapter; +namespace { +const long kThreadStartWaitMsec = 10; +const uint32_t kConnectionCreatedTimeoutMsec = 200; +} + class MockTransportAdapterController : public TransportAdapterController { public: MOCK_METHOD1(AddDevice, DeviceSptr(DeviceSptr device)); @@ -100,36 +120,495 @@ class MockTransportAdapterController : public TransportAdapterController { new_config)); }; -class TcpClientListenerTest : public ::testing::Test { +class MockNetworkInterfaceListener : public NetworkInterfaceListener { + public: + MOCK_METHOD0(Init, bool()); + MOCK_METHOD0(Deinit, void()); + MOCK_METHOD0(Start, bool()); + MOCK_METHOD0(Stop, bool()); +}; + +class TcpClientListenerTest : public ::testing::TestWithParam<std::string> { public: TcpClientListenerTest() - : port_(0) + : port_(0) /* On Linux, binding to port 0 lets the system choose an + available port */ , enable_keep_alive_(false) - , tcp_client_listener_( - &adapter_controller_mock_, port_, enable_keep_alive_) {} + , interface_listener_mock_(NULL) + , tcp_client_listener_(NULL) {} + virtual ~TcpClientListenerTest() { + delete tcp_client_listener_; + } protected: + void SetUp() OVERRIDE { + tcp_client_listener_ = new TcpClientListener( + &adapter_controller_mock_, port_, enable_keep_alive_, GetParam()); + interface_listener_mock_ = new MockNetworkInterfaceListener(); + tcp_client_listener_->set_network_interface_listener( + interface_listener_mock_); + } + + bool InterfaceNameSpecified() const { + return "" != GetParam(); + } + + void SleepFor(long msec) const { + if (msec > 0) { + struct timespec ts = {0, msec * 1000 * 1000}; + nanosleep(&ts, NULL); + } + } + uint16_t port_; bool enable_keep_alive_; MockTransportAdapterController adapter_controller_mock_; - TcpClientListener tcp_client_listener_; + MockNetworkInterfaceListener* interface_listener_mock_; + TcpClientListener* tcp_client_listener_; }; -TEST_F(TcpClientListenerTest, Ctor_test) { - EXPECT_EQ(0, tcp_client_listener_.port()); - EXPECT_TRUE(NULL != tcp_client_listener_.thread()); - EXPECT_EQ(-1, tcp_client_listener_.get_socket()); +TEST_P(TcpClientListenerTest, Ctor_test) { + EXPECT_EQ(0, tcp_client_listener_->port()); + EXPECT_TRUE(NULL != tcp_client_listener_->thread()); + EXPECT_EQ(-1, tcp_client_listener_->get_socket()); } -TEST_F(TcpClientListenerTest, IsInitialised) { +TEST_P(TcpClientListenerTest, IsInitialised) { // should return false until Init() is called - EXPECT_FALSE(tcp_client_listener_.IsInitialised()); + EXPECT_FALSE(tcp_client_listener_->IsInitialised()); +} + +TEST_P(TcpClientListenerTest, Init) { + EXPECT_CALL(*interface_listener_mock_, Init()).WillOnce(Return(true)); + + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->Init()); + + if (InterfaceNameSpecified()) { + // TcpClientListener will create socket once IP address of the network is + // notified. + EXPECT_EQ(-1, tcp_client_listener_->get_socket()); + } else { + // Interface name is not designated. In this case, TcpClientListener will + // create socket with Init(). + EXPECT_TRUE(0 <= tcp_client_listener_->get_socket()); + } + + EXPECT_TRUE(tcp_client_listener_->IsInitialised()); + + // Deinit() will be called during destructor + EXPECT_CALL(*interface_listener_mock_, Deinit()).Times(1); +} + +TEST_P(TcpClientListenerTest, Terminate) { + EXPECT_CALL(*interface_listener_mock_, Init()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->Init()); + + EXPECT_CALL(*interface_listener_mock_, Deinit()).Times(1); + + tcp_client_listener_->Terminate(); + + EXPECT_EQ(-1, tcp_client_listener_->get_socket()); +} + +TEST_P(TcpClientListenerTest, StartListening) { + EXPECT_CALL(*interface_listener_mock_, Init()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->Init()); + + EXPECT_CALL(*interface_listener_mock_, Start()).WillOnce(Return(true)); + + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->StartListening()); + + // the "isThreadRunning_" flag of the thread will be update slightly later + SleepFor(kThreadStartWaitMsec); + + if (InterfaceNameSpecified()) { + EXPECT_FALSE(tcp_client_listener_->thread()->is_running()); + } else { + EXPECT_TRUE(tcp_client_listener_->thread()->is_running()); + } + + // Stop() and Deinit() will be called during destructor + EXPECT_CALL(*interface_listener_mock_, Stop()).WillOnce(Return(true)); + EXPECT_CALL(*interface_listener_mock_, Deinit()).Times(1); +} + +TEST_P(TcpClientListenerTest, StartListening_twice) { + EXPECT_CALL(*interface_listener_mock_, Init()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->Init()); + EXPECT_CALL(*interface_listener_mock_, Start()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->StartListening()); + + // call again + EXPECT_EQ(TransportAdapter::BAD_STATE, + tcp_client_listener_->StartListening()); + + // Stop() and Deinit() will be called during destructor + EXPECT_CALL(*interface_listener_mock_, Stop()).WillOnce(Return(true)); + EXPECT_CALL(*interface_listener_mock_, Deinit()).Times(1); +} + +TEST_P(TcpClientListenerTest, StopListening) { + EXPECT_CALL(*interface_listener_mock_, Init()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->Init()); + EXPECT_CALL(*interface_listener_mock_, Start()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->StartListening()); + + EXPECT_CALL(*interface_listener_mock_, Stop()).WillOnce(Return(true)); + + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->StopListening()); + EXPECT_FALSE(tcp_client_listener_->thread()->is_running()); + + EXPECT_CALL(*interface_listener_mock_, Deinit()).Times(1); +} + +TEST_P(TcpClientListenerTest, StopListening_twice) { + EXPECT_CALL(*interface_listener_mock_, Init()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->Init()); + EXPECT_CALL(*interface_listener_mock_, Start()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->StartListening()); + EXPECT_CALL(*interface_listener_mock_, Stop()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->StopListening()); + + // call again + EXPECT_EQ(TransportAdapter::BAD_STATE, tcp_client_listener_->StopListening()); + + EXPECT_CALL(*interface_listener_mock_, Deinit()).Times(1); +} + +TEST_P(TcpClientListenerTest, ClientConnection) { + EXPECT_CALL(*interface_listener_mock_, Init()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->Init()); + EXPECT_CALL(*interface_listener_mock_, Start()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->StartListening()); + + if (InterfaceNameSpecified()) { + // set up a server socket by notifying a dummy IP address + EXPECT_CALL(adapter_controller_mock_, TransportConfigUpdated(_)).Times(1); + tcp_client_listener_->OnIPAddressUpdated(std::string("192.168.1.1"), + std::string("")); + } + + // get the port number (assigned by system) that the socket is listening on + struct sockaddr_in server_addr; + socklen_t server_addr_len = sizeof(server_addr); + EXPECT_EQ(0, + getsockname(tcp_client_listener_->get_socket(), + reinterpret_cast<struct sockaddr*>(&server_addr), + &server_addr_len)); + + // try connecting to the socket + struct sockaddr_in client_addr; + client_addr.sin_family = AF_INET; + client_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + client_addr.sin_port = server_addr.sin_port; + socklen_t client_addr_len = sizeof(client_addr); + + int s = socket(AF_INET, SOCK_STREAM, 0); + EXPECT_TRUE(0 <= s); + + TestAsyncWaiter waiter; + + // controller should be notified of AddDevice event + DeviceSptr mock_device = utils::MakeShared<MockTCPDevice>( + htonl(INADDR_LOOPBACK), "dummy_tcp_device"); + EXPECT_CALL(adapter_controller_mock_, AddDevice(_)) + .WillOnce(Return(mock_device)); + EXPECT_CALL(adapter_controller_mock_, ConnectionCreated(_, _, _)) + .WillOnce(NotifyTestAsyncWaiter(&waiter)); + + // adapter_controller_mock_ may also receive ConnectDone() and + // ConnectionFinished() from ThreadedSocketConnection. Ignore them as hey are + // not part ly client listener's tests. + EXPECT_CALL(adapter_controller_mock_, ConnectDone(_, _)).Times(AtLeast(0)); + EXPECT_CALL(adapter_controller_mock_, ConnectionFinished(_, _)) + .Times(AtLeast(0)); + + EXPECT_EQ(0, + connect(s, + reinterpret_cast<struct sockaddr*>(&client_addr), + client_addr_len)); + + // since the connection is handled on another thread, wait for some time + EXPECT_TRUE(waiter.WaitFor(1, kConnectionCreatedTimeoutMsec)); + + close(s); + + EXPECT_CALL(*interface_listener_mock_, Stop()).WillOnce(Return(true)); + EXPECT_CALL(*interface_listener_mock_, Deinit()).Times(1); +} + +TEST_P(TcpClientListenerTest, OnIPAddressUpdated_ValidIPv4Address) { + EXPECT_CALL(*interface_listener_mock_, Init()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->Init()); + EXPECT_CALL(*interface_listener_mock_, Start()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->StartListening()); + + const std::string test_ipv4_addr = "192.168.1.1"; + const std::string test_ipv6_addr = ""; + char buf[16]; + snprintf(buf, sizeof(buf), "%u", port_); + const std::string test_port(buf); + + TransportConfig expected_config; + expected_config.insert(std::make_pair(std::string("enabled"), "true")); + expected_config.insert( + std::make_pair(std::string("tcp_ip_address"), test_ipv4_addr)); + expected_config.insert(std::make_pair(std::string("tcp_port"), test_port)); + + EXPECT_CALL(adapter_controller_mock_, TransportConfigUpdated(expected_config)) + .Times(1); + + tcp_client_listener_->OnIPAddressUpdated(test_ipv4_addr, test_ipv6_addr); + + if (InterfaceNameSpecified()) { + // when the client listener runs with designated interface name, it should + // start the thread here + EXPECT_TRUE(0 <= tcp_client_listener_->get_socket()); + + SleepFor(kThreadStartWaitMsec); + + EXPECT_TRUE(tcp_client_listener_->thread()->is_running()); + } + + EXPECT_CALL(*interface_listener_mock_, Stop()).WillOnce(Return(true)); + EXPECT_CALL(*interface_listener_mock_, Deinit()).Times(1); +} + +TEST_P(TcpClientListenerTest, OnIPAddressUpdated_IPv4Address_changed) { + EXPECT_CALL(*interface_listener_mock_, Init()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->Init()); + EXPECT_CALL(*interface_listener_mock_, Start()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->StartListening()); + + const std::string test_ipv4_addr_1 = "192.168.1.1"; + const std::string test_ipv6_addr = ""; + char buf[16]; + snprintf(buf, sizeof(buf), "%u", port_); + const std::string test_port(buf); + + TransportConfig expected_config_1; + expected_config_1.insert(std::make_pair(std::string("enabled"), "true")); + expected_config_1.insert( + std::make_pair(std::string("tcp_ip_address"), test_ipv4_addr_1)); + expected_config_1.insert(std::make_pair(std::string("tcp_port"), test_port)); + + EXPECT_CALL(adapter_controller_mock_, + TransportConfigUpdated(expected_config_1)).Times(1); + + tcp_client_listener_->OnIPAddressUpdated(test_ipv4_addr_1, test_ipv6_addr); + + const std::string test_ipv4_addr_2 = "172.16.2.3"; + TransportConfig expected_config_2; + expected_config_2.insert(std::make_pair(std::string("enabled"), "true")); + expected_config_2.insert( + std::make_pair(std::string("tcp_ip_address"), test_ipv4_addr_2)); + expected_config_2.insert(std::make_pair(std::string("tcp_port"), test_port)); + + EXPECT_CALL(adapter_controller_mock_, + TransportConfigUpdated(expected_config_2)).Times(1); + + tcp_client_listener_->OnIPAddressUpdated(test_ipv4_addr_2, test_ipv6_addr); + + if (InterfaceNameSpecified()) { + EXPECT_TRUE(0 <= tcp_client_listener_->get_socket()); + + SleepFor(kThreadStartWaitMsec); + + EXPECT_TRUE(tcp_client_listener_->thread()->is_running()); + } + + EXPECT_CALL(*interface_listener_mock_, Stop()).WillOnce(Return(true)); + EXPECT_CALL(*interface_listener_mock_, Deinit()).Times(1); +} + +TEST_P(TcpClientListenerTest, OnIPAddressUpdated_IPv4Address_same) { + EXPECT_CALL(*interface_listener_mock_, Init()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->Init()); + EXPECT_CALL(*interface_listener_mock_, Start()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->StartListening()); + + const std::string test_ipv4_addr_1 = "192.168.1.1"; + const std::string test_ipv6_addr = ""; + char buf[16]; + snprintf(buf, sizeof(buf), "%u", port_); + const std::string test_port(buf); + + TransportConfig expected_config_1; + expected_config_1.insert(std::make_pair(std::string("enabled"), "true")); + expected_config_1.insert( + std::make_pair(std::string("tcp_ip_address"), test_ipv4_addr_1)); + expected_config_1.insert(std::make_pair(std::string("tcp_port"), test_port)); + + EXPECT_CALL(adapter_controller_mock_, + TransportConfigUpdated(expected_config_1)).Times(1); + + tcp_client_listener_->OnIPAddressUpdated(test_ipv4_addr_1, test_ipv6_addr); + + const std::string test_ipv4_addr_2 = "192.168.1.1"; // same as before + TransportConfig expected_config_2; + expected_config_2.insert(std::make_pair(std::string("enabled"), "true")); + expected_config_2.insert( + std::make_pair(std::string("tcp_ip_address"), test_ipv4_addr_2)); + expected_config_2.insert(std::make_pair(std::string("tcp_port"), test_port)); + + // client listener should not generate TransportConfigUpdated event + EXPECT_CALL(adapter_controller_mock_, TransportConfigUpdated(_)).Times(0); + + tcp_client_listener_->OnIPAddressUpdated(test_ipv4_addr_2, test_ipv6_addr); + + if (InterfaceNameSpecified()) { + EXPECT_TRUE(0 <= tcp_client_listener_->get_socket()); + + SleepFor(kThreadStartWaitMsec); + + EXPECT_TRUE(tcp_client_listener_->thread()->is_running()); + } + + EXPECT_CALL(*interface_listener_mock_, Stop()).WillOnce(Return(true)); + EXPECT_CALL(*interface_listener_mock_, Deinit()).Times(1); +} + +TEST_P(TcpClientListenerTest, OnIPAddressUpdated_IPv4Address_disabled) { + EXPECT_CALL(*interface_listener_mock_, Init()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->Init()); + EXPECT_CALL(*interface_listener_mock_, Start()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->StartListening()); + + const std::string test_ipv4_addr_1 = "192.168.1.1"; + const std::string test_ipv6_addr = ""; + char buf[16]; + snprintf(buf, sizeof(buf), "%u", port_); + const std::string test_port(buf); + + TransportConfig expected_config_1; + expected_config_1.insert(std::make_pair(std::string("enabled"), "true")); + expected_config_1.insert( + std::make_pair(std::string("tcp_ip_address"), test_ipv4_addr_1)); + expected_config_1.insert(std::make_pair(std::string("tcp_port"), test_port)); + + EXPECT_CALL(adapter_controller_mock_, + TransportConfigUpdated(expected_config_1)).Times(1); + + tcp_client_listener_->OnIPAddressUpdated(test_ipv4_addr_1, test_ipv6_addr); + + const std::string test_ipv4_addr_2 = ""; + TransportConfig expected_config_2; + expected_config_2.insert(std::make_pair(std::string("enabled"), "false")); + expected_config_2.insert( + std::make_pair(std::string("tcp_ip_address"), test_ipv4_addr_2)); + expected_config_2.insert(std::make_pair(std::string("tcp_port"), test_port)); + + EXPECT_CALL(adapter_controller_mock_, + TransportConfigUpdated(expected_config_2)).Times(1); + + tcp_client_listener_->OnIPAddressUpdated(test_ipv4_addr_2, test_ipv6_addr); + + if (InterfaceNameSpecified()) { + EXPECT_EQ(-1, tcp_client_listener_->get_socket()); + + SleepFor(kThreadStartWaitMsec); + + EXPECT_FALSE(tcp_client_listener_->thread()->is_running()); + } + + EXPECT_CALL(*interface_listener_mock_, Stop()).WillOnce(Return(true)); + EXPECT_CALL(*interface_listener_mock_, Deinit()).Times(1); +} + +TEST_P(TcpClientListenerTest, OnIPAddressUpdated_IPv4Address_reenabled) { + EXPECT_CALL(*interface_listener_mock_, Init()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->Init()); + EXPECT_CALL(*interface_listener_mock_, Start()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->StartListening()); + + const std::string test_ipv4_addr_1 = "192.168.1.1"; + const std::string test_ipv6_addr = ""; + char buf[16]; + snprintf(buf, sizeof(buf), "%u", port_); + const std::string test_port(buf); + + TransportConfig expected_config_1; + expected_config_1.insert(std::make_pair(std::string("enabled"), "true")); + expected_config_1.insert( + std::make_pair(std::string("tcp_ip_address"), test_ipv4_addr_1)); + expected_config_1.insert(std::make_pair(std::string("tcp_port"), test_port)); + + EXPECT_CALL(adapter_controller_mock_, + TransportConfigUpdated(expected_config_1)).Times(1); + + tcp_client_listener_->OnIPAddressUpdated(test_ipv4_addr_1, test_ipv6_addr); + + const std::string test_ipv4_addr_2 = ""; + TransportConfig expected_config_2; + expected_config_2.insert(std::make_pair(std::string("enabled"), "false")); + expected_config_2.insert( + std::make_pair(std::string("tcp_ip_address"), test_ipv4_addr_2)); + expected_config_2.insert(std::make_pair(std::string("tcp_port"), test_port)); + + EXPECT_CALL(adapter_controller_mock_, + TransportConfigUpdated(expected_config_2)).Times(1); + + tcp_client_listener_->OnIPAddressUpdated(test_ipv4_addr_2, test_ipv6_addr); + + const std::string test_ipv4_addr_3 = "192.168.1.1"; + TransportConfig expected_config_3; + expected_config_3.insert(std::make_pair(std::string("enabled"), "true")); + expected_config_3.insert( + std::make_pair(std::string("tcp_ip_address"), test_ipv4_addr_3)); + expected_config_3.insert(std::make_pair(std::string("tcp_port"), test_port)); + + EXPECT_CALL(adapter_controller_mock_, + TransportConfigUpdated(expected_config_3)).Times(1); + + tcp_client_listener_->OnIPAddressUpdated(test_ipv4_addr_3, test_ipv6_addr); + + if (InterfaceNameSpecified()) { + EXPECT_TRUE(0 <= tcp_client_listener_->get_socket()); + + SleepFor(kThreadStartWaitMsec); + + EXPECT_TRUE(tcp_client_listener_->thread()->is_running()); + } + + EXPECT_CALL(*interface_listener_mock_, Stop()).WillOnce(Return(true)); + EXPECT_CALL(*interface_listener_mock_, Deinit()).Times(1); } -TEST_F(TcpClientListenerTest, Init) { - EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_.Init()); +TEST_P(TcpClientListenerTest, OnIPAddressUpdated_EmptyIPv4Address) { + EXPECT_CALL(*interface_listener_mock_, Init()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->Init()); + EXPECT_CALL(*interface_listener_mock_, Start()).WillOnce(Return(true)); + EXPECT_EQ(TransportAdapter::OK, tcp_client_listener_->StartListening()); + + const std::string test_ipv4_addr = ""; + const std::string test_ipv6_addr = ""; + char buf[16]; + snprintf(buf, sizeof(buf), "%u", port_); + const std::string test_port(buf); + + // if the client listener receives an empty IP address after started, it + // should ignore it + EXPECT_CALL(adapter_controller_mock_, TransportConfigUpdated(_)).Times(0); + + tcp_client_listener_->OnIPAddressUpdated(test_ipv4_addr, test_ipv6_addr); + + if (InterfaceNameSpecified()) { + EXPECT_EQ(-1, tcp_client_listener_->get_socket()); + + SleepFor(kThreadStartWaitMsec); + + EXPECT_FALSE(tcp_client_listener_->thread()->is_running()); + } + + EXPECT_CALL(*interface_listener_mock_, Stop()).WillOnce(Return(true)); + EXPECT_CALL(*interface_listener_mock_, Deinit()).Times(1); } +INSTANTIATE_TEST_CASE_P(NetworkInterfaceName, + TcpClientListenerTest, + ::testing::Values(std::string(""), + std::string("dummy_interface0"))); + } // namespace transport_manager_test } // namespace components } // namespace test diff --git a/src/components/transport_manager/test/tcp_transport_adapter_test.cc b/src/components/transport_manager/test/tcp_transport_adapter_test.cc index 5bd5b96954..d6abf5e813 100644 --- a/src/components/transport_manager/test/tcp_transport_adapter_test.cc +++ b/src/components/transport_manager/test/tcp_transport_adapter_test.cc @@ -347,6 +347,37 @@ TEST_F(TcpAdapterTest, StoreDataWithSeveralDevices_RestoreData) { } } +TEST_F(TcpAdapterTest, NotifyTransportConfigUpdated) { + MockTransportAdapterListener mock_adapter_listener; + + MockTCPTransportAdapter transport_adapter( + port, last_state_, transport_manager_settings); + transport_adapter.AddListener(&mock_adapter_listener); + + TransportConfig config; + config["enabled"] = std::string("enabled"); + config["tcp_ip_address"] = std::string("192.168.1.1"); + config["tcp_port"] = std::string("12345"); + + EXPECT_CALL(mock_adapter_listener, OnTransportConfigUpdated(_)).Times(1); + + transport_adapter.TransportConfigUpdated(config); +} + +TEST_F(TcpAdapterTest, GetTransportConfiguration) { + MockTCPTransportAdapter transport_adapter( + port, last_state_, transport_manager_settings); + + TransportConfig config; + config["enabled"] = std::string("enabled"); + config["tcp_ip_address"] = std::string("192.168.1.1"); + config["tcp_port"] = std::string("12345"); + + transport_adapter.TransportConfigUpdated(config); + + EXPECT_EQ(config, transport_adapter.GetTransportConfiguration()); +} + } // namespace transport_manager_test } // namespace components } // namespace test diff --git a/src/components/transport_manager/test/transport_adapter_listener_test.cc b/src/components/transport_manager/test/transport_adapter_listener_test.cc index 14b8850b49..1494844519 100644 --- a/src/components/transport_manager/test/transport_adapter_listener_test.cc +++ b/src/components/transport_manager/test/transport_adapter_listener_test.cc @@ -230,6 +230,15 @@ TEST_F(TransportAdapterListenerTest, OnUnexpectedDisconnect) { &adapter_mock, dev_id, app_handle, err); } +TEST_F(TransportAdapterListenerTest, OnTransportConfigUpdated) { + EXPECT_CALL( + tr_mock, + ReceiveEventFromDevice(IsEvent( + EventTypeEnum::ON_TRANSPORT_CONFIG_UPDATED, &adapter_mock, "", 0))) + .WillOnce(Return(E_SUCCESS)); + transport_listener.OnTransportConfigUpdated(&adapter_mock); +} + } // namespace transport_manager_test } // namespace components } // namespace test diff --git a/src/components/transport_manager/test/transport_adapter_test.cc b/src/components/transport_manager/test/transport_adapter_test.cc index 6d709e0c17..ab2683193c 100644 --- a/src/components/transport_manager/test/transport_adapter_test.cc +++ b/src/components/transport_manager/test/transport_adapter_test.cc @@ -809,6 +809,35 @@ TEST_F(TransportAdapterTest, StopDevice) { transport_adapter.StopDevice(uniq_id); } +TEST_F(TransportAdapterTest, TransportConfigUpdated) { + MockTransportAdapterImpl transport_adapter( + NULL, NULL, NULL, last_state_, transport_manager_settings); + EXPECT_CALL(transport_adapter, Restore()).WillOnce(Return(true)); + transport_adapter.Init(); + + MockTransportAdapterListener mock_listener; + transport_adapter.AddListener(&mock_listener); + + TransportConfig config; + config["enabled"] = std::string("enabled"); + config["tcp_ip_address"] = std::string("192.168.1.1"); + config["tcp_port"] = std::string("12345"); + + EXPECT_CALL(mock_listener, OnTransportConfigUpdated(_)); + transport_adapter.TransportConfigUpdated(config); +} + +TEST_F(TransportAdapterTest, GetTransportConfigration) { + MockTransportAdapterImpl transport_adapter( + NULL, NULL, NULL, last_state_, transport_manager_settings); + EXPECT_CALL(transport_adapter, Restore()).WillOnce(Return(true)); + transport_adapter.Init(); + + TransportConfig empty_config; + + EXPECT_EQ(empty_config, transport_adapter.GetTransportConfiguration()); +} + } // namespace transport_manager_test } // namespace components } // namespace test diff --git a/src/components/transport_manager/test/transport_manager_impl_test.cc b/src/components/transport_manager/test/transport_manager_impl_test.cc index eebb247908..b2643f2938 100644 --- a/src/components/transport_manager/test/transport_manager_impl_test.cc +++ b/src/components/transport_manager/test/transport_manager_impl_test.cc @@ -1323,6 +1323,26 @@ TEST_F(TransportManagerImplTest, tm_.OnDeviceListUpdated(mock_adapter_); } +TEST_F(TransportManagerImplTest, OnTransportConfigUpdated) { + TransportAdapterEvent test_event(EventTypeEnum::ON_TRANSPORT_CONFIG_UPDATED, + mock_adapter_, + "", + 0, + test_message_, + error_); + + transport_adapter::TransportConfig config; + config["enabled"] = std::string("enabled"); + config["tcp_ip_address"] = std::string("192.168.1.1"); + config["tcp_port"] = std::string("12345"); + + EXPECT_CALL(*mock_adapter_, GetTransportConfiguration()) + .WillOnce(Return(config)); + + EXPECT_CALL(*tm_listener_, OnTransportConfigUpdated(config)); + tm_.TestHandle(test_event); +} + } // namespace transport_manager_test } // namespace components } // namespace test |