Index: webrtc/p2p/base/p2ptransportchannel_unittest.cc |
diff --git a/webrtc/p2p/base/p2ptransportchannel_unittest.cc b/webrtc/p2p/base/p2ptransportchannel_unittest.cc |
index cf54fc4a27fdcf6dbc4c615ae6216097b0f2dd01..6673f8f54d3e9f96c501662664bd2106b9097ab5 100644 |
--- a/webrtc/p2p/base/p2ptransportchannel_unittest.cc |
+++ b/webrtc/p2p/base/p2ptransportchannel_unittest.cc |
@@ -350,6 +350,8 @@ class P2PTransportChannelTestBase : public testing::Test, |
this, &P2PTransportChannelTestBase::OnReadPacket); |
channel->SignalRoleConflict.connect( |
this, &P2PTransportChannelTestBase::OnRoleConflict); |
+ channel->SignalSelectedCandidatePairChanged.connect( |
+ this, &P2PTransportChannelTestBase::OnSelectedCandidatePairChanged); |
channel->SetIceCredentials(local_ice_ufrag, local_ice_pwd); |
if (remote_ice_credential_source_ == FROM_SETICECREDENTIALS) { |
channel->SetRemoteIceCredentials(remote_ice_ufrag, remote_ice_pwd); |
@@ -688,6 +690,19 @@ class P2PTransportChannelTestBase : public testing::Test, |
new CandidatesData(ch, c)); |
} |
} |
+ void OnSelectedCandidatePairChanged( |
+ TransportChannel* transport_channel, |
+ CandidatePairInterface* selected_candidate_pair, |
+ int last_sent_packet_id, |
+ bool ready_to_send) { |
+ ++selected_candidate_pair_switches_; |
+ } |
+ |
+ int reset_selected_candidate_pair_switches() { |
+ int switches = selected_candidate_pair_switches_; |
+ selected_candidate_pair_switches_ = 0; |
+ return switches; |
+ } |
void PauseCandidates(int endpoint) { |
GetEndpoint(endpoint)->save_candidates_ = true; |
@@ -850,6 +865,7 @@ class P2PTransportChannelTestBase : public testing::Test, |
Endpoint ep2_; |
RemoteIceCredentialSource remote_ice_credential_source_ = FROM_CANDIDATE; |
bool force_relay_; |
+ int selected_candidate_pair_switches_ = 0; |
}; |
// The tests have only a few outcomes, which we predefine. |
@@ -1867,6 +1883,7 @@ TEST_F(P2PTransportChannelMultihomedTest, DISABLED_TestBasic) { |
// Test that we can quickly switch links if an interface goes down. |
// The controlled side has two interfaces and one will die. |
TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControlledSide) { |
+ rtc::ScopedFakeClock clock; |
AddAddress(0, kPublicAddrs[0]); |
// Adding alternate address will make sure |kPublicAddrs| has the higher |
// priority than others. This is due to FakeNetwork::AddInterface method. |
@@ -1880,9 +1897,10 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControlledSide) { |
// Create channels and let them go writable, as usual. |
CreateChannels(1); |
- EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
- ep2_ch1()->receiving() && ep2_ch1()->writable(), |
- 1000, 1000); |
+ EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
+ ep2_ch1()->receiving() && |
+ ep2_ch1()->writable(), |
+ 3000, clock); |
EXPECT_TRUE(ep1_ch1()->selected_connection() && |
ep2_ch1()->selected_connection() && |
LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) && |
@@ -1896,19 +1914,19 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControlledSide) { |
// Blackhole any traffic to or from the public addrs. |
LOG(LS_INFO) << "Failing over..."; |
fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, kPublicAddrs[1]); |
- // The selected connections will switch, so keep references to them. |
+ // The selected connections may switch, so keep references to them. |
const Connection* selected_connection1 = ep1_ch1()->selected_connection(); |
const Connection* selected_connection2 = ep2_ch1()->selected_connection(); |
// We should detect loss of receiving within 1 second or so. |
- EXPECT_TRUE_WAIT( |
+ EXPECT_TRUE_SIMULATED_WAIT( |
!selected_connection1->receiving() && !selected_connection2->receiving(), |
- 3000); |
+ 3000, clock); |
- // We should switch over to use the alternate addr immediately on both sides |
+ // We should switch over to use the alternate addr on both sides |
// when we are not receiving. |
- EXPECT_TRUE_WAIT(ep1_ch1()->selected_connection()->receiving() && |
- ep2_ch1()->selected_connection()->receiving(), |
- 1000); |
+ EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->selected_connection()->receiving() && |
+ ep2_ch1()->selected_connection()->receiving(), |
+ 3000, clock); |
EXPECT_TRUE(LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0])); |
EXPECT_TRUE( |
RemoteCandidate(ep1_ch1())->address().EqualIPs(kAlternateAddrs[1])); |
@@ -1921,6 +1939,7 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControlledSide) { |
// Test that we can quickly switch links if an interface goes down. |
// The controlling side has two interfaces and one will die. |
TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControllingSide) { |
+ rtc::ScopedFakeClock clock; |
// Adding alternate address will make sure |kPublicAddrs| has the higher |
// priority than others. This is due to FakeNetwork::AddInterface method. |
AddAddress(0, kAlternateAddrs[0]); |
@@ -1933,9 +1952,10 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControllingSide) { |
// Create channels and let them go writable, as usual. |
CreateChannels(1); |
- EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
- ep2_ch1()->receiving() && ep2_ch1()->writable(), |
- 1000, 1000); |
+ EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
+ ep2_ch1()->receiving() && |
+ ep2_ch1()->writable(), |
+ 3000, clock); |
EXPECT_TRUE(ep1_ch1()->selected_connection() && |
ep2_ch1()->selected_connection() && |
LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) && |
@@ -1953,15 +1973,15 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControllingSide) { |
const Connection* selected_connection1 = ep1_ch1()->selected_connection(); |
const Connection* selected_connection2 = ep2_ch1()->selected_connection(); |
// We should detect loss of receiving within 1 second or so. |
- EXPECT_TRUE_WAIT( |
+ EXPECT_TRUE_SIMULATED_WAIT( |
!selected_connection1->receiving() && !selected_connection2->receiving(), |
- 3000); |
+ 3000, clock); |
- // We should switch over to use the alternate addr immediately on both sides |
+ // We should switch over to use the alternate addr on both sides |
// when we are not receiving. |
- EXPECT_TRUE_WAIT(ep1_ch1()->selected_connection()->receiving() && |
- ep2_ch1()->selected_connection()->receiving(), |
- 1000); |
+ EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->selected_connection()->receiving() && |
+ ep2_ch1()->selected_connection()->receiving(), |
+ 3000, clock); |
EXPECT_TRUE( |
LocalCandidate(ep1_ch1())->address().EqualIPs(kAlternateAddrs[0])); |
EXPECT_TRUE(RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1])); |
@@ -1971,6 +1991,126 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControllingSide) { |
DestroyChannels(); |
} |
+// Test that if an interface fails temporarily and then recovers quickly, |
+// the selected connection will not switch. |
+// The case that it will switch over to the backup connection if the selected |
+// connection does not recover after enough time is covered in |
+// TestFailoverControlledSide and TestFailoverControllingSide. |
+TEST_F(P2PTransportChannelMultihomedTest, |
+ TestConnectionSwitchDampeningControlledSide) { |
+ rtc::ScopedFakeClock clock; |
+ AddAddress(0, kPublicAddrs[0]); |
+ // Adding alternate address will make sure |kPublicAddrs| has the higher |
+ // priority than others. This is due to FakeNetwork::AddInterface method. |
+ AddAddress(1, kAlternateAddrs[1]); |
+ AddAddress(1, kPublicAddrs[1]); |
+ |
+ // Use only local ports for simplicity. |
+ SetAllocatorFlags(0, kOnlyLocalPorts); |
+ SetAllocatorFlags(1, kOnlyLocalPorts); |
+ |
+ // Create channels and let them go writable, as usual. |
+ CreateChannels(1); |
+ |
+ EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
+ ep2_ch1()->receiving() && |
+ ep2_ch1()->writable(), |
+ 3000, clock); |
+ EXPECT_TRUE(ep1_ch1()->selected_connection() && |
+ ep2_ch1()->selected_connection() && |
+ LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) && |
+ RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1])); |
+ |
+ // Make the receiving timeout shorter for testing. |
+ IceConfig config = CreateIceConfig(1000, GATHER_ONCE); |
+ ep1_ch1()->SetIceConfig(config); |
+ ep2_ch1()->SetIceConfig(config); |
+ reset_selected_candidate_pair_switches(); |
+ |
+ // Blackhole any traffic to or from the public addrs. |
+ LOG(LS_INFO) << "Failing over..."; |
+ fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, kPublicAddrs[1]); |
+ |
+ // The selected connections may switch, so keep references to them. |
+ const Connection* selected_connection1 = ep1_ch1()->selected_connection(); |
+ const Connection* selected_connection2 = ep2_ch1()->selected_connection(); |
+ // We should detect loss of receiving within 1 second or so. |
+ EXPECT_TRUE_SIMULATED_WAIT( |
+ !selected_connection1->receiving() && !selected_connection2->receiving(), |
+ 3000, clock); |
+ // After a short while, the link recovers itself. |
+ SIMULATED_WAIT(false, 10, clock); |
+ fw()->ClearRules(); |
+ |
+ // We should remain on the public address on both sides and no connection |
+ // switches should have happened. |
+ EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->selected_connection()->receiving() && |
+ ep2_ch1()->selected_connection()->receiving(), |
+ 3000, clock); |
+ EXPECT_TRUE(RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1])); |
+ EXPECT_TRUE(LocalCandidate(ep2_ch1())->address().EqualIPs(kPublicAddrs[1])); |
+ EXPECT_EQ(0, reset_selected_candidate_pair_switches()); |
+ |
+ DestroyChannels(); |
+} |
+ |
+// Test that if an interface fails temporarily and then recovers quickly, |
+// the selected connection will not switch. |
+TEST_F(P2PTransportChannelMultihomedTest, |
+ TestConnectionSwitchDampeningControllingSide) { |
+ rtc::ScopedFakeClock clock; |
+ // Adding alternate address will make sure |kPublicAddrs| has the higher |
+ // priority than others. This is due to FakeNetwork::AddInterface method. |
+ AddAddress(0, kAlternateAddrs[0]); |
+ AddAddress(0, kPublicAddrs[0]); |
+ AddAddress(1, kPublicAddrs[1]); |
+ |
+ // Use only local ports for simplicity. |
+ SetAllocatorFlags(0, kOnlyLocalPorts); |
+ SetAllocatorFlags(1, kOnlyLocalPorts); |
+ |
+ // Create channels and let them go writable, as usual. |
+ CreateChannels(1); |
+ EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
+ ep2_ch1()->receiving() && |
+ ep2_ch1()->writable(), |
+ 3000, clock); |
+ EXPECT_TRUE(ep1_ch1()->selected_connection() && |
+ ep2_ch1()->selected_connection() && |
+ LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) && |
+ RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1])); |
+ |
+ // Make the receiving timeout shorter for testing. |
+ IceConfig config = CreateIceConfig(1000, GATHER_ONCE); |
+ ep1_ch1()->SetIceConfig(config); |
+ ep2_ch1()->SetIceConfig(config); |
+ reset_selected_candidate_pair_switches(); |
+ |
+ // Blackhole any traffic to or from the public addrs. |
+ LOG(LS_INFO) << "Failing over..."; |
+ fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, kPublicAddrs[0]); |
+ // The selected connections may switch, so keep references to them. |
+ const Connection* selected_connection1 = ep1_ch1()->selected_connection(); |
+ const Connection* selected_connection2 = ep2_ch1()->selected_connection(); |
+ // We should detect loss of receiving within 1 second or so. |
+ EXPECT_TRUE_SIMULATED_WAIT( |
+ !selected_connection1->receiving() && !selected_connection2->receiving(), |
+ 3000, clock); |
+ // The link recovers after a short while. |
+ SIMULATED_WAIT(false, 10, clock); |
+ fw()->ClearRules(); |
+ |
+ // We should not switch to the alternate addr on both sides because of the |
+ // dampening. |
+ EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->selected_connection()->receiving() && |
+ ep2_ch1()->selected_connection()->receiving(), |
+ 3000, clock); |
+ EXPECT_TRUE(LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0])); |
+ EXPECT_TRUE(RemoteCandidate(ep2_ch1())->address().EqualIPs(kPublicAddrs[0])); |
+ EXPECT_EQ(0, reset_selected_candidate_pair_switches()); |
+ DestroyChannels(); |
+} |
+ |
// Tests that a Wifi-Wifi connection has the highest precedence. |
TEST_F(P2PTransportChannelMultihomedTest, TestPreferWifiToWifiConnection) { |
// The interface names are chosen so that |cellular| would have higher |
@@ -2448,7 +2588,7 @@ class P2PTransportChannelPingTest : public testing::Test, |
bool channel_ready_to_send() { return channel_ready_to_send_; } |
void reset_channel_ready_to_send() { channel_ready_to_send_ = false; } |
TransportChannelState channel_state() { return channel_state_; } |
- int get_and_reset_selected_candidate_pair_switches() { |
+ int reset_selected_candidate_pair_switches() { |
int switches = selected_candidate_pair_switches_; |
selected_candidate_pair_switches_ = 0; |
return switches; |
@@ -3055,7 +3195,7 @@ TEST_F(P2PTransportChannelPingTest, |
ASSERT_TRUE(conn2 != nullptr); |
// Initially, connections are selected based on priority. |
- EXPECT_EQ(1, get_and_reset_selected_candidate_pair_switches()); |
+ EXPECT_EQ(1, reset_selected_candidate_pair_switches()); |
EXPECT_EQ(conn1, last_selected_candidate_pair()); |
// conn2 receives data; it becomes selected. |
@@ -3063,17 +3203,17 @@ TEST_F(P2PTransportChannelPingTest, |
// conn2 is larger. |
SIMULATED_WAIT(false, 1, clock); |
conn2->OnReadPacket("XYZ", 3, rtc::CreatePacketTime(0)); |
- EXPECT_EQ(1, get_and_reset_selected_candidate_pair_switches()); |
+ EXPECT_EQ(1, reset_selected_candidate_pair_switches()); |
EXPECT_EQ(conn2, last_selected_candidate_pair()); |
// conn1 also receives data; it becomes selected due to priority again. |
conn1->OnReadPacket("XYZ", 3, rtc::CreatePacketTime(0)); |
- EXPECT_EQ(1, get_and_reset_selected_candidate_pair_switches()); |
+ EXPECT_EQ(1, reset_selected_candidate_pair_switches()); |
EXPECT_EQ(conn1, last_selected_candidate_pair()); |
// Make sure sorting won't reselect candidate pair. |
SIMULATED_WAIT(false, 10, clock); |
- EXPECT_EQ(0, get_and_reset_selected_candidate_pair_switches()); |
+ EXPECT_EQ(0, reset_selected_candidate_pair_switches()); |
} |
TEST_F(P2PTransportChannelPingTest, |
@@ -3097,28 +3237,28 @@ TEST_F(P2PTransportChannelPingTest, |
// Advance the clock to have a non-zero last-data-receiving time. |
SIMULATED_WAIT(false, 1, clock); |
conn1->OnReadPacket("XYZ", 3, rtc::CreatePacketTime(0)); |
- EXPECT_EQ(1, get_and_reset_selected_candidate_pair_switches()); |
+ EXPECT_EQ(1, reset_selected_candidate_pair_switches()); |
EXPECT_EQ(conn1, last_selected_candidate_pair()); |
// conn2 is nominated; it becomes the selected connection. |
NominateConnection(conn2); |
- EXPECT_EQ(1, get_and_reset_selected_candidate_pair_switches()); |
+ EXPECT_EQ(1, reset_selected_candidate_pair_switches()); |
EXPECT_EQ(conn2, last_selected_candidate_pair()); |
NominateConnection(conn1); |
- EXPECT_EQ(1, get_and_reset_selected_candidate_pair_switches()); |
+ EXPECT_EQ(1, reset_selected_candidate_pair_switches()); |
EXPECT_EQ(conn1, last_selected_candidate_pair()); |
// conn2 received data more recently; it is selected now because it |
// received data more recently. |
SIMULATED_WAIT(false, 1, clock); |
conn2->OnReadPacket("XYZ", 3, rtc::CreatePacketTime(0)); |
- EXPECT_EQ(1, get_and_reset_selected_candidate_pair_switches()); |
+ EXPECT_EQ(1, reset_selected_candidate_pair_switches()); |
EXPECT_EQ(conn2, last_selected_candidate_pair()); |
// Make sure sorting won't reselect candidate pair. |
SIMULATED_WAIT(false, 10, clock); |
- EXPECT_EQ(0, get_and_reset_selected_candidate_pair_switches()); |
+ EXPECT_EQ(0, reset_selected_candidate_pair_switches()); |
} |
TEST_F(P2PTransportChannelPingTest, |
@@ -3139,26 +3279,26 @@ TEST_F(P2PTransportChannelPingTest, |
ASSERT_TRUE(conn2 != nullptr); |
NominateConnection(conn1); |
- EXPECT_EQ(1, get_and_reset_selected_candidate_pair_switches()); |
+ EXPECT_EQ(1, reset_selected_candidate_pair_switches()); |
// conn2 becomes writable; it is selected even though it is not nominated. |
conn2->ReceivedPingResponse(LOW_RTT); |
- EXPECT_EQ_SIMULATED_WAIT(1, get_and_reset_selected_candidate_pair_switches(), |
+ EXPECT_EQ_SIMULATED_WAIT(1, reset_selected_candidate_pair_switches(), |
kDefaultTimeout, clock); |
EXPECT_EQ_SIMULATED_WAIT(conn2, last_selected_candidate_pair(), |
kDefaultTimeout, clock); |
// If conn1 is also writable, it will become selected. |
conn1->ReceivedPingResponse(LOW_RTT); |
- EXPECT_EQ_SIMULATED_WAIT(1, get_and_reset_selected_candidate_pair_switches(), |
+ EXPECT_EQ_SIMULATED_WAIT(1, reset_selected_candidate_pair_switches(), |
kDefaultTimeout, clock); |
EXPECT_EQ_SIMULATED_WAIT(conn1, last_selected_candidate_pair(), |
kDefaultTimeout, clock); |
// Make sure sorting won't reselect candidate pair. |
SIMULATED_WAIT(false, 10, clock); |
- EXPECT_EQ(0, get_and_reset_selected_candidate_pair_switches()); |
+ EXPECT_EQ(0, reset_selected_candidate_pair_switches()); |
} |
// Test that if a new remote candidate has the same address and port with |
@@ -3289,7 +3429,9 @@ TEST_F(P2PTransportChannelPingTest, TestConnectionPrunedAgain) { |
FakePortAllocator pa(rtc::Thread::Current(), nullptr); |
P2PTransportChannel ch("test channel", 1, &pa); |
PrepareChannel(&ch); |
- ch.SetIceConfig(CreateIceConfig(1000, GATHER_ONCE)); |
+ IceConfig config = CreateIceConfig(1000, GATHER_ONCE); |
+ config.receiving_switching_delay = rtc::Optional<int>(800); |
+ ch.SetIceConfig(config); |
ch.MaybeStartGathering(); |
ch.AddRemoteCandidate(CreateUdpCandidate(LOCAL_PORT_TYPE, "1.1.1.1", 1, 100)); |
Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1); |