test_tcp_server.cpp
Go to the documentation of this file.
1 // -- BEGIN LICENSE BLOCK ----------------------------------------------
2 // Copyright 2021 Universal Robots A/S
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 
16 // All source code contained in and/or linked to in this message (the “Source Code”) is subject to the copyright of
17 // Universal Robots A/S and/or its licensors. THE SOURCE CODE IS PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 // OR IMPLIED, INCLUDING – BUT NOT LIMITED TO – WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
19 // NONINFRINGEMENT. USE OF THE SOURCE CODE IS AT YOUR OWN RISK AND UNIVERSAL ROBOTS A/S AND ITS LICENSORS SHALL, TO THE
20 // MAXIMUM EXTENT PERMITTED BY LAW, NOT BE LIABLE FOR ANY ERRORS OR MALICIOUS CODE IN THE SOURCE CODE, ANY THIRD-PARTY
21 // CLAIMS, OR ANY OTHER CLAIMS AND DAMAGES, INCLUDING INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR PUNITIVE DAMAGES,
22 // OR ANY LOSS OF PROFITS, EXPECTED SAVINGS, OR REVENUES, WHETHER INCURRED DIRECTLY OR INDIRECTLY, OR ANY LOSS OF DATA,
23 // USE, GOODWILL, OR OTHER INTANGIBLE LOSSES, RESULTING FROM YOUR USE OF THE SOURCE CODE. You may make copies of the
24 // Source Code for use in connection with a Universal Robots or UR+ product, provided that you include (i) an
25 // appropriate copyright notice (“© [the year in which you received the Source Code or the Source Code was first
26 // published, e.g. “2021”] Universal Robots A/S and/or its licensors”) along with the capitalized section of this notice
27 // in all copies of the Source Code. By using the Source Code, you agree to the above terms. For more information,
28 // please contact legal@universal-robots.com.
29 // -- END LICENSE BLOCK ------------------------------------------------
30 
31 #include <gtest/gtest.h>
32 #include <condition_variable>
33 #include <chrono>
34 
37 
38 using namespace urcl;
39 
40 class TCPServerTest : public ::testing::Test
41 {
42 protected:
43  class Client : public comm::TCPSocket
44  {
45  public:
46  Client(const int& port)
47  {
48  std::string host = "127.0.0.1";
49  TCPSocket::setup(host, port);
50  }
51 
52  void send(const std::string& text)
53  {
54  size_t len = text.size();
55  const uint8_t* data = reinterpret_cast<const uint8_t*>(text.c_str());
56  size_t written;
57  TCPSocket::write(data, len, written);
58  }
59 
60  std::string recv()
61  {
62  std::stringstream result;
63  char character;
64  size_t read_chars = 99;
65  while (read_chars > 0)
66  {
67  TCPSocket::read((uint8_t*)&character, 1, read_chars);
68  result << character;
69  if (character == '\n')
70  {
71  break;
72  }
73  }
74  return result.str();
75  }
76 
77  protected:
78  virtual bool open(int socket_fd, struct sockaddr* address, size_t address_len)
79  {
80  return ::connect(socket_fd, address, address_len) == 0;
81  }
82  };
83 
84  // callback functions
85  void connectionCallback(const int filedescriptor)
86  {
87  std::lock_guard<std::mutex> lk(connect_mutex_);
88  client_fd_ = filedescriptor;
89  connect_cv_.notify_one();
90  connection_callback_ = true;
91  }
92 
93  void disconnectionCallback(const int filedescriptor)
94  {
95  std::lock_guard<std::mutex> lk(disconnect_mutex_);
96  client_fd_ = -1;
97  disconnect_cv_.notify_one();
98  disconnection_callback_ = true;
99  }
100 
101  void messageCallback(const int filedescriptor, char* buffer)
102  {
103  std::lock_guard<std::mutex> lk(message_mutex_);
104  message_ = std::string(buffer);
105  message_cv_.notify_one();
106  message_callback_ = true;
107  }
108 
109  bool waitForConnectionCallback(int milliseconds = 100)
110  {
111  std::unique_lock<std::mutex> lk(connect_mutex_);
112  if (connect_cv_.wait_for(lk, std::chrono::milliseconds(milliseconds)) == std::cv_status::no_timeout ||
113  connection_callback_ == true)
114  {
115  connection_callback_ = false;
116  return true;
117  }
118  else
119  {
120  return false;
121  }
122  }
123 
124  bool waitForDisconnectionCallback(int milliseconds = 100)
125  {
126  std::unique_lock<std::mutex> lk(disconnect_mutex_);
127  if (disconnect_cv_.wait_for(lk, std::chrono::milliseconds(milliseconds)) == std::cv_status::no_timeout ||
128  disconnection_callback_ == true)
129  {
130  disconnection_callback_ = false;
131  return true;
132  }
133  else
134  {
135  return false;
136  }
137  }
138 
139  bool waitForMessageCallback(int milliseconds = 100)
140  {
141  std::unique_lock<std::mutex> lk(message_mutex_);
142  if (message_cv_.wait_for(lk, std::chrono::milliseconds(milliseconds)) == std::cv_status::no_timeout ||
143  message_callback_ == true)
144  {
145  message_callback_ = false;
146  return true;
147  }
148  else
149  {
150  return false;
151  }
152  }
153 
154  int port_ = 50001;
155  std::string message_ = "";
156  int client_fd_ = -1;
157 
158 private:
159  std::condition_variable connect_cv_;
160  std::mutex connect_mutex_;
161 
162  std::condition_variable disconnect_cv_;
163  std::mutex disconnect_mutex_;
164 
165  std::condition_variable message_cv_;
166  std::mutex message_mutex_;
167 
168  bool connection_callback_ = false;
169  bool disconnection_callback_ = false;
170  bool message_callback_ = false;
171 };
172 
173 TEST_F(TCPServerTest, socket_creation)
174 {
175  comm::TCPServer server(port_);
176 
177  // Shouldn't be able to create antoher server on same port
178  EXPECT_THROW(comm::TCPServer server2(port_), std::system_error);
179 
180  server.start();
181 
182  // We should be able to connect to the server even though the callbacks haven't been configured
183  ASSERT_NO_THROW(Client client(port_));
184  Client client(port_);
185 
186  // We should also be able to send message and disconnect. We wait to be absolutely sure no exception is thrown
187  EXPECT_NO_THROW(client.send("message\n"));
188  EXPECT_NO_THROW(waitForMessageCallback());
189 
190  EXPECT_NO_THROW(client.close());
191  EXPECT_NO_THROW(waitForDisconnectionCallback());
192 }
193 
194 TEST_F(TCPServerTest, callback_functions)
195 {
196  comm::TCPServer server(port_);
197  server.setMessageCallback(std::bind(&TCPServerTest_callback_functions_Test::messageCallback, this,
198  std::placeholders::_1, std::placeholders::_2));
199  server.setConnectCallback(
200  std::bind(&TCPServerTest_callback_functions_Test::connectionCallback, this, std::placeholders::_1));
201  server.setDisconnectCallback(
202  std::bind(&TCPServerTest_callback_functions_Test::disconnectionCallback, this, std::placeholders::_1));
203  server.start();
204 
205  // Check that the appropriate callback functions are called
206  Client client(port_);
207  EXPECT_TRUE(waitForConnectionCallback());
208 
209  client.send("message\n");
210  EXPECT_TRUE(waitForMessageCallback());
211 
212  client.close();
213  EXPECT_TRUE(waitForDisconnectionCallback());
214 }
215 
216 TEST_F(TCPServerTest, unlimited_clients_allowed)
217 {
218  comm::TCPServer server(port_);
219  server.setMessageCallback(std::bind(&TCPServerTest_unlimited_clients_allowed_Test::messageCallback, this,
220  std::placeholders::_1, std::placeholders::_2));
221  server.setConnectCallback(
222  std::bind(&TCPServerTest_unlimited_clients_allowed_Test::connectionCallback, this, std::placeholders::_1));
223  server.setDisconnectCallback(
224  std::bind(&TCPServerTest_unlimited_clients_allowed_Test::disconnectionCallback, this, std::placeholders::_1));
225  server.start();
226 
227  // Test that a large number of clients can connect to the server
228  std::vector<Client*> clients;
229  Client* client;
230  for (unsigned int i = 0; i < 100; ++i)
231  {
232  client = new Client(port_);
233  ASSERT_TRUE(waitForConnectionCallback());
234  clients.push_back(client);
235  }
236 }
237 
238 TEST_F(TCPServerTest, max_clients_allowed)
239 {
240  comm::TCPServer server(port_);
241  server.setMessageCallback(std::bind(&TCPServerTest_max_clients_allowed_Test::messageCallback, this,
242  std::placeholders::_1, std::placeholders::_2));
243  server.setConnectCallback(
244  std::bind(&TCPServerTest_max_clients_allowed_Test::connectionCallback, this, std::placeholders::_1));
245  server.setDisconnectCallback(
246  std::bind(&TCPServerTest_max_clients_allowed_Test::disconnectionCallback, this, std::placeholders::_1));
247  server.start();
248  server.setMaxClientsAllowed(1);
249 
250  // Test that only one client can connect
251  Client client1(port_);
252  EXPECT_TRUE(waitForConnectionCallback());
253  Client client2(port_);
254  EXPECT_FALSE(waitForConnectionCallback());
255 }
256 
257 TEST_F(TCPServerTest, message_transmission)
258 {
259  comm::TCPServer server(port_);
260  server.setMessageCallback(std::bind(&TCPServerTest_message_transmission_Test::messageCallback, this,
261  std::placeholders::_1, std::placeholders::_2));
262  server.setConnectCallback(
263  std::bind(&TCPServerTest_message_transmission_Test::connectionCallback, this, std::placeholders::_1));
264  server.setDisconnectCallback(
265  std::bind(&TCPServerTest_message_transmission_Test::disconnectionCallback, this, std::placeholders::_1));
266  server.start();
267 
268  Client client(port_);
269  EXPECT_TRUE(waitForConnectionCallback());
270 
271  // Test that messages are transmitted corectly between client and server
272  std::string message = "test message\n";
273  client.send(message);
274 
275  EXPECT_TRUE(waitForMessageCallback());
276  EXPECT_EQ(message, message_);
277 
278  size_t len = message.size();
279  const uint8_t* data = reinterpret_cast<const uint8_t*>(message.c_str());
280  size_t written;
281 
282  ASSERT_TRUE(server.write(client_fd_, data, len, written));
283  EXPECT_EQ(client.recv(), message);
284 }
285 
286 TEST_F(TCPServerTest, client_connections)
287 {
288  comm::TCPServer server(port_);
289  server.setMessageCallback(std::bind(&TCPServerTest_client_connections_Test::messageCallback, this,
290  std::placeholders::_1, std::placeholders::_2));
291  server.setConnectCallback(
292  std::bind(&TCPServerTest_client_connections_Test::connectionCallback, this, std::placeholders::_1));
293  server.setDisconnectCallback(
294  std::bind(&TCPServerTest_client_connections_Test::disconnectionCallback, this, std::placeholders::_1));
295  server.start();
296 
297  std::string message = "text message\n";
298  size_t len = message.size();
299  const uint8_t* data = reinterpret_cast<const uint8_t*>(message.c_str());
300  size_t written;
301 
302  // Test that we can connect multiple clients
303  Client client1(port_);
304  EXPECT_TRUE(waitForConnectionCallback());
305  int client1_fd = client_fd_;
306 
307  Client client2(port_);
308  EXPECT_TRUE(waitForConnectionCallback());
309  int client2_fd = client_fd_;
310 
311  Client client3(port_);
312  EXPECT_TRUE(waitForConnectionCallback());
313  int client3_fd = client_fd_;
314 
315  // Test that the correct clients are disconnected on the server side.
316  client1.close();
317  EXPECT_TRUE(waitForDisconnectionCallback());
318 
319  EXPECT_FALSE(server.write(client1_fd, data, len, written));
320  EXPECT_TRUE(server.write(client2_fd, data, len, written));
321  EXPECT_TRUE(server.write(client3_fd, data, len, written));
322 
323  client2.close();
324  EXPECT_TRUE(waitForDisconnectionCallback());
325  EXPECT_FALSE(server.write(client1_fd, data, len, written));
326  EXPECT_FALSE(server.write(client2_fd, data, len, written));
327  EXPECT_TRUE(server.write(client3_fd, data, len, written));
328 
329  client3.close();
330  EXPECT_TRUE(waitForDisconnectionCallback());
331  EXPECT_FALSE(server.write(client1_fd, data, len, written));
332  EXPECT_FALSE(server.write(client2_fd, data, len, written));
333  EXPECT_FALSE(server.write(client3_fd, data, len, written));
334 }
335 
336 int main(int argc, char* argv[])
337 {
338  ::testing::InitGoogleTest(&argc, argv);
339 
340  return RUN_ALL_TESTS();
341 }
void setDisconnectCallback(std::function< void(const int)> func)
This callback will be triggered on clients disconnecting from the server.
Definition: tcp_server.h:83
bool waitForConnectionCallback(int milliseconds=100)
void setMaxClientsAllowed(const uint32_t &max_clients_allowed)
Set the maximum number of clients allowed to connect to this server.
Definition: tcp_server.h:143
void send(const std::string &text)
void setConnectCallback(std::function< void(const int)> func)
This callback will be triggered on clients connecting to the server.
Definition: tcp_server.h:72
std::condition_variable message_cv_
virtual bool open(int socket_fd, struct sockaddr *address, size_t address_len)
TEST_F(TCPServerTest, socket_creation)
Wrapper class for a TCP socket server.
Definition: tcp_server.h:59
void start()
Start event handling.
Definition: tcp_server.cpp:307
void connectionCallback(const int filedescriptor)
void messageCallback(const int filedescriptor, char *buffer)
bool waitForDisconnectionCallback(int milliseconds=100)
bool waitForMessageCallback(int milliseconds=100)
std::condition_variable connect_cv_
std::mutex message_mutex_
Client(const int &port)
std::condition_variable disconnect_cv_
bool write(const int fd, const uint8_t *buf, const size_t buf_len, size_t &written)
Writes to a client.
Definition: tcp_server.cpp:314
std::mutex disconnect_mutex_
std::mutex connect_mutex_
Class for TCP socket abstraction.
Definition: tcp_socket.h:48
void disconnectionCallback(const int filedescriptor)
void setMessageCallback(std::function< void(const int, char *)> func)
This callback will be triggered on messages received on the socket.
Definition: tcp_server.h:94
int main(int argc, char *argv[])


ur_client_library
Author(s): Thomas Timm Andersen, Simon Rasmussen, Felix Exner, Lea Steffen, Tristan Schnell
autogenerated on Sun May 9 2021 02:16:26