Skip to content

Commit 6eddca6

Browse files
authored
Fix flakyness in ReverseInterfaceTest.handle_program_state (UniversalRobots#430)
The test was flaky because of re-using the real reverse interface's port. So, it was potentially interfacing with something running on URSim. This Commit uses a random port for testing the reverse interface.
1 parent 7b57b66 commit 6eddca6

4 files changed

Lines changed: 78 additions & 25 deletions

File tree

include/ur_client_library/comm/tcp_server.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,21 @@ class TCPServer
154154
max_clients_allowed_ = max_clients_allowed;
155155
}
156156

157+
/*!
158+
* \brief Get the port this server is bound to
159+
*
160+
* If port number 0 is passed during initialization, the server will bind to a random free port.
161+
* In this case, this function can be used to get the actual port number the server is bound to.
162+
* This should only be called after the server has been initialized, otherwise the returned port
163+
* number might not be correct.
164+
*
165+
* \returns The port number this server is bound to
166+
*/
167+
int getPort() const
168+
{
169+
return port_;
170+
}
171+
157172
private:
158173
void init();
159174
void bind(const size_t max_num_tries, const std::chrono::milliseconds reconnection_time);
@@ -197,4 +212,4 @@ class TCPServer
197212
} // namespace comm
198213
} // namespace urcl
199214

200-
#endif // ifndef UR_CLIENT_LIBRARY_TCP_SERVER_H_INCLUDED
215+
#endif // ifndef UR_CLIENT_LIBRARY_TCP_SERVER_H_INCLUDED

include/ur_client_library/control/reverse_interface.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,19 @@ class ReverseInterface
194194
return client_fd_ != INVALID_SOCKET;
195195
}
196196

197+
/*!
198+
* \brief Get the port number the server is bound to.
199+
*
200+
* If port number 0 is passed during initialization, the server will bind to a random free port.
201+
* In this case, this function can be used to get the actual port number the server is bound to.
202+
*
203+
* \returns The port number the server is bound to.
204+
*/
205+
int getPort() const
206+
{
207+
return server_.getPort();
208+
}
209+
197210
protected:
198211
virtual void connectionCallback(const socket_t filedescriptor);
199212

src/comm/tcp_server.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,17 @@ void TCPServer::startListen()
170170
ss << "Failed to start listen on port " << port_;
171171
throw std::system_error(std::error_code(errno, std::generic_category()), ss.str());
172172
}
173+
struct sockaddr_in sin;
174+
socklen_t len = sizeof(sin);
175+
if (getsockname(listen_fd_, (struct sockaddr*)&sin, &len) == -1)
176+
{
177+
URCL_LOG_ERROR("getsockname() failed to get port number for listening socket: %s", strerror(errno));
178+
}
179+
180+
else
181+
{
182+
port_ = ntohs(sin.sin_port);
183+
}
173184
URCL_LOG_DEBUG("Listening on port %d", port_);
174185
}
175186

tests/test_reverse_interface.cpp

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class TestableReverseInterface : public control::ReverseInterface
6565
std::atomic<bool> connected = false;
6666
};
6767

