Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / libjingle / source / talk / app / webrtc / webrtcsession_unittest.cc
index 0ddc16c..b746493 100644 (file)
@@ -53,6 +53,7 @@
 #include "talk/media/devices/fakedevicemanager.h"
 #include "talk/p2p/base/stunserver.h"
 #include "talk/p2p/base/teststunserver.h"
+#include "talk/p2p/base/testturnserver.h"
 #include "talk/p2p/client/basicportallocator.h"
 #include "talk/session/media/channelmanager.h"
 #include "talk/session/media/mediasession.h"
@@ -72,6 +73,7 @@ using cricket::NS_JINGLE_ICE_UDP;
 using cricket::TransportInfo;
 using talk_base::SocketAddress;
 using talk_base::scoped_ptr;
+using talk_base::Thread;
 using webrtc::CreateSessionDescription;
 using webrtc::CreateSessionDescriptionObserver;
 using webrtc::CreateSessionDescriptionRequest;
@@ -86,20 +88,25 @@ using webrtc::PeerConnectionInterface;
 using webrtc::SessionDescriptionInterface;
 using webrtc::StreamCollection;
 using webrtc::WebRtcSession;
+using webrtc::kBundleWithoutRtcpMux;
+using webrtc::kCreateChannelFailed;
+using webrtc::kInvalidSdp;
 using webrtc::kMlineMismatch;
-using webrtc::kSdpWithoutCrypto;
-using webrtc::kSdpWithoutSdesAndDtlsDisabled;
+using webrtc::kPushDownTDFailed;
 using webrtc::kSdpWithoutIceUfragPwd;
+using webrtc::kSdpWithoutDtlsFingerprint;
+using webrtc::kSdpWithoutSdesCrypto;
 using webrtc::kSessionError;
-using webrtc::kSetLocalSdpFailed;
-using webrtc::kSetRemoteSdpFailed;
-using webrtc::kPushDownAnswerTDFailed;
-using webrtc::kPushDownPranswerTDFailed;
-using webrtc::kBundleWithoutRtcpMux;
+using webrtc::kSessionErrorDesc;
 
-static const SocketAddress kClientAddr1("11.11.11.11", 0);
-static const SocketAddress kClientAddr2("22.22.22.22", 0);
-static const SocketAddress kStunAddr("99.99.99.1", cricket::STUN_SERVER_PORT);
+static const int kClientAddrPort = 0;
+static const char kClientAddrHost1[] = "11.11.11.11";
+static const char kClientAddrHost2[] = "22.22.22.22";
+static const char kStunAddrHost[] = "99.99.99.1";
+static const SocketAddress kTurnUdpIntAddr("99.99.99.4", 3478);
+static const SocketAddress kTurnUdpExtAddr("99.99.99.6", 0);
+static const char kTurnUsername[] = "test";
+static const char kTurnPassword[] = "test";
 
 static const char kSessionVersion[] = "1";
 
@@ -113,11 +120,6 @@ static const char kMediaContentName1[] = "video";
 
 static const int kIceCandidatesTimeout = 10000;
 
-static const cricket::AudioCodec
-    kTelephoneEventCodec(106, "telephone-event", 8000, 0, 1, 0);
-static const cricket::AudioCodec kCNCodec1(102, "CN", 8000, 0, 1, 0);
-static const cricket::AudioCodec kCNCodec2(103, "CN", 16000, 0, 1, 0);
-
 static const char kFakeDtlsFingerprint[] =
     "BB:CD:72:F7:2F:D0:BA:43:F3:68:B1:0C:23:72:B6:4A:"
     "0F:DE:34:06:BC:E0:FE:01:BC:73:C8:6D:F4:65:D5:24";
