Commit a9d61b8e authored by Steve Anton's avatar Steve Anton Committed by Commit Bot

Implement RTCIceTransport.onselectedcandidatepairchange

Bug: 864871
Change-Id: I710b18f8644be51d08b33b9852c00f4fbacec286
Reviewed-on: https://chromium-review.googlesource.com/1208961Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarHenrik Boström <hbos@chromium.org>
Reviewed-by: default avatarHarald Alvestrand <hta@chromium.org>
Commit-Queue: Steve Anton <steveanton@chromium.org>
Cr-Commit-Position: refs/heads/master@{#595700}
parent f41f01a8
......@@ -15,12 +15,6 @@
// makeIceTransport
// makeGatherAndStartTwoIceTransports
function makeIceTransport(t) {
const iceTransport = new RTCIceTransport();
t.add_cleanup(() => iceTransport.stop());
return iceTransport;
}
test(() => {
const iceTransport = new RTCIceTransport();
}, 'RTCIceTransport constructor does not throw');
......@@ -260,4 +254,49 @@ promise_test(async t => {
]);
}, 'Two RTCIceTransports connect to each other');
promise_test(async t => {
async function waitForConnectedThenSelectedCandidatePairChange(t, transport,
transportName) {
const watcher = new EventWatcher(t, localTransport,
[ 'statechange', 'selectedcandidatepairchange' ]);
await watcher.wait_for('statechange');
assert_equals(transport.state, 'connected',
`${transportName} state should be 'connected'`);
await watcher.wait_for('selectedcandidatepairchange');
const selectedCandidatePair = transport.getSelectedCandidatePair();
assert_not_equals(selectedCandidatePair, null,
`${transportName} selected candidate pair should not be null once ` +
'the selectedcandidatepairchange event fires');
assert_true(
transport.getLocalCandidates().some(
({ candidate }) =>
candidate === selectedCandidatePair.local.candidate),
`${transportName} selected candidate pair local should be in the ` +
'list of local candidates');
assert_true(
transport.getRemoteCandidates().some(
({ candidate }) =>
candidate === selectedCandidatePair.remote.candidate),
`${transportName} selected candidate pair local should be in the ` +
'list of remote candidates');
}
const [ localTransport, remoteTransport ] =
makeGatherAndStartTwoIceTransports(t);
await Promise.all([
waitForConnectedThenSelectedCandidatePairChange(t, localTransport,
'local transport'),
waitForConnectedThenSelectedCandidatePairChange(t, remoteTransport,
'remote transport'),
]);
}, 'Selected candidate pair changes once the RTCIceTransports connect.');
promise_test(async t => {
const [ transport, ] = makeGatherAndStartTwoIceTransports(t);
const watcher = new EventWatcher(t, transport, 'selectedcandidatepairchange');
await watcher.wait_for('selectedcandidatepairchange');
transport.stop();
assert_equals(transport.getSelectedCandidatePair(), null);
}, 'getSelectedCandidatePair() returns null once the RTCIceTransport is ' +
'stopped.');
</script>
......@@ -338,7 +338,7 @@ PASS RTCIceTransport interface: operation getLocalParameters()
PASS RTCIceTransport interface: operation getRemoteParameters()
PASS RTCIceTransport interface: attribute onstatechange
PASS RTCIceTransport interface: attribute ongatheringstatechange
FAIL RTCIceTransport interface: attribute onselectedcandidatepairchange assert_true: The prototype object must have a property "onselectedcandidatepairchange" expected true got false
PASS RTCIceTransport interface: attribute onselectedcandidatepairchange
FAIL RTCIceTransport must be primary interface of idlTestObjects.iceTransport assert_equals: wrong typeof object expected "object" but got "undefined"
FAIL Stringification of idlTestObjects.iceTransport assert_equals: wrong typeof object expected "object" but got "undefined"
FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "role" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
......
......@@ -338,7 +338,7 @@ PASS RTCIceTransport interface: operation getLocalParameters()
PASS RTCIceTransport interface: operation getRemoteParameters()
PASS RTCIceTransport interface: attribute onstatechange
PASS RTCIceTransport interface: attribute ongatheringstatechange
FAIL RTCIceTransport interface: attribute onselectedcandidatepairchange assert_true: The prototype object must have a property "onselectedcandidatepairchange" expected true got false
PASS RTCIceTransport interface: attribute onselectedcandidatepairchange
FAIL RTCIceTransport must be primary interface of idlTestObjects.iceTransport assert_equals: wrong typeof object expected "object" but got "undefined"
FAIL Stringification of idlTestObjects.iceTransport assert_equals: wrong typeof object expected "object" but got "undefined"
FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "role" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
......
......@@ -5496,6 +5496,7 @@ interface RTCIceTransport : EventTarget
getter gatheringState
getter ongatheringstatechange
getter onicecandidate
getter onselectedcandidatepairchange
getter onstatechange
getter role
getter state
......@@ -5511,6 +5512,7 @@ interface RTCIceTransport : EventTarget
method stop
setter ongatheringstatechange
setter onicecandidate
setter onselectedcandidatepairchange
setter onstatechange
interface RTCPeerConnection : EventTarget
static method generateCertificate
......
......@@ -236,6 +236,7 @@
"seeked",
"seeking",
"select",
"selectedcandidatepairchange",
"selectionchange",
"selectstart",
"selectend",
......
......@@ -47,6 +47,12 @@ class IceTransportAdapter {
// Called asynchronously when the ICE connection state has changed.
virtual void OnStateChanged(cricket::IceTransportState new_state) {}
// Called asynchronously when the ICE agent selects a different candidate
// pair for the active connection.
virtual void OnSelectedCandidatePairChanged(
const std::pair<cricket::Candidate, cricket::Candidate>&
selected_candidate_pair) {}
};
virtual ~IceTransportAdapter() = default;
......
......@@ -106,6 +106,8 @@ IceTransportAdapterImpl::IceTransportAdapterImpl(
this, &IceTransportAdapterImpl::OnCandidateGathered);
p2p_transport_channel_->SignalStateChanged.connect(
this, &IceTransportAdapterImpl::OnStateChanged);
p2p_transport_channel_->SignalNetworkRouteChanged.connect(
this, &IceTransportAdapterImpl::OnNetworkRouteChanged);
// The ICE tiebreaker is used to determine which side is controlling/
// controlled when both sides start in the same role. The number is randomly
// generated so that each peer can calculate a.tiebreaker <= b.tiebreaker
......@@ -192,4 +194,21 @@ void IceTransportAdapterImpl::OnStateChanged(
delegate_->OnStateChanged(p2p_transport_channel_->GetState());
}
void IceTransportAdapterImpl::OnNetworkRouteChanged(
absl::optional<rtc::NetworkRoute> new_network_route) {
const cricket::CandidatePairInterface* selected_connection =
p2p_transport_channel_->selected_connection();
if (!selected_connection) {
// The selected connection will only be null if the ICE connection has
// totally failed, at which point we'll get a StateChanged signal. The
// client will implicitly clear the selected candidate pair when it receives
// the failed state change, so we don't need to give an explicit callback
// here.
return;
}
delegate_->OnSelectedCandidatePairChanged(
std::make_pair(selected_connection->local_candidate(),
selected_connection->remote_candidate()));
}
} // namespace blink
......@@ -45,6 +45,8 @@ class IceTransportAdapterImpl final : public IceTransportAdapter,
void OnCandidateGathered(cricket::IceTransportInternal* transport,
const cricket::Candidate& candidate);
void OnStateChanged(cricket::IceTransportInternal* transport);
void OnNetworkRouteChanged(
absl::optional<rtc::NetworkRoute> new_network_route);
Delegate* const delegate_;
std::unique_ptr<cricket::PortAllocator> port_allocator_;
......
......@@ -108,4 +108,14 @@ void IceTransportHost::OnStateChanged(cricket::IceTransportState new_state) {
CrossThreadBind(&IceTransportProxy::OnStateChanged, proxy_, new_state));
}
void IceTransportHost::OnSelectedCandidatePairChanged(
const std::pair<cricket::Candidate, cricket::Candidate>&
selected_candidate_pair) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
PostCrossThreadTask(
*proxy_thread_, FROM_HERE,
CrossThreadBind(&IceTransportProxy::OnSelectedCandidatePairChanged,
proxy_, selected_candidate_pair));
}
} // namespace blink
......@@ -73,6 +73,9 @@ class IceTransportHost final : public IceTransportAdapter::Delegate {
void OnGatheringStateChanged(cricket::IceGatheringState new_state) override;
void OnCandidateGathered(const cricket::Candidate& candidate) override;
void OnStateChanged(cricket::IceTransportState new_state) override;
void OnSelectedCandidatePairChanged(
const std::pair<cricket::Candidate, cricket::Candidate>&
selected_candidate_pair) override;
const scoped_refptr<base::SingleThreadTaskRunner> proxy_thread_;
std::unique_ptr<IceTransportAdapter> transport_;
......
......@@ -140,4 +140,11 @@ void IceTransportProxy::OnStateChanged(cricket::IceTransportState new_state) {
delegate_->OnStateChanged(new_state);
}
void IceTransportProxy::OnSelectedCandidatePairChanged(
const std::pair<cricket::Candidate, cricket::Candidate>&
selected_candidate_pair) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
delegate_->OnSelectedCandidatePairChanged(selected_candidate_pair);
}
} // namespace blink
......@@ -50,6 +50,9 @@ class IceTransportProxy final {
}
virtual void OnCandidateGathered(const cricket::Candidate& candidate) {}
virtual void OnStateChanged(cricket::IceTransportState new_state) {}
virtual void OnSelectedCandidatePairChanged(
const std::pair<cricket::Candidate, cricket::Candidate>&
selected_candidate_pair) {}
};
// Construct a Proxy with the underlying ICE implementation running on the
......@@ -94,6 +97,9 @@ class IceTransportProxy final {
void OnGatheringStateChanged(cricket::IceGatheringState new_state);
void OnCandidateGathered(const cricket::Candidate& candidate);
void OnStateChanged(cricket::IceTransportState new_state);
void OnSelectedCandidatePairChanged(
const std::pair<cricket::Candidate, cricket::Candidate>&
selected_candidate_pair);
const scoped_refptr<base::SingleThreadTaskRunner> proxy_thread_;
const scoped_refptr<base::SingleThreadTaskRunner> host_thread_;
......
......@@ -79,6 +79,13 @@ struct CrossThreadCopier<std::vector<rtc::scoped_refptr<rtc::RTCCertificate>>>
STATIC_ONLY(CrossThreadCopier);
};
template <>
struct CrossThreadCopier<std::pair<cricket::Candidate, cricket::Candidate>>
: public CrossThreadCopierPassThrough<
std::pair<cricket::Candidate, cricket::Candidate>> {
STATIC_ONLY(CrossThreadCopier);
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_WEB_RTC_CROSS_THREAD_COPIER_H_
......@@ -380,6 +380,7 @@ void RTCIceTransport::stop() {
// Stopping the consumer should cause it to disconnect.
DCHECK(!HasConsumer());
state_ = RTCIceTransportState::kClosed;
selected_candidate_pair_ = base::nullopt;
proxy_.reset();
}
......@@ -455,9 +456,25 @@ void RTCIceTransport::OnStateChanged(cricket::IceTransportState new_state) {
return;
}
state_ = local_new_state;
if (state_ == RTCIceTransportState::kFailed) {
selected_candidate_pair_ = base::nullopt;
}
DispatchEvent(*Event::Create(EventTypeNames::statechange));
}
void RTCIceTransport::OnSelectedCandidatePairChanged(
const std::pair<cricket::Candidate, cricket::Candidate>&
selected_candidate_pair) {
RTCIceCandidate* local =
ConvertToRtcIceCandidate(selected_candidate_pair.first);
RTCIceCandidate* remote =
ConvertToRtcIceCandidate(selected_candidate_pair.second);
selected_candidate_pair_ = RTCIceCandidatePair();
selected_candidate_pair_->setLocal(local);
selected_candidate_pair_->setRemote(remote);
DispatchEvent(*Event::Create(EventTypeNames::selectedcandidatepairchange));
}
bool RTCIceTransport::RaiseExceptionIfClosed(
ExceptionState& exception_state) const {
if (IsClosed()) {
......
......@@ -99,6 +99,7 @@ class MODULES_EXPORT RTCIceTransport final
ExceptionState& exception_state);
DEFINE_ATTRIBUTE_EVENT_LISTENER(statechange);
DEFINE_ATTRIBUTE_EVENT_LISTENER(gatheringstatechange);
DEFINE_ATTRIBUTE_EVENT_LISTENER(selectedcandidatepairchange);
DEFINE_ATTRIBUTE_EVENT_LISTENER(icecandidate);
// EventTarget overrides.
......@@ -125,6 +126,9 @@ class MODULES_EXPORT RTCIceTransport final
void OnGatheringStateChanged(cricket::IceGatheringState new_state) override;
void OnCandidateGathered(const cricket::Candidate& candidate) override;
void OnStateChanged(cricket::IceTransportState new_state) override;
void OnSelectedCandidatePairChanged(
const std::pair<cricket::Candidate, cricket::Candidate>&
selected_candidate_pair) override;
// Fills in |local_parameters_| with a random usernameFragment and a random
// password.
......
......@@ -47,7 +47,7 @@ enum RTCIceGatheringState {
RTCIceParameters? getRemoteParameters();
attribute EventHandler onstatechange;
attribute EventHandler ongatheringstatechange;
// TODO(crbug.com/864871): Implement onselectedcandidatepairchange.
attribute EventHandler onselectedcandidatepairchange;
// The following is defined in the WebRTC-ICE extension specification.
// https://w3c.github.io/webrtc-ice/#rtcicetransport*
......
......@@ -25,6 +25,7 @@ namespace {
using testing::_;
using testing::Assign;
using testing::AllOf;
using testing::DoDefault;
using testing::ElementsAre;
using testing::Field;
using testing::InSequence;
......@@ -55,8 +56,23 @@ RTCIceParameters CreateRemoteRTCIceParameters1() {
return ice_parameters;
}
constexpr char kRemoteIceCandidateStr1[] =
constexpr char kRemoteUsernameFragment2[] = "secondUsernameFragment";
constexpr char kRemotePassword2[] = "secondPassword";
RTCIceParameters CreateRemoteRTCIceParameters2() {
RTCIceParameters ice_parameters;
ice_parameters.setUsernameFragment(kRemoteUsernameFragment2);
ice_parameters.setPassword(kRemotePassword2);
return ice_parameters;
}
constexpr char kLocalIceCandidateStr1[] =
"candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host generation 2";
constexpr char kRemoteIceCandidateStr1[] =
"candidate:a0+B/2 1 udp 2130706432 ::1 1238 typ host generation 2";
constexpr char kRemoteIceCandidateStr2[] =
"candidate:a0+B/3 1 udp 2130706432 74.125.127.126 2345 typ srflx raddr "
"192.168.1.5 rport 2346 generation 2";
RTCIceCandidate* RTCIceCandidateFromString(V8TestingScope& scope,
const String& candidate_str) {
......@@ -66,6 +82,15 @@ RTCIceCandidate* RTCIceCandidateFromString(V8TestingScope& scope,
ASSERT_NO_EXCEPTION);
}
cricket::Candidate CricketCandidateFromString(
const std::string& candidate_str) {
cricket::Candidate candidate;
bool success =
webrtc::SdpDeserializeCandidate("", candidate_str, &candidate, nullptr);
DCHECK(success);
return candidate;
}
class MockIceTransportAdapterCrossThreadFactory
: public IceTransportAdapterCrossThreadFactory {
public:
......@@ -341,4 +366,255 @@ TEST_F(RTCIceTransportTest, RemoteCandidatesNotPassedUntilStartCalled) {
ASSERT_NO_EXCEPTION);
}
// Test that receiving an OnStateChanged callback with the completed state
// updates the RTCIceTransport state to 'connected' and fires a statechange
// event.
TEST_F(RTCIceTransportTest, OnStateChangedCompletedUpdatesStateAndFiresEvent) {
V8TestingScope scope;
IceTransportAdapter::Delegate* delegate = nullptr;
Persistent<RTCIceTransport> ice_transport =
CreateIceTransport(scope, &delegate);
ice_transport->start(CreateRemoteRTCIceParameters1(), "controlling",
ASSERT_NO_EXCEPTION);
RunUntilIdle();
ASSERT_TRUE(delegate);
Persistent<MockEventListener> event_listener = CreateMockEventListener();
EXPECT_CALL(*event_listener, handleEvent(_, _))
.WillOnce(InvokeWithoutArgs(
[ice_transport] { EXPECT_EQ("connected", ice_transport->state()); }));
ice_transport->addEventListener(EventTypeNames::statechange, event_listener);
ice_transport->addRemoteCandidate(
RTCIceCandidateFromString(scope, kRemoteIceCandidateStr1),
ASSERT_NO_EXCEPTION);
delegate->OnCandidateGathered(
CricketCandidateFromString(kLocalIceCandidateStr1));
delegate->OnStateChanged(cricket::IceTransportState::STATE_COMPLETED);
RunUntilIdle();
}
// Test that receiving an OnStateChanged callback with the failed state updates
// the RTCIceTransport state to 'failed' and fires a statechange event.
TEST_F(RTCIceTransportTest, OnStateChangedFailedUpdatesStateAndFiresEvent) {
V8TestingScope scope;
IceTransportAdapter::Delegate* delegate = nullptr;
Persistent<RTCIceTransport> ice_transport =
CreateIceTransport(scope, &delegate);
ice_transport->start(CreateRemoteRTCIceParameters1(), "controlling",
ASSERT_NO_EXCEPTION);
RunUntilIdle();
ASSERT_TRUE(delegate);
Persistent<MockEventListener> event_listener = CreateMockEventListener();
EXPECT_CALL(*event_listener, handleEvent(_, _))
.WillOnce(InvokeWithoutArgs(
[ice_transport] { EXPECT_EQ("failed", ice_transport->state()); }));
ice_transport->addEventListener(EventTypeNames::statechange, event_listener);
ice_transport->addRemoteCandidate(
RTCIceCandidateFromString(scope, kRemoteIceCandidateStr1),
ASSERT_NO_EXCEPTION);
delegate->OnCandidateGathered(
CricketCandidateFromString(kLocalIceCandidateStr1));
delegate->OnStateChanged(cricket::IceTransportState::STATE_FAILED);
RunUntilIdle();
}
// Test that calling OnSelectedCandidatePairChanged the first time fires the
// selectedcandidatepairchange event and sets the selected candidate pair.
TEST_F(RTCIceTransportTest, InitialOnSelectedCandidatePairChangedFiresEvent) {
V8TestingScope scope;
IceTransportAdapter::Delegate* delegate = nullptr;
Persistent<RTCIceTransport> ice_transport =
CreateIceTransport(scope, &delegate);
ice_transport->start(CreateRemoteRTCIceParameters1(), "controlling",
ASSERT_NO_EXCEPTION);
RunUntilIdle();
ASSERT_TRUE(delegate);
Persistent<MockEventListener> event_listener = CreateMockEventListener();
EXPECT_CALL(*event_listener, handleEvent(_, _))
.WillOnce(InvokeWithoutArgs([ice_transport] {
base::Optional<RTCIceCandidatePair> selected_candidate_pair;
ice_transport->getSelectedCandidatePair(selected_candidate_pair);
ASSERT_TRUE(selected_candidate_pair);
EXPECT_EQ(ice_transport->getLocalCandidates()[0]->candidate(),
selected_candidate_pair->local()->candidate());
EXPECT_EQ(ice_transport->getRemoteCandidates()[0]->candidate(),
selected_candidate_pair->remote()->candidate());
}));
ice_transport->addEventListener(EventTypeNames::selectedcandidatepairchange,
event_listener);
ice_transport->addRemoteCandidate(
RTCIceCandidateFromString(scope, kRemoteIceCandidateStr1),
ASSERT_NO_EXCEPTION);
delegate->OnCandidateGathered(
CricketCandidateFromString(kLocalIceCandidateStr1));
delegate->OnSelectedCandidatePairChanged(
std::make_pair(CricketCandidateFromString(kLocalIceCandidateStr1),
CricketCandidateFromString(kRemoteIceCandidateStr1)));
RunUntilIdle();
}
// Test that calling OnSelectedCandidatePairChanged with a different remote
// candidate fires the event and updates the selected candidate pair.
TEST_F(RTCIceTransportTest,
OnSelectedCandidatePairChangedWithDifferentRemoteCandidateFiresEvent) {
V8TestingScope scope;
IceTransportAdapter::Delegate* delegate = nullptr;
Persistent<RTCIceTransport> ice_transport =
CreateIceTransport(scope, &delegate);
ice_transport->start(CreateRemoteRTCIceParameters1(), "controlling",
ASSERT_NO_EXCEPTION);
RunUntilIdle();
ASSERT_TRUE(delegate);
Persistent<MockEventListener> event_listener = CreateMockEventListener();
EXPECT_CALL(*event_listener, handleEvent(_, _))
.WillOnce(DoDefault()) // First event is already tested above.
.WillOnce(InvokeWithoutArgs([ice_transport] {
base::Optional<RTCIceCandidatePair> selected_candidate_pair;
ice_transport->getSelectedCandidatePair(selected_candidate_pair);
ASSERT_TRUE(selected_candidate_pair);
EXPECT_EQ(ice_transport->getLocalCandidates()[0]->candidate(),
selected_candidate_pair->local()->candidate());
EXPECT_EQ(ice_transport->getRemoteCandidates()[1]->candidate(),
selected_candidate_pair->remote()->candidate());
}));
ice_transport->addEventListener(EventTypeNames::selectedcandidatepairchange,
event_listener);
ice_transport->addRemoteCandidate(
RTCIceCandidateFromString(scope, kRemoteIceCandidateStr1),
ASSERT_NO_EXCEPTION);
delegate->OnCandidateGathered(
CricketCandidateFromString(kLocalIceCandidateStr1));
delegate->OnSelectedCandidatePairChanged(
std::make_pair(CricketCandidateFromString(kLocalIceCandidateStr1),
CricketCandidateFromString(kRemoteIceCandidateStr1)));
ice_transport->addRemoteCandidate(
RTCIceCandidateFromString(scope, kRemoteIceCandidateStr2),
ASSERT_NO_EXCEPTION);
delegate->OnSelectedCandidatePairChanged(
std::make_pair(CricketCandidateFromString(kLocalIceCandidateStr1),
CricketCandidateFromString(kRemoteIceCandidateStr2)));
RunUntilIdle();
}
// Test that receiving an OnStateChange callback to the failed state once a
// connection has been established clears the selected candidate pair without
// firing the selectedcandidatepairchange event.
TEST_F(RTCIceTransportTest,
OnStateChangeFailedAfterConnectedClearsSelectedCandidatePair) {
V8TestingScope scope;
IceTransportAdapter::Delegate* delegate = nullptr;
Persistent<RTCIceTransport> ice_transport =
CreateIceTransport(scope, &delegate);
ice_transport->start(CreateRemoteRTCIceParameters1(), "controlling",
ASSERT_NO_EXCEPTION);
RunUntilIdle();
ASSERT_TRUE(delegate);
Persistent<MockEventListener> state_change_event_listener =
CreateMockEventListener();
EXPECT_CALL(*state_change_event_listener, handleEvent(_, _))
.WillOnce(DoDefault()) // First event is for 'connected'.
.WillOnce(InvokeWithoutArgs([ice_transport] {
EXPECT_EQ("failed", ice_transport->state());
base::Optional<RTCIceCandidatePair> selected_candidate_pair;
ice_transport->getSelectedCandidatePair(selected_candidate_pair);
EXPECT_EQ(base::nullopt, selected_candidate_pair);
}));
ice_transport->addEventListener(EventTypeNames::statechange,
state_change_event_listener);
Persistent<MockEventListener> selected_candidate_pair_change_event_listener =
CreateMockEventListener();
EXPECT_CALL(*selected_candidate_pair_change_event_listener, handleEvent(_, _))
.Times(1); // First event is for the connected pair.
ice_transport->addEventListener(
EventTypeNames::selectedcandidatepairchange,
selected_candidate_pair_change_event_listener);
// Establish the connection
ice_transport->addRemoteCandidate(
RTCIceCandidateFromString(scope, kRemoteIceCandidateStr1),
ASSERT_NO_EXCEPTION);
delegate->OnCandidateGathered(
CricketCandidateFromString(kLocalIceCandidateStr1));
delegate->OnStateChanged(cricket::IceTransportState::STATE_COMPLETED);
delegate->OnSelectedCandidatePairChanged(
std::make_pair(CricketCandidateFromString(kLocalIceCandidateStr1),
CricketCandidateFromString(kRemoteIceCandidateStr1)));
// Transition to failed.
delegate->OnStateChanged(cricket::IceTransportState::STATE_FAILED);
RunUntilIdle();
}
// Test that receiving an OnSelectedCandidatePairChange callback after a remote
// ICE restart still updates the selected candidate pair.
TEST_F(RTCIceTransportTest,
RemoteIceRestartRaceWithSelectedCandidatePairChange) {
V8TestingScope scope;
IceTransportAdapter::Delegate* delegate = nullptr;
Persistent<RTCIceTransport> ice_transport =
CreateIceTransport(scope, &delegate);
ice_transport->start(CreateRemoteRTCIceParameters1(), "controlling",
ASSERT_NO_EXCEPTION);
RunUntilIdle();
ASSERT_TRUE(delegate);
Persistent<MockEventListener> event_listener = CreateMockEventListener();
EXPECT_CALL(*event_listener, handleEvent(_, _))
.WillOnce(InvokeWithoutArgs([ice_transport] {
base::Optional<RTCIceCandidatePair> selected_candidate_pair;
ice_transport->getSelectedCandidatePair(selected_candidate_pair);
ASSERT_TRUE(selected_candidate_pair);
EXPECT_EQ(kLocalIceCandidateStr1,
selected_candidate_pair->local()->candidate());
EXPECT_EQ(kRemoteIceCandidateStr1,
selected_candidate_pair->remote()->candidate());
}));
ice_transport->addEventListener(EventTypeNames::selectedcandidatepairchange,
event_listener);
ice_transport->addRemoteCandidate(
RTCIceCandidateFromString(scope, kRemoteIceCandidateStr1),
ASSERT_NO_EXCEPTION);
// Changing remote ICE parameters indicate a remote ICE restart. This clears
// the stored list of remote candidates.
ice_transport->start(CreateRemoteRTCIceParameters2(), "controlling",
ASSERT_NO_EXCEPTION);
// These callbacks are part of the previous generation but should still take
// effect.
delegate->OnCandidateGathered(
CricketCandidateFromString(kLocalIceCandidateStr1));
delegate->OnStateChanged(cricket::IceTransportState::STATE_COMPLETED);
delegate->OnSelectedCandidatePairChanged(
std::make_pair(CricketCandidateFromString(kLocalIceCandidateStr1),
CricketCandidateFromString(kRemoteIceCandidateStr1)));
RunUntilIdle();
}
} // namespace blink
......@@ -442,6 +442,7 @@ _CONFIG = [
# AtomicString or HeapVector) are used cross thread. These Blink types
# are converted to the STL/WebRTC counterparts in the parent directory.
'allowed': [
'absl::.+',
'base::OnTaskRunnerDeleter',
'sigslot::.+',
],
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment