Skip to content

Commit e1efb04

Browse files
committed
[#3366] Fixed failures in CfgIface UTs
Tests were subject to wide variations in retry intervals on certain VMs. Reworked stop when done but allow for longer overal timeout src/lib/dhcp/testutils/pkt_filter_test_stub.cc PktFilterTestStub::openSocket() - close fd on simulated open error src/lib/dhcpsrv/tests/cfg_iface_unittest.cc CfgIfaceTest::stopWait() - added func to stop io_service_ CfgIfaceTest::TearDown() - close sockets before clearing ifaces TEST_F(CfgIfaceTest, retryOpenServiceSockets4) TEST_F(CfgIfaceTest, retryOpenServiceSockets4OmitBound) TEST_F(CfgIfaceTest, retryOpenServiceSockets6) TEST_F(CfgIfaceTest, retryOpenServiceSockets6OmitBound) - modified to use stopWait(), longer timeout, shorter wait
1 parent a78fa73 commit e1efb04

File tree

2 files changed

+70
-24
lines changed

2 files changed

+70
-24
lines changed

src/lib/dhcp/testutils/pkt_filter_test_stub.cc

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,14 @@ PktFilterTestStub::openSocket(Iface&,
4444
"PktFilterTestStub: cannot open /dev/null:" << errmsg);
4545
}
4646