68-
class ReverseIntefaceTest : public ::testing::Test
68+
class ReverseInterfaceTest : public ::testing::Test
6969
{
7070
protected:
7171
class Client : public comm::TCPSocket
@@ -181,10 +181,11 @@ class ReverseIntefaceTest : public ::testing::Test
181181
void SetUp()
182182
{
183183
control::ReverseInterfaceConfig config;
184-
config.port = 50001;
185-
config.handle_program_state = std::bind(&ReverseIntefaceTest::handleProgramState, this, std::placeholders::_1);
184+
config.port = 0;
185+
config.handle_program_state = std::bind(&ReverseInterfaceTest::handleProgramState, this, std::placeholders::_1);
186186
reverse_interface_.reset(new TestableReverseInterface(config));
187-
client_.reset(new Client(50001));
187+
test_port_ = reverse_interface_->getPort();
188+
client_.reset(new Client(test_port_));
188189
std::unique_lock<std::mutex> lk(g_connection_mutex);
189190
g_connection_condition.wait_for(lk, std::chrono::seconds(1),
190191
[&]() { return reverse_interface_->connected.load(); });
@@ -202,34 +203,47 @@ class ReverseIntefaceTest : public ::testing::Test
202203
void handleProgramState(bool program_state)
203204
{
204205
std::lock_guard<std::mutex> lk(program_running_mutex_);
205-
program_running_.notify_one();
206+
new_program_state_received_ = true;
206207
program_state_ = program_state;
208+
program_running_.notify_one();
207209
}
208210

209211
bool waitForProgramState(int milliseconds = 100, bool program_state = true)
210212
{
213+
// Wait for new state until timeout has elapsed
211214
std::unique_lock<std::mutex> lk(program_running_mutex_);
212-
if (program_running_.wait_for(lk, std::chrono::milliseconds(milliseconds)) == std::cv_status::no_timeout ||
213-
program_state_ == program_state)
215+
std::chrono::steady_clock::time_point start_time = std::chrono::steady_clock::now();
216+
while (
217+
program_state_ != program_state &&
218+
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time).count() <
219+
milliseconds)
214220
{
215-
if (program_state_ == program_state)
221+
if (program_running_.wait_for(lk, std::chrono::milliseconds(milliseconds / 10),
222+
[this] { return new_program_state_received_.load(); }))
216223
{
217-
return true;
224+
new_program_state_received_ = false;
225+
// Check whether the new state matches the expected state
226+
if (program_state_ == program_state)
227+
{
228+
return true;
229+
}
218230
}
219231
}
220-
return false;
232+
return program_state_ == program_state;
221233
}
222234

223235
std::unique_ptr<TestableReverseInterface> reverse_interface_;
224236
std::unique_ptr<Client> client_;
237+
int test_port_;
225238

226239
private:
227240
std::atomic<bool> program_state_ = ATOMIC_VAR_INIT(false);
241+
std::atomic<bool> new_program_state_received_ = ATOMIC_VAR_INIT(false);
228242
std::condition_variable program_running_;
229243
std::mutex program_running_mutex_;
230244
};
231245

232-
TEST_F(ReverseIntefaceTest, handle_program_state)
246+
TEST_F(ReverseInterfaceTest, handle_program_state)
233247
{
234248
// Test that handle program state is called when the client connects to the server
235249
EXPECT_TRUE(waitForProgramState(1000, true));
@@ -239,7 +253,7 @@ TEST_F(ReverseIntefaceTest, handle_program_state)
239253
EXPECT_TRUE(waitForProgramState(1000, false));
240254
}
241255

242-
TEST_F(ReverseIntefaceTest, write_positions)
256+
TEST_F(ReverseInterfaceTest, write_positions)
243257
{
244258
// Wait for the client to connect to the server
245259
EXPECT_TRUE(waitForProgramState(1000, true));
@@ -256,7 +270,7 @@ TEST_F(ReverseIntefaceTest, write_positions)
256270
EXPECT_EQ(written_positions[5], ((double)received_positions[5]) / reverse_interface_->MULT_JOINTSTATE);
257271
}
258272

259-
TEST_F(ReverseIntefaceTest, write_trajectory_control_message)
273+
TEST_F(ReverseInterfaceTest, write_trajectory_control_message)
260274
{
261275
// Wait for the client to connect to the server
262276
EXPECT_TRUE(waitForProgramState(1000, true));
@@ -280,7 +294,7 @@ TEST_F(ReverseIntefaceTest, write_trajectory_control_message)
280294
EXPECT_EQ(toUnderlying(written_control_message), received_control_message);
281295
}
282296

283-
TEST_F(ReverseIntefaceTest, write_trajectory_point_number)
297+
TEST_F(ReverseInterfaceTest, write_trajectory_point_number)
284298
{
285299
// Wait for the client to connect to the server
286300
EXPECT_TRUE(waitForProgramState(1000, true));
@@ -293,7 +307,7 @@ TEST_F(ReverseIntefaceTest, write_trajectory_point_number)
293307
EXPECT_EQ(written_point_number, received_point_number);
294308
}
295309

296-
TEST_F(ReverseIntefaceTest, control_mode_is_forward)
310+
TEST_F(ReverseInterfaceTest, control_mode_is_forward)
297311
{
298312
// Wait for the client to connect to the server
299313
EXPECT_TRUE(waitForProgramState(1000, true));
@@ -306,7 +320,7 @@ TEST_F(ReverseIntefaceTest, control_mode_is_forward)
306320
EXPECT_EQ(toUnderlying(expected_control_mode), received_control_mode);
307321
}
308322