@@ -159,11 +161,17 @@ class MockIceObserver : public webrtc::IceObserver {
 
   // Found a new candidate.
   virtual void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) {
-    if (candidate->sdp_mline_index() == kMediaContentIndex0) {
-      mline_0_candidates_.push_back(candidate->candidate());
-    } else if (candidate->sdp_mline_index() == kMediaContentIndex1) {
-      mline_1_candidates_.push_back(candidate->candidate());
+    switch (candidate->sdp_mline_index()) {
+      case kMediaContentIndex0:
+        mline_0_candidates_.push_back(candidate->candidate());
+        break;
+      case kMediaContentIndex1:
+        mline_1_candidates_.push_back(candidate->candidate());
+        break;
+      default:
+        ASSERT(false);
     }
+
     // The ICE gathering state should always be Gathering when a candidate is
     // received (or possibly Completed in the case of the final candidate).
     EXPECT_NE(PeerConnectionInterface::kIceGatheringNew, ice_gathering_state_);
@@ -247,7 +255,11 @@ class WebRtcSessionCreateSDPObserverForTest
 
 class FakeAudioRenderer : public cricket::AudioRenderer {
  public:
-  FakeAudioRenderer() : channel_id_(-1) {}
+  FakeAudioRenderer() : channel_id_(-1), sink_(NULL) {}
+  virtual ~FakeAudioRenderer() {
+    if (sink_)
+      sink_->OnClose();
+  }
 
   virtual void AddChannel(int channel_id) OVERRIDE {
     ASSERT(channel_id_ == -1);
@@ -257,10 +269,15 @@ class FakeAudioRenderer : public cricket::AudioRenderer {
     ASSERT(channel_id == channel_id_);
     channel_id_ = -1;
   }
+  virtual void SetSink(Sink* sink) OVERRIDE {
+    sink_ = sink;
+  }
 
   int channel_id() const { return channel_id_; }
+  cricket::AudioRenderer::Sink* sink() const { return sink_; }
  private:
   int channel_id_;
+  cricket::AudioRenderer::Sink* sink_;
 };
 
 class WebRtcSessionTest : public testing::Test {
@@ -281,16 +298,22 @@ class WebRtcSessionTest : public testing::Test {
       vss_(new talk_base::VirtualSocketServer(pss_.get())),
       fss_(new talk_base::FirewallSocketServer(vss_.get())),
       ss_scope_(fss_.get()),
-      stun_server_(talk_base::Thread::Current(), kStunAddr),
-      allocator_(&network_manager_, kStunAddr,
-                 SocketAddress(), SocketAddress(), SocketAddress()),
-      mediastream_signaling_(channel_manager_.get()) {
+      stun_socket_addr_(talk_base::SocketAddress(kStunAddrHost,
+                                                 cricket::STUN_SERVER_PORT)),
+      stun_server_(Thread::Current(), stun_socket_addr_),
+      turn_server_(Thread::Current(), kTurnUdpIntAddr, kTurnUdpExtAddr),
+      allocator_(new cricket::BasicPortAllocator(
+          &network_manager_, stun_socket_addr_,
+          SocketAddress(), SocketAddress(), SocketAddress())),
+      mediastream_signaling_(channel_manager_.get()),
+      ice_type_(PeerConnectionInterface::kAll) {
     tdesc_factory_->set_protocol(cricket::ICEPROTO_HYBRID);
-    allocator_.set_flags(cricket::PORTALLOCATOR_DISABLE_TCP |
+    allocator_->set_flags(cricket::PORTALLOCATOR_DISABLE_TCP |
                          cricket::PORTALLOCATOR_DISABLE_RELAY |
                          cricket::PORTALLOCATOR_ENABLE_BUNDLE);
     EXPECT_TRUE(channel_manager_->Init());
     desc_factory_->set_add_legacy_streams(false);
+    allocator_->set_step_delay(cricket::kMinimumStepDelay);
   }
 
   static void SetUpTestCase() {
@@ -305,11 +328,15 @@ class WebRtcSessionTest : public testing::Test {
     network_manager_.AddInterface(addr);
   }
 
+  void SetIceTransportType(PeerConnectionInterface::IceTransportsType type) {
+    ice_type_ = type;
+  }
+
   void Init(DTLSIdentityServiceInterface* identity_service) {
     ASSERT_TRUE(session_.get() == NULL);
     session_.reset(new WebRtcSessionForTest(
         channel_manager_.get(), talk_base::Thread::Current(),
-        talk_base::Thread::Current(), &allocator_,
+        talk_base::Thread::Current(), allocator_.get(),
         &observer_,
         &mediastream_signaling_));
 
@@ -319,11 +346,13 @@ class WebRtcSessionTest : public testing::Test {
         observer_.ice_gathering_state_);
 
     EXPECT_TRUE(session_->Initialize(options_, constraints_.get(),
-                                     identity_service));
+                                     identity_service, ice_type_));
   }
 
   void InitWithDtmfCodec() {
     // Add kTelephoneEventCodec for dtmf test.
+    const cricket::AudioCodec kTelephoneEventCodec(
+        106, "telephone-event", 8000, 0, 1, 0);
     std::vector<cricket::AudioCodec> codecs;
     codecs.push_back(kTelephoneEventCodec);
     media_engine_->SetAudioCodecs(codecs);
@@ -370,12 +399,12 @@ class WebRtcSessionTest : public testing::Test {
     return observer->ReleaseDescription();
   }
 
-  bool ChannelsExist() {
+  bool ChannelsExist() const {
     return (session_->voice_channel() != NULL &&
             session_->video_channel() != NULL);
   }
 
-  void CheckTransportChannels() {
+  void CheckTransportChannels() const {
     EXPECT_TRUE(session_->GetChannel(cricket::CN_AUDIO, 1) != NULL);
     EXPECT_TRUE(session_->GetChannel(cricket::CN_AUDIO, 2) != NULL);
     EXPECT_TRUE(session_->GetChannel(cricket::CN_VIDEO, 1) != NULL);
@@ -443,7 +472,7 @@ class WebRtcSessionTest : public testing::Test {
 
   // Set the internal fake description factories to do DTLS-SRTP.
   void SetFactoryDtlsSrtp() {
-    desc_factory_->set_secure(cricket::SEC_ENABLED);
+    desc_factory_->set_secure(cricket::SEC_DISABLED);
     std::string identity_name = "WebRTC" +
         talk_base::ToString(talk_base::CreateRandomId());
     identity_.reset(talk_base::SSLIdentity::Generate(identity_name));
@@ -469,8 +498,8 @@ class WebRtcSessionTest : public testing::Test {
         CreateRemoteOffer(options, cricket::SEC_DISABLED));
     ASSERT_TRUE(offer != NULL);
     VerifyNoCryptoParams(offer->description(), false);
-    SetRemoteDescriptionExpectError("Called with a SDP without crypto enabled",
-                                     offer);
+    SetRemoteDescriptionOfferExpectError(kSdpWithoutSdesCrypto,
+                                         offer);
     const webrtc::SessionDescriptionInterface* answer = CreateAnswer(NULL);
     // Answer should be NULL as no crypto params in offer.
     ASSERT_TRUE(answer == NULL);
@@ -561,13 +590,26 @@ class WebRtcSessionTest : public testing::Test {
     SetLocalDescriptionWithoutError(desc);
     EXPECT_EQ(expected_state, session_->state());
   }
-  void SetLocalDescriptionExpectError(const std::string& expected_error,
+  void SetLocalDescriptionExpectError(const std::string& action,
+                                      const std::string& expected_error,
                                       SessionDescriptionInterface* desc) {
     std::string error;
     EXPECT_FALSE(session_->SetLocalDescription(desc, &error));
-    EXPECT_NE(std::string::npos, error.find(kSetLocalSdpFailed));
+    std::string sdp_type = "local ";
+    sdp_type.append(action);
+    EXPECT_NE(std::string::npos, error.find(sdp_type));
     EXPECT_NE(std::string::npos, error.find(expected_error));
   }
+  void SetLocalDescriptionOfferExpectError(const std::string& expected_error,
+                                           SessionDescriptionInterface* desc) {
+    SetLocalDescriptionExpectError(SessionDescriptionInterface::kOffer,
+                                   expected_error, desc);
+  }
+  void SetLocalDescriptionAnswerExpectError(const std::string& expected_error,
+                                            SessionDescriptionInterface* desc) {
+    SetLocalDescriptionExpectError(SessionDescriptionInterface::kAnswer,
+                                   expected_error, desc);
+  }
   void SetRemoteDescriptionWithoutError(SessionDescriptionInterface* desc) {
     EXPECT_TRUE(session_->SetRemoteDescription(desc, NULL));
   }
@@ -576,13 +618,31 @@ class WebRtcSessionTest : public testing::Test {
     SetRemoteDescriptionWithoutError(desc);
     EXPECT_EQ(expected_state, session_->state());
   }
-  void SetRemoteDescriptionExpectError(const std::string& expected_error,
+  void SetRemoteDescriptionExpectError(const std::string& action,
+                                       const std::string& expected_error,
                                        SessionDescriptionInterface* desc) {
     std::string error;
     EXPECT_FALSE(session_->SetRemoteDescription(desc, &error));
-    EXPECT_NE(std::string::npos, error.find(kSetRemoteSdpFailed));
+    std::string sdp_type = "remote ";
+    sdp_type.append(action);
+    EXPECT_NE(std::string::npos, error.find(sdp_type));
     EXPECT_NE(std::string::npos, error.find(expected_error));
   }
+  void SetRemoteDescriptionOfferExpectError(
+      const std::string& expected_error, SessionDescriptionInterface* desc) {
+    SetRemoteDescriptionExpectError(SessionDescriptionInterface::kOffer,
+                                    expected_error, desc);
+  }
+  void SetRemoteDescriptionPranswerExpectError(
+      const std::string& expected_error, SessionDescriptionInterface* desc) {
+    SetRemoteDescriptionExpectError(SessionDescriptionInterface::kPrAnswer,
+                                    expected_error, desc);
+  }
+  void SetRemoteDescriptionAnswerExpectError(
+      const std::string& expected_error, SessionDescriptionInterface* desc) {
+    SetRemoteDescriptionExpectError(SessionDescriptionInterface::kAnswer,
+                                    expected_error, desc);
+  }
 
   void CreateCryptoOfferAndNonCryptoAnswer(SessionDescriptionInterface** offer,
       SessionDescriptionInterface** nocrypto_answer) {
@@ -599,6 +659,28 @@ class WebRtcSessionTest : public testing::Test {
     EXPECT_TRUE(*nocrypto_answer != NULL);
   }
 
+  void CreateDtlsOfferAndNonDtlsAnswer(SessionDescriptionInterface** offer,
+      SessionDescriptionInterface** nodtls_answer) {
+    cricket::MediaSessionOptions options;
+    options.has_video = true;
+    options.bundle_enabled = true;
+
+    talk_base::scoped_ptr<SessionDescriptionInterface> temp_offer(
+        CreateRemoteOffer(options, cricket::SEC_ENABLED));
+
+    *nodtls_answer =
+        CreateRemoteAnswer(temp_offer.get(), options, cricket::SEC_ENABLED);
+    EXPECT_TRUE(*nodtls_answer != NULL);
+    VerifyFingerprintStatus((*nodtls_answer)->description(), false);
+    VerifyCryptoParams((*nodtls_answer)->description());
+
+    SetFactoryDtlsSrtp();
+    *offer = CreateRemoteOffer(options, cricket::SEC_ENABLED);
+    ASSERT_TRUE(*offer != NULL);
+    VerifyFingerprintStatus((*offer)->description(), true);
+    VerifyCryptoParams((*offer)->description());
+  }
+
   JsepSessionDescription* CreateRemoteOfferWithVersion(
         cricket::MediaSessionOptions options,
         cricket::SecurePolicy secure_policy,
@@ -627,8 +709,9 @@ class WebRtcSessionTest : public testing::Test {
                                         kSessionVersion, NULL);
   }
   JsepSessionDescription* CreateRemoteOffer(
-      cricket::MediaSessionOptions options, cricket::SecurePolicy policy) {
-    return CreateRemoteOfferWithVersion(options, policy, kSessionVersion, NULL);
+      cricket::MediaSessionOptions options, cricket::SecurePolicy sdes_policy) {
+    return CreateRemoteOfferWithVersion(
+        options, sdes_policy, kSessionVersion, NULL);
   }
   JsepSessionDescription* CreateRemoteOffer(
       cricket::MediaSessionOptions options,
@@ -710,7 +793,7 @@ class WebRtcSessionTest : public testing::Test {
   }
 
   void TestSessionCandidatesWithBundleRtcpMux(bool bundle, bool rtcp_mux) {
-    AddInterface(kClientAddr1);
+    AddInterface(talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort));
     Init(NULL);
     mediastream_signaling_.SendAudioVideoStream1();
     FakeConstraints constraints;
@@ -771,16 +854,18 @@ class WebRtcSessionTest : public testing::Test {
 
   // The method sets up a call from the session to itself, in a loopback
   // arrangement.  It also uses a firewall rule to create a temporary
-  // disconnection.  This code is placed as a method so that it can be invoked
+  // disconnection, and then a permanent disconnection.
+  // This code is placed in a method so that it can be invoked
   // by multiple tests with different allocators (e.g. with and without BUNDLE).
   // While running the call, this method also checks if the session goes through
   // the correct sequence of ICE states when a connection is established,
   // broken, and re-established.
   // The Connection state should go:
-  // New -> Checking -> Connected -> Disconnected -> Connected.
+  // New -> Checking -> (Connected) -> Completed -> Disconnected -> Completed
+  //     -> Failed.
   // The Gathering state should go: New -> Gathering -> Completed.
   void TestLoopbackCall() {
-    AddInterface(kClientAddr1);
+    AddInterface(talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort));
     Init(NULL);
     mediastream_signaling_.SendAudioVideoStream1();
     SessionDescriptionInterface* offer = CreateOffer(NULL);
@@ -808,14 +893,18 @@ class WebRtcSessionTest : public testing::Test {
     EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionChecking,
                    observer_.ice_connection_state_,
                    kIceCandidatesTimeout);
-    EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionConnected,
+
+    // The ice connection state is "Connected" too briefly to catch in a test.
+    EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
                    observer_.ice_connection_state_,
                    kIceCandidatesTimeout);
-    // TODO(bemasc): EXPECT(Completed) once the details are standardized.
 
     // Adding firewall rule to block ping requests, which should cause
     // transport channel failure.
-    fss_->AddRule(false, talk_base::FP_ANY, talk_base::FD_ANY, kClientAddr1);
+    fss_->AddRule(false,
+                  talk_base::FP_ANY,
+                  talk_base::FD_ANY,
+                  talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort));
     EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionDisconnected,
                    observer_.ice_connection_state_,
                    kIceCandidatesTimeout);
@@ -825,10 +914,21 @@ class WebRtcSessionTest : public testing::Test {
     // Session is automatically calling OnSignalingReady after creation of
     // new portallocator session which will allocate new set of candidates.
 
-    // TODO(bemasc): Change this to Completed once the details are standardized.
-    EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionConnected,
+    EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
                    observer_.ice_connection_state_,
                    kIceCandidatesTimeout);
+
+    // Now we block ping requests and wait until the ICE connection transitions
+    // to the Failed state.  This will take at least 30 seconds because it must
+    // wait for the Port to timeout.
+    int port_timeout = 30000;
+    fss_->AddRule(false,
+                  talk_base::FP_ANY,
+                  talk_base::FD_ANY,
+                  talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort));
+    EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionFailed,
+                   observer_.ice_connection_state_,
+                   kIceCandidatesTimeout + port_timeout);
   }
 
   void VerifyTransportType(const std::string& content_name,
@@ -840,7 +940,10 @@ class WebRtcSessionTest : public testing::Test {
 
   // Adds CN codecs to FakeMediaEngine and MediaDescriptionFactory.
   void AddCNCodecs() {
-    // Add kTelephoneEventCodec for dtmf test.
+    const cricket::AudioCodec kCNCodec1(102, "CN", 8000, 0, 1, 0);
+    const cricket::AudioCodec kCNCodec2(103, "CN", 16000, 0, 1, 0);
+
+    // Add kCNCodec for dtmf test.
     std::vector<cricket::AudioCodec> codecs = media_engine_->audio_codecs();;
     codecs.push_back(kCNCodec1);
     codecs.push_back(kCNCodec2);
@@ -862,7 +965,7 @@ class WebRtcSessionTest : public testing::Test {
   }
 
   void SetLocalDescriptionWithDataChannel() {
-    webrtc::DataChannelInit dci;
+    webrtc::InternalDataChannelInit dci;
     dci.reliable = false;
     session_->CreateDataChannel("datachannel", &dci);
     SessionDescriptionInterface* offer = CreateOffer(NULL);
@@ -872,11 +975,11 @@ class WebRtcSessionTest : public testing::Test {
   void VerifyMultipleAsyncCreateDescription(
       bool success, CreateSessionDescriptionRequest::Type type) {
     InitWithDtls(!success);
-
+    SetFactoryDtlsSrtp();
     if (type == CreateSessionDescriptionRequest::kAnswer) {
       cricket::MediaSessionOptions options;
       scoped_ptr<JsepSessionDescription> offer(
-            CreateRemoteOffer(options, cricket::SEC_REQUIRED));
+            CreateRemoteOffer(options, cricket::SEC_DISABLED));
       ASSERT_TRUE(offer.get() != NULL);
       SetRemoteDescriptionWithoutError(offer.release());
     }
@@ -918,9 +1021,11 @@ class WebRtcSessionTest : public testing::Test {
   talk_base::scoped_ptr<talk_base::VirtualSocketServer> vss_;
   talk_base::scoped_ptr<talk_base::FirewallSocketServer> fss_;
   talk_base::SocketServerScope ss_scope_;
+  talk_base::SocketAddress stun_socket_addr_;
   cricket::TestStunServer stun_server_;
+  cricket::TestTurnServer turn_server_;
   talk_base::FakeNetworkManager network_manager_;
-  cricket::BasicPortAllocator allocator_;
+  talk_base::scoped_ptr<cricket::BasicPortAllocator> allocator_;
   PeerConnectionFactoryInterface::Options options_;
   talk_base::scoped_ptr<FakeConstraints> constraints_;
   FakeMediaStreamSignaling mediastream_signaling_;
@@ -928,20 +1033,19 @@ class WebRtcSessionTest : public testing::Test {
   MockIceObserver observer_;
   cricket::FakeVideoMediaChannel* video_channel_;
   cricket::FakeVoiceMediaChannel* voice_channel_;
+  PeerConnectionInterface::IceTransportsType ice_type_;
 };
 
-TEST_F(WebRtcSessionTest, TestInitialize) {
-  Init(NULL);
-}
-
 TEST_F(WebRtcSessionTest, TestInitializeWithDtls) {
   InitWithDtls();
+  // SDES is disabled when DTLS is on.
+  EXPECT_EQ(cricket::SEC_DISABLED, session_->SdesPolicy());
 }
 
-// Verifies that WebRtcSession uses SEC_REQUIRED by default.
-TEST_F(WebRtcSessionTest, TestDefaultSetSecurePolicy) {
+TEST_F(WebRtcSessionTest, TestInitializeWithoutDtls) {
   Init(NULL);
-  EXPECT_EQ(cricket::SEC_REQUIRED, session_->secure_policy());
+  // SDES is required if DTLS is off.
+  EXPECT_EQ(cricket::SEC_REQUIRED, session_->SdesPolicy());
 }
 
 TEST_F(WebRtcSessionTest, TestSessionCandidates) {
@@ -959,8 +1063,8 @@ TEST_F(WebRtcSessionTest, TestSessionCandidatesWithBundleRtcpMux) {
 }
 
 TEST_F(WebRtcSessionTest, TestMultihomeCandidates) {
-  AddInterface(kClientAddr1);
-  AddInterface(kClientAddr2);
+  AddInterface(talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort));
+  AddInterface(talk_base::SocketAddress(kClientAddrHost2, kClientAddrPort));
   Init(NULL);
   mediastream_signaling_.SendAudioVideoStream1();
   InitiateCall();
@@ -970,21 +1074,33 @@ TEST_F(WebRtcSessionTest, TestMultihomeCandidates) {
 }
 
 TEST_F(WebRtcSessionTest, TestStunError) {
-  AddInterface(kClientAddr1);
-  AddInterface(kClientAddr2);
-  fss_->AddRule(false, talk_base::FP_UDP, talk_base::FD_ANY, kClientAddr1);
+  AddInterface(talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort));
+  AddInterface(talk_base::SocketAddress(kClientAddrHost2, kClientAddrPort));
+  fss_->AddRule(false,
+                talk_base::FP_UDP,
+                talk_base::FD_ANY,
+                talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort));
   Init(NULL);
   mediastream_signaling_.SendAudioVideoStream1();
   InitiateCall();
-  // Since kClientAddr1 is blocked, not expecting stun candidates for it.
+  // Since kClientAddrHost1 is blocked, not expecting stun candidates for it.
   EXPECT_TRUE_WAIT(observer_.oncandidatesready_, kIceCandidatesTimeout);
   EXPECT_EQ(6u, observer_.mline_0_candidates_.size());
   EXPECT_EQ(6u, observer_.mline_1_candidates_.size());
 }
 
+TEST_F(WebRtcSessionTest, SetSdpFailedOnInvalidSdp) {
+  Init(NULL);
+  SessionDescriptionInterface* offer = NULL;
+  // Since |offer| is NULL, there's no way to tell if it's an offer or answer.
+  std::string unknown_action;
+  SetLocalDescriptionExpectError(unknown_action, kInvalidSdp, offer);
+  SetRemoteDescriptionExpectError(unknown_action, kInvalidSdp, offer);
+}
+
 // Test creating offers and receive answers and make sure the
 // media engine creates the expected send and receive streams.
-TEST_F(WebRtcSessionTest, TestCreateOfferReceiveAnswer) {
+TEST_F(WebRtcSessionTest, TestCreateSdesOfferReceiveSdesAnswer) {
   Init(NULL);
   mediastream_signaling_.SendAudioVideoStream1();
   SessionDescriptionInterface* offer = CreateOffer(NULL);
@@ -1039,14 +1155,16 @@ TEST_F(WebRtcSessionTest, TestCreateOfferReceiveAnswer) {
 
 // Test receiving offers and creating answers and make sure the
 // media engine creates the expected send and receive streams.
-TEST_F(WebRtcSessionTest, TestReceiveOfferCreateAnswer) {
+TEST_F(WebRtcSessionTest, TestReceiveSdesOfferCreateSdesAnswer) {
   Init(NULL);
   mediastream_signaling_.SendAudioVideoStream2();
   SessionDescriptionInterface* offer = CreateOffer(NULL);
+  VerifyCryptoParams(offer->description());
   SetRemoteDescriptionWithoutError(offer);
 
   mediastream_signaling_.SendAudioVideoStream1();
   SessionDescriptionInterface* answer = CreateAnswer(NULL);
+  VerifyCryptoParams(answer->description());
   SetLocalDescriptionWithoutError(answer);
 
   const std::string session_id_orig = answer->session_id();
@@ -1093,9 +1211,53 @@ TEST_F(WebRtcSessionTest, TestReceiveOfferCreateAnswer) {
   EXPECT_EQ(0u, voice_channel_->send_streams().size());
 }
 
-// Test we will return fail when apply an offer that doesn't have
-// crypto enabled.
-TEST_F(WebRtcSessionTest, SetNonCryptoOffer) {
+TEST_F(WebRtcSessionTest, SetLocalSdpFailedOnCreateChannel) {
+  Init(NULL);
+  media_engine_->set_fail_create_channel(true);
+
+  SessionDescriptionInterface* offer = CreateOffer(NULL);
+  ASSERT_TRUE(offer != NULL);
+  // SetRemoteDescription and SetLocalDescription will take the ownership of
+  // the offer.
+  SetRemoteDescriptionOfferExpectError(kCreateChannelFailed, offer);
+  offer = CreateOffer(NULL);
+  ASSERT_TRUE(offer != NULL);
+  SetLocalDescriptionOfferExpectError(kCreateChannelFailed, offer);
+}
+
+//
+// Tests for creating/setting SDP under different SDES/DTLS polices:
+//
+// --DTLS off and SDES on
+// TestCreateSdesOfferReceiveSdesAnswer/TestReceiveSdesOfferCreateSdesAnswer:
+//     set local/remote offer/answer with crypto --> success
+// TestSetNonSdesOfferWhenSdesOn: set local/remote offer without crypto --->
+//     failure
+// TestSetLocalNonSdesAnswerWhenSdesOn: set local answer without crypto -->
+//     failure
+// TestSetRemoteNonSdesAnswerWhenSdesOn: set remote answer without crypto -->
+//     failure
+//
+// --DTLS on and SDES off
+// TestCreateDtlsOfferReceiveDtlsAnswer/TestReceiveDtlsOfferCreateDtlsAnswer:
+//     set local/remote offer/answer with DTLS fingerprint --> success
+// TestReceiveNonDtlsOfferWhenDtlsOn: set local/remote offer without DTLS
+//     fingerprint --> failure
+// TestSetLocalNonDtlsAnswerWhenDtlsOn: set local answer without fingerprint
+//     --> failure
+// TestSetRemoteNonDtlsAnswerWhenDtlsOn: set remote answer without fingerprint
+//     --> failure
+//
+// --Encryption disabled: DTLS off and SDES off
+// TestCreateOfferReceiveAnswerWithoutEncryption: set local offer and remote
+//     answer without SDES or DTLS --> success
+// TestCreateAnswerReceiveOfferWithoutEncryption: set remote offer and local
+//     answer without SDES or DTLS --> success
+//
+
+// Test that we return a failure when applying a remote/local offer that doesn't
+// have cryptos enabled when DTLS is off.
+TEST_F(WebRtcSessionTest, TestSetNonSdesOfferWhenSdesOn) {
   Init(NULL);
   cricket::MediaSessionOptions options;
   options.has_video = true;
@@ -1105,15 +1267,15 @@ TEST_F(WebRtcSessionTest, SetNonCryptoOffer) {
   VerifyNoCryptoParams(offer->description(), false);
   // SetRemoteDescription and SetLocalDescription will take the ownership of
   // the offer.
-  SetRemoteDescriptionExpectError(kSdpWithoutCrypto, offer);
+  SetRemoteDescriptionOfferExpectError(kSdpWithoutSdesCrypto, offer);
   offer = CreateRemoteOffer(options, cricket::SEC_DISABLED);
   ASSERT_TRUE(offer != NULL);
-  SetLocalDescriptionExpectError(kSdpWithoutCrypto, offer);
+  SetLocalDescriptionOfferExpectError(kSdpWithoutSdesCrypto, offer);
 }
 
-// Test we will return fail when apply an answer that doesn't have
-// crypto enabled.
-TEST_F(WebRtcSessionTest, SetLocalNonCryptoAnswer) {
+// Test that we return a failure when applying a local answer that doesn't have
+// cryptos enabled when DTLS is off.
+TEST_F(WebRtcSessionTest, TestSetLocalNonSdesAnswerWhenSdesOn) {
   Init(NULL);
   SessionDescriptionInterface* offer = NULL;
   SessionDescriptionInterface* answer = NULL;
@@ -1121,12 +1283,12 @@ TEST_F(WebRtcSessionTest, SetLocalNonCryptoAnswer) {
   // SetRemoteDescription and SetLocalDescription will take the ownership of
   // the offer.
   SetRemoteDescriptionWithoutError(offer);
-  SetLocalDescriptionExpectError(kSdpWithoutCrypto, answer);
+  SetLocalDescriptionAnswerExpectError(kSdpWithoutSdesCrypto, answer);
 }
 
-// Test we will return fail when apply an answer that doesn't have
-// crypto enabled.
-TEST_F(WebRtcSessionTest, SetRemoteNonCryptoAnswer) {
+// Test we will return fail when apply an remote answer that doesn't have
+// crypto enabled when DTLS is off.
+TEST_F(WebRtcSessionTest, TestSetRemoteNonSdesAnswerWhenSdesOn) {
   Init(NULL);
   SessionDescriptionInterface* offer = NULL;
   SessionDescriptionInterface* answer = NULL;
@@ -1134,32 +1296,23 @@ TEST_F(WebRtcSessionTest, SetRemoteNonCryptoAnswer) {
   // SetRemoteDescription and SetLocalDescription will take the ownership of
   // the offer.
   SetLocalDescriptionWithoutError(offer);
-  SetRemoteDescriptionExpectError(kSdpWithoutCrypto, answer);
+  SetRemoteDescriptionAnswerExpectError(kSdpWithoutSdesCrypto, answer);
 }
 
-// Test that we can create and set an offer with a DTLS fingerprint.
-TEST_F(WebRtcSessionTest, CreateSetDtlsOffer) {
+// Test that we accept an offer with a DTLS fingerprint when DTLS is on
+// and that we return an answer with a DTLS fingerprint.
+TEST_F(WebRtcSessionTest, TestReceiveDtlsOfferCreateDtlsAnswer) {
   MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
-  InitWithDtls();
   mediastream_signaling_.SendAudioVideoStream1();
-  SessionDescriptionInterface* offer = CreateOffer(NULL);
-  ASSERT_TRUE(offer != NULL);
-  VerifyFingerprintStatus(offer->description(), true);
-  // SetLocalDescription will take the ownership of the offer.
-  SetLocalDescriptionWithoutError(offer);
-}
-
-// Test that we can process an offer with a DTLS fingerprint
-// and that we return an answer with a fingerprint.
-TEST_F(WebRtcSessionTest, ReceiveDtlsOfferCreateAnswer) {
-  MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
   InitWithDtls();
   SetFactoryDtlsSrtp();
   cricket::MediaSessionOptions options;
   options.has_video = true;
-  JsepSessionDescription* offer = CreateRemoteOffer(options);
+  JsepSessionDescription* offer =
+      CreateRemoteOffer(options, cricket::SEC_DISABLED);
   ASSERT_TRUE(offer != NULL);
   VerifyFingerprintStatus(offer->description(), true);
+  VerifyNoCryptoParams(offer->description(), true);
 
   // SetRemoteDescription will take the ownership of the offer.
   SetRemoteDescriptionWithoutError(offer);
@@ -1175,28 +1328,148 @@ TEST_F(WebRtcSessionTest, ReceiveDtlsOfferCreateAnswer) {
   SetLocalDescriptionWithoutError(answer);
 }
 
-// Test that even if we support DTLS, if the other side didn't offer a
-// fingerprint, we don't either.
-TEST_F(WebRtcSessionTest, ReceiveNoDtlsOfferCreateAnswer) {
+// Test that we set a local offer with a DTLS fingerprint when DTLS is on
+// and then we accept a remote answer with a DTLS fingerprint successfully.
+TEST_F(WebRtcSessionTest, TestCreateDtlsOfferReceiveDtlsAnswer) {
   MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
+  mediastream_signaling_.SendAudioVideoStream1();
   InitWithDtls();
+  SetFactoryDtlsSrtp();
+
+  // Verify that we get a crypto fingerprint in the answer.
+  SessionDescriptionInterface* offer = CreateOffer(NULL);
+  ASSERT_TRUE(offer != NULL);
+  VerifyFingerprintStatus(offer->description(), true);
+  // Check that we don't have an a=crypto line in the offer.
+  VerifyNoCryptoParams(offer->description(), true);
+
+  // Now set the local description, which should work, even without a=crypto.
+  SetLocalDescriptionWithoutError(offer);
+
   cricket::MediaSessionOptions options;
   options.has_video = true;
+  JsepSessionDescription* answer =
+      CreateRemoteAnswer(offer, options, cricket::SEC_DISABLED);
+  ASSERT_TRUE(answer != NULL);
+  VerifyFingerprintStatus(answer->description(), true);
+  VerifyNoCryptoParams(answer->description(), true);
+
+  // SetRemoteDescription will take the ownership of the answer.
+  SetRemoteDescriptionWithoutError(answer);
+}
+
+// Test that if we support DTLS and the other side didn't offer a fingerprint,
+// we will fail to set the remote description.
+TEST_F(WebRtcSessionTest, TestReceiveNonDtlsOfferWhenDtlsOn) {
+  MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
+  InitWithDtls();
+  cricket::MediaSessionOptions options;
+  options.has_video = true;
+  options.bundle_enabled = true;
   JsepSessionDescription* offer = CreateRemoteOffer(
       options, cricket::SEC_REQUIRED);
   ASSERT_TRUE(offer != NULL);
   VerifyFingerprintStatus(offer->description(), false);
+  VerifyCryptoParams(offer->description());
 
-  // SetRemoteDescription will take the ownership of
-  // the offer.
+  // SetRemoteDescription will take the ownership of the offer.
+  SetRemoteDescriptionOfferExpectError(
+      kSdpWithoutDtlsFingerprint, offer);
+
+  offer = CreateRemoteOffer(options, cricket::SEC_REQUIRED);
+  // SetLocalDescription will take the ownership of the offer.
+  SetLocalDescriptionOfferExpectError(
+      kSdpWithoutDtlsFingerprint, offer);
+}
+
+// Test that we return a failure when applying a local answer that doesn't have
+// a DTLS fingerprint when DTLS is required.
+TEST_F(WebRtcSessionTest, TestSetLocalNonDtlsAnswerWhenDtlsOn) {
+  MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
+  InitWithDtls();
+  SessionDescriptionInterface* offer = NULL;
+  SessionDescriptionInterface* answer = NULL;
+  CreateDtlsOfferAndNonDtlsAnswer(&offer, &answer);
+
+  // SetRemoteDescription and SetLocalDescription will take the ownership of
+  // the offer and answer.
   SetRemoteDescriptionWithoutError(offer);
+  SetLocalDescriptionAnswerExpectError(
+      kSdpWithoutDtlsFingerprint, answer);
+}
+
+// Test that we return a failure when applying a remote answer that doesn't have
+// a DTLS fingerprint when DTLS is required.
+TEST_F(WebRtcSessionTest, TestSetRemoteNonDtlsAnswerWhenDtlsOn) {
+  MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
+  InitWithDtls();
+  SessionDescriptionInterface* offer = CreateOffer(NULL);
+  cricket::MediaSessionOptions options;
+  options.has_video = true;
+  JsepSessionDescription* answer =
+      CreateRemoteAnswer(offer, options, cricket::SEC_ENABLED);
 
-  // Verify that we don't get a crypto fingerprint in the answer.
+  // SetRemoteDescription and SetLocalDescription will take the ownership of
+  // the offer and answer.
+  SetLocalDescriptionWithoutError(offer);
+  SetRemoteDescriptionAnswerExpectError(
+      kSdpWithoutDtlsFingerprint, answer);
+}
+
+// Test that we create a local offer without SDES or DTLS and accept a remote
+// answer without SDES or DTLS when encryption is disabled.
+TEST_F(WebRtcSessionTest, TestCreateOfferReceiveAnswerWithoutEncryption) {
+  mediastream_signaling_.SendAudioVideoStream1();
+  options_.disable_encryption = true;
+  InitWithDtls();
+
+  // Verify that we get a crypto fingerprint in the answer.
+  SessionDescriptionInterface* offer = CreateOffer(NULL);
+  ASSERT_TRUE(offer != NULL);
+  VerifyFingerprintStatus(offer->description(), false);
+  // Check that we don't have an a=crypto line in the offer.
+  VerifyNoCryptoParams(offer->description(), false);
+
+  // Now set the local description, which should work, even without a=crypto.
+  SetLocalDescriptionWithoutError(offer);
+
+  cricket::MediaSessionOptions options;
+  options.has_video = true;
+  JsepSessionDescription* answer =
+      CreateRemoteAnswer(offer, options, cricket::SEC_DISABLED);
+  ASSERT_TRUE(answer != NULL);
+  VerifyFingerprintStatus(answer->description(), false);
+  VerifyNoCryptoParams(answer->description(), false);
+
+  // SetRemoteDescription will take the ownership of the answer.
+  SetRemoteDescriptionWithoutError(answer);
+}
+
+// Test that we create a local answer without SDES or DTLS and accept a remote
+// offer without SDES or DTLS when encryption is disabled.
+TEST_F(WebRtcSessionTest, TestCreateAnswerReceiveOfferWithoutEncryption) {
+  options_.disable_encryption = true;
+  InitWithDtls();
+
+  cricket::MediaSessionOptions options;
+  options.has_video = true;
+  JsepSessionDescription* offer =
+      CreateRemoteOffer(options, cricket::SEC_DISABLED);
+  ASSERT_TRUE(offer != NULL);
+  VerifyFingerprintStatus(offer->description(), false);
+  VerifyNoCryptoParams(offer->description(), false);
+
+  // SetRemoteDescription will take the ownership of the offer.
+  SetRemoteDescriptionWithoutError(offer);
+
+  // Verify that we get a crypto fingerprint in the answer.
   SessionDescriptionInterface* answer = CreateAnswer(NULL);
   ASSERT_TRUE(answer != NULL);
   VerifyFingerprintStatus(answer->description(), false);
+  // Check that we don't have an a=crypto line in the answer.
+  VerifyNoCryptoParams(answer->description(), false);
 
-  // Now set the local description.
+  // Now set the local description, which should work, even without a=crypto.
   SetLocalDescriptionWithoutError(answer);
 }
 
@@ -1229,9 +1502,8 @@ TEST_F(WebRtcSessionTest, TestSetLocalAndRemoteOffer) {
   SessionDescriptionInterface* offer = CreateOffer(NULL);
   SetLocalDescriptionWithoutError(offer);
   offer = CreateOffer(NULL);
-  SetRemoteDescriptionExpectError(
-      "Called with type in wrong state, type: offer state: STATE_SENTINITIATE",
-      offer);
+  SetRemoteDescriptionOfferExpectError(
+      "Called in wrong state: STATE_SENTINITIATE", offer);
 }
 
 TEST_F(WebRtcSessionTest, TestSetRemoteAndLocalOffer) {
@@ -1240,10 +1512,8 @@ TEST_F(WebRtcSessionTest, TestSetRemoteAndLocalOffer) {
   SessionDescriptionInterface* offer = CreateOffer(NULL);
   SetRemoteDescriptionWithoutError(offer);
   offer = CreateOffer(NULL);
-  SetLocalDescriptionExpectError(
-      "Called with type in wrong state, type: "
-      "offer state: STATE_RECEIVEDINITIATE",
-      offer);
+  SetLocalDescriptionOfferExpectError(
+      "Called in wrong state: STATE_RECEIVEDINITIATE", offer);
 }
 
 TEST_F(WebRtcSessionTest, TestSetLocalPrAnswer) {
@@ -1303,9 +1573,8 @@ TEST_F(WebRtcSessionTest, TestSetLocalAnswerWithoutOffer) {
       CreateOffer(NULL));
   SessionDescriptionInterface* answer =
       CreateRemoteAnswer(offer.get());
-  SetLocalDescriptionExpectError(
-      "Called with type in wrong state, type: answer state: STATE_INIT",
-      answer);
+  SetLocalDescriptionAnswerExpectError("Called in wrong state: STATE_INIT",
+                                       answer);
 }
 
 TEST_F(WebRtcSessionTest, TestSetRemoteAnswerWithoutOffer) {
@@ -1315,9 +1584,8 @@ TEST_F(WebRtcSessionTest, TestSetRemoteAnswerWithoutOffer) {
         CreateOffer(NULL));
   SessionDescriptionInterface* answer =
       CreateRemoteAnswer(offer.get());
-  SetRemoteDescriptionExpectError(
-      "Called with type in wrong state, type: answer state: STATE_INIT",
-      answer);
+  SetRemoteDescriptionAnswerExpectError(
+      "Called in wrong state: STATE_INIT", answer);
 }
 
 TEST_F(WebRtcSessionTest, TestAddRemoteCandidate) {
@@ -1356,10 +1624,12 @@ TEST_F(WebRtcSessionTest, TestAddRemoteCandidate) {
   EXPECT_EQ(1, candidates->at(0)->candidate().component());
   EXPECT_EQ(2, candidates->at(1)->candidate().component());
 
+  // |ice_candidate3| is identical to |ice_candidate2|.  It can be added
+  // successfully, but the total count of candidates will not increase.
   candidate.set_component(2);
   JsepIceCandidate ice_candidate3(kMediaContentName0, 0, candidate);
   EXPECT_TRUE(session_->ProcessIceMessage(&ice_candidate3));
-  ASSERT_EQ(3u, candidates->count());
+  ASSERT_EQ(2u, candidates->count());
 
   JsepIceCandidate bad_ice_candidate("bad content name", 99, candidate);
   EXPECT_FALSE(session_->ProcessIceMessage(&bad_ice_candidate));
@@ -1420,7 +1690,7 @@ TEST_F(WebRtcSessionTest, TestRemoteCandidatesAddedToSessionDescription) {
 // Test that local candidates are added to the local session description and
 // that they are retained if the local session description is changed.
 TEST_F(WebRtcSessionTest, TestLocalCandidatesAddedToSessionDescription) {
-  AddInterface(kClientAddr1);
+  AddInterface(talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort));
   Init(NULL);
   mediastream_signaling_.SendAudioVideoStream1();
   CreateAndSetRemoteOfferAndLocalAnswer();
@@ -1484,7 +1754,7 @@ TEST_F(WebRtcSessionTest, TestSetRemoteSessionDescriptionWithCandidates) {
 // Test that offers and answers contains ice candidates when Ice candidates have
 // been gathered.
 TEST_F(WebRtcSessionTest, TestSetLocalAndRemoteDescriptionWithCandidates) {
-  AddInterface(kClientAddr1);
+  AddInterface(talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort));
   Init(NULL);
   mediastream_signaling_.SendAudioVideoStream1();
   // Ice is started but candidates are not provided until SetLocalDescription
@@ -1963,7 +2233,7 @@ TEST_F(WebRtcSessionTest, TestSetLocalDescriptionWithoutIce) {
   RemoveIceUfragPwdLines(offer.get(), &sdp);
   SessionDescriptionInterface* modified_offer =
     CreateSessionDescription(JsepSessionDescription::kOffer, sdp, NULL);
-  SetLocalDescriptionExpectError(kSdpWithoutIceUfragPwd, modified_offer);
+  SetLocalDescriptionOfferExpectError(kSdpWithoutIceUfragPwd, modified_offer);
 }
 
 // This test verifies that setRemoteDescription fails if
@@ -1975,7 +2245,7 @@ TEST_F(WebRtcSessionTest, TestSetRemoteDescriptionWithoutIce) {
   RemoveIceUfragPwdLines(offer.get(), &sdp);
   SessionDescriptionInterface* modified_offer =
     CreateSessionDescription(JsepSessionDescription::kOffer, sdp, NULL);
-  SetRemoteDescriptionExpectError(kSdpWithoutIceUfragPwd, modified_offer);
+  SetRemoteDescriptionOfferExpectError(kSdpWithoutIceUfragPwd, modified_offer);
 }
 
 TEST_F(WebRtcSessionTest, VerifyBundleFlagInPA) {
@@ -1983,8 +2253,8 @@ TEST_F(WebRtcSessionTest, VerifyBundleFlagInPA) {
   // local description is removed by the application, BUNDLE flag should be
   // disabled in PortAllocator. By default BUNDLE is enabled in the WebRtc.
   Init(NULL);
-  EXPECT_TRUE((cricket::PORTALLOCATOR_ENABLE_BUNDLE & allocator_.flags()) ==
-      cricket::PORTALLOCATOR_ENABLE_BUNDLE);
+  EXPECT_TRUE((cricket::PORTALLOCATOR_ENABLE_BUNDLE &
+      allocator_->flags()) == cricket::PORTALLOCATOR_ENABLE_BUNDLE);
   talk_base::scoped_ptr<SessionDescriptionInterface> offer(
       CreateOffer(NULL));
   cricket::SessionDescription* offer_copy =
@@ -1995,14 +2265,14 @@ TEST_F(WebRtcSessionTest, VerifyBundleFlagInPA) {
   modified_offer->Initialize(offer_copy, "1", "1");
 
   SetLocalDescriptionWithoutError(modified_offer);
-  EXPECT_FALSE(allocator_.flags() & cricket::PORTALLOCATOR_ENABLE_BUNDLE);
+  EXPECT_FALSE(allocator_->flags() & cricket::PORTALLOCATOR_ENABLE_BUNDLE);
 }
 
 TEST_F(WebRtcSessionTest, TestDisabledBundleInAnswer) {
   Init(NULL);
   mediastream_signaling_.SendAudioVideoStream1();
-  EXPECT_TRUE((cricket::PORTALLOCATOR_ENABLE_BUNDLE & allocator_.flags()) ==
-      cricket::PORTALLOCATOR_ENABLE_BUNDLE);
+  EXPECT_TRUE((cricket::PORTALLOCATOR_ENABLE_BUNDLE &
+      allocator_->flags()) == cricket::PORTALLOCATOR_ENABLE_BUNDLE);
   FakeConstraints constraints;
   constraints.SetMandatoryUseRtpMux(true);
   SessionDescriptionInterface* offer = CreateOffer(&constraints);
@@ -2016,8 +2286,8 @@ TEST_F(WebRtcSessionTest, TestDisabledBundleInAnswer) {
       new JsepSessionDescription(JsepSessionDescription::kAnswer);
   modified_answer->Initialize(answer_copy, "1", "1");
   SetRemoteDescriptionWithoutError(modified_answer);
-  EXPECT_TRUE((cricket::PORTALLOCATOR_ENABLE_BUNDLE & allocator_.flags()) ==
-      cricket::PORTALLOCATOR_ENABLE_BUNDLE);
+  EXPECT_TRUE((cricket::PORTALLOCATOR_ENABLE_BUNDLE &
+      allocator_->flags()) == cricket::PORTALLOCATOR_ENABLE_BUNDLE);
 
   video_channel_ = media_engine_->GetVideoChannel(0);
   voice_channel_ = media_engine_->GetVoiceChannel(0);
@@ -2039,8 +2309,8 @@ TEST_F(WebRtcSessionTest, TestDisabledBundleInAnswer) {
 TEST_F(WebRtcSessionTest, TestDisabledRtcpMuxWithBundleEnabled) {
   WebRtcSessionTest::Init(NULL);
   mediastream_signaling_.SendAudioVideoStream1();
-  EXPECT_TRUE((cricket::PORTALLOCATOR_ENABLE_BUNDLE & allocator_.flags()) ==
-      cricket::PORTALLOCATOR_ENABLE_BUNDLE);
+  EXPECT_TRUE((cricket::PORTALLOCATOR_ENABLE_BUNDLE &
+      allocator_->flags()) == cricket::PORTALLOCATOR_ENABLE_BUNDLE);
   FakeConstraints constraints;
   constraints.SetMandatoryUseRtpMux(true);
   SessionDescriptionInterface* offer = CreateOffer(&constraints);
@@ -2055,11 +2325,11 @@ TEST_F(WebRtcSessionTest, TestDisabledRtcpMuxWithBundleEnabled) {
   JsepSessionDescription *local_offer =
       new JsepSessionDescription(JsepSessionDescription::kOffer);
   EXPECT_TRUE((local_offer)->Initialize(offer_str, NULL));
-  SetLocalDescriptionExpectError(kBundleWithoutRtcpMux, local_offer);
+  SetLocalDescriptionOfferExpectError(kBundleWithoutRtcpMux, local_offer);
   JsepSessionDescription *remote_offer =
       new JsepSessionDescription(JsepSessionDescription::kOffer);
   EXPECT_TRUE((remote_offer)->Initialize(offer_str, NULL));
-  SetRemoteDescriptionExpectError(kBundleWithoutRtcpMux, remote_offer);
+  SetRemoteDescriptionOfferExpectError(kBundleWithoutRtcpMux, remote_offer);
   // Trying unmodified SDP.
   SetLocalDescriptionWithoutError(offer);
 }
@@ -2107,13 +2377,39 @@ TEST_F(WebRtcSessionTest, SetAudioSend) {
   EXPECT_TRUE(channel->IsStreamMuted(send_ssrc));
   EXPECT_FALSE(channel->options().echo_cancellation.IsSet());
   EXPECT_EQ(0, renderer->channel_id());
+  EXPECT_TRUE(renderer->sink() != NULL);
 
+  // This will trigger SetSink(NULL) to the |renderer|.
   session_->SetAudioSend(send_ssrc, true, options, NULL);
   EXPECT_FALSE(channel->IsStreamMuted(send_ssrc));
   bool value;
   EXPECT_TRUE(channel->options().echo_cancellation.Get(&value));
   EXPECT_TRUE(value);
   EXPECT_EQ(-1, renderer->channel_id());
+  EXPECT_TRUE(renderer->sink() == NULL);
+}
+
+TEST_F(WebRtcSessionTest, AudioRendererForLocalStream) {
+  Init(NULL);
+  mediastream_signaling_.SendAudioVideoStream1();
+  CreateAndSetRemoteOfferAndLocalAnswer();
+  cricket::FakeVoiceMediaChannel* channel = media_engine_->GetVoiceChannel(0);
+  ASSERT_TRUE(channel != NULL);
+  ASSERT_EQ(1u, channel->send_streams().size());
+  uint32 send_ssrc  = channel->send_streams()[0].first_ssrc();
+
+  talk_base::scoped_ptr<FakeAudioRenderer> renderer(new FakeAudioRenderer());
+  cricket::AudioOptions options;
+  session_->SetAudioSend(send_ssrc, true, options, renderer.get());
+  EXPECT_TRUE(renderer->sink() != NULL);
+
+  // Delete the |renderer| and it will trigger OnClose() to the sink, and this
+  // will invalidate the |renderer_| pointer in the sink and prevent getting a
+  // SetSink(NULL) callback afterwards.
+  renderer.reset();
+
+  // This will trigger SetSink(NULL) if no OnClose() callback.
+  session_->SetAudioSend(send_ssrc, true, options, NULL);
 }
 
 TEST_F(WebRtcSessionTest, SetVideoPlayout) {
@@ -2303,13 +2599,13 @@ TEST_F(WebRtcSessionTest, TestIceOfferGIceOnlyAnswer) {
   SessionDescriptionInterface* pranswer_with_gice =
       CreateSessionDescription(JsepSessionDescription::kPrAnswer,
                                original_offer_sdp, NULL);
-  SetRemoteDescriptionExpectError(kPushDownPranswerTDFailed,
-                                  pranswer_with_gice);
+  SetRemoteDescriptionPranswerExpectError(kPushDownTDFailed,
+                                          pranswer_with_gice);
   SessionDescriptionInterface* answer_with_gice =
       CreateSessionDescription(JsepSessionDescription::kAnswer,
                                original_offer_sdp, NULL);
-  SetRemoteDescriptionExpectError(kPushDownAnswerTDFailed,
-                                  answer_with_gice);
+  SetRemoteDescriptionAnswerExpectError(kPushDownTDFailed,
+                                        answer_with_gice);
 }
 
 // Verifing local offer and remote answer have matching m-lines as per RFC 3264.
@@ -2329,23 +2625,32 @@ TEST_F(WebRtcSessionTest, TestIncorrectMLinesInRemoteAnswer) {
   EXPECT_TRUE(modified_answer->Initialize(answer_copy,
                                           answer->session_id(),
                                           answer->session_version()));
-  SetRemoteDescriptionExpectError(kMlineMismatch, modified_answer);
+  SetRemoteDescriptionAnswerExpectError(kMlineMismatch, modified_answer);
 
-  // Modifying content names.
+  // Different content names.
   std::string sdp;
   EXPECT_TRUE(answer->ToString(&sdp));
   const std::string kAudioMid = "a=mid:audio";
   const std::string kAudioMidReplaceStr = "a=mid:audio_content_name";
-
-  // Replacing |audio| with |audio_content_name|.
   talk_base::replace_substrs(kAudioMid.c_str(), kAudioMid.length(),
                              kAudioMidReplaceStr.c_str(),
                              kAudioMidReplaceStr.length(),
                              &sdp);
-
   SessionDescriptionInterface* modified_answer1 =
       CreateSessionDescription(JsepSessionDescription::kAnswer, sdp, NULL);
-  SetRemoteDescriptionExpectError(kMlineMismatch, modified_answer1);
+  SetRemoteDescriptionAnswerExpectError(kMlineMismatch, modified_answer1);
+
+  // Different media types.
+  EXPECT_TRUE(answer->ToString(&sdp));
+  const std::string kAudioMline = "m=audio";
+  const std::string kAudioMlineReplaceStr = "m=video";
+  talk_base::replace_substrs(kAudioMline.c_str(), kAudioMline.length(),
+                             kAudioMlineReplaceStr.c_str(),
+                             kAudioMlineReplaceStr.length(),
+                             &sdp);
+  SessionDescriptionInterface* modified_answer2 =
+      CreateSessionDescription(JsepSessionDescription::kAnswer, sdp, NULL);
+  SetRemoteDescriptionAnswerExpectError(kMlineMismatch, modified_answer2);
 
   SetRemoteDescriptionWithoutError(answer.release());
 }
@@ -2367,7 +2672,7 @@ TEST_F(WebRtcSessionTest, TestIncorrectMLinesInLocalAnswer) {
   EXPECT_TRUE(modified_answer->Initialize(answer_copy,
                                           answer->session_id(),
                                           answer->session_version()));
-  SetLocalDescriptionExpectError(kMlineMismatch, modified_answer);
+  SetLocalDescriptionAnswerExpectError(kMlineMismatch, modified_answer);
   SetLocalDescriptionWithoutError(answer);
 }
 
@@ -2521,33 +2826,49 @@ TEST_F(WebRtcSessionTest, TestSessionContentError) {
   mediastream_signaling_.SendAudioVideoStream2();
   SessionDescriptionInterface* answer =
       CreateRemoteAnswer(session_->local_description());
-  SetRemoteDescriptionExpectError("ERROR_CONTENT", answer);
+  SetRemoteDescriptionAnswerExpectError("ERROR_CONTENT", answer);
 }
 
 // Runs the loopback call test with BUNDLE and STUN disabled.
 TEST_F(WebRtcSessionTest, TestIceStatesBasic) {
   // Lets try with only UDP ports.
-  allocator_.set_flags(cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG |
+  allocator_->set_flags(cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG |
                        cricket::PORTALLOCATOR_DISABLE_TCP |
                        cricket::PORTALLOCATOR_DISABLE_STUN |
                        cricket::PORTALLOCATOR_DISABLE_RELAY);
   TestLoopbackCall();
 }
 
-// Regression-test for a crash which should have been an error.
-TEST_F(WebRtcSessionTest, TestNoStateTransitionPendingError) {
+// Runs the loopback call test with BUNDLE and STUN enabled.
+TEST_F(WebRtcSessionTest, TestIceStatesBundle) {
+  allocator_->set_flags(cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG |
+                       cricket::PORTALLOCATOR_ENABLE_BUNDLE |
+                       cricket::PORTALLOCATOR_DISABLE_TCP |
+                       cricket::PORTALLOCATOR_DISABLE_RELAY);
+  TestLoopbackCall();
+}
+
+TEST_F(WebRtcSessionTest, SetSdpFailedOnSessionError) {
   Init(NULL);
   cricket::MediaSessionOptions options;
   options.has_audio = true;
   options.has_video = true;
 
-  session_->SetError(cricket::BaseSession::ERROR_CONTENT);
+  cricket::BaseSession::Error error_code = cricket::BaseSession::ERROR_CONTENT;
+  std::string error_code_str = "ERROR_CONTENT";
+  std::string error_desc = "Fake session error description.";
+  session_->SetError(error_code, error_desc);
+
   SessionDescriptionInterface* offer = CreateRemoteOffer(options);
   SessionDescriptionInterface* answer =
       CreateRemoteAnswer(offer, options);
-  SetRemoteDescriptionExpectError(kSessionError, offer);
-  SetLocalDescriptionExpectError(kSessionError, answer);
-  // Not crashing is our success.
+
+  std::string action;
+  std::ostringstream session_error_msg;
+  session_error_msg << kSessionError << error_code_str << ". ";
+  session_error_msg << kSessionErrorDesc << error_desc << ".";
+  SetRemoteDescriptionExpectError(action, session_error_msg.str(), offer);
+  SetLocalDescriptionExpectError(action, session_error_msg.str(), answer);
 }
 
 TEST_F(WebRtcSessionTest, TestRtpDataChannel) {
@@ -2568,7 +2889,7 @@ TEST_F(WebRtcSessionTest, TestRtpDataChannelConstraintTakesPrecedence) {
       webrtc::MediaConstraintsInterface::kEnableRtpDataChannels, true);
   options_.disable_sctp_data_channels = false;
 
-  InitWithDtls(false);
+  InitWithDtls();
 
   SetLocalDescriptionWithDataChannel();
   EXPECT_EQ(cricket::DCT_RTP, data_engine_->last_channel_type());
@@ -2577,7 +2898,7 @@ TEST_F(WebRtcSessionTest, TestRtpDataChannelConstraintTakesPrecedence) {
 TEST_F(WebRtcSessionTest, TestCreateOfferWithSctpEnabledWithoutStreams) {
   MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
 
-  InitWithDtls(false);
+  InitWithDtls();
 
   talk_base::scoped_ptr<SessionDescriptionInterface> offer(CreateOffer(NULL));
   EXPECT_TRUE(offer->description()->GetContentByName("data") == NULL);
@@ -2587,13 +2908,13 @@ TEST_F(WebRtcSessionTest, TestCreateOfferWithSctpEnabledWithoutStreams) {
 TEST_F(WebRtcSessionTest, TestCreateAnswerWithSctpInOfferAndNoStreams) {
   MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
   SetFactoryDtlsSrtp();
-  InitWithDtls(false);
+  InitWithDtls();
 
   // Create remote offer with SCTP.
   cricket::MediaSessionOptions options;
   options.data_channel_type = cricket::DCT_SCTP;
   JsepSessionDescription* offer =
-      CreateRemoteOffer(options, cricket::SEC_ENABLED);
+      CreateRemoteOffer(options, cricket::SEC_DISABLED);
   SetRemoteDescriptionWithoutError(offer);
 
   // Verifies the answer contains SCTP.
@@ -2607,7 +2928,7 @@ TEST_F(WebRtcSessionTest, TestSctpDataChannelWithoutDtls) {
   constraints_.reset(new FakeConstraints());
   constraints_->AddOptional(
       webrtc::MediaConstraintsInterface::kEnableDtlsSrtp, false);
-  InitWithDtls(false);
+  InitWithDtls();
 
   SetLocalDescriptionWithDataChannel();
   EXPECT_EQ(cricket::DCT_NONE, data_engine_->last_channel_type());
@@ -2616,7 +2937,7 @@ TEST_F(WebRtcSessionTest, TestSctpDataChannelWithoutDtls) {
 TEST_F(WebRtcSessionTest, TestSctpDataChannelWithDtls) {
   MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
 
-  InitWithDtls(false);
+  InitWithDtls();
 
   SetLocalDescriptionWithDataChannel();
   EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type());
@@ -2625,7 +2946,7 @@ TEST_F(WebRtcSessionTest, TestSctpDataChannelWithDtls) {
 TEST_F(WebRtcSessionTest, TestDisableSctpDataChannels) {
   MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
   options_.disable_sctp_data_channels = true;
-  InitWithDtls(false);
+  InitWithDtls();
 
   SetLocalDescriptionWithDataChannel();
   EXPECT_EQ(cricket::DCT_NONE, data_engine_->last_channel_type());
@@ -2636,7 +2957,7 @@ TEST_F(WebRtcSessionTest, TestSctpDataChannelSendPortParsing) {
   const int new_send_port = 9998;
   const int new_recv_port = 7775;
 
-  InitWithDtls(false);
+  InitWithDtls();
   SetFactoryDtlsSrtp();
 
   // By default, don't actually add the codecs to desc_factory_; they don't
@@ -2659,7 +2980,7 @@ TEST_F(WebRtcSessionTest, TestSctpDataChannelSendPortParsing) {
 
   // TEST PLAN: Set the port number to something new, set it in the SDP,
   // and pass it all the way down.
-  webrtc::DataChannelInit dci;
+  webrtc::InternalDataChannelInit dci;
   dci.reliable = true;
   EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type());
   talk_base::scoped_refptr<webrtc::DataChannel> dc =
@@ -2689,34 +3010,41 @@ TEST_F(WebRtcSessionTest, TestSctpDataChannelSendPortParsing) {
 // identity generation is finished.
 TEST_F(WebRtcSessionTest, TestCreateOfferBeforeIdentityRequestReturnSuccess) {
   MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
-  InitWithDtls(false);
+  InitWithDtls();
 
   EXPECT_TRUE(session_->waiting_for_identity());
+  mediastream_signaling_.SendAudioVideoStream1();
   talk_base::scoped_ptr<SessionDescriptionInterface> offer(CreateOffer(NULL));
   EXPECT_TRUE(offer != NULL);
+  VerifyNoCryptoParams(offer->description(), true);
+  VerifyFingerprintStatus(offer->description(), true);
 }
 
 // Verifies that CreateAnswer succeeds when CreateOffer is called before async
 // identity generation is finished.
 TEST_F(WebRtcSessionTest, TestCreateAnswerBeforeIdentityRequestReturnSuccess) {
   MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
-  InitWithDtls(false);
+  InitWithDtls();
+  SetFactoryDtlsSrtp();
 
   cricket::MediaSessionOptions options;
+  options.has_video = true;
   scoped_ptr<JsepSessionDescription> offer(
-        CreateRemoteOffer(options, cricket::SEC_REQUIRED));
+        CreateRemoteOffer(options, cricket::SEC_DISABLED));
   ASSERT_TRUE(offer.get() != NULL);
   SetRemoteDescriptionWithoutError(offer.release());
 
   talk_base::scoped_ptr<SessionDescriptionInterface> answer(CreateAnswer(NULL));
   EXPECT_TRUE(answer != NULL);
+  VerifyNoCryptoParams(answer->description(), true);
+  VerifyFingerprintStatus(answer->description(), true);
 }
 
 // Verifies that CreateOffer succeeds when CreateOffer is called after async
 // identity generation is finished.
 TEST_F(WebRtcSessionTest, TestCreateOfferAfterIdentityRequestReturnSuccess) {
   MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
-  InitWithDtls(false);
+  InitWithDtls();
 
   EXPECT_TRUE_WAIT(!session_->waiting_for_identity(), 1000);
   talk_base::scoped_ptr<SessionDescriptionInterface> offer(CreateOffer(NULL));
@@ -2787,8 +3115,8 @@ TEST_F(WebRtcSessionTest, TestSetRemoteOfferFailIfDtlsDisabledAndNoCrypto) {
   audio->description.identity_fingerprint.reset(
       talk_base::SSLFingerprint::CreateFromRfc4572(
           talk_base::DIGEST_SHA_256, kFakeDtlsFingerprint));
-  SetRemoteDescriptionExpectError(kSdpWithoutSdesAndDtlsDisabled,
-                                  offer);
+  SetRemoteDescriptionOfferExpectError(kSdpWithoutSdesCrypto,
+                                       offer);
 }
 
 // This test verifies DSCP is properly applied on the media channels.
@@ -2817,6 +3145,26 @@ TEST_F(WebRtcSessionTest, TestDscpConstraint) {
   EXPECT_TRUE(video_options.dscp.GetWithDefaultIfUnset(false));
 }
 
+TEST_F(WebRtcSessionTest, TestSuspendBelowMinBitrateConstraint) {
+  constraints_.reset(new FakeConstraints());
+  constraints_->AddOptional(
+      webrtc::MediaConstraintsInterface::kEnableVideoSuspendBelowMinBitrate,
+      true);
+  Init(NULL);
+  mediastream_signaling_.SendAudioVideoStream1();
+  SessionDescriptionInterface* offer = CreateOffer(NULL);
+
+  SetLocalDescriptionWithoutError(offer);
+
+  video_channel_ = media_engine_->GetVideoChannel(0);
+
+  ASSERT_TRUE(video_channel_ != NULL);
+  cricket::VideoOptions video_options;
+  EXPECT_TRUE(video_channel_->GetOptions(&video_options));
+  EXPECT_TRUE(
+      video_options.suspend_below_min_bitrate.GetWithDefaultIfUnset(false));
+}
+
 // TODO(bemasc): Add a TestIceStatesBundle with BUNDLE enabled.  That test
 // currently fails because upon disconnection and reconnection OnIceComplete is
 // called more than once without returning to IceGatheringGathering.