|
| 1 | +/* |
| 2 | + * Copyright (c) 2018, Arm Limited and affiliates |
| 3 | + * SPDX-License-Identifier: Apache-2.0 |
| 4 | + * |
| 5 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | + * you may not use this file except in compliance with the License. |
| 7 | + * You may obtain a copy of the License at |
| 8 | + * |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | + * |
| 11 | + * Unless required by applicable law or agreed to in writing, software |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | + * See the License for the specific language governing permissions and |
| 15 | + * limitations under the License. |
| 16 | + */ |
| 17 | + |
| 18 | +#include "gtest/gtest.h" |
| 19 | +#include "gmock/gmock.h" |
| 20 | +#include "features/netsocket/EthernetInterface.h" |
| 21 | +#include <iostream> |
| 22 | + |
| 23 | +class MockEMAC : public EMAC { |
| 24 | +public: |
| 25 | + MOCK_METHOD0(power_up, bool()); |
| 26 | + MOCK_METHOD0(power_down, void()); |
| 27 | + MOCK_CONST_METHOD0(get_mtu_size, uint32_t()); |
| 28 | + MOCK_CONST_METHOD0(get_align_preference, uint32_t()); |
| 29 | + MOCK_CONST_METHOD2(get_ifname, void(char *name, uint8_t size)); |
| 30 | + MOCK_CONST_METHOD0(get_hwaddr_size, uint8_t()); |
| 31 | + MOCK_CONST_METHOD1(get_hwaddr, bool(uint8_t *addr)); |
| 32 | + MOCK_METHOD1(set_hwaddr, void(const uint8_t *)); |
| 33 | + MOCK_METHOD1(link_out, bool(emac_mem_buf_t *buf)); |
| 34 | + MOCK_METHOD1(set_link_input_cb, void(emac_link_input_cb_t input_cb)); |
| 35 | + MOCK_METHOD1(set_link_state_cb, void(emac_link_state_change_cb_t state_cb)); |
| 36 | + MOCK_METHOD1(add_multicast_group, void(const uint8_t *address)); |
| 37 | + MOCK_METHOD1(remove_multicast_group, void(const uint8_t *address)); |
| 38 | + MOCK_METHOD1(set_all_multicast, void(bool all)); |
| 39 | + MOCK_METHOD1(set_memory_manager, void(EMACMemoryManager &mem_mngr)); |
| 40 | + |
| 41 | + static MockEMAC &get_instance() |
| 42 | + { |
| 43 | + static MockEMAC emacMock1; |
| 44 | + return emacMock1; |
| 45 | + } |
| 46 | +}; |
| 47 | + |
| 48 | +MBED_WEAK EMAC &EMAC::get_default_instance() |
| 49 | +{ |
| 50 | + return MockEMAC::get_instance(); |
| 51 | +} |
| 52 | + |
| 53 | +class EmacNetworkStackMock : public OnboardNetworkStack { |
| 54 | +public: |
| 55 | + MOCK_METHOD3(gethostbyname, nsapi_error_t(const char *host, SocketAddress *address, nsapi_version_t version)); |
| 56 | + MOCK_METHOD1(add_dns_server, nsapi_error_t(const SocketAddress &address)); |
| 57 | + MOCK_METHOD2(call_in, nsapi_error_t(int delay, mbed::Callback<void()> func)); |
| 58 | + MOCK_METHOD2(socket_open, nsapi_error_t(nsapi_socket_t *handle, nsapi_protocol_t proto)); |
| 59 | + MOCK_METHOD1(socket_close, nsapi_error_t(nsapi_socket_t handle)); |
| 60 | + MOCK_METHOD2(socket_bind, nsapi_error_t(nsapi_socket_t handle, const SocketAddress &address)); |
| 61 | + MOCK_METHOD2(socket_listen, nsapi_error_t(nsapi_socket_t handle, int backlog)); |
| 62 | + MOCK_METHOD2(socket_connect, nsapi_error_t(nsapi_socket_t handle, const SocketAddress &address)); |
| 63 | + MOCK_METHOD3(socket_accept, nsapi_error_t(nsapi_socket_t server, nsapi_socket_t *handle, SocketAddress *address)); |
| 64 | + MOCK_METHOD3(socket_send, nsapi_error_t(nsapi_socket_t handle, const void *data, nsapi_size_t size)); |
| 65 | + MOCK_METHOD3(socket_recv, nsapi_error_t(nsapi_socket_t handle, void *data, nsapi_size_t size)); |
| 66 | + MOCK_METHOD4(socket_sendto, nsapi_error_t(nsapi_socket_t handle, const SocketAddress &address, const void *data, nsapi_size_t size)); |
| 67 | + MOCK_METHOD4(socket_recvfrom, nsapi_error_t(nsapi_socket_t handle, SocketAddress *address, void *data, nsapi_size_t size)); |
| 68 | + MOCK_METHOD5(setsockopt, nsapi_error_t(nsapi_socket_t handle, int level, int optname, const void *optval, unsigned optlen)); |
| 69 | + MOCK_METHOD5(getsockopt, nsapi_error_t(nsapi_socket_t handle, int level, int optname, const void *optval, unsigned *optlen)); |
| 70 | + MOCK_METHOD3(socket_attach, void(nsapi_socket_t handle, void (*callback)(void *), void *data)); |
| 71 | + MOCK_METHOD3(add_ethernet_interface, nsapi_error_t(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out)); |
| 72 | + |
| 73 | + static EmacNetworkStackMock &get_instance() |
| 74 | + { |
| 75 | + static EmacNetworkStackMock stackMock1; |
| 76 | + return stackMock1; |
| 77 | + } |
| 78 | + |
| 79 | + class InterfaceMock : public OnboardNetworkStack::Interface { |
| 80 | + public: |
| 81 | + |
| 82 | + static InterfaceMock &get_instance() |
| 83 | + { |
| 84 | + static InterfaceMock test_interface; |
| 85 | + return test_interface; |
| 86 | + } |
| 87 | + MOCK_METHOD6(bringup, nsapi_error_t(bool dhcp, const char *ip, |
| 88 | + const char *netmask, const char *gw, |
| 89 | + nsapi_ip_stack_t stack, |
| 90 | + bool blocking |
| 91 | + )); |
| 92 | + MOCK_METHOD0(bringdown, nsapi_error_t()); |
| 93 | + MOCK_METHOD1(attach, void(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb)); |
| 94 | + MOCK_CONST_METHOD0(get_connection_status, nsapi_connection_status_t()); |
| 95 | + MOCK_METHOD2(get_mac_address, char *(char *buf, nsapi_size_t buflen)); |
| 96 | + MOCK_METHOD2(get_ip_address, char *(char *buf, nsapi_size_t buflen)); |
| 97 | + MOCK_METHOD2(get_netmask, char *(char *buf, nsapi_size_t buflen)); |
| 98 | + MOCK_METHOD2(get_gateway, char *(char *buf, nsapi_size_t buflen)); |
| 99 | + }; |
| 100 | +}; |
| 101 | + |
| 102 | +OnboardNetworkStack &OnboardNetworkStack::get_default_instance() |
| 103 | +{ |
| 104 | + return EmacNetworkStackMock::get_instance(); |
| 105 | +} |
| 106 | + |
| 107 | +// Implementaion in in NetworkInterfaceDefaults.cpp |
| 108 | +MBED_WEAK EthInterface *EthInterface::get_default_instance() |
| 109 | +{ |
| 110 | + return get_target_default_instance(); |
| 111 | +} |
| 112 | + |
| 113 | +using ::testing::_; |
| 114 | +using ::testing::Return; |
| 115 | +using ::testing::ReturnArg; |
| 116 | +using ::testing::SaveArg; |
| 117 | +using ::testing::SaveArgPointee; |
| 118 | +using ::testing::SetArrayArgument; |
| 119 | +using ::testing::SetArgPointee; |
| 120 | +using ::testing::SetArgReferee; |
| 121 | + |
| 122 | +class TestEthernetInterface: public testing::Test { |
| 123 | +protected: |
| 124 | + EthernetInterface *iface; |
| 125 | + EmacNetworkStackMock *stackMock; |
| 126 | + MockEMAC *emacMock; |
| 127 | + EmacNetworkStackMock::InterfaceMock *netStackIface; |
| 128 | + virtual void SetUp() |
| 129 | + { |
| 130 | + stackMock = &EmacNetworkStackMock::get_instance(); |
| 131 | + emacMock = &MockEMAC::get_instance(); |
| 132 | + netStackIface = &EmacNetworkStackMock::InterfaceMock::get_instance(); |
| 133 | + iface = new EthernetInterface(MockEMAC::get_instance(), EmacNetworkStackMock::get_instance()); |
| 134 | + } |
| 135 | + |
| 136 | + virtual void TearDown() |
| 137 | + { |
| 138 | + // Do not delete the mocks pointers, as they point to statically allocated singletons. |
| 139 | + delete iface; |
| 140 | + } |
| 141 | + |
| 142 | + /* Enclose the heavily-used connection procedure to improve code redability */ |
| 143 | + void doConnect(bool dhcp = true, bool blocking = true) |
| 144 | + { |
| 145 | + EXPECT_CALL(*stackMock, add_ethernet_interface(testing::Ref(*emacMock), true, _)) |
| 146 | + .Times(1) |
| 147 | + .WillOnce(DoAll(SetArgPointee<2>(netStackIface), Return(NSAPI_ERROR_OK))); |
| 148 | + EXPECT_CALL(*netStackIface, attach(_)) |
| 149 | + .Times(1) |
| 150 | + .RetiresOnSaturation();; |
| 151 | + EXPECT_CALL(*netStackIface, bringup(dhcp, NULL, NULL, NULL, DEFAULT_STACK, blocking)) |
| 152 | + .Times(1) |
| 153 | + .WillOnce(Return(NSAPI_ERROR_OK)); |
| 154 | + EXPECT_EQ(NSAPI_ERROR_OK, iface->connect()); |
| 155 | + } |
| 156 | + |
| 157 | + static void cb(nsapi_event_t ev, intptr_t ptr) |
| 158 | + { |
| 159 | + |
| 160 | + } |
| 161 | +}; |
| 162 | + |
| 163 | +TEST_F(TestEthernetInterface, constructor_default) |
| 164 | +{ |
| 165 | + EXPECT_TRUE(iface); |
| 166 | +} |
| 167 | + |
| 168 | +TEST_F(TestEthernetInterface, constructor_getter) |
| 169 | +{ |
| 170 | + EthInterface *eth = EthInterface::get_default_instance(); |
| 171 | +} |
| 172 | + |
| 173 | + |
| 174 | + |
| 175 | +TEST_F(TestEthernetInterface, connect) |
| 176 | +{ |
| 177 | + doConnect(); |
| 178 | +} |
| 179 | + |
| 180 | +TEST_F(TestEthernetInterface, connect_failure) |
| 181 | +{ |
| 182 | + EXPECT_CALL(*stackMock, add_ethernet_interface(testing::Ref(*emacMock), true, _)) |
| 183 | + .Times(1) |
| 184 | + .WillOnce(DoAll(SetArgPointee<2>(netStackIface), Return(NSAPI_ERROR_NO_MEMORY))); |
| 185 | + EXPECT_EQ(NSAPI_ERROR_NO_MEMORY, iface->connect()); |
| 186 | +} |
| 187 | + |
| 188 | +TEST_F(TestEthernetInterface, disconnect_without_connecting) |
| 189 | +{ |
| 190 | + EXPECT_EQ(NSAPI_ERROR_NO_CONNECTION, iface->disconnect()); |
| 191 | +} |
| 192 | + |
| 193 | +TEST_F(TestEthernetInterface, disconnect) |
| 194 | +{ |
| 195 | + doConnect(); |
| 196 | + |
| 197 | + EXPECT_CALL(*netStackIface, bringdown()) |
| 198 | + .Times(1) |
| 199 | + .WillOnce(Return(NSAPI_ERROR_OK)); |
| 200 | + EXPECT_EQ(NSAPI_ERROR_OK, iface->disconnect()); |
| 201 | +} |
| 202 | + |
| 203 | +TEST_F(TestEthernetInterface, set_network) |
| 204 | +{ |
| 205 | + char ipAddress[NSAPI_IPv4_SIZE] = "127.0.0.1"; |
| 206 | + char netmask[NSAPI_IPv4_SIZE] = "255.255.0.0"; |
| 207 | + char gateway[NSAPI_IPv4_SIZE] = "127.0.0.2"; |
| 208 | + |
| 209 | + const char *ipAddressArg; |
| 210 | + const char *netmaskArg; |
| 211 | + const char *gatewayArg; |
| 212 | + |
| 213 | + EXPECT_EQ(0, iface->get_ip_address()); |
| 214 | + EXPECT_EQ(0, iface->get_netmask()); |
| 215 | + EXPECT_EQ(0, iface->get_gateway()); |
| 216 | + |
| 217 | + // Set the network data |
| 218 | + EXPECT_EQ(NSAPI_ERROR_OK, iface->set_network(ipAddress, netmask, gateway)); |
| 219 | + |
| 220 | + // Now the bringup should have different arguments. We can't use doConnect method. |
| 221 | + EXPECT_CALL(*stackMock, add_ethernet_interface(testing::Ref(*emacMock), true, _)) |
| 222 | + .Times(1) |
| 223 | + .WillOnce(DoAll(SetArgPointee<2>(netStackIface), Return(NSAPI_ERROR_OK))); |
| 224 | + EXPECT_CALL(*netStackIface, attach(_)) |
| 225 | + .Times(1) |
| 226 | + .RetiresOnSaturation(); |
| 227 | + // Do not put the expected char * arguments, as they are pointers and would not match |
| 228 | + EXPECT_CALL(*netStackIface, bringup(false, _, _, _, DEFAULT_STACK, true)) |
| 229 | + .Times(1) |
| 230 | + .WillOnce(DoAll(SaveArg<1>(&ipAddressArg), |
| 231 | + SaveArg<2>(&netmaskArg), |
| 232 | + SaveArg<3>(&gatewayArg), |
| 233 | + Return(NSAPI_ERROR_OK))); |
| 234 | + EXPECT_EQ(NSAPI_ERROR_OK, iface->connect()); |
| 235 | + // Check the contents of the stored pointer arguments. |
| 236 | + EXPECT_TRUE(0 == strcmp(ipAddress, ipAddressArg)); |
| 237 | + EXPECT_TRUE(0 == strcmp(netmask, netmaskArg)); |
| 238 | + EXPECT_TRUE(0 == strcmp(gateway, gatewayArg)); |
| 239 | + |
| 240 | + // Testing the getters makes sense now. |
| 241 | + EXPECT_CALL(*netStackIface, get_ip_address(_, _)) |
| 242 | + .Times(1) |
| 243 | + .WillOnce(DoAll(SetArgPointee<0>(*ipAddress), Return(ipAddress))); |
| 244 | + EXPECT_EQ(std::string(ipAddress), std::string(iface->get_ip_address())); |
| 245 | + |
| 246 | + EXPECT_CALL(*netStackIface, get_netmask(_, _)) |
| 247 | + .Times(1) |
| 248 | + .WillOnce(DoAll(SetArgPointee<0>(*netmask), Return(netmask))); |
| 249 | + EXPECT_EQ(std::string(netmask), std::string(iface->get_netmask())); |
| 250 | + |
| 251 | + EXPECT_CALL(*netStackIface, get_gateway(_, _)) |
| 252 | + .Times(1) |
| 253 | + .WillOnce(DoAll(SetArgPointee<0>(*gateway), Return(gateway))); |
| 254 | + EXPECT_EQ(std::string(gateway), std::string(iface->get_gateway())); |
| 255 | +} |
| 256 | + |
| 257 | +TEST_F(TestEthernetInterface, get_connection_status) |
| 258 | +{ |
| 259 | + EXPECT_EQ(NSAPI_STATUS_DISCONNECTED, iface->get_connection_status()); |
| 260 | + |
| 261 | + doConnect(); |
| 262 | + |
| 263 | + EXPECT_CALL(*netStackIface, get_connection_status()) |
| 264 | + .Times(1) |
| 265 | + .WillOnce(Return(NSAPI_STATUS_LOCAL_UP)); |
| 266 | + EXPECT_EQ(NSAPI_STATUS_LOCAL_UP, iface->get_connection_status()); |
| 267 | +} |
| 268 | + |
| 269 | +TEST_F(TestEthernetInterface, attach) |
| 270 | +{ |
| 271 | + doConnect(); |
| 272 | + EXPECT_CALL(*netStackIface, attach(_)) // TODO: check that the correct function is passed. |
| 273 | + .Times(1); |
| 274 | + iface->attach(cb); |
| 275 | +} |
| 276 | + |
| 277 | +TEST_F(TestEthernetInterface, set_dhcp) |
| 278 | +{ |
| 279 | + EXPECT_EQ(NSAPI_ERROR_OK, iface->set_dhcp(false)); |
| 280 | + doConnect(false, true); |
| 281 | +} |
| 282 | + |
| 283 | +TEST_F(TestEthernetInterface, set_blocking) |
| 284 | +{ |
| 285 | + EXPECT_EQ(NSAPI_ERROR_OK, iface->set_blocking(false)); |
| 286 | + doConnect(true, false); |
| 287 | +} |
0 commit comments