309-
TEST_F(ReverseIntefaceTest, remaining_message_points_are_zeros)
323+
TEST_F(ReverseInterfaceTest, remaining_message_points_are_zeros)
310324
{
311325
// Wait for the client to connect to the server
312326
EXPECT_TRUE(waitForProgramState(1000, true));
@@ -323,7 +337,7 @@ TEST_F(ReverseIntefaceTest, remaining_message_points_are_zeros)
323337
EXPECT_EQ(0, received_pos[5]);
324338
}
325339

326-
TEST_F(ReverseIntefaceTest, read_timeout)
340+
TEST_F(ReverseInterfaceTest, read_timeout)
327341
{
328342
// Wait for the client to connect to the server
329343
EXPECT_TRUE(waitForProgramState(1000, true));
@@ -352,7 +366,7 @@ TEST_F(ReverseIntefaceTest, read_timeout)
352366
EXPECT_EQ(expected_read_timeout, received_read_timeout);
353367
}
354368

355-
TEST_F(ReverseIntefaceTest, default_read_timeout)
369+
TEST_F(ReverseInterfaceTest, default_read_timeout)
356370
{
357371
// Wait for the client to connect to the server
358372
EXPECT_TRUE(waitForProgramState(1000, true));
@@ -379,7 +393,7 @@ TEST_F(ReverseIntefaceTest, default_read_timeout)
379393
EXPECT_EQ(expected_read_timeout, received_read_timeout);
380394
}
381395

382-
TEST_F(ReverseIntefaceTest, write_control_mode)
396+
TEST_F(ReverseInterfaceTest, write_control_mode)
383397
{
384398
// Wait for the client to connect to the server
385399
EXPECT_TRUE(waitForProgramState(1000, true));
@@ -438,7 +452,7 @@ TEST_F(ReverseIntefaceTest, write_control_mode)
438452
EXPECT_EQ(toUnderlying(expected_control_mode), received_control_mode);
439453
}
440454

441-
TEST_F(ReverseIntefaceTest, write_freedrive_control_message)
455+
TEST_F(ReverseInterfaceTest, write_freedrive_control_message)
442456
{
443457
// Wait for the client to connect to the server
444458
EXPECT_TRUE(waitForProgramState(1000, true));
@@ -462,7 +476,7 @@ TEST_F(ReverseIntefaceTest, write_freedrive_control_message)
462476
EXPECT_EQ(toUnderlying(written_freedrive_message), received_freedrive_message);
463477
}
464478

465-
TEST_F(ReverseIntefaceTest, deprecated_set_keep_alive_count)
479+
TEST_F(ReverseInterfaceTest, deprecated_set_keep_alive_count)
466480
{
467481
// Wait for the client to connect to the server
468482
EXPECT_TRUE(waitForProgramState(1000, true));
@@ -489,7 +503,7 @@ TEST_F(ReverseIntefaceTest, deprecated_set_keep_alive_count)
489503
EXPECT_EQ(expected_read_timeout, received_read_timeout);
490504
}
491505

492-
TEST_F(ReverseIntefaceTest, disconnected_callbacks_are_called)
506+
TEST_F(ReverseInterfaceTest, disconnected_callbacks_are_called)
493507
{
494508
// Wait for the client to connect to the server
495509
EXPECT_TRUE(waitForProgramState(1000, true));
@@ -520,7 +534,7 @@ TEST_F(ReverseIntefaceTest, disconnected_callbacks_are_called)
520534
// Unregister 1. 2 should still be called
521535
disconnect_called_1 = false;
522536
disconnect_called_2 = false;
523-
client_.reset(new Client(50001));
537+
client_.reset(new Client(test_port_));
524538
EXPECT_TRUE(waitForProgramState(1000, true));
525539
reverse_interface_->unregisterDisconnectionCallback(disconnection_callback_id_1);
526540
client_->close();
@@ -532,7 +546,7 @@ TEST_F(ReverseIntefaceTest, disconnected_callbacks_are_called)
532546
// Unregister both. None should be called
533547
disconnect_called_1 = false;
534548
disconnect_called_2 = false;
535-
client_.reset(new Client(50001));
549+
client_.reset(new Client(test_port_));
536550
EXPECT_TRUE(waitForProgramState(1000, true));
537551
reverse_interface_->unregisterDisconnectionCallback(disconnection_callback_id_2);
538552
client_->close();

0 commit comments

Comments
 (0)