test_tcp_socket.cpp
Go to the documentation of this file.
1 // -- BEGIN LICENSE BLOCK ----------------------------------------------
2 // Copyright 2022 Universal Robots A/S
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 //
14 // * Neither the name of the {copyright_holder} nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 // POSSIBILITY OF SUCH DAMAGE.
29 // -- END LICENSE BLOCK ------------------------------------------------
30 
31 #include <gtest/gtest.h>
32 #include <condition_variable>
33 
37 
38 using namespace urcl;
39 
40 class TCPSocketTest : public ::testing::Test
41 {
42 protected:
43  void SetUp()
44  {
45  server_.reset(new comm::TCPServer(60001));
46  server_->setConnectCallback(std::bind(&TCPSocketTest::connectionCallback, this, std::placeholders::_1));
47  server_->setMessageCallback(
48  std::bind(&TCPSocketTest::messageCallback, this, std::placeholders::_1, std::placeholders::_2));
49  server_->start();
50 
51  client_.reset(new Client(60001));
52  }
53 
54  void TearDown()
55  {
56  server_.reset();
57  client_.reset();
58  }
59 
60  // callback functions for the tcp server
61  void messageCallback(const int filedescriptor, char* buffer)
62  {
63  std::lock_guard<std::mutex> lk(message_mutex_);
64  received_message_ = std::string(buffer);
65  message_cv_.notify_one();
66  message_callback_ = true;
67  }
68 
69  void connectionCallback(const int filedescriptor)
70  {
71  std::lock_guard<std::mutex> lk(connect_mutex_);
72  client_fd_ = filedescriptor;
73  connect_cv_.notify_one();
74  connection_callback_ = true;
75  }
76 
77  bool waitForMessageCallback(int milliseconds = 100)
78  {
79  std::unique_lock<std::mutex> lk(message_mutex_);
80  if (message_cv_.wait_for(lk, std::chrono::milliseconds(milliseconds)) == std::cv_status::no_timeout ||
81  message_callback_ == true)
82  {
83  message_callback_ = false;
84  return true;
85  }
86  return false;
87  }
88 
89  bool waitForConnectionCallback(int milliseconds = 100)
90  {
91  std::unique_lock<std::mutex> lk(connect_mutex_);
92  if (connect_cv_.wait_for(lk, std::chrono::milliseconds(milliseconds)) == std::cv_status::no_timeout ||
93  connection_callback_ == true)
94  {
95  connection_callback_ = false;
96  return true;
97  }
98  return false;
99  }
100 
101  class Client : public comm::TCPSocket
102  {
103  public:
104  Client(int port)
105  {
106  port_ = port;
107  }
108 
109  bool setup()
110  {
111  std::string ip = "127.0.0.1";
112  return TCPSocket::setup(ip, port_);
113  }
114 
116  {
117  done_setting_up_client_ = false;
118  client_setup_thread_ = std::thread(&Client::setupClient, this, port_);
119  }
120 
122  {
123  unsigned int max_count = 50;
124  unsigned int count = 0;
125  while (count < max_count)
126  {
127  if (done_setting_up_client_)
128  {
129  client_setup_thread_.join();
130  return true;
131  }
132  std::this_thread::sleep_for(std::chrono::milliseconds(100));
133  count++;
134  }
135  client_setup_thread_.detach();
136  return false;
137  }
138 
139  protected:
140  virtual bool open(int socket_fd, struct sockaddr* address, size_t address_len)
141  {
142  return ::connect(socket_fd, address, address_len) == 0;
143  }
144 
145  private:
146  std::thread client_setup_thread_;
147  int port_;
149 
150  void setupClient(int port)
151  {
152  std::string ip = "127.0.0.1";
153  TCPSocket::setup(ip, port);
154  done_setting_up_client_ = true;
155  }
156  };
157 
158  std::string received_message_;
160 
161  std::unique_ptr<comm::TCPServer> server_;
162  std::unique_ptr<Client> client_;
163 
164 private:
165  std::condition_variable message_cv_;
166  std::mutex message_mutex_;
167 
168  std::condition_variable connect_cv_;
169  std::mutex connect_mutex_;
170 
171  bool connection_callback_ = false;
172  bool message_callback_ = false;
173 };
174 
175 TEST_F(TCPSocketTest, socket_state)
176 {
177  // Client state should be invalid
179  comm::SocketState actual_state = client_->getState();
180 
181  EXPECT_EQ(toUnderlying(expected_state), toUnderlying(actual_state));
182 
183  // Client state should be connected after setup
184  client_->setup();
185  expected_state = comm::SocketState::Connected;
186  actual_state = client_->getState();
187 
188  EXPECT_EQ(toUnderlying(expected_state), toUnderlying(actual_state));
189 
190  // Client state should be closed after close call
191  client_->close();
192  expected_state = comm::SocketState::Closed;
193  actual_state = client_->getState();
194 
195  EXPECT_EQ(toUnderlying(expected_state), toUnderlying(actual_state));
196 }
197 
198 TEST_F(TCPSocketTest, setup_client_before_server)
199 {
200  // Make server unavailable
201  server_.reset();
202 
203  client_->setReconnectionTime(std::chrono::seconds(1));
204  client_->setupClientBeforeServer();
205 
206  // Make sure that the client has tried to connect to the server, before creating the server
207  std::this_thread::sleep_for(std::chrono::seconds(1));
208 
209  // Client state should be invalid as long as the server is not available
211  comm::SocketState actual_state = client_->getState();
212 
213  EXPECT_EQ(toUnderlying(expected_state), toUnderlying(actual_state));
214 
215  server_.reset(new comm::TCPServer(60001));
216  server_->start();
217 
218  // Test that client goes into connected state after the server has been started
219  EXPECT_TRUE(client_->waitForClientSetupThread());
220  expected_state = comm::SocketState::Connected;
221  actual_state = client_->getState();
222 
223  EXPECT_EQ(toUnderlying(expected_state), toUnderlying(actual_state));
224 }
225 
227 {
228  // Ip should be empty when the client is not connected to any server
229  std::string expected_ip = "";
230  std::string actual_ip = client_->getIP();
231 
232  EXPECT_EQ(expected_ip, actual_ip);
233 
234  client_->setup();
235  expected_ip = "127.0.0.1";
236  actual_ip = client_->getIP();
237 
238  EXPECT_EQ(expected_ip, actual_ip);
239 }
240 
241 TEST_F(TCPSocketTest, write_on_non_connected_socket)
242 {
243  std::string message = "test message";
244  const uint8_t* data = reinterpret_cast<const uint8_t*>(message.c_str());
245  size_t len = message.size();
246  size_t written;
247 
248  EXPECT_FALSE(client_->write(data, len, written));
249 }
250 
251 TEST_F(TCPSocketTest, read_on_non_connected_socket)
252 {
253  char character;
254  size_t read_chars = 0;
255 
256  EXPECT_FALSE(client_->read((uint8_t*)&character, 1, read_chars));
257 }
258 
259 TEST_F(TCPSocketTest, write_on_connected_socket)
260 {
261  client_->setup();
262 
263  std::string message = "test message";
264  const uint8_t* data = reinterpret_cast<const uint8_t*>(message.c_str());
265  size_t len = message.size();
266  size_t written;
267  client_->write(data, len, written);
268 
269  EXPECT_TRUE(waitForMessageCallback());
270  EXPECT_EQ(message, received_message_);
271 }
272 
273 TEST_F(TCPSocketTest, read_on_connected_socket)
274 {
275  client_->setup();
276 
277  // Make sure the client has connected to the server, before writing to the client
278  EXPECT_TRUE(waitForConnectionCallback());
279 
280  std::string send_message = "test message";
281  size_t len = send_message.size();
282  const uint8_t* data = reinterpret_cast<const uint8_t*>(send_message.c_str());
283  size_t written;
284  server_->write(client_fd_, data, len, written);
285 
286  std::stringstream ss;
287  char characters;
288  size_t read_chars = 0;
289  while (len > 0)
290  {
291  client_->read((uint8_t*)&characters, 1, read_chars);
292  ss << characters;
293  len -= 1;
294  }
295 
296  std::string received_message = ss.str();
297 
298  EXPECT_EQ(send_message, received_message);
299 }
300 
301 TEST_F(TCPSocketTest, get_socket_fd)
302 {
303  // When the client is not connected to any socket the fd should be -1
304  int expected_fd = -1;
305  int actual_fd = client_->getSocketFD();
306 
307  EXPECT_EQ(expected_fd, actual_fd);
308 
309  client_->setup();
310  actual_fd = client_->getSocketFD();
311 
312  // When the client has connected to the socket the file descriptor should be different from -1
313  EXPECT_NE(expected_fd, actual_fd);
314 
315  client_->close();
316  actual_fd = client_->getSocketFD();
317 
318  EXPECT_EQ(expected_fd, actual_fd);
319 }
320 
321 TEST_F(TCPSocketTest, receive_timeout)
322 {
323  client_->setup();
324 
325  timeval tv;
326  tv.tv_sec = 1;
327  tv.tv_usec = 0;
328  client_->setReceiveTimeout(tv);
329 
330  char character;
331  size_t read_chars = 0;
332 
333  // Read should return false, when it times out
334  EXPECT_FALSE(client_->read((uint8_t*)&character, 1, read_chars));
335 }
336 
337 TEST_F(TCPSocketTest, setup_while_client_is_connected)
338 {
339  client_->setup();
340 
341  EXPECT_FALSE(client_->setup());
342 }
343 
344 int main(int argc, char* argv[])
345 {
346  ::testing::InitGoogleTest(&argc, argv);
347 
348  return RUN_ALL_TESTS();
349 }
std::condition_variable message_cv_
void connectionCallback(const int filedescriptor)
void messageCallback(const int filedescriptor, char *buffer)
Connection to socket got closed.
Wrapper class for a TCP socket server.
Definition: tcp_server.h:59
SocketState
State the socket can be in.
Definition: tcp_socket.h:37
void setupClient(int port)
std::mutex message_mutex_
bool waitForMessageCallback(int milliseconds=100)
Socket is initialized or setup failed.
int main(int argc, char *argv[])
std::thread client_setup_thread_
TEST_F(TCPSocketTest, socket_state)
std::condition_variable connect_cv_
std::unique_ptr< comm::TCPServer > server_
std::mutex connect_mutex_
std::unique_ptr< Client > client_
virtual bool open(int socket_fd, struct sockaddr *address, size_t address_len)
Class for TCP socket abstraction.
Definition: tcp_socket.h:48
constexpr std::underlying_type< E >::type toUnderlying(const E e) noexcept
Converts an enum type to its underlying type.
Definition: types.h:58
Socket is connected and ready to use.
std::string received_message_
bool waitForConnectionCallback(int milliseconds=100)


ur_client_library
Author(s): Thomas Timm Andersen, Simon Rasmussen, Felix Exner, Lea Steffen, Tristan Schnell
autogenerated on Tue Jul 4 2023 02:09:47