[PeerConnection] Implement parameterless SetLocalDescription().
For background, motivation, requirements and implementation notes, see
https://docs.google.com/document/d/1XLwNN2kUIGGTwz9LQ0NwJNkcybi9oKnynUEZB1jGA14/edit?usp=sharing
The parameterless SetLocalDescription() will implicitly create an
offer or answer to be set by chaining create offer or answer with
setting the session description, as per spec:
https://w3c.github.io/webrtc-pc/#dom-peerconnection-setlocaldescription
Bug: chromium:980885
Change-Id: Ia430160869df18fd47b756b9adf9e7e23ba8e969
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/157444
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29653}
diff --git a/pc/peer_connection_signaling_unittest.cc b/pc/peer_connection_signaling_unittest.cc
index e79ee3d..30b11ce 100644
--- a/pc/peer_connection_signaling_unittest.cc
+++ b/pc/peer_connection_signaling_unittest.cc
@@ -565,6 +565,32 @@
EXPECT_TRUE(observer->called());
}
+TEST_P(PeerConnectionSignalingTest, ImplicitCreateOfferAndShutdown) {
+ auto caller = CreatePeerConnection();
+ auto observer = MockSetSessionDescriptionObserver::Create();
+ caller->pc()->SetLocalDescription(observer);
+ caller.reset(nullptr);
+ EXPECT_FALSE(observer->called());
+}
+
+TEST_P(PeerConnectionSignalingTest, CloseBeforeImplicitCreateOfferAndShutdown) {
+ auto caller = CreatePeerConnection();
+ auto observer = MockSetSessionDescriptionObserver::Create();
+ caller->pc()->Close();
+ caller->pc()->SetLocalDescription(observer);
+ caller.reset(nullptr);
+ EXPECT_FALSE(observer->called());
+}
+
+TEST_P(PeerConnectionSignalingTest, CloseAfterImplicitCreateOfferAndShutdown) {
+ auto caller = CreatePeerConnection();
+ auto observer = MockSetSessionDescriptionObserver::Create();
+ caller->pc()->SetLocalDescription(observer);
+ caller->pc()->Close();
+ caller.reset(nullptr);
+ EXPECT_FALSE(observer->called());
+}
+
TEST_P(PeerConnectionSignalingTest, SetRemoteDescriptionExecutesImmediately) {
auto caller = CreatePeerConnectionWithAudioVideo();
auto callee = CreatePeerConnection();
@@ -608,6 +634,143 @@
EXPECT_EQ(2u, callee->pc()->GetReceivers().size());
}
+TEST_P(PeerConnectionSignalingTest,
+ ParameterlessSetLocalDescriptionCreatesOffer) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+
+ auto observer = MockSetSessionDescriptionObserver::Create();
+ caller->pc()->SetLocalDescription(observer);
+
+ // The offer is created asynchronously; message processing is needed for it to
+ // complete.
+ EXPECT_FALSE(observer->called());
+ EXPECT_FALSE(caller->pc()->pending_local_description());
+ EXPECT_EQ(PeerConnection::kStable, caller->signaling_state());
+
+ // Wait for messages to be processed.
+ EXPECT_TRUE_WAIT(observer->called(), kWaitTimeout);
+ EXPECT_TRUE(observer->result());
+ EXPECT_TRUE(caller->pc()->pending_local_description());
+ EXPECT_EQ(SdpType::kOffer,
+ caller->pc()->pending_local_description()->GetType());
+ EXPECT_EQ(PeerConnection::kHaveLocalOffer, caller->signaling_state());
+}
+
+TEST_P(PeerConnectionSignalingTest,
+ ParameterlessSetLocalDescriptionCreatesAnswer) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ auto callee = CreatePeerConnectionWithAudioVideo();
+
+ callee->SetRemoteDescription(caller->CreateOffer());
+ EXPECT_EQ(PeerConnection::kHaveRemoteOffer, callee->signaling_state());
+
+ auto observer = MockSetSessionDescriptionObserver::Create();
+ callee->pc()->SetLocalDescription(observer);
+
+ // The answer is created asynchronously; message processing is needed for it
+ // to complete.
+ EXPECT_FALSE(observer->called());
+ EXPECT_FALSE(callee->pc()->current_local_description());
+
+ // Wait for messages to be processed.
+ EXPECT_TRUE_WAIT(observer->called(), kWaitTimeout);
+ EXPECT_TRUE(observer->result());
+ EXPECT_TRUE(callee->pc()->current_local_description());
+ EXPECT_EQ(SdpType::kAnswer,
+ callee->pc()->current_local_description()->GetType());
+ EXPECT_EQ(PeerConnection::kStable, callee->signaling_state());
+}
+
+TEST_P(PeerConnectionSignalingTest,
+ ParameterlessSetLocalDescriptionFullExchange) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ auto callee = CreatePeerConnectionWithAudioVideo();
+
+ // SetLocalDescription(), implicitly creating an offer.
+ rtc::scoped_refptr<MockSetSessionDescriptionObserver>
+ caller_set_local_description_observer(
+ new rtc::RefCountedObject<MockSetSessionDescriptionObserver>());
+ caller->pc()->SetLocalDescription(caller_set_local_description_observer);
+ EXPECT_TRUE_WAIT(caller_set_local_description_observer->called(),
+ kWaitTimeout);
+ ASSERT_TRUE(caller->pc()->pending_local_description());
+
+ // SetRemoteDescription(offer)
+ rtc::scoped_refptr<MockSetSessionDescriptionObserver>
+ callee_set_remote_description_observer(
+ new rtc::RefCountedObject<MockSetSessionDescriptionObserver>());
+ callee->pc()->SetRemoteDescription(
+ callee_set_remote_description_observer.get(),
+ CloneSessionDescription(caller->pc()->pending_local_description())
+ .release());
+
+ // SetLocalDescription(), implicitly creating an answer.
+ rtc::scoped_refptr<MockSetSessionDescriptionObserver>
+ callee_set_local_description_observer(
+ new rtc::RefCountedObject<MockSetSessionDescriptionObserver>());
+ callee->pc()->SetLocalDescription(callee_set_local_description_observer);
+ EXPECT_TRUE_WAIT(callee_set_local_description_observer->called(),
+ kWaitTimeout);
+ // Chaining guarantees SetRemoteDescription() happened before
+ // SetLocalDescription().
+ EXPECT_TRUE(callee_set_remote_description_observer->called());
+ EXPECT_TRUE(callee->pc()->current_local_description());
+
+ // SetRemoteDescription(answer)
+ rtc::scoped_refptr<MockSetSessionDescriptionObserver>
+ caller_set_remote_description_observer(
+ new rtc::RefCountedObject<MockSetSessionDescriptionObserver>());
+ caller->pc()->SetRemoteDescription(
+ caller_set_remote_description_observer,
+ CloneSessionDescription(callee->pc()->current_local_description())
+ .release());
+ EXPECT_TRUE_WAIT(caller_set_remote_description_observer->called(),
+ kWaitTimeout);
+
+ EXPECT_EQ(PeerConnection::kStable, caller->signaling_state());
+ EXPECT_EQ(PeerConnection::kStable, callee->signaling_state());
+}
+
+TEST_P(PeerConnectionSignalingTest,
+ ParameterlessSetLocalDescriptionCloseBeforeCreatingOffer) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+
+ auto observer = MockSetSessionDescriptionObserver::Create();
+ caller->pc()->Close();
+ caller->pc()->SetLocalDescription(observer);
+
+ // The operation should fail asynchronously.
+ EXPECT_FALSE(observer->called());
+ EXPECT_TRUE_WAIT(observer->called(), kWaitTimeout);
+ EXPECT_FALSE(observer->result());
+ // This did not affect the signaling state.
+ EXPECT_EQ(PeerConnection::kClosed, caller->pc()->signaling_state());
+ EXPECT_EQ(
+ "SetLocalDescription failed to create session description - "
+ "SetLocalDescription called when PeerConnection is closed.",
+ observer->error());
+}
+
+TEST_P(PeerConnectionSignalingTest,
+ ParameterlessSetLocalDescriptionCloseWhileCreatingOffer) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+
+ auto observer = MockSetSessionDescriptionObserver::Create();
+ caller->pc()->SetLocalDescription(observer);
+ caller->pc()->Close();
+
+ // The operation should fail asynchronously.
+ EXPECT_FALSE(observer->called());
+ EXPECT_TRUE_WAIT(observer->called(), kWaitTimeout);
+ EXPECT_FALSE(observer->result());
+ // This did not affect the signaling state.
+ EXPECT_EQ(PeerConnection::kClosed, caller->pc()->signaling_state());
+ EXPECT_EQ(
+ "SetLocalDescription failed to create session description - "
+ "CreateOffer failed because the session was shut down",
+ observer->error());
+}
+
INSTANTIATE_TEST_SUITE_P(PeerConnectionSignalingTest,
PeerConnectionSignalingTest,
Values(SdpSemantics::kPlanB,