47-
if (open_socket_callback_) {
48-
open_socket_callback_(port);
47+
try {
48+
if (open_socket_callback_) {
49+
open_socket_callback_(port);
50+
}
51+
} catch(...) {
52+
// Don't leak fd on simulated errors.
53+
close(fd);
54+
throw;
4955
}
5056

5157
return (SocketInfo(addr, port, fd));

src/lib/dhcpsrv/tests/cfg_iface_unittest.cc

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ class CfgIfaceTest : public ::testing::Test {
6262
/// @param timeout Wait timeout in milliseconds.
6363
void doWait(const long timeout);
6464

65+
/// @brief Interrupt the current wait (if one).
66+
void stopWait();
67+
6568
/// @brief Holds a fake configuration of the interfaces.
6669
IfaceMgrTestConfig iface_mgr_test_config_;
6770

@@ -90,8 +93,8 @@ CfgIfaceTest::TearDown() {
9093
TimerMgr::instance()->unregisterTimers();
9194

9295
IfaceMgr::instance().setTestMode(false);
93-
IfaceMgr::instance().clearIfaces();
9496
IfaceMgr::instance().closeSockets();
97+
IfaceMgr::instance().clearIfaces();
9598
IfaceMgr::instance().detectIfaces();
9699

97100
// Reset global handlers
@@ -121,11 +124,20 @@ CfgIfaceTest::doWait(const long timeout) {
121124
timer.setup([this]() {
122125
io_service_->stop();
123126
}, timeout, asiolink::IntervalTimer::ONE_SHOT);
127+
124128
io_service_->run();
125129
io_service_->stop();
126130
io_service_->restart();
127131
}
128132

133+
void
134+
CfgIfaceTest::stopWait() {
135+
// Post it so we don't stop in the middle of a callback
136+
io_service_->post([this]() {
137+
io_service_->stop();
138+
});
139+
}
140+
129141
// This test checks that the interface names can be explicitly selected
130142
// by their names and IPv4 sockets are opened on these interfaces.
131143
TEST_F(CfgIfaceTest, explicitNamesV4) {
@@ -626,7 +638,7 @@ TEST_F(CfgIfaceTest, retryOpenServiceSockets4) {
626638

627639
// Parameters
628640
const uint16_t RETRIES = 5;
629-
const uint16_t WAIT_TIME = 10; // miliseconds
641+
const uint16_t WAIT_TIME = 5; // miliseconds
630642
// The number of sockets opened in a single retry attempt.
631643
// iface: eth0 addr: 10.0.0.1 port: 67 rbcast: 0 sbcast: 0
632644
// iface: eth1 addr: 192.0.2.3 port: 67 rbcast: 0 sbcast: 0
@@ -636,10 +648,13 @@ TEST_F(CfgIfaceTest, retryOpenServiceSockets4) {
636648
cfg4.setServiceSocketsMaxRetries(RETRIES);
637649
cfg4.setServiceSocketsRetryWaitTime(WAIT_TIME);
638650

651+
// For each interface perform 1 init open and a few retries.
652+
size_t exp_calls = CALLS_PER_RETRY * (RETRIES + 1);
653+
639654
// Set the callback to count calls and check wait time
640655
size_t total_calls = 0;
641656
auto last_call_time = std::chrono::system_clock::time_point::min();
642-
auto open_callback = [&total_calls, &last_call_time, WAIT_TIME](uint16_t) {
657+
auto open_callback = [this, &total_calls, &last_call_time, WAIT_TIME, exp_calls](uint16_t) {
643658
auto now = std::chrono::system_clock::now();
644659

645660
// Check waiting time only for the first call in a retry attempt.
@@ -660,6 +675,10 @@ TEST_F(CfgIfaceTest, retryOpenServiceSockets4) {
660675

661676
total_calls++;
662677

678+
if (total_calls == exp_calls) {
679+
stopWait();
680+
}
681+
663682
// Fail to open a socket
664683
isc_throw(Unexpected, "CfgIfaceTest: cannot open a port");
665684
};
@@ -677,10 +696,9 @@ TEST_F(CfgIfaceTest, retryOpenServiceSockets4) {
677696
ASSERT_NO_THROW(cfg4.openSockets(AF_INET, DHCP4_SERVER_PORT));
678697

679698
// Wait for a finish sockets binding (with a safe margin).
680-
doWait(RETRIES * WAIT_TIME * 2);
699+
doWait(RETRIES * WAIT_TIME * 10);
681700

682-
// For each interface perform 1 init open and a few retries.
683-
EXPECT_EQ(CALLS_PER_RETRY * (RETRIES + 1), total_calls);
701+
EXPECT_EQ(exp_calls, total_calls);
684702
}
685703

686704
// This test verifies that if any IPv4 socket fails to bind, the opening will
@@ -693,7 +711,7 @@ TEST_F(CfgIfaceTest, retryOpenServiceSockets4OmitBound) {
693711

694712
// Parameters
695713
const uint16_t RETRIES = 5;
696-
const uint16_t WAIT_TIME = 10; // miliseconds
714+
const uint16_t WAIT_TIME = 5; // miliseconds
697715

698716
// Require retry socket binding
699717
cfg4.setServiceSocketsMaxRetries(RETRIES);
@@ -702,7 +720,12 @@ TEST_F(CfgIfaceTest, retryOpenServiceSockets4OmitBound) {
702720
// Set the callback to count calls and check wait time
703721
size_t total_calls = 0;
704722
auto last_call_time = std::chrono::system_clock::time_point::min();
705-
auto open_callback = [&total_calls, &last_call_time, WAIT_TIME](uint16_t) {
723+
724+
// For eth0 interface perform 1 init open and a few retries,
725+
// for eth1 interface perform only init open.
726+
size_t exp_calls = (RETRIES + 1) + 1;
727+
728+
auto open_callback = [this, &total_calls, &last_call_time, WAIT_TIME, exp_calls](uint16_t) {
706729
auto now = std::chrono::system_clock::now();
707730
bool is_eth1 = total_calls == 1;
708731

@@ -726,10 +749,15 @@ TEST_F(CfgIfaceTest, retryOpenServiceSockets4OmitBound) {
726749

727750
total_calls++;
728751

752+
if (total_calls == exp_calls) {
753+
stopWait();
754+
}
755+
729756
// Fail to open a socket on eth0, success for eth1
730757
if (!is_eth1) {
731758
isc_throw(Unexpected, "CfgIfaceTest: cannot open a port");
732759
}
760+
733761
};
734762

735763
boost::shared_ptr<isc::dhcp::test::PktFilterTestStub> filter(
@@ -745,11 +773,9 @@ TEST_F(CfgIfaceTest, retryOpenServiceSockets4OmitBound) {
745773
ASSERT_NO_THROW(cfg4.openSockets(AF_INET, DHCP4_SERVER_PORT));
746774

747775
// Wait for a finish sockets binding (with a safe margin).
748-
doWait(RETRIES * WAIT_TIME * 2);
776+
doWait(RETRIES * WAIT_TIME * 10);
749777

750-
// For eth0 interface perform 1 init open and a few retries,
751-
// for eth1 interface perform only init open.
752-
EXPECT_EQ((RETRIES + 1) + 1, total_calls);
778+
EXPECT_EQ(exp_calls, total_calls);
753779
}
754780

755781
// Test that only one reopen timer is active simultaneously. If a new opening
@@ -822,7 +848,7 @@ TEST_F(CfgIfaceTest, retryOpenServiceSockets6) {
822848

823849
// Parameters
824850
const uint16_t RETRIES = 5;
825-
const uint16_t WAIT_TIME = 10; // miliseconds
851+
const uint16_t WAIT_TIME = 5; // miliseconds
826852
// The number of sockets opened in a single retry attempt.
827853
// 1 unicast and 2 multicast sockets.
828854
// iface: eth0 addr: 2001:db8:1::1 port: 547 multicast: 0
@@ -834,10 +860,13 @@ TEST_F(CfgIfaceTest, retryOpenServiceSockets6) {
834860
cfg6.setServiceSocketsMaxRetries(RETRIES);
835861
cfg6.setServiceSocketsRetryWaitTime(WAIT_TIME);
836862

863+
// For each interface perform 1 init open and a few retries.
864+
size_t exp_calls = CALLS_PER_RETRY * (RETRIES + 1);
865+
837866
// Set the callback to count calls and check wait time
838867
size_t total_calls = 0;
839868
auto last_call_time = std::chrono::system_clock::time_point::min();
840-
auto open_callback = [&total_calls, &last_call_time, WAIT_TIME](uint16_t) {
869+
auto open_callback = [this, &total_calls, &last_call_time, WAIT_TIME, exp_calls](uint16_t) {
841870
auto now = std::chrono::system_clock::now();
842871

843872
// Check waiting time only for the first call in a retry attempt.
@@ -858,6 +887,10 @@ TEST_F(CfgIfaceTest, retryOpenServiceSockets6) {
858887

859888
total_calls++;
860889

890+
if (total_calls == exp_calls) {
891+
stopWait();
892+
}
893+
861894
// Fail to open a socket
862895
isc_throw(Unexpected, "CfgIfaceTest: cannot open a port");
863896
};
@@ -873,10 +906,10 @@ TEST_F(CfgIfaceTest, retryOpenServiceSockets6) {
873906
ASSERT_NO_THROW(cfg6.openSockets(AF_INET6, DHCP6_SERVER_PORT));
874907

875908
// Wait for a finish sockets binding (with a safe margin).
876-
doWait(RETRIES * WAIT_TIME * 2);
909+
doWait(RETRIES * WAIT_TIME * 10);
877910

878911
// For each interface perform 1 init open and a few retries.
879-
EXPECT_EQ(CALLS_PER_RETRY * (RETRIES + 1), total_calls);
912+
EXPECT_EQ(exp_calls, total_calls);
880913
}
881914

882915
// This test verifies that if any IPv6 socket fails to bind, the opening will
@@ -889,7 +922,7 @@ TEST_F(CfgIfaceTest, retryOpenServiceSockets6OmitBound) {
889922

890923
// Parameters
891924
const uint16_t RETRIES = 5;
892-
const uint16_t WAIT_TIME = 10; // miliseconds
925+
const uint16_t WAIT_TIME = 5; // miliseconds
893926

894927
// Require retry socket binding
895928
cfg6.setServiceSocketsMaxRetries(RETRIES);
@@ -901,10 +934,15 @@ TEST_F(CfgIfaceTest, retryOpenServiceSockets6OmitBound) {
901934
const uint32_t opened_by_eth0 = 2;
902935
#endif
903936

937+
938+
// For eth0 interface perform only 3 (on Linux Systems or 2 otherwise) init open,
939+
// for eth1 interface perform 1 init open and a few retries.
940+
size_t exp_calls = RETRIES + 1 + opened_by_eth0;
941+
904942
// Set the callback to count calls and check wait time
905943
size_t total_calls = 0;
906944
auto last_call_time = std::chrono::system_clock::time_point::min();
907-
auto open_callback = [&total_calls, &last_call_time, WAIT_TIME](uint16_t) {
945+
auto open_callback = [this, &total_calls, &last_call_time, WAIT_TIME, exp_calls](uint16_t) {
908946
auto now = std::chrono::system_clock::now();
909947
bool is_eth0 = total_calls < opened_by_eth0;
910948

@@ -931,6 +969,10 @@ TEST_F(CfgIfaceTest, retryOpenServiceSockets6OmitBound) {
931969

932970
total_calls++;
933971

972+
if (total_calls == exp_calls) {
973+
stopWait();
974+
}
975+
934976
// Fail to open a socket on eth1, success for eth0
935977
if (!is_eth0) {
936978
isc_throw(Unexpected, "CfgIfaceTest: cannot open a port");
@@ -950,11 +992,9 @@ TEST_F(CfgIfaceTest, retryOpenServiceSockets6OmitBound) {
950992
ASSERT_NO_THROW(cfg6.openSockets(AF_INET6, DHCP6_SERVER_PORT));
951993

952994
// Wait for a finish sockets binding (with a safe margin).
953-
doWait(RETRIES * WAIT_TIME * 2);
995+
doWait(RETRIES * WAIT_TIME * 10);
954996

955-
// For eth0 interface perform only 3 (on Linux Systems or 2 otherwise) init open,
956-
// for eth1 interface perform 1 init open and a few retries.
957-
EXPECT_EQ((RETRIES + 1) + opened_by_eth0, total_calls);
997+
EXPECT_EQ(exp_calls, total_calls);
958998
}
959999

9601000
// Test that only one reopen timer is active simultaneously. If a new opening

0 commit comments

Comments
 (0)