michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "base/basictypes.h" michael@0: #include "logging.h" michael@0: michael@0: #define GTEST_HAS_RTTI 0 michael@0: #include "gtest/gtest.h" michael@0: #include "gtest_utils.h" michael@0: michael@0: #include "nspr.h" michael@0: #include "nss.h" michael@0: #include "ssl.h" michael@0: #include "prthread.h" michael@0: michael@0: #include "cpr_stdlib.h" michael@0: #include "FakePCObserver.h" michael@0: #include "FakeMediaStreams.h" michael@0: #include "FakeMediaStreamsImpl.h" michael@0: #include "PeerConnectionImpl.h" michael@0: #include "PeerConnectionCtx.h" michael@0: #include "PeerConnectionMedia.h" michael@0: #include "MediaPipeline.h" michael@0: #include "runnable_utils.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "mozilla/Services.h" michael@0: #include "nsIPrefService.h" michael@0: #include "nsIPrefBranch.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsIIOService.h" michael@0: #include "nsIDNSService.h" michael@0: #include "nsWeakReference.h" michael@0: #include "nricectx.h" michael@0: #include "rlogringbuffer.h" michael@0: #include "mozilla/SyncRunnable.h" michael@0: #include "logging.h" michael@0: #include "stunserver.h" michael@0: #include "stunserver.cpp" michael@0: #include "PeerConnectionImplEnumsBinding.cpp" michael@0: michael@0: #include "mtransport_test_utils.h" michael@0: #include "gtest_ringbuffer_dumper.h" michael@0: MtransportTestUtils *test_utils; michael@0: nsCOMPtr gMainThread; michael@0: nsCOMPtr gGtestThread; michael@0: bool gTestsComplete = false; michael@0: michael@0: #ifndef USE_FAKE_MEDIA_STREAMS michael@0: #error USE_FAKE_MEDIA_STREAMS undefined michael@0: #endif michael@0: #ifndef USE_FAKE_PCOBSERVER michael@0: #error USE_FAKE_PCOBSERVER undefined michael@0: #endif michael@0: michael@0: static int kDefaultTimeout = 7000; michael@0: static bool fRtcpMux = true; michael@0: michael@0: static std::string callerName = "caller"; michael@0: static std::string calleeName = "callee"; michael@0: michael@0: #define ARRAY_TO_STL(container, type, array) \ michael@0: (container((array), (array) + PR_ARRAY_SIZE(array))) michael@0: michael@0: #define ARRAY_TO_SET(type, array) ARRAY_TO_STL(std::set, type, array) michael@0: michael@0: std::string g_stun_server_address((char *)"23.21.150.121"); michael@0: uint16_t g_stun_server_port(3478); michael@0: std::string kBogusSrflxAddress((char *)"192.0.2.1"); michael@0: uint16_t kBogusSrflxPort(1001); michael@0: michael@0: namespace sipcc { michael@0: michael@0: // We can't use mozilla/dom/MediaConstraintsBinding.h here because it uses michael@0: // nsString, so we pass constraints in using MediaConstraintsExternal instead michael@0: michael@0: class MediaConstraints : public MediaConstraintsExternal { michael@0: public: michael@0: void setBooleanConstraint(const char *namePtr, bool value, bool mandatory) { michael@0: cc_boolean_constraint_t &member (getMember(namePtr)); michael@0: member.was_passed = true; michael@0: member.value = value; michael@0: member.mandatory = mandatory; michael@0: } michael@0: private: michael@0: cc_boolean_constraint_t &getMember(const char *namePtr) { michael@0: if (strcmp(namePtr, "OfferToReceiveAudio") == 0) { michael@0: return mConstraints.offer_to_receive_audio; michael@0: } michael@0: if (strcmp(namePtr, "OfferToReceiveVideo") == 0) { michael@0: return mConstraints.offer_to_receive_video; michael@0: } michael@0: MOZ_ASSERT(false); michael@0: return mConstraints.moz_dont_offer_datachannel; michael@0: } michael@0: }; michael@0: } michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: namespace test { michael@0: std::string indent(const std::string &s, int width = 4) { michael@0: std::string prefix; michael@0: std::string out; michael@0: char previous = '\n'; michael@0: prefix.assign(width, ' '); michael@0: for (std::string::const_iterator i = s.begin(); i != s.end(); i++) { michael@0: if (previous == '\n') { michael@0: out += prefix; michael@0: } michael@0: out += *i; michael@0: previous = *i; michael@0: } michael@0: return out; michael@0: } michael@0: michael@0: static const std::string strSampleSdpAudioVideoNoIce = michael@0: "v=0\r\n" michael@0: "o=Mozilla-SIPUA 4949 0 IN IP4 10.86.255.143\r\n" michael@0: "s=SIP Call\r\n" michael@0: "t=0 0\r\n" michael@0: "a=ice-ufrag:qkEP\r\n" michael@0: "a=ice-pwd:ed6f9GuHjLcoCN6sC/Eh7fVl\r\n" michael@0: "m=audio 16384 RTP/AVP 0 8 9 101\r\n" michael@0: "c=IN IP4 10.86.255.143\r\n" michael@0: "a=rtpmap:0 PCMU/8000\r\n" michael@0: "a=rtpmap:8 PCMA/8000\r\n" michael@0: "a=rtpmap:9 G722/8000\r\n" michael@0: "a=rtpmap:101 telephone-event/8000\r\n" michael@0: "a=fmtp:101 0-15\r\n" michael@0: "a=sendrecv\r\n" michael@0: "a=candidate:1 1 UDP 2130706431 192.168.2.1 50005 typ host\r\n" michael@0: "a=candidate:2 2 UDP 2130706431 192.168.2.2 50006 typ host\r\n" michael@0: "m=video 1024 RTP/AVP 97\r\n" michael@0: "c=IN IP4 10.86.255.143\r\n" michael@0: "a=rtpmap:120 VP8/90000\r\n" michael@0: "a=fmtp:97 profile-level-id=42E00C\r\n" michael@0: "a=sendrecv\r\n" michael@0: "a=candidate:1 1 UDP 2130706431 192.168.2.3 50007 typ host\r\n" michael@0: "a=candidate:2 2 UDP 2130706431 192.168.2.4 50008 typ host\r\n"; michael@0: michael@0: static const std::string strSampleCandidate = michael@0: "a=candidate:1 1 UDP 2130706431 192.168.2.1 50005 typ host\r\n"; michael@0: michael@0: static const std::string strSampleMid = ""; michael@0: michael@0: static const unsigned short nSamplelevel = 2; michael@0: michael@0: static const std::string strG711SdpOffer = michael@0: "v=0\r\n" michael@0: "o=- 1 1 IN IP4 148.147.200.251\r\n" michael@0: "s=-\r\n" michael@0: "b=AS:64\r\n" michael@0: "t=0 0\r\n" michael@0: "a=fingerprint:sha-256 F3:FA:20:C0:CD:48:C4:5F:02:5F:A5:D3:21:D0:2D:48:" michael@0: "7B:31:60:5C:5A:D8:0D:CD:78:78:6C:6D:CE:CC:0C:67\r\n" michael@0: "m=audio 9000 RTP/AVP 0 8 126\r\n" michael@0: "c=IN IP4 148.147.200.251\r\n" michael@0: "b=TIAS:64000\r\n" michael@0: "a=rtpmap:0 PCMU/8000\r\n" michael@0: "a=rtpmap:8 PCMA/8000\r\n" michael@0: "a=rtpmap:126 telephone-event/8000\r\n" michael@0: "a=candidate:0 1 udp 2130706432 148.147.200.251 9000 typ host\r\n" michael@0: "a=candidate:0 2 udp 2130706432 148.147.200.251 9005 typ host\r\n" michael@0: "a=ice-ufrag:cYuakxkEKH+RApYE\r\n" michael@0: "a=ice-pwd:bwtpzLZD+3jbu8vQHvEa6Xuq\r\n" michael@0: "a=sendrecv\r\n"; michael@0: michael@0: michael@0: enum sdpTestFlags michael@0: { michael@0: SHOULD_SEND_AUDIO = (1<<0), michael@0: SHOULD_RECV_AUDIO = (1<<1), michael@0: SHOULD_INACTIVE_AUDIO = (1<<2), michael@0: SHOULD_REJECT_AUDIO = (1<<3), michael@0: SHOULD_OMIT_AUDIO = (1<<4), michael@0: DONT_CHECK_AUDIO = (1<<5), michael@0: SHOULD_CHECK_AUDIO = (1<<6), michael@0: michael@0: SHOULD_SEND_VIDEO = (1<<8), michael@0: SHOULD_RECV_VIDEO = (1<<9), michael@0: SHOULD_INACTIVE_VIDEO = (1<<10), michael@0: SHOULD_REJECT_VIDEO = (1<<11), michael@0: SHOULD_OMIT_VIDEO = (1<<12), michael@0: DONT_CHECK_VIDEO = (1<<13), michael@0: SHOULD_CHECK_VIDEO = (1<<14), michael@0: michael@0: SHOULD_INCLUDE_DATA = (1 << 16), michael@0: DONT_CHECK_DATA = (1 << 17), michael@0: michael@0: SHOULD_SENDRECV_AUDIO = SHOULD_SEND_AUDIO | SHOULD_RECV_AUDIO, michael@0: SHOULD_SENDRECV_VIDEO = SHOULD_SEND_VIDEO | SHOULD_RECV_VIDEO, michael@0: SHOULD_SENDRECV_AV = SHOULD_SENDRECV_AUDIO | SHOULD_SENDRECV_VIDEO, michael@0: SHOULD_CHECK_AV = SHOULD_CHECK_AUDIO | SHOULD_CHECK_VIDEO, michael@0: michael@0: AUDIO_FLAGS = SHOULD_SEND_AUDIO | SHOULD_RECV_AUDIO michael@0: | SHOULD_INACTIVE_AUDIO | SHOULD_REJECT_AUDIO michael@0: | DONT_CHECK_AUDIO | SHOULD_OMIT_AUDIO michael@0: | SHOULD_CHECK_AUDIO, michael@0: michael@0: VIDEO_FLAGS = SHOULD_SEND_VIDEO | SHOULD_RECV_VIDEO michael@0: | SHOULD_INACTIVE_VIDEO | SHOULD_REJECT_VIDEO michael@0: | DONT_CHECK_VIDEO | SHOULD_OMIT_VIDEO michael@0: | SHOULD_CHECK_VIDEO michael@0: }; michael@0: michael@0: enum offerAnswerFlags michael@0: { michael@0: OFFER_NONE = 0, // Sugar to make function calls clearer. michael@0: OFFER_AUDIO = (1<<0), michael@0: OFFER_VIDEO = (1<<1), michael@0: // Leaving some room here for other media types michael@0: ANSWER_NONE = 0, // Sugar to make function calls clearer. michael@0: ANSWER_AUDIO = (1<<8), michael@0: ANSWER_VIDEO = (1<<9), michael@0: michael@0: OFFER_AV = OFFER_AUDIO | OFFER_VIDEO, michael@0: ANSWER_AV = ANSWER_AUDIO | ANSWER_VIDEO michael@0: }; michael@0: michael@0: enum mediaPipelineFlags michael@0: { michael@0: PIPELINE_LOCAL = (1<<0), michael@0: PIPELINE_RTCP_MUX = (1<<1), michael@0: PIPELINE_SEND = (1<<2), michael@0: PIPELINE_VIDEO = (1<<3), michael@0: PIPELINE_RTCP_NACK = (1<<4) michael@0: }; michael@0: michael@0: michael@0: class TestObserver : public AFakePCObserver michael@0: { michael@0: public: michael@0: TestObserver(sipcc::PeerConnectionImpl *peerConnection, michael@0: const std::string &aName) : michael@0: AFakePCObserver(peerConnection, aName) {} michael@0: michael@0: size_t MatchingCandidates(const std::string& cand) { michael@0: size_t count = 0; michael@0: michael@0: for (size_t i=0; i(code); michael@0: state = stateError; michael@0: std::cout << name << ": onSetLocalDescriptionError = " << code michael@0: << " (" << message << ")" << std::endl; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TestObserver::OnSetRemoteDescriptionError(uint32_t code, const char *message, ER&) michael@0: { michael@0: lastStatusCode = static_cast(code); michael@0: state = stateError; michael@0: std::cout << name << ": onSetRemoteDescriptionError = " << code michael@0: << " (" << message << ")" << std::endl; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TestObserver::NotifyConnection(ER&) michael@0: { michael@0: std::cout << name << ": NotifyConnection" << std::endl; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TestObserver::NotifyClosedConnection(ER&) michael@0: { michael@0: std::cout << name << ": NotifyClosedConnection" << std::endl; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TestObserver::NotifyDataChannel(nsIDOMDataChannel *channel, ER&) michael@0: { michael@0: std::cout << name << ": NotifyDataChannel" << std::endl; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TestObserver::OnStateChange(PCObserverStateType state_type, ER&, void*) michael@0: { michael@0: nsresult rv; michael@0: PCImplReadyState gotready; michael@0: PCImplIceConnectionState gotice; michael@0: PCImplIceGatheringState goticegathering; michael@0: PCImplSipccState gotsipcc; michael@0: PCImplSignalingState gotsignaling; michael@0: michael@0: std::cout << name << ": "; michael@0: michael@0: switch (state_type) michael@0: { michael@0: case PCObserverStateType::ReadyState: michael@0: rv = pc->ReadyState(&gotready); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: std::cout << "Ready State: " michael@0: << PCImplReadyStateValues::strings[int(gotready)].value michael@0: << std::endl; michael@0: break; michael@0: case PCObserverStateType::IceConnectionState: michael@0: rv = pc->IceConnectionState(&gotice); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: std::cout << "ICE Connection State: " michael@0: << PCImplIceConnectionStateValues::strings[int(gotice)].value michael@0: << std::endl; michael@0: break; michael@0: case PCObserverStateType::IceGatheringState: michael@0: rv = pc->IceGatheringState(&goticegathering); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: std::cout michael@0: << "ICE Gathering State: " michael@0: << PCImplIceGatheringStateValues::strings[int(goticegathering)].value michael@0: << std::endl; michael@0: break; michael@0: case PCObserverStateType::SdpState: michael@0: std::cout << "SDP State: " << std::endl; michael@0: // NS_ENSURE_SUCCESS(rv, rv); michael@0: break; michael@0: case PCObserverStateType::SipccState: michael@0: rv = pc->SipccState(&gotsipcc); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: std::cout << "SIPCC State: " michael@0: << PCImplSipccStateValues::strings[int(gotsipcc)].value michael@0: << std::endl; michael@0: break; michael@0: case PCObserverStateType::SignalingState: michael@0: rv = pc->SignalingState(&gotsignaling); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: std::cout << "Signaling State: " michael@0: << PCImplSignalingStateValues::strings[int(gotsignaling)].value michael@0: << std::endl; michael@0: break; michael@0: default: michael@0: // Unknown State michael@0: MOZ_CRASH("Unknown state change type."); michael@0: break; michael@0: } michael@0: michael@0: lastStateType = state_type; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: TestObserver::OnAddStream(nsIDOMMediaStream *stream, ER&) michael@0: { michael@0: PR_ASSERT(stream); michael@0: michael@0: DOMMediaStream *ms = static_cast(stream); michael@0: michael@0: std::cout << name << ": OnAddStream called hints=" << ms->GetHintContents() michael@0: << " thread=" << PR_GetCurrentThread() << std::endl ; michael@0: michael@0: onAddStreamCalled = true; michael@0: michael@0: streams.push_back(ms); michael@0: michael@0: // We know that the media stream is secretly a Fake_SourceMediaStream, michael@0: // so now we can start it pulling from us michael@0: Fake_SourceMediaStream *fs = static_cast(ms->GetStream()); michael@0: michael@0: test_utils->sts_target()->Dispatch( michael@0: WrapRunnable(fs, &Fake_SourceMediaStream::Start), michael@0: NS_DISPATCH_NORMAL); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TestObserver::OnRemoveStream(ER&) michael@0: { michael@0: state = stateSuccess; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TestObserver::OnAddTrack(ER&) michael@0: { michael@0: state = stateSuccess; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TestObserver::OnRemoveTrack(ER&) michael@0: { michael@0: state = stateSuccess; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TestObserver::OnIceCandidate(uint16_t level, michael@0: const char * mid, michael@0: const char * candidate, ER&) michael@0: { michael@0: std::cout << name << ": onIceCandidate [" << level << "/" michael@0: << mid << "] " << candidate << std::endl; michael@0: candidates.push_back(candidate); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TestObserver::OnAddIceCandidateSuccess(ER&) michael@0: { michael@0: lastStatusCode = sipcc::PeerConnectionImpl::kNoError; michael@0: state = stateSuccess; michael@0: std::cout << name << ": onAddIceCandidateSuccess" << std::endl; michael@0: addIceSuccessCount++; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TestObserver::OnAddIceCandidateError(uint32_t code, const char *message, ER&) michael@0: { michael@0: lastStatusCode = static_cast(code); michael@0: state = stateError; michael@0: std::cout << name << ": onAddIceCandidateError = " << code michael@0: << " (" << message << ")" << std::endl; michael@0: return NS_OK; michael@0: } michael@0: michael@0: class ParsedSDP { michael@0: public: michael@0: //Line number with the corresponding SDP line. michael@0: typedef std::pair SdpLine; michael@0: michael@0: ParsedSDP(std::string sdp): michael@0: sdp_(), michael@0: sdp_without_ice_(), michael@0: ice_candidates_(), michael@0: levels_(0), michael@0: num_lines(0) michael@0: { michael@0: sdp_ = sdp; michael@0: Parse(); michael@0: } michael@0: michael@0: void DeleteAllLines(std::string objType) michael@0: { michael@0: int count = sdp_map_.count(objType); michael@0: std::cout << "Removing " << count << " lines from SDP (" << objType michael@0: << ")" << std::endl; michael@0: michael@0: for (int i = 0; i < count; i++) { michael@0: DeleteLine(objType); michael@0: } michael@0: } michael@0: michael@0: void DeleteLine(std::string objType) michael@0: { michael@0: ReplaceLine(objType, ""); michael@0: } michael@0: michael@0: // Replaces the first instance of objType in the SDP with michael@0: // a new string. michael@0: // If content is an empty string then the line will be removed michael@0: void ReplaceLine(std::string objType, std::string content) michael@0: { michael@0: std::multimap::iterator it; michael@0: it = sdp_map_.find(objType); michael@0: if(it != sdp_map_.end()) { michael@0: SdpLine sdp_line_pair = (*it).second; michael@0: int line_no = sdp_line_pair.first; michael@0: sdp_map_.erase(it); michael@0: if(content.empty()) { michael@0: return; michael@0: } michael@0: std::string value = content.substr(objType.length() + 1); michael@0: sdp_map_.insert(std::pair(objType, michael@0: std::make_pair(line_no,value))); michael@0: } michael@0: } michael@0: michael@0: void AddLine(std::string content) michael@0: { michael@0: size_t whiteSpace = content.find(' '); michael@0: std::string key; michael@0: std::string value; michael@0: if(whiteSpace == std::string::npos) { michael@0: key = content.substr(0, content.size() - 2); michael@0: value = ""; michael@0: } else { michael@0: key = content.substr(0, whiteSpace); michael@0: value = content.substr(whiteSpace+1); michael@0: } michael@0: sdp_map_.insert(std::pair(key, michael@0: std::make_pair(num_lines,value))); michael@0: num_lines++; michael@0: } michael@0: michael@0: // Returns the values for all lines of the indicated type michael@0: // Removes trailing "\r\n" from values. michael@0: std::vector GetLines(std::string objType) const michael@0: { michael@0: std::multimap::const_iterator it, end; michael@0: std::vector values; michael@0: it = sdp_map_.lower_bound(objType); michael@0: end = sdp_map_.upper_bound(objType); michael@0: while (it != end) { michael@0: std::string value = it->second.second; michael@0: if (value.find("\r") != std::string::npos) { michael@0: value = value.substr(0, value.find("\r")); michael@0: } else { michael@0: ADD_FAILURE(); michael@0: } michael@0: values.push_back(value); michael@0: ++it; michael@0: } michael@0: return values; michael@0: } michael@0: michael@0: //Parse SDP as std::string into map that looks like: michael@0: // key: sdp content till first space michael@0: // value : michael@0: void Parse() michael@0: { michael@0: size_t prev = 0; michael@0: size_t found = 0; michael@0: num_lines = 0; michael@0: for(;;) { michael@0: found = sdp_.find('\n', found + 1); michael@0: if (found == std::string::npos) michael@0: break; michael@0: std::string line = sdp_.substr(prev, (found - prev) + 1); michael@0: size_t whiteSpace = line.find(' '); michael@0: std::string key; michael@0: std::string value; michael@0: if(whiteSpace == std::string::npos) { michael@0: //this is the line with no extra contents michael@0: //example, v=0, a=sendrecv michael@0: key = line.substr(0, line.size() - 2); michael@0: //: michael@0: value = ""; michael@0: } else { michael@0: key = line.substr(0, whiteSpace); michael@0: //: michael@0: value = line.substr(whiteSpace+1); michael@0: } michael@0: SdpLine sdp_line_pair = std::make_pair(num_lines,value); michael@0: sdp_map_.insert(std::pair(key, sdp_line_pair)); michael@0: num_lines++; michael@0: //storing ice candidates separately for quick acesss as needed michael@0: //for the trickle unit tests michael@0: if (line.find("a=candidate") == 0) { michael@0: // This is a candidate, strip of a= and \r\n michael@0: std::string cand = line.substr(2, line.size() - 4); michael@0: ice_candidates_.insert(std::pair(levels_, cand)); michael@0: } else { michael@0: sdp_without_ice_ += line; michael@0: } michael@0: if (line.find("m=") == 0) { michael@0: // This is an m-line michael@0: ++levels_; michael@0: } michael@0: prev = found + 1; michael@0: } michael@0: } michael@0: michael@0: //Convert Internal SDP representation into String representation michael@0: std::string getSdp() michael@0: { michael@0: std::vector sdp_lines(num_lines); michael@0: for (std::multimap::iterator it = sdp_map_.begin(); michael@0: it != sdp_map_.end(); ++it) { michael@0: michael@0: SdpLine sdp_line_pair = (*it).second; michael@0: std::string value; michael@0: if(sdp_line_pair.second.length() == 0) { michael@0: value = (*it).first + "\r\n"; michael@0: sdp_lines[sdp_line_pair.first] = value; michael@0: } else { michael@0: value = (*it).first + ' ' + sdp_line_pair.second; michael@0: sdp_lines[sdp_line_pair.first] = value; michael@0: } michael@0: } michael@0: michael@0: //generate our final sdp in std::string format michael@0: std::string sdp; michael@0: for (size_t i = 0; i < sdp_lines.size(); i++) michael@0: { michael@0: sdp += sdp_lines[i]; michael@0: } michael@0: michael@0: return sdp; michael@0: } michael@0: michael@0: michael@0: michael@0: std::string sdp_; michael@0: std::string sdp_without_ice_; michael@0: std::multimap ice_candidates_; michael@0: std::multimap sdp_map_; michael@0: int levels_; michael@0: int num_lines; michael@0: }; michael@0: michael@0: class SignalingAgent { michael@0: public: michael@0: SignalingAgent(const std::string &aName) : pc(nullptr), name(aName) { michael@0: cfg_.addStunServer(g_stun_server_address, g_stun_server_port); michael@0: michael@0: pc = sipcc::PeerConnectionImpl::CreatePeerConnection(); michael@0: EXPECT_TRUE(pc); michael@0: } michael@0: michael@0: SignalingAgent(const std::string &aName, const std::string stun_addr, michael@0: uint16_t stun_port) : pc(nullptr), name(aName) { michael@0: cfg_.addStunServer(stun_addr, stun_port); michael@0: michael@0: pc = sipcc::PeerConnectionImpl::CreatePeerConnection(); michael@0: EXPECT_TRUE(pc); michael@0: } michael@0: michael@0: michael@0: ~SignalingAgent() { michael@0: mozilla::SyncRunnable::DispatchToThread(gMainThread, michael@0: WrapRunnable(this, &SignalingAgent::Close)); michael@0: } michael@0: michael@0: void Init_m(nsCOMPtr thread) michael@0: { michael@0: pObserver = new TestObserver(pc, name); michael@0: ASSERT_TRUE(pObserver); michael@0: michael@0: ASSERT_EQ(pc->Initialize(*pObserver, nullptr, cfg_, thread), NS_OK); michael@0: } michael@0: michael@0: void Init(nsCOMPtr thread) michael@0: { michael@0: mozilla::SyncRunnable::DispatchToThread(thread, michael@0: WrapRunnable(this, &SignalingAgent::Init_m, thread)); michael@0: michael@0: ASSERT_TRUE_WAIT(sipcc_state() == PCImplSipccState::Started, michael@0: kDefaultTimeout); michael@0: } michael@0: michael@0: void WaitForGather() { michael@0: ASSERT_TRUE_WAIT(ice_gathering_state() == PCImplIceGatheringState::Complete, michael@0: kDefaultTimeout); michael@0: michael@0: std::cout << name << ": Init Complete" << std::endl; michael@0: } michael@0: michael@0: bool WaitForGatherAllowFail() { michael@0: EXPECT_TRUE_WAIT( michael@0: ice_gathering_state() == PCImplIceGatheringState::Complete || michael@0: ice_connection_state() == PCImplIceConnectionState::Failed, michael@0: kDefaultTimeout); michael@0: michael@0: if (ice_connection_state() == PCImplIceConnectionState::Failed) { michael@0: std::cout << name << ": Init Failed" << std::endl; michael@0: return false; michael@0: } michael@0: michael@0: std::cout << name << "Init Complete" << std::endl; michael@0: return true; michael@0: } michael@0: michael@0: PCImplSipccState sipcc_state() michael@0: { michael@0: return pc->SipccState(); michael@0: } michael@0: michael@0: PCImplIceConnectionState ice_connection_state() michael@0: { michael@0: return pc->IceConnectionState(); michael@0: } michael@0: michael@0: PCImplIceGatheringState ice_gathering_state() michael@0: { michael@0: return pc->IceGatheringState(); michael@0: } michael@0: michael@0: PCImplSignalingState signaling_state() michael@0: { michael@0: return pc->SignalingState(); michael@0: } michael@0: michael@0: void Close() michael@0: { michael@0: if (pc) { michael@0: std::cout << name << ": Close" << std::endl; michael@0: michael@0: pc->Close(); michael@0: pc = nullptr; michael@0: } michael@0: michael@0: // Shutdown is synchronous evidently. michael@0: // ASSERT_TRUE(pObserver->WaitForObserverCall()); michael@0: // ASSERT_EQ(pc->sipcc_state(), sipcc::PeerConnectionInterface::kIdle); michael@0: } michael@0: michael@0: bool OfferContains(const std::string& str) { michael@0: std::string o(offer()); michael@0: michael@0: return o.find(str) != std::string::npos; michael@0: } michael@0: michael@0: bool AnswerContains(const std::string& str) { michael@0: std::string o(answer()); michael@0: michael@0: return o.find(str) != std::string::npos; michael@0: } michael@0: michael@0: size_t MatchingCandidates(const std::string& cand) { michael@0: return pObserver->MatchingCandidates(cand); michael@0: } michael@0: michael@0: char* offer() const { return offer_; } michael@0: char* answer() const { return answer_; } michael@0: michael@0: std::string getLocalDescription() const { michael@0: char *sdp = nullptr; michael@0: pc->GetLocalDescription(&sdp); michael@0: if (!sdp) { michael@0: return ""; michael@0: } michael@0: return sdp; michael@0: } michael@0: michael@0: std::string getRemoteDescription() const { michael@0: char *sdp = 0; michael@0: pc->GetRemoteDescription(&sdp); michael@0: if (!sdp) { michael@0: return ""; michael@0: } michael@0: return sdp; michael@0: } michael@0: michael@0: // Adds a stream to the PeerConnection. michael@0: void AddStream(uint32_t hint = michael@0: DOMMediaStream::HINT_CONTENTS_AUDIO | michael@0: DOMMediaStream::HINT_CONTENTS_VIDEO, michael@0: MediaStream *stream = nullptr, michael@0: sipcc::MediaConstraints *constraints = nullptr michael@0: ) { michael@0: michael@0: sipcc::MediaConstraints noConstraints; michael@0: if (!constraints) { michael@0: constraints = &noConstraints; michael@0: } michael@0: michael@0: nsRefPtr domMediaStream; michael@0: if (stream) { michael@0: domMediaStream = new DOMMediaStream(stream); michael@0: } else { michael@0: domMediaStream = new DOMMediaStream(); michael@0: } michael@0: michael@0: domMediaStream->SetHintContents(hint); michael@0: ASSERT_EQ(pc->AddStream(*domMediaStream, *constraints), NS_OK); michael@0: domMediaStream_ = domMediaStream; michael@0: } michael@0: michael@0: michael@0: // Removes a stream from the PeerConnection. If the stream michael@0: // parameter is absent, removes the stream that was most michael@0: // recently added to the PeerConnection. michael@0: void RemoveLastStreamAdded() { michael@0: ASSERT_EQ(pc->RemoveStream(*domMediaStream_), NS_OK); michael@0: } michael@0: michael@0: void CreateOffer(sipcc::MediaConstraints& constraints, michael@0: uint32_t offerFlags, uint32_t sdpCheck, michael@0: PCImplSignalingState endState = michael@0: PCImplSignalingState::SignalingStable) { michael@0: michael@0: // Create a media stream as if it came from GUM michael@0: Fake_AudioStreamSource *audio_stream = michael@0: new Fake_AudioStreamSource(); michael@0: michael@0: nsresult ret; michael@0: mozilla::SyncRunnable::DispatchToThread( michael@0: test_utils->sts_target(), michael@0: WrapRunnableRet(audio_stream, &Fake_MediaStream::Start, &ret)); michael@0: michael@0: ASSERT_TRUE(NS_SUCCEEDED(ret)); michael@0: michael@0: uint32_t aHintContents = 0; michael@0: if (offerFlags & OFFER_AUDIO) { michael@0: aHintContents |= DOMMediaStream::HINT_CONTENTS_AUDIO; michael@0: } michael@0: if (offerFlags & OFFER_VIDEO) { michael@0: aHintContents |= DOMMediaStream::HINT_CONTENTS_VIDEO; michael@0: } michael@0: AddStream(aHintContents, audio_stream); michael@0: michael@0: // Now call CreateOffer as JS would michael@0: pObserver->state = TestObserver::stateNoResponse; michael@0: ASSERT_EQ(pc->CreateOffer(constraints), NS_OK); michael@0: ASSERT_TRUE_WAIT(pObserver->state != TestObserver::stateNoResponse, michael@0: kDefaultTimeout); michael@0: ASSERT_EQ(pObserver->state, TestObserver::stateSuccess); michael@0: SDPSanityCheck(pObserver->lastString, sdpCheck, true); michael@0: ASSERT_EQ(signaling_state(), endState); michael@0: offer_ = pObserver->lastString; michael@0: } michael@0: michael@0: void CreateAnswer(sipcc::MediaConstraints& constraints, std::string offer, michael@0: uint32_t offerAnswerFlags, michael@0: uint32_t sdpCheck = DONT_CHECK_AUDIO| michael@0: DONT_CHECK_VIDEO| michael@0: DONT_CHECK_DATA, michael@0: PCImplSignalingState endState = michael@0: PCImplSignalingState::SignalingHaveRemoteOffer) { michael@0: michael@0: uint32_t aHintContents = 0; michael@0: if (offerAnswerFlags & ANSWER_AUDIO) { michael@0: aHintContents |= DOMMediaStream::HINT_CONTENTS_AUDIO; michael@0: } michael@0: if (offerAnswerFlags & ANSWER_VIDEO) { michael@0: aHintContents |= DOMMediaStream::HINT_CONTENTS_VIDEO; michael@0: } michael@0: AddStream(aHintContents); michael@0: michael@0: // Decide if streams are disabled for offer or answer michael@0: // then perform SDP checking based on which stream disabled michael@0: pObserver->state = TestObserver::stateNoResponse; michael@0: ASSERT_EQ(pc->CreateAnswer(constraints), NS_OK); michael@0: ASSERT_TRUE_WAIT(pObserver->state != TestObserver::stateNoResponse, michael@0: kDefaultTimeout); michael@0: ASSERT_EQ(pObserver->state, TestObserver::stateSuccess); michael@0: SDPSanityCheck(pObserver->lastString, sdpCheck, false); michael@0: ASSERT_EQ(signaling_state(), endState); michael@0: michael@0: answer_ = pObserver->lastString; michael@0: } michael@0: michael@0: // At present, we use the hints field in a stream to find and michael@0: // remove it. This only works if the specified hints flags are michael@0: // unique among all streams in the PeerConnection. This is not michael@0: // generally true, and will need significant revision once michael@0: // multiple streams are supported. michael@0: void CreateOfferRemoveStream(sipcc::MediaConstraints& constraints, michael@0: uint32_t hints, uint32_t sdpCheck) { michael@0: michael@0: domMediaStream_->SetHintContents(hints); michael@0: michael@0: // This currently "removes" a stream that has the same audio/video michael@0: // hints as were passed in. michael@0: // When complete RemoveStream will remove and entire stream and its tracks michael@0: // not just disable a track as this is currently doing michael@0: ASSERT_EQ(pc->RemoveStream(*domMediaStream_), NS_OK); michael@0: michael@0: // Now call CreateOffer as JS would michael@0: pObserver->state = TestObserver::stateNoResponse; michael@0: ASSERT_EQ(pc->CreateOffer(constraints), NS_OK); michael@0: ASSERT_TRUE_WAIT(pObserver->state != TestObserver::stateNoResponse, michael@0: kDefaultTimeout); michael@0: ASSERT_TRUE(pObserver->state == TestObserver::stateSuccess); michael@0: SDPSanityCheck(pObserver->lastString, sdpCheck, true); michael@0: offer_ = pObserver->lastString; michael@0: } michael@0: michael@0: void SetRemote(TestObserver::Action action, std::string remote, michael@0: bool ignoreError = false, michael@0: PCImplSignalingState endState = michael@0: PCImplSignalingState::SignalingInvalid) { michael@0: michael@0: if (endState == PCImplSignalingState::SignalingInvalid) { michael@0: endState = (action == TestObserver::OFFER ? michael@0: PCImplSignalingState::SignalingHaveRemoteOffer : michael@0: PCImplSignalingState::SignalingStable); michael@0: } michael@0: michael@0: pObserver->state = TestObserver::stateNoResponse; michael@0: ASSERT_EQ(pc->SetRemoteDescription(action, remote.c_str()), NS_OK); michael@0: ASSERT_TRUE_WAIT(pObserver->state != TestObserver::stateNoResponse, michael@0: kDefaultTimeout); michael@0: ASSERT_EQ(signaling_state(), endState); michael@0: if (!ignoreError) { michael@0: ASSERT_EQ(pObserver->state, TestObserver::stateSuccess); michael@0: } michael@0: } michael@0: michael@0: void SetLocal(TestObserver::Action action, std::string local, michael@0: bool ignoreError = false, michael@0: PCImplSignalingState endState = michael@0: PCImplSignalingState::SignalingInvalid) { michael@0: michael@0: if (endState == PCImplSignalingState::SignalingInvalid) { michael@0: endState = (action == TestObserver::OFFER ? michael@0: PCImplSignalingState::SignalingHaveLocalOffer : michael@0: PCImplSignalingState::SignalingStable); michael@0: } michael@0: michael@0: pObserver->state = TestObserver::stateNoResponse; michael@0: ASSERT_EQ(pc->SetLocalDescription(action, local.c_str()), NS_OK); michael@0: ASSERT_TRUE_WAIT(pObserver->state != TestObserver::stateNoResponse, michael@0: kDefaultTimeout); michael@0: ASSERT_EQ(signaling_state(), endState); michael@0: if (!ignoreError) { michael@0: ASSERT_EQ(pObserver->state, TestObserver::stateSuccess); michael@0: } michael@0: } michael@0: michael@0: void DoTrickleIce(ParsedSDP &sdp) { michael@0: int expectAddIce = 0; michael@0: pObserver->addIceSuccessCount = 0; michael@0: for (std::multimap::iterator it = michael@0: sdp.ice_candidates_.begin(); michael@0: it != sdp.ice_candidates_.end(); ++it) { michael@0: if ((*it).first != 0) { michael@0: std::cerr << "Adding trickle ICE candidate " << (*it).second michael@0: << std::endl; michael@0: ASSERT_TRUE(NS_SUCCEEDED(pc->AddIceCandidate((*it).second.c_str(), "", (*it).first))); michael@0: expectAddIce++; michael@0: } michael@0: } michael@0: ASSERT_TRUE_WAIT(pObserver->addIceSuccessCount == expectAddIce, michael@0: kDefaultTimeout); michael@0: } michael@0: michael@0: michael@0: void DoTrickleIceChrome(ParsedSDP &sdp) { michael@0: int expectAddIce = 0; michael@0: pObserver->addIceSuccessCount = 0; michael@0: for (std::multimap::iterator it = michael@0: sdp.ice_candidates_.begin(); michael@0: it != sdp.ice_candidates_.end(); ++it) { michael@0: if ((*it).first != 0) { michael@0: std::string candidate = "a=" + (*it).second + "\r\n"; michael@0: std::cerr << "Adding trickle ICE candidate " << candidate << std::endl; michael@0: michael@0: ASSERT_TRUE(NS_SUCCEEDED(pc->AddIceCandidate(candidate.c_str(), "", (*it).first))); michael@0: expectAddIce++; michael@0: } michael@0: } michael@0: ASSERT_TRUE_WAIT(pObserver->addIceSuccessCount == expectAddIce, michael@0: kDefaultTimeout); michael@0: } michael@0: michael@0: michael@0: bool IceCompleted() { michael@0: return pc->IceConnectionState() == PCImplIceConnectionState::Connected; michael@0: } michael@0: michael@0: void AddIceCandidate(const char* candidate, const char* mid, unsigned short level, michael@0: bool expectSuccess) { michael@0: PCImplSignalingState endState = signaling_state(); michael@0: pObserver->state = TestObserver::stateNoResponse; michael@0: pc->AddIceCandidate(candidate, mid, level); michael@0: ASSERT_TRUE_WAIT(pObserver->state != TestObserver::stateNoResponse, michael@0: kDefaultTimeout); michael@0: ASSERT_TRUE(pObserver->state == michael@0: expectSuccess ? TestObserver::stateSuccess : michael@0: TestObserver::stateError michael@0: ); michael@0: michael@0: // Verify that adding ICE candidates does not change the signaling state michael@0: ASSERT_EQ(signaling_state(), endState); michael@0: } michael@0: michael@0: int GetPacketsReceived(int stream) { michael@0: std::vector streams = pObserver->GetStreams(); michael@0: michael@0: if ((int) streams.size() <= stream) { michael@0: return 0; michael@0: } michael@0: michael@0: return streams[stream]->GetStream()->AsSourceStream()->GetSegmentsAdded(); michael@0: } michael@0: michael@0: int GetPacketsSent(int stream) { michael@0: return static_cast( michael@0: domMediaStream_->GetStream())->GetSegmentsAdded(); michael@0: } michael@0: michael@0: //Stops generating new audio data for transmission. michael@0: //Should be called before Cleanup of the peer connection. michael@0: void CloseSendStreams() { michael@0: static_cast( michael@0: domMediaStream_->GetStream())->StopStream(); michael@0: } michael@0: michael@0: //Stops pulling audio data off the receivers. michael@0: //Should be called before Cleanup of the peer connection. michael@0: void CloseReceiveStreams() { michael@0: std::vector streams = michael@0: pObserver->GetStreams(); michael@0: for (size_t i = 0; i < streams.size(); i++) { michael@0: streams[i]->GetStream()->AsSourceStream()->StopStream(); michael@0: } michael@0: } michael@0: michael@0: mozilla::RefPtr GetMediaPipeline( michael@0: bool local, int stream, int track) { michael@0: sipcc::SourceStreamInfo *streamInfo; michael@0: michael@0: if (local) { michael@0: streamInfo = pc->media()->GetLocalStream(stream); michael@0: } else { michael@0: streamInfo = pc->media()->GetRemoteStream(stream); michael@0: } michael@0: michael@0: if (!streamInfo) { michael@0: return nullptr; michael@0: } michael@0: michael@0: const auto &pipelines = streamInfo->GetPipelines(); michael@0: auto it = pipelines.find(track); michael@0: return (it == pipelines.end())? nullptr : it->second; michael@0: } michael@0: michael@0: void CheckMediaPipeline(int stream, int track, uint32_t flags, michael@0: VideoSessionConduit::FrameRequestType frameRequestMethod = michael@0: VideoSessionConduit::FrameRequestNone) { michael@0: michael@0: std::cout << name << ": Checking media pipeline settings for " michael@0: << ((flags & PIPELINE_LOCAL) ? "local " : "remote ") michael@0: << ((flags & PIPELINE_SEND) ? "sending " : "receiving ") michael@0: << ((flags & PIPELINE_VIDEO) ? "video" : "audio") michael@0: << " pipeline (stream " << stream michael@0: << ", track " << track << "); expect " michael@0: << ((flags & PIPELINE_RTCP_MUX) ? "MUX, " : "no MUX, ") michael@0: << ((flags & PIPELINE_RTCP_NACK) ? "NACK." : "no NACK.") michael@0: << std::endl; michael@0: michael@0: mozilla::RefPtr pipeline = michael@0: GetMediaPipeline((flags & PIPELINE_LOCAL), stream, track); michael@0: ASSERT_TRUE(pipeline); michael@0: ASSERT_EQ(pipeline->IsDoingRtcpMux(), !!(flags & PIPELINE_RTCP_MUX)); michael@0: // We cannot yet test send/recv with video. michael@0: if (!(flags & PIPELINE_VIDEO)) { michael@0: if (flags & PIPELINE_SEND) { michael@0: ASSERT_TRUE_WAIT(pipeline->rtp_packets_sent() >= 40 && michael@0: pipeline->rtcp_packets_received() >= 1, michael@0: kDefaultTimeout); michael@0: ASSERT_GE(pipeline->rtp_packets_sent(), 40); michael@0: ASSERT_GE(pipeline->rtcp_packets_received(), 1); michael@0: } else { michael@0: ASSERT_TRUE_WAIT(pipeline->rtp_packets_received() >= 40 && michael@0: pipeline->rtcp_packets_sent() >= 1, michael@0: kDefaultTimeout); michael@0: ASSERT_GE(pipeline->rtp_packets_received(), 40); michael@0: ASSERT_GE(pipeline->rtcp_packets_sent(), 1); michael@0: } michael@0: } michael@0: michael@0: michael@0: // Check feedback method for video michael@0: if (flags & PIPELINE_VIDEO) { michael@0: mozilla::MediaSessionConduit *conduit = pipeline->Conduit(); michael@0: ASSERT_TRUE(conduit); michael@0: ASSERT_EQ(conduit->type(), mozilla::MediaSessionConduit::VIDEO); michael@0: mozilla::VideoSessionConduit *video_conduit = michael@0: static_cast(conduit); michael@0: ASSERT_EQ(!!(flags & PIPELINE_RTCP_NACK), michael@0: video_conduit->UsingNackBasic()); michael@0: ASSERT_EQ(frameRequestMethod, video_conduit->FrameRequestMethod()); michael@0: } michael@0: } michael@0: michael@0: public: michael@0: mozilla::RefPtr pc; michael@0: nsRefPtr pObserver; michael@0: char* offer_; michael@0: char* answer_; michael@0: nsRefPtr domMediaStream_; michael@0: sipcc::IceConfiguration cfg_; michael@0: const std::string name; michael@0: michael@0: private: michael@0: void SDPSanityCheck(std::string sdp, uint32_t flags, bool offer) michael@0: { michael@0: ASSERT_TRUE(pObserver->state == TestObserver::stateSuccess); michael@0: ASSERT_NE(sdp.find("v=0"), std::string::npos); michael@0: ASSERT_NE(sdp.find("c=IN IP4"), std::string::npos); michael@0: ASSERT_NE(sdp.find("a=fingerprint:sha-256"), std::string::npos); michael@0: michael@0: std::cout << name << ": SDPSanityCheck flags for " michael@0: << (offer ? "offer" : "answer") michael@0: << " = " << std::hex << std::showbase michael@0: << flags << std::dec michael@0: michael@0: << ((flags & SHOULD_SEND_AUDIO)?" SHOULD_SEND_AUDIO":"") michael@0: << ((flags & SHOULD_RECV_AUDIO)?" SHOULD_RECV_AUDIO":"") michael@0: << ((flags & SHOULD_INACTIVE_AUDIO)?" SHOULD_INACTIVE_AUDIO":"") michael@0: << ((flags & SHOULD_REJECT_AUDIO)?" SHOULD_REJECT_AUDIO":"") michael@0: << ((flags & SHOULD_OMIT_AUDIO)?" SHOULD_OMIT_AUDIO":"") michael@0: << ((flags & DONT_CHECK_AUDIO)?" DONT_CHECK_AUDIO":"") michael@0: michael@0: << ((flags & SHOULD_SEND_VIDEO)?" SHOULD_SEND_VIDEO":"") michael@0: << ((flags & SHOULD_RECV_VIDEO)?" SHOULD_RECV_VIDEO":"") michael@0: << ((flags & SHOULD_INACTIVE_VIDEO)?" SHOULD_INACTIVE_VIDEO":"") michael@0: << ((flags & SHOULD_REJECT_VIDEO)?" SHOULD_REJECT_VIDEO":"") michael@0: << ((flags & SHOULD_OMIT_VIDEO)?" SHOULD_OMIT_VIDEO":"") michael@0: << ((flags & DONT_CHECK_VIDEO)?" DONT_CHECK_VIDEO":"") michael@0: michael@0: << ((flags & SHOULD_INCLUDE_DATA)?" SHOULD_INCLUDE_DATA":"") michael@0: << ((flags & DONT_CHECK_DATA)?" DONT_CHECK_DATA":"") michael@0: << std::endl; michael@0: michael@0: switch(flags & AUDIO_FLAGS) { michael@0: case 0: michael@0: ASSERT_EQ(sdp.find("a=rtpmap:109 opus/48000"), std::string::npos); michael@0: break; michael@0: case SHOULD_CHECK_AUDIO: michael@0: ASSERT_NE(sdp.find("a=rtpmap:109 opus/48000"), std::string::npos); michael@0: if (offer) { michael@0: ASSERT_NE(sdp.find("a=rtpmap:0 PCMU/8000"), std::string::npos); michael@0: } michael@0: break; michael@0: case SHOULD_SEND_AUDIO: michael@0: ASSERT_NE(sdp.find("a=rtpmap:109 opus/48000"), std::string::npos); michael@0: ASSERT_NE(sdp.find(" 0-15\r\na=sendonly"), std::string::npos); michael@0: if (offer) { michael@0: ASSERT_NE(sdp.find("a=rtpmap:0 PCMU/8000"), std::string::npos); michael@0: } michael@0: break; michael@0: case SHOULD_RECV_AUDIO: michael@0: ASSERT_NE(sdp.find("a=rtpmap:109 opus/48000"), std::string::npos); michael@0: ASSERT_NE(sdp.find(" 0-15\r\na=recvonly"), std::string::npos); michael@0: if (offer) { michael@0: ASSERT_NE(sdp.find("a=rtpmap:0 PCMU/8000"), std::string::npos); michael@0: } michael@0: break; michael@0: case SHOULD_SENDRECV_AUDIO: michael@0: ASSERT_NE(sdp.find("a=rtpmap:109 opus/48000"), std::string::npos); michael@0: ASSERT_NE(sdp.find(" 0-15\r\na=sendrecv"), std::string::npos); michael@0: if (offer) { michael@0: ASSERT_NE(sdp.find("a=rtpmap:0 PCMU/8000"), std::string::npos); michael@0: } michael@0: break; michael@0: case SHOULD_INACTIVE_AUDIO: michael@0: ASSERT_NE(sdp.find("a=rtpmap:109 opus/48000"), std::string::npos); michael@0: ASSERT_NE(sdp.find(" 0-15\r\na=inactive"), std::string::npos); michael@0: break; michael@0: case SHOULD_REJECT_AUDIO: michael@0: ASSERT_EQ(sdp.find("a=rtpmap:109 opus/48000"), std::string::npos); michael@0: ASSERT_NE(sdp.find("m=audio 0 "), std::string::npos); michael@0: break; michael@0: case SHOULD_OMIT_AUDIO: michael@0: ASSERT_EQ(sdp.find("m=audio"), std::string::npos); michael@0: break; michael@0: case DONT_CHECK_AUDIO: michael@0: break; michael@0: default: michael@0: ASSERT_FALSE("Missing case in switch statement"); michael@0: } michael@0: michael@0: switch(flags & VIDEO_FLAGS) { michael@0: case 0: michael@0: ASSERT_EQ(sdp.find("a=rtpmap:120 VP8/90000"), std::string::npos); michael@0: break; michael@0: case SHOULD_CHECK_VIDEO: michael@0: ASSERT_NE(sdp.find("a=rtpmap:120 VP8/90000"), std::string::npos); michael@0: break; michael@0: case SHOULD_SEND_VIDEO: michael@0: ASSERT_NE(sdp.find("a=rtpmap:120 VP8/90000\r\na=sendonly"), michael@0: std::string::npos); michael@0: break; michael@0: case SHOULD_RECV_VIDEO: michael@0: ASSERT_NE(sdp.find("a=rtpmap:120 VP8/90000\r\na=recvonly"), michael@0: std::string::npos); michael@0: break; michael@0: case SHOULD_SENDRECV_VIDEO: michael@0: ASSERT_NE(sdp.find("a=rtpmap:120 VP8/90000\r\na=sendrecv"), michael@0: std::string::npos); michael@0: break; michael@0: case SHOULD_INACTIVE_VIDEO: michael@0: ASSERT_NE(sdp.find("a=rtpmap:120 VP8/90000\r\na=inactive"), michael@0: std::string::npos); michael@0: break; michael@0: case SHOULD_REJECT_VIDEO: michael@0: ASSERT_NE(sdp.find("m=video 0 "), std::string::npos); michael@0: break; michael@0: case SHOULD_OMIT_VIDEO: michael@0: ASSERT_EQ(sdp.find("m=video"), std::string::npos); michael@0: break; michael@0: case DONT_CHECK_VIDEO: michael@0: break; michael@0: default: michael@0: ASSERT_FALSE("Missing case in switch statement"); michael@0: } michael@0: michael@0: if (flags & SHOULD_INCLUDE_DATA) { michael@0: ASSERT_NE(sdp.find("m=application"), std::string::npos); michael@0: } else if (!(flags & DONT_CHECK_DATA)) { michael@0: ASSERT_EQ(sdp.find("m=application"), std::string::npos); michael@0: } michael@0: } michael@0: }; michael@0: michael@0: class SignalingEnvironment : public ::testing::Environment { michael@0: public: michael@0: void TearDown() { michael@0: // Signaling is shut down in XPCOM shutdown michael@0: } michael@0: }; michael@0: michael@0: class SignalingAgentTest : public ::testing::Test { michael@0: public: michael@0: static void SetUpTestCase() { michael@0: } michael@0: michael@0: void TearDown() { michael@0: // Delete all the agents. michael@0: for (size_t i=0; i < agents_.size(); i++) { michael@0: delete agents_[i]; michael@0: } michael@0: } michael@0: michael@0: bool CreateAgent() { michael@0: return CreateAgent(g_stun_server_address, g_stun_server_port); michael@0: } michael@0: michael@0: bool CreateAgent(const std::string stun_addr, uint16_t stun_port, michael@0: bool wait_for_gather = true) { michael@0: ScopedDeletePtr agent( michael@0: new SignalingAgent("agent", stun_addr, stun_port)); michael@0: michael@0: agent->Init(gMainThread); michael@0: michael@0: if (wait_for_gather) { michael@0: if (!agent->WaitForGatherAllowFail()) michael@0: return false; michael@0: } michael@0: michael@0: agents_.push_back(agent.forget()); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void CreateAgentNoInit() { michael@0: ScopedDeletePtr agent(new SignalingAgent("agent")); michael@0: agents_.push_back(agent.forget()); michael@0: } michael@0: michael@0: SignalingAgent *agent(size_t i) { michael@0: return agents_[i]; michael@0: } michael@0: michael@0: private: michael@0: std::vector agents_; michael@0: }; michael@0: michael@0: michael@0: class SignalingTest : public ::testing::Test { michael@0: public: michael@0: SignalingTest() michael@0: : init_(false), michael@0: a1_(nullptr), michael@0: a2_(nullptr), michael@0: wait_for_gather_(true), michael@0: stun_addr_(g_stun_server_address), michael@0: stun_port_(g_stun_server_port) {} michael@0: michael@0: SignalingTest(const std::string& stun_addr, uint16_t stun_port) michael@0: : a1_(nullptr), michael@0: a2_(nullptr), michael@0: wait_for_gather_(true), michael@0: stun_addr_(stun_addr), michael@0: stun_port_(stun_port) {} michael@0: michael@0: static void SetUpTestCase() { michael@0: } michael@0: michael@0: void EnsureInit() { michael@0: michael@0: if (init_) michael@0: return; michael@0: michael@0: a1_ = new SignalingAgent(callerName, stun_addr_, stun_port_); michael@0: a2_ = new SignalingAgent(calleeName, stun_addr_, stun_port_); michael@0: michael@0: a1_->Init(gMainThread); michael@0: a2_->Init(gMainThread); michael@0: michael@0: if (wait_for_gather_) { michael@0: WaitForGather(); michael@0: } michael@0: } michael@0: michael@0: void WaitForGather() { michael@0: a1_->WaitForGather(); michael@0: a2_->WaitForGather(); michael@0: } michael@0: michael@0: static void TearDownTestCase() { michael@0: } michael@0: michael@0: void CreateOffer(sipcc::MediaConstraints& constraints, michael@0: uint32_t offerFlags, uint32_t sdpCheck) { michael@0: EnsureInit(); michael@0: a1_->CreateOffer(constraints, offerFlags, sdpCheck); michael@0: } michael@0: michael@0: void CreateSetOffer(sipcc::MediaConstraints& constraints, uint32_t sdpCheck) { michael@0: EnsureInit(); michael@0: a1_->CreateOffer(constraints, OFFER_AV, sdpCheck); michael@0: a1_->SetLocal(TestObserver::OFFER, a1_->offer()); michael@0: } michael@0: michael@0: void OfferAnswer(sipcc::MediaConstraints& aconstraints, michael@0: sipcc::MediaConstraints& bconstraints, michael@0: uint32_t offerAnswerFlags, michael@0: bool finishAfterAnswer, uint32_t offerSdpCheck, michael@0: uint32_t answerSdpCheck) { michael@0: EnsureInit(); michael@0: a1_->CreateOffer(aconstraints, offerAnswerFlags, offerSdpCheck); michael@0: a1_->SetLocal(TestObserver::OFFER, a1_->offer()); michael@0: a2_->SetRemote(TestObserver::OFFER, a1_->offer()); michael@0: a2_->CreateAnswer(bconstraints, a1_->offer(), michael@0: offerAnswerFlags, answerSdpCheck); michael@0: if(true == finishAfterAnswer) { michael@0: a2_->SetLocal(TestObserver::ANSWER, a2_->answer()); michael@0: a1_->SetRemote(TestObserver::ANSWER, a2_->answer()); michael@0: michael@0: ASSERT_TRUE_WAIT(a1_->IceCompleted() == true, kDefaultTimeout); michael@0: ASSERT_TRUE_WAIT(a2_->IceCompleted() == true, kDefaultTimeout); michael@0: } michael@0: } michael@0: michael@0: void OfferModifiedAnswer(sipcc::MediaConstraints& aconstraints, michael@0: sipcc::MediaConstraints& bconstraints, michael@0: uint32_t offerSdpCheck, uint32_t answerSdpCheck) { michael@0: EnsureInit(); michael@0: a1_->CreateOffer(aconstraints, OFFER_AUDIO, offerSdpCheck); michael@0: a1_->SetLocal(TestObserver::OFFER, a1_->offer()); michael@0: a2_->SetRemote(TestObserver::OFFER, a1_->offer()); michael@0: a2_->CreateAnswer(bconstraints, a1_->offer(), OFFER_AUDIO | ANSWER_AUDIO, michael@0: answerSdpCheck); michael@0: a2_->SetLocal(TestObserver::ANSWER, a2_->answer()); michael@0: ParsedSDP sdpWrapper(a2_->answer()); michael@0: sdpWrapper.ReplaceLine("m=audio", "m=audio 65375 RTP/SAVPF 109 8 101\r\n"); michael@0: sdpWrapper.AddLine("a=rtpmap:8 PCMA/8000\r\n"); michael@0: std::cout << "Modified SDP " << std::endl michael@0: << indent(sdpWrapper.getSdp()) << std::endl; michael@0: a1_->SetRemote(TestObserver::ANSWER, sdpWrapper.getSdp()); michael@0: ASSERT_TRUE_WAIT(a1_->IceCompleted() == true, kDefaultTimeout); michael@0: ASSERT_TRUE_WAIT(a2_->IceCompleted() == true, kDefaultTimeout); michael@0: } michael@0: michael@0: void OfferAnswerTrickle(sipcc::MediaConstraints& aconstraints, michael@0: sipcc::MediaConstraints& bconstraints, michael@0: uint32_t offerSdpCheck, uint32_t answerSdpCheck) { michael@0: EnsureInit(); michael@0: a1_->CreateOffer(aconstraints, OFFER_AV, offerSdpCheck); michael@0: a1_->SetLocal(TestObserver::OFFER, a1_->offer()); michael@0: ParsedSDP a1_offer(a1_->offer()); michael@0: a2_->SetRemote(TestObserver::OFFER, a1_offer.sdp_without_ice_); michael@0: a2_->CreateAnswer(bconstraints, a1_offer.sdp_without_ice_, michael@0: OFFER_AV|ANSWER_AV, answerSdpCheck); michael@0: a2_->SetLocal(TestObserver::ANSWER, a2_->answer()); michael@0: ParsedSDP a2_answer(a2_->answer()); michael@0: a1_->SetRemote(TestObserver::ANSWER, a2_answer.sdp_without_ice_); michael@0: // Now set the trickle ICE candidates michael@0: a1_->DoTrickleIce(a2_answer); michael@0: a2_->DoTrickleIce(a1_offer); michael@0: ASSERT_TRUE_WAIT(a1_->IceCompleted() == true, kDefaultTimeout); michael@0: ASSERT_TRUE_WAIT(a2_->IceCompleted() == true, kDefaultTimeout); michael@0: } michael@0: michael@0: michael@0: void OfferAnswerTrickleChrome(sipcc::MediaConstraints& aconstraints, michael@0: sipcc::MediaConstraints& bconstraints, michael@0: uint32_t offerSdpCheck, uint32_t answerSdpCheck) { michael@0: EnsureInit(); michael@0: a1_->CreateOffer(aconstraints, OFFER_AV, offerSdpCheck); michael@0: a1_->SetLocal(TestObserver::OFFER, a1_->offer()); michael@0: ParsedSDP a1_offer(a1_->offer()); michael@0: a2_->SetRemote(TestObserver::OFFER, a1_offer.sdp_without_ice_); michael@0: a2_->CreateAnswer(bconstraints, a1_offer.sdp_without_ice_, michael@0: OFFER_AV|ANSWER_AV, answerSdpCheck); michael@0: a2_->SetLocal(TestObserver::ANSWER, a2_->answer()); michael@0: ParsedSDP a2_answer(a2_->answer()); michael@0: a1_->SetRemote(TestObserver::ANSWER, a2_answer.sdp_without_ice_); michael@0: // Now set the trickle ICE candidates michael@0: a1_->DoTrickleIceChrome(a2_answer); michael@0: a2_->DoTrickleIceChrome(a1_offer); michael@0: ASSERT_TRUE_WAIT(a1_->IceCompleted() == true, kDefaultTimeout); michael@0: ASSERT_TRUE_WAIT(a2_->IceCompleted() == true, kDefaultTimeout); michael@0: } michael@0: michael@0: void CreateOfferRemoveStream(sipcc::MediaConstraints& constraints, michael@0: uint32_t hints, uint32_t sdpCheck) { michael@0: EnsureInit(); michael@0: sipcc::MediaConstraints aconstraints; michael@0: aconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: aconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: a1_->CreateOffer(aconstraints, OFFER_AV, SHOULD_SENDRECV_AV ); michael@0: a1_->CreateOfferRemoveStream(constraints, hints, sdpCheck); michael@0: } michael@0: michael@0: void CreateOfferAudioOnly(sipcc::MediaConstraints& constraints, michael@0: uint32_t sdpCheck) { michael@0: EnsureInit(); michael@0: a1_->CreateOffer(constraints, OFFER_AUDIO, sdpCheck); michael@0: } michael@0: michael@0: void CreateOfferAddCandidate(sipcc::MediaConstraints& constraints, michael@0: const char * candidate, const char * mid, michael@0: unsigned short level, uint32_t sdpCheck) { michael@0: EnsureInit(); michael@0: a1_->CreateOffer(constraints, OFFER_AV, sdpCheck); michael@0: a1_->AddIceCandidate(candidate, mid, level, true); michael@0: } michael@0: michael@0: void AddIceCandidateEarly(const char * candidate, const char * mid, michael@0: unsigned short level) { michael@0: EnsureInit(); michael@0: a1_->AddIceCandidate(candidate, mid, level, false); michael@0: } michael@0: michael@0: void CheckRtcpFbSdp(const std::string &sdp, michael@0: const std::set& expected) { michael@0: michael@0: std::set::const_iterator it; michael@0: michael@0: // Iterate through the list of expected feedback types and ensure michael@0: // that none of them are missing. michael@0: for (it = expected.begin(); it != expected.end(); ++it) { michael@0: std::string attr = std::string("\r\na=rtcp-fb:120 ") + (*it) + "\r\n"; michael@0: std::cout << " - Checking for a=rtcp-fb: '" << *it << "'" << std::endl; michael@0: ASSERT_NE(sdp.find(attr), std::string::npos); michael@0: } michael@0: michael@0: // Iterate through all of the rtcp-fb lines in the SDP and ensure michael@0: // that all of them are expected. michael@0: ParsedSDP sdpWrapper(sdp); michael@0: std::vector values = sdpWrapper.GetLines("a=rtcp-fb:120"); michael@0: std::vector::iterator it2; michael@0: for (it2 = values.begin(); it2 != values.end(); ++it2) { michael@0: std::cout << " - Verifying that rtcp-fb is okay: '" << *it2 michael@0: << "'" << std::endl; michael@0: ASSERT_NE(0U, expected.count(*it2)); michael@0: } michael@0: } michael@0: michael@0: void TestRtcpFb(const std::set& feedback, michael@0: uint32_t rtcpFbFlags, michael@0: VideoSessionConduit::FrameRequestType frameRequestMethod) { michael@0: EnsureInit(); michael@0: sipcc::MediaConstraints constraints; michael@0: michael@0: a1_->CreateOffer(constraints, OFFER_AV, SHOULD_SENDRECV_AV); michael@0: a1_->SetLocal(TestObserver::OFFER, a1_->offer()); michael@0: michael@0: ParsedSDP sdpWrapper(a1_->offer()); michael@0: michael@0: // Strip out any existing rtcp-fb lines michael@0: sdpWrapper.DeleteAllLines("a=rtcp-fb:120"); michael@0: michael@0: // Add rtcp-fb lines for the desired feedback types michael@0: // We know that the video section is generated second (last), michael@0: // so appending these to the end of the SDP has the desired effect. michael@0: std::set::const_iterator it; michael@0: for (it = feedback.begin(); it != feedback.end(); ++it) { michael@0: sdpWrapper.AddLine(std::string("a=rtcp-fb:120 ") + (*it) + "\r\n"); michael@0: } michael@0: michael@0: std::cout << "Modified SDP " << std::endl michael@0: << indent(sdpWrapper.getSdp()) << std::endl; michael@0: michael@0: // Double-check that the offered SDP matches what we expect michael@0: CheckRtcpFbSdp(sdpWrapper.getSdp(), feedback); michael@0: michael@0: a2_->SetRemote(TestObserver::OFFER, sdpWrapper.getSdp()); michael@0: a2_->CreateAnswer(constraints, sdpWrapper.getSdp(), OFFER_AV | ANSWER_AV); michael@0: michael@0: CheckRtcpFbSdp(a2_->answer(), feedback); michael@0: michael@0: a2_->SetLocal(TestObserver::ANSWER, a2_->answer()); michael@0: a1_->SetRemote(TestObserver::ANSWER, a2_->answer()); michael@0: michael@0: ASSERT_TRUE_WAIT(a1_->IceCompleted() == true, kDefaultTimeout); michael@0: ASSERT_TRUE_WAIT(a2_->IceCompleted() == true, kDefaultTimeout); michael@0: michael@0: a1_->CloseSendStreams(); michael@0: a1_->CloseReceiveStreams(); michael@0: a2_->CloseSendStreams(); michael@0: a2_->CloseReceiveStreams(); michael@0: michael@0: // Check caller video settings for remote pipeline michael@0: a1_->CheckMediaPipeline(0, 2, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | michael@0: PIPELINE_SEND | PIPELINE_VIDEO | rtcpFbFlags, frameRequestMethod); michael@0: michael@0: // Check callee video settings for remote pipeline michael@0: a2_->CheckMediaPipeline(0, 2, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | michael@0: PIPELINE_VIDEO | rtcpFbFlags, frameRequestMethod); michael@0: } michael@0: michael@0: void SetTestStunServer() { michael@0: stun_addr_ = TestStunServer::GetInstance()->addr(); michael@0: stun_port_ = TestStunServer::GetInstance()->port(); michael@0: michael@0: TestStunServer::GetInstance()->SetActive(false); michael@0: TestStunServer::GetInstance()->SetResponseAddr( michael@0: kBogusSrflxAddress, kBogusSrflxPort); michael@0: } michael@0: michael@0: // Check max-fs and max-fr in SDP michael@0: void CheckMaxFsFrSdp(const std::string sdp, michael@0: int format, michael@0: int max_fs, michael@0: int max_fr) { michael@0: ParsedSDP sdpWrapper(sdp); michael@0: std::stringstream ss; michael@0: ss << "a=fmtp:" << format; michael@0: std::vector lines = sdpWrapper.GetLines(ss.str()); michael@0: michael@0: // Both max-fs and max-fr not exist michael@0: if (lines.empty()) { michael@0: ASSERT_EQ(max_fs, 0); michael@0: ASSERT_EQ(max_fr, 0); michael@0: return; michael@0: } michael@0: michael@0: // At most one instance allowed for each format michael@0: ASSERT_EQ(lines.size(), 1U); michael@0: michael@0: std::string line = lines.front(); michael@0: michael@0: // Make sure that max-fs doesn't exist michael@0: if (max_fs == 0) { michael@0: ASSERT_EQ(line.find("max-fs="), std::string::npos); michael@0: } michael@0: // Check max-fs value michael@0: if (max_fs > 0) { michael@0: std::stringstream ss; michael@0: ss << "max-fs=" << max_fs; michael@0: ASSERT_NE(line.find(ss.str()), std::string::npos); michael@0: } michael@0: // Make sure that max-fr doesn't exist michael@0: if (max_fr == 0) { michael@0: ASSERT_EQ(line.find("max-fr="), std::string::npos); michael@0: } michael@0: // Check max-fr value michael@0: if (max_fr > 0) { michael@0: std::stringstream ss; michael@0: ss << "max-fr=" << max_fr; michael@0: ASSERT_NE(line.find(ss.str()), std::string::npos); michael@0: } michael@0: } michael@0: michael@0: protected: michael@0: bool init_; michael@0: ScopedDeletePtr a1_; // Canonically "caller" michael@0: ScopedDeletePtr a2_; // Canonically "callee" michael@0: bool wait_for_gather_; michael@0: std::string stun_addr_; michael@0: uint16_t stun_port_; michael@0: }; michael@0: michael@0: static void SetIntPrefOnMainThread(nsCOMPtr prefs, michael@0: const char *pref_name, michael@0: int new_value) { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: prefs->SetIntPref(pref_name, new_value); michael@0: } michael@0: michael@0: static void SetMaxFsFr(nsCOMPtr prefs, michael@0: int max_fs, michael@0: int max_fr) { michael@0: gMainThread->Dispatch( michael@0: WrapRunnableNM(SetIntPrefOnMainThread, michael@0: prefs, michael@0: "media.navigator.video.max_fs", michael@0: max_fs), michael@0: NS_DISPATCH_SYNC); michael@0: michael@0: gMainThread->Dispatch( michael@0: WrapRunnableNM(SetIntPrefOnMainThread, michael@0: prefs, michael@0: "media.navigator.video.max_fr", michael@0: max_fr), michael@0: NS_DISPATCH_SYNC); michael@0: } michael@0: michael@0: class FsFrPrefClearer { michael@0: public: michael@0: FsFrPrefClearer(nsCOMPtr prefs): mPrefs(prefs) {} michael@0: ~FsFrPrefClearer() { michael@0: gMainThread->Dispatch( michael@0: WrapRunnableNM(FsFrPrefClearer::ClearUserPrefOnMainThread, michael@0: mPrefs, michael@0: "media.navigator.video.max_fs"), michael@0: NS_DISPATCH_SYNC); michael@0: gMainThread->Dispatch( michael@0: WrapRunnableNM(FsFrPrefClearer::ClearUserPrefOnMainThread, michael@0: mPrefs, michael@0: "media.navigator.video.max_fr"), michael@0: NS_DISPATCH_SYNC); michael@0: } michael@0: michael@0: static void ClearUserPrefOnMainThread(nsCOMPtr prefs, michael@0: const char *pref_name) { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: prefs->ClearUserPref(pref_name); michael@0: } michael@0: private: michael@0: nsCOMPtr mPrefs; michael@0: }; michael@0: michael@0: TEST_F(SignalingTest, JustInit) michael@0: { michael@0: } michael@0: michael@0: TEST_F(SignalingTest, CreateSetOffer) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: CreateSetOffer(constraints, SHOULD_SENDRECV_AV); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, CreateOfferAudioVideoConstraintUndefined) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: CreateOffer(constraints, OFFER_AV, SHOULD_SENDRECV_AV); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, CreateOfferNoVideoStreamRecvVideo) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: constraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: constraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: CreateOffer(constraints, OFFER_AUDIO, michael@0: SHOULD_SENDRECV_AUDIO | SHOULD_RECV_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, CreateOfferNoAudioStreamRecvAudio) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: constraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: constraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: CreateOffer(constraints, OFFER_VIDEO, michael@0: SHOULD_RECV_AUDIO | SHOULD_SENDRECV_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, CreateOfferNoVideoStream) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: constraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: constraints.setBooleanConstraint("OfferToReceiveVideo", false, false); michael@0: CreateOffer(constraints, OFFER_AUDIO, michael@0: SHOULD_SENDRECV_AUDIO | SHOULD_OMIT_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, CreateOfferNoAudioStream) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: constraints.setBooleanConstraint("OfferToReceiveAudio", false, false); michael@0: constraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: CreateOffer(constraints, OFFER_VIDEO, michael@0: SHOULD_OMIT_AUDIO | SHOULD_SENDRECV_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, CreateOfferDontReceiveAudio) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: constraints.setBooleanConstraint("OfferToReceiveAudio", false, false); michael@0: constraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: CreateOffer(constraints, OFFER_AV, michael@0: SHOULD_SEND_AUDIO | SHOULD_SENDRECV_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, CreateOfferDontReceiveVideo) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: constraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: constraints.setBooleanConstraint("OfferToReceiveVideo", false, false); michael@0: CreateOffer(constraints, OFFER_AV, michael@0: SHOULD_SENDRECV_AUDIO | SHOULD_SEND_VIDEO); michael@0: } michael@0: michael@0: // XXX Disabled pending resolution of Bug 840728 michael@0: TEST_F(SignalingTest, DISABLED_CreateOfferRemoveAudioStream) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: constraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: constraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: CreateOfferRemoveStream(constraints, DOMMediaStream::HINT_CONTENTS_AUDIO, michael@0: SHOULD_RECV_AUDIO | SHOULD_SENDRECV_VIDEO); michael@0: } michael@0: michael@0: // XXX Disabled pending resolution of Bug 840728 michael@0: TEST_F(SignalingTest, DISABLED_CreateOfferDontReceiveAudioRemoveAudioStream) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: constraints.setBooleanConstraint("OfferToReceiveAudio", false, false); michael@0: constraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: CreateOfferRemoveStream(constraints, DOMMediaStream::HINT_CONTENTS_AUDIO, michael@0: SHOULD_SENDRECV_VIDEO); michael@0: } michael@0: michael@0: // XXX Disabled pending resolution of Bug 840728 michael@0: TEST_F(SignalingTest, DISABLED_CreateOfferDontReceiveVideoRemoveVideoStream) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: constraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: constraints.setBooleanConstraint("OfferToReceiveVideo", false, false); michael@0: CreateOfferRemoveStream(constraints, DOMMediaStream::HINT_CONTENTS_VIDEO, michael@0: SHOULD_SENDRECV_AUDIO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferAnswerNothingDisabled) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: OfferAnswer(constraints, constraints, OFFER_AV | ANSWER_AV, false, michael@0: SHOULD_SENDRECV_AV, SHOULD_SENDRECV_AV); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferAnswerDontReceiveAudioOnOffer) michael@0: { michael@0: sipcc::MediaConstraints offerconstraints; michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveAudio", false, false); michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: sipcc::MediaConstraints answerconstraints; michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: OfferAnswer(offerconstraints, answerconstraints, OFFER_AV | ANSWER_AV, michael@0: false, SHOULD_SEND_AUDIO | SHOULD_SENDRECV_VIDEO, michael@0: SHOULD_RECV_AUDIO | SHOULD_SENDRECV_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferAnswerDontReceiveVideoOnOffer) michael@0: { michael@0: sipcc::MediaConstraints offerconstraints; michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveVideo", false, false); michael@0: sipcc::MediaConstraints answerconstraints; michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: OfferAnswer(offerconstraints, answerconstraints, OFFER_AV | ANSWER_AV, michael@0: false, SHOULD_SENDRECV_AUDIO | SHOULD_SEND_VIDEO, michael@0: SHOULD_SENDRECV_AUDIO | SHOULD_RECV_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferAnswerDontReceiveAudioOnAnswer) michael@0: { michael@0: sipcc::MediaConstraints offerconstraints; michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: sipcc::MediaConstraints answerconstraints; michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveAudio", false, false); michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: OfferAnswer(offerconstraints, answerconstraints, OFFER_AV | ANSWER_AV, michael@0: false, SHOULD_SENDRECV_AV, michael@0: SHOULD_SEND_AUDIO | SHOULD_SENDRECV_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferAnswerDontReceiveVideoOnAnswer) michael@0: { michael@0: sipcc::MediaConstraints offerconstraints; michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: sipcc::MediaConstraints answerconstraints; michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveVideo", false, false); michael@0: OfferAnswer(offerconstraints, answerconstraints, OFFER_AV | ANSWER_AV, michael@0: false, SHOULD_SENDRECV_AV, michael@0: SHOULD_SENDRECV_AUDIO | SHOULD_SEND_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferAnswerDontAddAudioStreamOnOfferRecvAudio) michael@0: { michael@0: sipcc::MediaConstraints offerconstraints; michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: sipcc::MediaConstraints answerconstraints; michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: OfferAnswer(offerconstraints, answerconstraints, OFFER_VIDEO | ANSWER_AV, michael@0: false, SHOULD_RECV_AUDIO | SHOULD_SENDRECV_VIDEO, michael@0: SHOULD_SEND_AUDIO | SHOULD_SENDRECV_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferAnswerDontAddAudioStreamOnOffer) michael@0: { michael@0: sipcc::MediaConstraints offerconstraints; michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveAudio", false, false); michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: sipcc::MediaConstraints answerconstraints; michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: OfferAnswer(offerconstraints, answerconstraints, OFFER_VIDEO | ANSWER_AV, michael@0: false, SHOULD_OMIT_AUDIO | SHOULD_SENDRECV_VIDEO, michael@0: SHOULD_OMIT_AUDIO | SHOULD_SENDRECV_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferAnswerDontAddVideoStreamOnOfferRecvVideo) michael@0: { michael@0: sipcc::MediaConstraints offerconstraints; michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: sipcc::MediaConstraints answerconstraints; michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: OfferAnswer(offerconstraints, answerconstraints, OFFER_AUDIO | ANSWER_AV, michael@0: false, SHOULD_SENDRECV_AUDIO | SHOULD_RECV_VIDEO, michael@0: SHOULD_SENDRECV_AUDIO | SHOULD_SEND_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferAnswerDontAddVideoStreamOnOffer) michael@0: { michael@0: sipcc::MediaConstraints offerconstraints; michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveVideo", false, false); michael@0: sipcc::MediaConstraints answerconstraints; michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: OfferAnswer(offerconstraints, answerconstraints, OFFER_AUDIO | ANSWER_AV, michael@0: false, SHOULD_SENDRECV_AUDIO | SHOULD_OMIT_VIDEO, michael@0: SHOULD_SENDRECV_AUDIO | SHOULD_OMIT_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferAnswerDontAddAudioStreamOnAnswer) michael@0: { michael@0: sipcc::MediaConstraints offerconstraints; michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: sipcc::MediaConstraints answerconstraints; michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: OfferAnswer(offerconstraints, answerconstraints, OFFER_AV | ANSWER_VIDEO, michael@0: false, SHOULD_SENDRECV_AV, michael@0: SHOULD_RECV_AUDIO | SHOULD_SENDRECV_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferAnswerDontAddVideoStreamOnAnswer) michael@0: { michael@0: sipcc::MediaConstraints offerconstraints; michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: sipcc::MediaConstraints answerconstraints; michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: OfferAnswer(offerconstraints, answerconstraints, OFFER_AV | ANSWER_AUDIO, michael@0: false, SHOULD_SENDRECV_AV, michael@0: SHOULD_SENDRECV_AUDIO | SHOULD_RECV_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferAnswerDontAddVideoStreamOnAnswerDontReceiveVideoOnAnswer) michael@0: { michael@0: sipcc::MediaConstraints offerconstraints; michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: sipcc::MediaConstraints answerconstraints; michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveVideo", false, false); michael@0: OfferAnswer(offerconstraints, answerconstraints, OFFER_AV | ANSWER_AUDIO, michael@0: false, SHOULD_SENDRECV_AV, SHOULD_SENDRECV_AUDIO ); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferAnswerDontAddAudioStreamOnAnswerDontReceiveAudioOnAnswer) michael@0: { michael@0: sipcc::MediaConstraints offerconstraints; michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: sipcc::MediaConstraints answerconstraints; michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveAudio", false, false); michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: OfferAnswer(offerconstraints, answerconstraints, OFFER_AV | ANSWER_VIDEO, michael@0: false, SHOULD_SENDRECV_AV, michael@0: SHOULD_REJECT_AUDIO | SHOULD_SENDRECV_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferAnswerDontAddAudioStreamOnOfferDontReceiveAudioOnOffer) michael@0: { michael@0: sipcc::MediaConstraints offerconstraints; michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveAudio", false, false); michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: sipcc::MediaConstraints answerconstraints; michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: OfferAnswer(offerconstraints, answerconstraints, OFFER_VIDEO | ANSWER_AV, michael@0: false, SHOULD_SENDRECV_VIDEO, SHOULD_SENDRECV_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferAnswerDontAddVideoStreamOnOfferDontReceiveVideoOnOffer) michael@0: { michael@0: sipcc::MediaConstraints offerconstraints; michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveVideo", false, false); michael@0: sipcc::MediaConstraints answerconstraints; michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: OfferAnswer(offerconstraints, answerconstraints, OFFER_AUDIO | ANSWER_AV, michael@0: false, SHOULD_SENDRECV_AUDIO | SHOULD_OMIT_VIDEO, michael@0: SHOULD_SENDRECV_AUDIO | SHOULD_OMIT_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferAnswerDontReceiveAudioNoAudioStreamOnOfferDontReceiveVideoOnAnswer) michael@0: { michael@0: sipcc::MediaConstraints offerconstraints; michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveAudio", false, false); michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: sipcc::MediaConstraints answerconstraints; michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveVideo", false, false); michael@0: OfferAnswer(offerconstraints, answerconstraints, OFFER_VIDEO | ANSWER_AV, michael@0: false, SHOULD_SENDRECV_VIDEO, SHOULD_SEND_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, CreateOfferAddCandidate) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: CreateOfferAddCandidate(constraints, strSampleCandidate.c_str(), michael@0: strSampleMid.c_str(), nSamplelevel, michael@0: SHOULD_SENDRECV_AV); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, AddIceCandidateEarly) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: AddIceCandidateEarly(strSampleCandidate.c_str(), michael@0: strSampleMid.c_str(), nSamplelevel); michael@0: } michael@0: michael@0: // XXX adam@nostrum.com -- This test seems questionable; we need to think michael@0: // through what actually needs to be tested here. michael@0: TEST_F(SignalingTest, DISABLED_OfferAnswerReNegotiateOfferAnswerDontReceiveVideoNoVideoStream) michael@0: { michael@0: sipcc::MediaConstraints aconstraints; michael@0: aconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: aconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: michael@0: sipcc::MediaConstraints bconstraints; michael@0: bconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: bconstraints.setBooleanConstraint("OfferToReceiveVideo", false, false); michael@0: michael@0: OfferAnswer(aconstraints, aconstraints, OFFER_AV | ANSWER_AV, michael@0: false, SHOULD_SENDRECV_AV, SHOULD_SENDRECV_AV); michael@0: OfferAnswer(bconstraints, bconstraints, OFFER_AUDIO | ANSWER_AV, michael@0: false, SHOULD_SENDRECV_AUDIO | SHOULD_SEND_VIDEO, michael@0: SHOULD_SENDRECV_AUDIO | SHOULD_INACTIVE_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferAnswerDontAddAudioStreamOnAnswerNoConstraints) michael@0: { michael@0: sipcc::MediaConstraints offerconstraints; michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: sipcc::MediaConstraints answerconstraints; michael@0: OfferAnswer(offerconstraints, answerconstraints, OFFER_AV | ANSWER_VIDEO, michael@0: false, SHOULD_SENDRECV_AV, michael@0: SHOULD_RECV_AUDIO | SHOULD_SENDRECV_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferAnswerDontAddVideoStreamOnAnswerNoConstraints) michael@0: { michael@0: sipcc::MediaConstraints offerconstraints; michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: sipcc::MediaConstraints answerconstraints; michael@0: OfferAnswer(offerconstraints, answerconstraints, OFFER_AV | ANSWER_AUDIO, michael@0: false, SHOULD_SENDRECV_AV, michael@0: SHOULD_SENDRECV_AUDIO | SHOULD_RECV_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferAnswerDontAddAudioVideoStreamsOnAnswerNoConstraints) michael@0: { michael@0: sipcc::MediaConstraints offerconstraints; michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: offerconstraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: sipcc::MediaConstraints answerconstraints; michael@0: OfferAnswer(offerconstraints, answerconstraints, OFFER_AV | ANSWER_NONE, michael@0: false, SHOULD_SENDRECV_AV, michael@0: SHOULD_RECV_AUDIO | SHOULD_RECV_VIDEO); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, FullCall) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: OfferAnswer(constraints, constraints, OFFER_AV | ANSWER_AV, michael@0: true, SHOULD_SENDRECV_AV, SHOULD_SENDRECV_AV); michael@0: michael@0: // Wait for some data to get written michael@0: ASSERT_TRUE_WAIT(a1_->GetPacketsSent(0) >= 40 && michael@0: a2_->GetPacketsReceived(0) >= 40, kDefaultTimeout * 2); michael@0: michael@0: a1_->CloseSendStreams(); michael@0: a2_->CloseReceiveStreams(); michael@0: // Check that we wrote a bunch of data michael@0: ASSERT_GE(a1_->GetPacketsSent(0), 40); michael@0: //ASSERT_GE(a2_->GetPacketsSent(0), 40); michael@0: //ASSERT_GE(a1_->GetPacketsReceived(0), 40); michael@0: ASSERT_GE(a2_->GetPacketsReceived(0), 40); michael@0: michael@0: // Check the low-level media pipeline michael@0: // for RTP and RTCP flows michael@0: // The first Local pipeline gets stored at 0 michael@0: a1_->CheckMediaPipeline(0, 0, fRtcpMux ? michael@0: PIPELINE_LOCAL | PIPELINE_RTCP_MUX | PIPELINE_SEND : michael@0: PIPELINE_LOCAL | PIPELINE_SEND); michael@0: michael@0: // The first Remote pipeline gets stored at 1 michael@0: a2_->CheckMediaPipeline(0, 1, (fRtcpMux ? PIPELINE_RTCP_MUX : 0)); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, FullCallAudioOnly) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: OfferAnswer(constraints, constraints, OFFER_AUDIO | ANSWER_AUDIO, michael@0: true, SHOULD_SENDRECV_AUDIO, SHOULD_SENDRECV_AUDIO); michael@0: michael@0: // Wait for some data to get written michael@0: ASSERT_TRUE_WAIT(a1_->GetPacketsSent(0) >= 40 && michael@0: a2_->GetPacketsReceived(0) >= 40, kDefaultTimeout * 2); michael@0: michael@0: a1_->CloseSendStreams(); michael@0: a2_->CloseReceiveStreams(); michael@0: // Check that we wrote a bunch of data michael@0: ASSERT_GE(a1_->GetPacketsSent(0), 40); michael@0: //ASSERT_GE(a2_->GetPacketsSent(0), 40); michael@0: //ASSERT_GE(a1_->GetPacketsReceived(0), 40); michael@0: ASSERT_GE(a2_->GetPacketsReceived(0), 40); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, FullCallAnswererRejectsVideo) michael@0: { michael@0: sipcc::MediaConstraints offerconstraints; michael@0: sipcc::MediaConstraints answerconstraints; michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: answerconstraints.setBooleanConstraint("OfferToReceiveVideo", false, false); michael@0: OfferAnswer(offerconstraints, answerconstraints, OFFER_AV | ANSWER_AUDIO, michael@0: true, SHOULD_SENDRECV_AV, SHOULD_SENDRECV_AUDIO); michael@0: michael@0: // Wait for some data to get written michael@0: ASSERT_TRUE_WAIT(a1_->GetPacketsSent(0) >= 40 && michael@0: a2_->GetPacketsReceived(0) >= 40, kDefaultTimeout * 2); michael@0: michael@0: a1_->CloseSendStreams(); michael@0: a2_->CloseReceiveStreams(); michael@0: // Check that we wrote a bunch of data michael@0: ASSERT_GE(a1_->GetPacketsSent(0), 40); michael@0: //ASSERT_GE(a2_->GetPacketsSent(0), 40); michael@0: //ASSERT_GE(a1_->GetPacketsReceived(0), 40); michael@0: ASSERT_GE(a2_->GetPacketsReceived(0), 40); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, FullCallVideoOnly) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: OfferAnswer(constraints, constraints, OFFER_VIDEO | ANSWER_VIDEO, michael@0: true, SHOULD_SENDRECV_VIDEO, SHOULD_SENDRECV_VIDEO); michael@0: michael@0: // If we could check for video packets, we would wait for some to be written michael@0: // here. Since we can't, we don't. michael@0: // ASSERT_TRUE_WAIT(a1_->GetPacketsSent(0) >= 40 && michael@0: // a2_->GetPacketsReceived(0) >= 40, kDefaultTimeout * 2); michael@0: michael@0: a1_->CloseSendStreams(); michael@0: a2_->CloseReceiveStreams(); michael@0: michael@0: // FIXME -- Ideally we would check that packets were sent michael@0: // and received; however, the test driver setup does not michael@0: // currently support sending/receiving with Fake_VideoStreamSource. michael@0: // michael@0: // Check that we wrote a bunch of data michael@0: // ASSERT_GE(a1_->GetPacketsSent(0), 40); michael@0: //ASSERT_GE(a2_->GetPacketsSent(0), 40); michael@0: //ASSERT_GE(a1_->GetPacketsReceived(0), 40); michael@0: // ASSERT_GE(a2_->GetPacketsReceived(0), 40); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferModifiedAnswer) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: OfferModifiedAnswer(constraints, constraints, SHOULD_SENDRECV_AUDIO, michael@0: SHOULD_SENDRECV_AUDIO); michael@0: a1_->CloseSendStreams(); michael@0: a2_->CloseReceiveStreams(); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, FullCallTrickle) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: OfferAnswerTrickle(constraints, constraints, michael@0: SHOULD_SENDRECV_AV, SHOULD_SENDRECV_AV); michael@0: michael@0: std::cerr << "ICE handshake completed" << std::endl; michael@0: michael@0: // Wait for some data to get written michael@0: ASSERT_TRUE_WAIT(a1_->GetPacketsSent(0) >= 40 && michael@0: a2_->GetPacketsReceived(0) >= 40, kDefaultTimeout * 2); michael@0: michael@0: a1_->CloseSendStreams(); michael@0: a2_->CloseReceiveStreams(); michael@0: ASSERT_GE(a1_->GetPacketsSent(0), 40); michael@0: ASSERT_GE(a2_->GetPacketsReceived(0), 40); michael@0: } michael@0: michael@0: // Offer answer with trickle but with chrome-style candidates michael@0: TEST_F(SignalingTest, FullCallTrickleChrome) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: OfferAnswerTrickleChrome(constraints, constraints, michael@0: SHOULD_SENDRECV_AV, SHOULD_SENDRECV_AV); michael@0: michael@0: std::cerr << "ICE handshake completed" << std::endl; michael@0: michael@0: // Wait for some data to get written michael@0: ASSERT_TRUE_WAIT(a1_->GetPacketsSent(0) >= 40 && michael@0: a2_->GetPacketsReceived(0) >= 40, kDefaultTimeout * 2); michael@0: michael@0: a1_->CloseSendStreams(); michael@0: a2_->CloseReceiveStreams(); michael@0: ASSERT_GE(a1_->GetPacketsSent(0), 40); michael@0: ASSERT_GE(a2_->GetPacketsReceived(0), 40); michael@0: } michael@0: michael@0: // This test comes from Bug 810220 michael@0: TEST_F(SignalingTest, AudioOnlyG711Call) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: const std::string& offer(strG711SdpOffer); michael@0: michael@0: std::cout << "Setting offer to:" << std::endl << indent(offer) << std::endl; michael@0: a2_->SetRemote(TestObserver::OFFER, offer); michael@0: michael@0: std::cout << "Creating answer:" << std::endl; michael@0: a2_->CreateAnswer(constraints, offer, OFFER_AUDIO | ANSWER_AUDIO, michael@0: DONT_CHECK_AUDIO | DONT_CHECK_VIDEO | DONT_CHECK_DATA); michael@0: michael@0: std::string answer = a2_->answer(); michael@0: michael@0: // They didn't offer opus, so our answer shouldn't include it. michael@0: ASSERT_EQ(answer.find(" opus/"), std::string::npos); michael@0: michael@0: // They also didn't offer video or application michael@0: ASSERT_EQ(answer.find("video"), std::string::npos); michael@0: ASSERT_EQ(answer.find("application"), std::string::npos); michael@0: michael@0: // We should answer with PCMU and telephone-event michael@0: ASSERT_NE(answer.find(" PCMU/8000"), std::string::npos); michael@0: ASSERT_NE(answer.find(" telephone-event/8000"), std::string::npos); michael@0: michael@0: // Double-check the directionality michael@0: ASSERT_NE(answer.find("\r\na=sendrecv"), std::string::npos); michael@0: michael@0: } michael@0: michael@0: // This test comes from Bug814038 michael@0: TEST_F(SignalingTest, ChromeOfferAnswer) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: michael@0: // This is captured SDP from an early interop attempt with Chrome. michael@0: std::string offer = michael@0: "v=0\r\n" michael@0: "o=- 1713781661 2 IN IP4 127.0.0.1\r\n" michael@0: "s=-\r\n" michael@0: "t=0 0\r\n" michael@0: "a=group:BUNDLE audio video\r\n" michael@0: michael@0: "m=audio 1 RTP/SAVPF 103 104 111 0 8 107 106 105 13 126\r\n" michael@0: "a=fingerprint:sha-1 4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:" michael@0: "5D:49:6B:19:E5:7C:AB\r\n" michael@0: "c=IN IP4 0.0.0.0\r\n" michael@0: "a=rtcp:1 IN IP4 0.0.0.0\r\n" michael@0: "a=ice-ufrag:lBrbdDfrVBH1cldN\r\n" michael@0: "a=ice-pwd:rzh23jet4QpCaEoj9Sl75pL3\r\n" michael@0: "a=ice-options:google-ice\r\n" michael@0: "a=sendrecv\r\n" michael@0: "a=mid:audio\r\n" michael@0: "a=rtcp-mux\r\n" michael@0: "a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:" michael@0: "RzrYlzpkTsvgYFD1hQqNCzQ7y4emNLKI1tODsjim\r\n" michael@0: "a=rtpmap:103 ISAC/16000\r\n" michael@0: "a=rtpmap:104 ISAC/32000\r\n" michael@0: // NOTE: the actual SDP that Chrome sends at the moment michael@0: // doesn't indicate two channels. I've amended their SDP michael@0: // here, under the assumption that the constraints michael@0: // described in draft-spittka-payload-rtp-opus will michael@0: // eventually be implemented by Google. michael@0: "a=rtpmap:111 opus/48000/2\r\n" michael@0: "a=rtpmap:0 PCMU/8000\r\n" michael@0: "a=rtpmap:8 PCMA/8000\r\n" michael@0: "a=rtpmap:107 CN/48000\r\n" michael@0: "a=rtpmap:106 CN/32000\r\n" michael@0: "a=rtpmap:105 CN/16000\r\n" michael@0: "a=rtpmap:13 CN/8000\r\n" michael@0: "a=rtpmap:126 telephone-event/8000\r\n" michael@0: "a=ssrc:661333377 cname:KIXaNxUlU5DP3fVS\r\n" michael@0: "a=ssrc:661333377 msid:A5UL339RyGxT7zwgyF12BFqesxkmbUsaycp5 a0\r\n" michael@0: "a=ssrc:661333377 mslabel:A5UL339RyGxT7zwgyF12BFqesxkmbUsaycp5\r\n" michael@0: "a=ssrc:661333377 label:A5UL339RyGxT7zwgyF12BFqesxkmbUsaycp5a0\r\n" michael@0: michael@0: "m=video 1 RTP/SAVPF 100 101 102\r\n" michael@0: "a=fingerprint:sha-1 4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:" michael@0: "6B:19:E5:7C:AB\r\n" michael@0: "c=IN IP4 0.0.0.0\r\n" michael@0: "a=rtcp:1 IN IP4 0.0.0.0\r\n" michael@0: "a=ice-ufrag:lBrbdDfrVBH1cldN\r\n" michael@0: "a=ice-pwd:rzh23jet4QpCaEoj9Sl75pL3\r\n" michael@0: "a=ice-options:google-ice\r\n" michael@0: "a=sendrecv\r\n" michael@0: "a=mid:video\r\n" michael@0: "a=rtcp-mux\r\n" michael@0: "a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:" michael@0: "RzrYlzpkTsvgYFD1hQqNCzQ7y4emNLKI1tODsjim\r\n" michael@0: "a=rtpmap:100 VP8/90000\r\n" michael@0: "a=rtpmap:101 red/90000\r\n" michael@0: "a=rtpmap:102 ulpfec/90000\r\n" michael@0: "a=rtcp-fb:100 nack\r\n" michael@0: "a=rtcp-fb:100 ccm fir\r\n" michael@0: "a=ssrc:3012607008 cname:KIXaNxUlU5DP3fVS\r\n" michael@0: "a=ssrc:3012607008 msid:A5UL339RyGxT7zwgyF12BFqesxkmbUsaycp5 v0\r\n" michael@0: "a=ssrc:3012607008 mslabel:A5UL339RyGxT7zwgyF12BFqesxkmbUsaycp5\r\n" michael@0: "a=ssrc:3012607008 label:A5UL339RyGxT7zwgyF12BFqesxkmbUsaycp5v0\r\n"; michael@0: michael@0: michael@0: std::cout << "Setting offer to:" << std::endl << indent(offer) << std::endl; michael@0: a2_->SetRemote(TestObserver::OFFER, offer); michael@0: michael@0: std::cout << "Creating answer:" << std::endl; michael@0: a2_->CreateAnswer(constraints, offer, OFFER_AUDIO | ANSWER_AUDIO); michael@0: michael@0: std::string answer = a2_->answer(); michael@0: } michael@0: michael@0: michael@0: TEST_F(SignalingTest, FullChromeHandshake) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: constraints.setBooleanConstraint("OfferToReceiveAudio", true, false); michael@0: constraints.setBooleanConstraint("OfferToReceiveVideo", true, false); michael@0: michael@0: std::string offer = "v=0\r\n" michael@0: "o=- 3835809413 2 IN IP4 127.0.0.1\r\n" michael@0: "s=-\r\n" michael@0: "t=0 0\r\n" michael@0: "a=group:BUNDLE audio video\r\n" michael@0: "a=msid-semantic: WMS ahheYQXHFU52slYMrWNtKUyHCtWZsOJgjlOH\r\n" michael@0: "m=audio 1 RTP/SAVPF 103 104 111 0 8 107 106 105 13 126\r\n" michael@0: "c=IN IP4 1.1.1.1\r\n" michael@0: "a=rtcp:1 IN IP4 1.1.1.1\r\n" michael@0: "a=ice-ufrag:jz9UBk9RT8eCQXiL\r\n" michael@0: "a=ice-pwd:iscXxsdU+0gracg0g5D45orx\r\n" michael@0: "a=ice-options:google-ice\r\n" michael@0: "a=fingerprint:sha-256 A8:76:8C:4C:FA:2E:67:D7:F8:1D:28:4E:90:24:04:" michael@0: "12:EB:B4:A6:69:3D:05:92:E4:91:C3:EA:F9:B7:54:D3:09\r\n" michael@0: "a=sendrecv\r\n" michael@0: "a=mid:audio\r\n" michael@0: "a=rtcp-mux\r\n" michael@0: "a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:/he/v44FKu/QvEhex86zV0pdn2V" michael@0: "4Y7wB2xaZ8eUy\r\n" michael@0: "a=rtpmap:103 ISAC/16000\r\n" michael@0: "a=rtpmap:104 ISAC/32000\r\n" michael@0: "a=rtpmap:111 opus/48000/2\r\n" michael@0: "a=rtpmap:0 PCMU/8000\r\n" michael@0: "a=rtpmap:8 PCMA/8000\r\n" michael@0: "a=rtpmap:107 CN/48000\r\n" michael@0: "a=rtpmap:106 CN/32000\r\n" michael@0: "a=rtpmap:105 CN/16000\r\n" michael@0: "a=rtpmap:13 CN/8000\r\n" michael@0: "a=rtpmap:126 telephone-event/8000\r\n" michael@0: "a=ssrc:3389377748 cname:G5I+Jxz4rcaq8IIK\r\n" michael@0: "a=ssrc:3389377748 msid:ahheYQXHFU52slYMrWNtKUyHCtWZsOJgjlOH a0\r\n" michael@0: "a=ssrc:3389377748 mslabel:ahheYQXHFU52slYMrWNtKUyHCtWZsOJgjlOH\r\n" michael@0: "a=ssrc:3389377748 label:ahheYQXHFU52slYMrWNtKUyHCtWZsOJgjlOHa0\r\n" michael@0: "m=video 1 RTP/SAVPF 100 116 117\r\n" michael@0: "c=IN IP4 1.1.1.1\r\n" michael@0: "a=rtcp:1 IN IP4 1.1.1.1\r\n" michael@0: "a=ice-ufrag:jz9UBk9RT8eCQXiL\r\n" michael@0: "a=ice-pwd:iscXxsdU+0gracg0g5D45orx\r\n" michael@0: "a=ice-options:google-ice\r\n" michael@0: "a=fingerprint:sha-256 A8:76:8C:4C:FA:2E:67:D7:F8:1D:28:4E:90:24:04:" michael@0: "12:EB:B4:A6:69:3D:05:92:E4:91:C3:EA:F9:B7:54:D3:09\r\n" michael@0: "a=sendrecv\r\n" michael@0: "a=mid:video\r\n" michael@0: "a=rtcp-mux\r\n" michael@0: "a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:/he/v44FKu/QvEhex86zV0pdn2V" michael@0: "4Y7wB2xaZ8eUy\r\n" michael@0: "a=rtpmap:100 VP8/90000\r\n" michael@0: "a=rtpmap:116 red/90000\r\n" michael@0: "a=rtpmap:117 ulpfec/90000\r\n" michael@0: "a=ssrc:3613537198 cname:G5I+Jxz4rcaq8IIK\r\n" michael@0: "a=ssrc:3613537198 msid:ahheYQXHFU52slYMrWNtKUyHCtWZsOJgjlOH v0\r\n" michael@0: "a=ssrc:3613537198 mslabel:ahheYQXHFU52slYMrWNtKUyHCtWZsOJgjlOH\r\n" michael@0: "a=ssrc:3613537198 label:ahheYQXHFU52slYMrWNtKUyHCtWZsOJgjlOHv0\r\n"; michael@0: michael@0: std::cout << "Setting offer to:" << std::endl << indent(offer) << std::endl; michael@0: a2_->SetRemote(TestObserver::OFFER, offer); michael@0: michael@0: std::cout << "Creating answer:" << std::endl; michael@0: a2_->CreateAnswer(constraints, offer, OFFER_AUDIO | ANSWER_AUDIO); michael@0: michael@0: std::cout << "Setting answer" << std::endl; michael@0: a2_->SetLocal(TestObserver::ANSWER, a2_->answer()); michael@0: michael@0: std::string answer = a2_->answer(); michael@0: ASSERT_NE(answer.find("111 opus/"), std::string::npos); michael@0: } michael@0: michael@0: // Disabled pending resolution of bug 818640. michael@0: TEST_F(SignalingTest, DISABLED_OfferAllDynamicTypes) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: std::string offer; michael@0: for (int i = 96; i < 128; i++) michael@0: { michael@0: std::stringstream ss; michael@0: ss << i; michael@0: std::cout << "Trying dynamic pt = " << i << std::endl; michael@0: offer = michael@0: "v=0\r\n" michael@0: "o=- 1 1 IN IP4 148.147.200.251\r\n" michael@0: "s=-\r\n" michael@0: "b=AS:64\r\n" michael@0: "t=0 0\r\n" michael@0: "a=fingerprint:sha-256 F3:FA:20:C0:CD:48:C4:5F:02:5F:A5:D3:21:D0:2D:48:" michael@0: "7B:31:60:5C:5A:D8:0D:CD:78:78:6C:6D:CE:CC:0C:67\r\n" michael@0: "m=audio 9000 RTP/AVP " + ss.str() + "\r\n" michael@0: "c=IN IP4 148.147.200.251\r\n" michael@0: "b=TIAS:64000\r\n" michael@0: "a=rtpmap:" + ss.str() +" opus/48000/2\r\n" michael@0: "a=candidate:0 1 udp 2130706432 148.147.200.251 9000 typ host\r\n" michael@0: "a=candidate:0 2 udp 2130706432 148.147.200.251 9005 typ host\r\n" michael@0: "a=ice-ufrag:cYuakxkEKH+RApYE\r\n" michael@0: "a=ice-pwd:bwtpzLZD+3jbu8vQHvEa6Xuq\r\n" michael@0: "a=sendrecv\r\n"; michael@0: michael@0: /* michael@0: std::cout << "Setting offer to:" << std::endl michael@0: << indent(offer) << std::endl; michael@0: */ michael@0: a2_->SetRemote(TestObserver::OFFER, offer); michael@0: michael@0: //std::cout << "Creating answer:" << std::endl; michael@0: a2_->CreateAnswer(constraints, offer, OFFER_AUDIO | ANSWER_AUDIO); michael@0: michael@0: std::string answer = a2_->answer(); michael@0: michael@0: ASSERT_NE(answer.find(ss.str() + " opus/"), std::string::npos); michael@0: } michael@0: michael@0: } michael@0: michael@0: TEST_F(SignalingTest, OfferAnswerCheckDescriptions) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: OfferAnswer(constraints, constraints, OFFER_AV | ANSWER_AV, true, michael@0: SHOULD_SENDRECV_AV, SHOULD_SENDRECV_AV); michael@0: michael@0: std::cout << "Caller's Local Description: " << std::endl michael@0: << indent(a1_->getLocalDescription()) << std::endl << std::endl; michael@0: michael@0: std::cout << "Caller's Remote Description: " << std::endl michael@0: << indent(a1_->getRemoteDescription()) << std::endl << std::endl; michael@0: michael@0: std::cout << "Callee's Local Description: " << std::endl michael@0: << indent(a2_->getLocalDescription()) << std::endl << std::endl; michael@0: michael@0: std::cout << "Callee's Remote Description: " << std::endl michael@0: << indent(a2_->getRemoteDescription()) << std::endl << std::endl; michael@0: michael@0: ASSERT_EQ(a1_->getLocalDescription(),a2_->getRemoteDescription()); michael@0: ASSERT_EQ(a2_->getLocalDescription(),a1_->getRemoteDescription()); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, CheckTrickleSdpChange) michael@0: { michael@0: sipcc::MediaConstraints constraints; michael@0: OfferAnswerTrickle(constraints, constraints, michael@0: SHOULD_SENDRECV_AV, SHOULD_SENDRECV_AV); michael@0: std::cerr << "ICE handshake completed" << std::endl; michael@0: michael@0: a1_->CloseSendStreams(); michael@0: a2_->CloseReceiveStreams(); michael@0: michael@0: std::cout << "Caller's Local Description: " << std::endl michael@0: << indent(a1_->getLocalDescription()) << std::endl << std::endl; michael@0: michael@0: std::cout << "Caller's Remote Description: " << std::endl michael@0: << indent(a1_->getRemoteDescription()) << std::endl << std::endl; michael@0: michael@0: std::cout << "Callee's Local Description: " << std::endl michael@0: << indent(a2_->getLocalDescription()) << std::endl << std::endl; michael@0: michael@0: std::cout << "Callee's Remote Description: " << std::endl michael@0: << indent(a2_->getRemoteDescription()) << std::endl << std::endl; michael@0: michael@0: ASSERT_NE(a1_->getLocalDescription().find("\r\na=candidate"), michael@0: std::string::npos); michael@0: ASSERT_NE(a1_->getRemoteDescription().find("\r\na=candidate"), michael@0: std::string::npos); michael@0: ASSERT_NE(a2_->getLocalDescription().find("\r\na=candidate"), michael@0: std::string::npos); michael@0: ASSERT_NE(a2_->getRemoteDescription().find("\r\na=candidate"), michael@0: std::string::npos); michael@0: /* TODO (abr): These checks aren't quite right, since trickle ICE michael@0: * can easily result in SDP that is semantically identical but michael@0: * varies syntactically (in particularly, the ordering of attributes michael@0: * withing an m-line section can be different). This needs to be updated michael@0: * to be a semantic comparision between the SDP. Currently, these checks michael@0: * will fail whenever we add any other attributes to the SDP, such as michael@0: * RTCP MUX or RTCP feedback. michael@0: ASSERT_EQ(a1_->getLocalDescription(),a2_->getRemoteDescription()); michael@0: ASSERT_EQ(a2_->getLocalDescription(),a1_->getRemoteDescription()); michael@0: */ michael@0: } michael@0: michael@0: TEST_F(SignalingTest, ipAddrAnyOffer) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: std::string offer = michael@0: "v=0\r\n" michael@0: "o=- 1 1 IN IP4 127.0.0.1\r\n" michael@0: "s=-\r\n" michael@0: "b=AS:64\r\n" michael@0: "t=0 0\r\n" michael@0: "a=fingerprint:sha-256 F3:FA:20:C0:CD:48:C4:5F:02:5F:A5:D3:21:D0:2D:48:" michael@0: "7B:31:60:5C:5A:D8:0D:CD:78:78:6C:6D:CE:CC:0C:67\r\n" michael@0: "m=audio 9000 RTP/AVP 99\r\n" michael@0: "c=IN IP4 0.0.0.0\r\n" michael@0: "a=rtpmap:99 opus/48000/2\r\n" michael@0: "a=ice-ufrag:cYuakxkEKH+RApYE\r\n" michael@0: "a=ice-pwd:bwtpzLZD+3jbu8vQHvEa6Xuq\r\n" michael@0: "a=sendrecv\r\n"; michael@0: michael@0: a2_->SetRemote(TestObserver::OFFER, offer); michael@0: ASSERT_TRUE(a2_->pObserver->state == TestObserver::stateSuccess); michael@0: a2_->CreateAnswer(constraints, offer, OFFER_AUDIO | ANSWER_AUDIO); michael@0: ASSERT_TRUE(a2_->pObserver->state == TestObserver::stateSuccess); michael@0: std::string answer = a2_->answer(); michael@0: ASSERT_NE(answer.find("a=sendrecv"), std::string::npos); michael@0: } michael@0: michael@0: static void CreateSDPForBigOTests(std::string& offer, const char *number) { michael@0: offer = michael@0: "v=0\r\n" michael@0: "o=- "; michael@0: offer += number; michael@0: offer += " "; michael@0: offer += number; michael@0: offer += " IN IP4 127.0.0.1\r\n" michael@0: "s=-\r\n" michael@0: "b=AS:64\r\n" michael@0: "t=0 0\r\n" michael@0: "a=fingerprint:sha-256 F3:FA:20:C0:CD:48:C4:5F:02:5F:A5:D3:21:D0:2D:48:" michael@0: "7B:31:60:5C:5A:D8:0D:CD:78:78:6C:6D:CE:CC:0C:67\r\n" michael@0: "m=audio 9000 RTP/AVP 99\r\n" michael@0: "c=IN IP4 0.0.0.0\r\n" michael@0: "a=rtpmap:99 opus/48000/2\r\n" michael@0: "a=ice-ufrag:cYuakxkEKH+RApYE\r\n" michael@0: "a=ice-pwd:bwtpzLZD+3jbu8vQHvEa6Xuq\r\n" michael@0: "a=sendrecv\r\n"; michael@0: } michael@0: michael@0: TEST_F(SignalingTest, BigOValues) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: std::string offer; michael@0: michael@0: CreateSDPForBigOTests(offer, "12345678901234567"); michael@0: michael@0: a2_->SetRemote(TestObserver::OFFER, offer); michael@0: ASSERT_EQ(a2_->pObserver->state, TestObserver::stateSuccess); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, BigOValuesExtraChars) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: std::string offer; michael@0: michael@0: CreateSDPForBigOTests(offer, "12345678901234567FOOBAR"); michael@0: michael@0: // The signaling state will remain "stable" because the unparsable michael@0: // SDP leads to a failure in SetRemoteDescription. michael@0: a2_->SetRemote(TestObserver::OFFER, offer, true, michael@0: PCImplSignalingState::SignalingStable); michael@0: ASSERT_TRUE(a2_->pObserver->state == TestObserver::stateError); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, BigOValuesTooBig) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: std::string offer; michael@0: michael@0: CreateSDPForBigOTests(offer, "18446744073709551615"); michael@0: michael@0: // The signaling state will remain "stable" because the unparsable michael@0: // SDP leads to a failure in SetRemoteDescription. michael@0: a2_->SetRemote(TestObserver::OFFER, offer, true, michael@0: PCImplSignalingState::SignalingStable); michael@0: ASSERT_TRUE(a2_->pObserver->state == TestObserver::stateError); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, SetLocalAnswerInStable) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO); michael@0: michael@0: // The signaling state will remain "stable" because the michael@0: // SetLocalDescription call fails. michael@0: a1_->SetLocal(TestObserver::ANSWER, a1_->offer(), true, michael@0: PCImplSignalingState::SignalingStable); michael@0: ASSERT_EQ(a1_->pObserver->lastStatusCode, michael@0: sipcc::PeerConnectionImpl::kInvalidState); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, SetRemoteAnswerInStable) { michael@0: EnsureInit(); michael@0: michael@0: // The signaling state will remain "stable" because the michael@0: // SetRemoteDescription call fails. michael@0: a1_->SetRemote(TestObserver::ANSWER, strSampleSdpAudioVideoNoIce, true, michael@0: PCImplSignalingState::SignalingStable); michael@0: ASSERT_EQ(a1_->pObserver->lastStatusCode, michael@0: sipcc::PeerConnectionImpl::kInvalidState); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, SetLocalAnswerInHaveLocalOffer) { michael@0: sipcc::MediaConstraints constraints; michael@0: CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO); michael@0: a1_->SetLocal(TestObserver::OFFER, a1_->offer()); michael@0: ASSERT_EQ(a1_->pObserver->lastStatusCode, michael@0: sipcc::PeerConnectionImpl::kNoError); michael@0: michael@0: // The signaling state will remain "have-local-offer" because the michael@0: // SetLocalDescription call fails. michael@0: a1_->SetLocal(TestObserver::ANSWER, a1_->offer(), true, michael@0: PCImplSignalingState::SignalingHaveLocalOffer); michael@0: ASSERT_EQ(a1_->pObserver->lastStatusCode, michael@0: sipcc::PeerConnectionImpl::kInvalidState); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, SetRemoteOfferInHaveLocalOffer) { michael@0: sipcc::MediaConstraints constraints; michael@0: CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO); michael@0: a1_->SetLocal(TestObserver::OFFER, a1_->offer()); michael@0: ASSERT_EQ(a1_->pObserver->lastStatusCode, michael@0: sipcc::PeerConnectionImpl::kNoError); michael@0: michael@0: // The signaling state will remain "have-local-offer" because the michael@0: // SetRemoteDescription call fails. michael@0: a1_->SetRemote(TestObserver::OFFER, a1_->offer(), true, michael@0: PCImplSignalingState::SignalingHaveLocalOffer); michael@0: ASSERT_EQ(a1_->pObserver->lastStatusCode, michael@0: sipcc::PeerConnectionImpl::kInvalidState); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, SetLocalOfferInHaveRemoteOffer) { michael@0: sipcc::MediaConstraints constraints; michael@0: CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO); michael@0: a2_->SetRemote(TestObserver::OFFER, a1_->offer()); michael@0: ASSERT_EQ(a2_->pObserver->lastStatusCode, michael@0: sipcc::PeerConnectionImpl::kNoError); michael@0: michael@0: // The signaling state will remain "have-remote-offer" because the michael@0: // SetLocalDescription call fails. michael@0: a2_->SetLocal(TestObserver::OFFER, a1_->offer(), true, michael@0: PCImplSignalingState::SignalingHaveRemoteOffer); michael@0: ASSERT_EQ(a2_->pObserver->lastStatusCode, michael@0: sipcc::PeerConnectionImpl::kInvalidState); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, SetRemoteAnswerInHaveRemoteOffer) { michael@0: sipcc::MediaConstraints constraints; michael@0: CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO); michael@0: a2_->SetRemote(TestObserver::OFFER, a1_->offer()); michael@0: ASSERT_EQ(a2_->pObserver->lastStatusCode, michael@0: sipcc::PeerConnectionImpl::kNoError); michael@0: michael@0: // The signaling state will remain "have-remote-offer" because the michael@0: // SetRemoteDescription call fails. michael@0: a2_->SetRemote(TestObserver::ANSWER, a1_->offer(), true, michael@0: PCImplSignalingState::SignalingHaveRemoteOffer); michael@0: ASSERT_EQ(a2_->pObserver->lastStatusCode, michael@0: sipcc::PeerConnectionImpl::kInvalidState); michael@0: } michael@0: michael@0: // Disabled until the spec adds a failure callback to addStream michael@0: TEST_F(SignalingTest, DISABLED_AddStreamInHaveLocalOffer) { michael@0: sipcc::MediaConstraints constraints; michael@0: CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO); michael@0: a1_->SetLocal(TestObserver::OFFER, a1_->offer()); michael@0: ASSERT_EQ(a1_->pObserver->lastStatusCode, michael@0: sipcc::PeerConnectionImpl::kNoError); michael@0: a1_->AddStream(); michael@0: ASSERT_EQ(a1_->pObserver->lastStatusCode, michael@0: sipcc::PeerConnectionImpl::kInvalidState); michael@0: } michael@0: michael@0: // Disabled until the spec adds a failure callback to removeStream michael@0: TEST_F(SignalingTest, DISABLED_RemoveStreamInHaveLocalOffer) { michael@0: sipcc::MediaConstraints constraints; michael@0: CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO); michael@0: a1_->SetLocal(TestObserver::OFFER, a1_->offer()); michael@0: ASSERT_EQ(a1_->pObserver->lastStatusCode, michael@0: sipcc::PeerConnectionImpl::kNoError); michael@0: a1_->RemoveLastStreamAdded(); michael@0: ASSERT_EQ(a1_->pObserver->lastStatusCode, michael@0: sipcc::PeerConnectionImpl::kInvalidState); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, AddCandidateInHaveLocalOffer) { michael@0: sipcc::MediaConstraints constraints; michael@0: CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO); michael@0: a1_->SetLocal(TestObserver::OFFER, a1_->offer()); michael@0: ASSERT_EQ(a1_->pObserver->lastStatusCode, michael@0: sipcc::PeerConnectionImpl::kNoError); michael@0: a1_->AddIceCandidate(strSampleCandidate.c_str(), michael@0: strSampleMid.c_str(), nSamplelevel, false); michael@0: ASSERT_EQ(a1_->pObserver->lastStatusCode, michael@0: sipcc::PeerConnectionImpl::kInvalidState); michael@0: } michael@0: michael@0: TEST_F(SignalingAgentTest, CreateOffer) { michael@0: CreateAgent(); michael@0: sipcc::MediaConstraints constraints; michael@0: agent(0)->CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO); michael@0: PR_Sleep(20000); michael@0: } michael@0: michael@0: TEST_F(SignalingAgentTest, CreateOfferTrickleTestServer) { michael@0: TestStunServer::GetInstance()->SetActive(false); michael@0: TestStunServer::GetInstance()->SetResponseAddr( michael@0: kBogusSrflxAddress, kBogusSrflxPort); michael@0: michael@0: CreateAgent( michael@0: TestStunServer::GetInstance()->addr(), michael@0: TestStunServer::GetInstance()->port(), michael@0: false); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: agent(0)->CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO); michael@0: michael@0: // Verify that the bogus addr is not there. michael@0: ASSERT_FALSE(agent(0)->OfferContains(kBogusSrflxAddress)); michael@0: michael@0: // Now enable the STUN server. michael@0: TestStunServer::GetInstance()->SetActive(true); michael@0: agent(0)->WaitForGather(); michael@0: michael@0: // There shouldn't be any candidates until SetLocal. michael@0: ASSERT_EQ(0U, agent(0)->MatchingCandidates(kBogusSrflxAddress)); michael@0: michael@0: // Verify that the candidates appear in the offer. michael@0: size_t match; michael@0: match = agent(0)->getLocalDescription().find(kBogusSrflxAddress); michael@0: ASSERT_LT(0U, match); michael@0: } michael@0: michael@0: TEST_F(SignalingAgentTest, CreateOfferSetLocalTrickleTestServer) { michael@0: TestStunServer::GetInstance()->SetActive(false); michael@0: TestStunServer::GetInstance()->SetResponseAddr( michael@0: kBogusSrflxAddress, kBogusSrflxPort); michael@0: michael@0: CreateAgent( michael@0: TestStunServer::GetInstance()->addr(), michael@0: TestStunServer::GetInstance()->port(), michael@0: false); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: agent(0)->CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO); michael@0: michael@0: // Verify that the bogus addr is not there. michael@0: ASSERT_FALSE(agent(0)->OfferContains(kBogusSrflxAddress)); michael@0: michael@0: // Now enable the STUN server. michael@0: TestStunServer::GetInstance()->SetActive(true); michael@0: agent(0)->WaitForGather(); michael@0: michael@0: // There shouldn't be any candidates until SetLocal. michael@0: ASSERT_EQ(0U, agent(0)->MatchingCandidates(kBogusSrflxAddress)); michael@0: michael@0: agent(0)->SetLocal(TestObserver::OFFER, agent(0)->offer()); michael@0: PR_Sleep(1000); // Give time for the message queues. michael@0: michael@0: // Verify that we got our candidates. michael@0: ASSERT_LE(2U, agent(0)->MatchingCandidates(kBogusSrflxAddress)); michael@0: michael@0: // Verify that the candidates appear in the offer. michael@0: size_t match; michael@0: match = agent(0)->getLocalDescription().find(kBogusSrflxAddress); michael@0: ASSERT_LT(0U, match); michael@0: } michael@0: michael@0: michael@0: TEST_F(SignalingAgentTest, CreateAnswerSetLocalTrickleTestServer) { michael@0: TestStunServer::GetInstance()->SetActive(false); michael@0: TestStunServer::GetInstance()->SetResponseAddr( michael@0: kBogusSrflxAddress, kBogusSrflxPort); michael@0: michael@0: CreateAgent( michael@0: TestStunServer::GetInstance()->addr(), michael@0: TestStunServer::GetInstance()->port(), michael@0: false); michael@0: michael@0: std::string offer(strG711SdpOffer); michael@0: agent(0)->SetRemote(TestObserver::OFFER, offer, true, michael@0: PCImplSignalingState::SignalingHaveRemoteOffer); michael@0: ASSERT_EQ(agent(0)->pObserver->lastStatusCode, michael@0: sipcc::PeerConnectionImpl::kNoError); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: agent(0)->CreateAnswer(constraints, offer, ANSWER_AUDIO, DONT_CHECK_AUDIO); michael@0: michael@0: // Verify that the bogus addr is not there. michael@0: ASSERT_FALSE(agent(0)->AnswerContains(kBogusSrflxAddress)); michael@0: michael@0: // Now enable the STUN server. michael@0: TestStunServer::GetInstance()->SetActive(true); michael@0: agent(0)->WaitForGather(); michael@0: michael@0: // There shouldn't be any candidates until SetLocal. michael@0: ASSERT_EQ(0U, agent(0)->MatchingCandidates(kBogusSrflxAddress)); michael@0: michael@0: agent(0)->SetLocal(TestObserver::ANSWER, agent(0)->answer()); michael@0: PR_Sleep(1000); // Give time for the message queues. michael@0: michael@0: // Verify that we got our candidates. michael@0: ASSERT_LE(2U, agent(0)->MatchingCandidates(kBogusSrflxAddress)); michael@0: michael@0: // Verify that the candidates appear in the answer. michael@0: size_t match; michael@0: match = agent(0)->getLocalDescription().find(kBogusSrflxAddress); michael@0: ASSERT_LT(0U, match); michael@0: } michael@0: michael@0: michael@0: michael@0: TEST_F(SignalingAgentTest, CreateUntilFailThenWait) { michael@0: int i; michael@0: michael@0: for (i=0; ; i++) { michael@0: if (!CreateAgent()) michael@0: break; michael@0: std::cerr << "Created agent " << i << std::endl; michael@0: } michael@0: std::cerr << "Failed after creating " << i << " PCs " << std::endl; michael@0: PR_Sleep(10000); // Wait to see if we crash michael@0: } michael@0: michael@0: // Test for bug 856433. michael@0: TEST_F(SignalingAgentTest, CreateNoInit) { michael@0: CreateAgentNoInit(); michael@0: } michael@0: michael@0: michael@0: /* michael@0: * Test for Bug 843595 michael@0: */ michael@0: TEST_F(SignalingTest, missingUfrag) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: std::string offer = michael@0: "v=0\r\n" michael@0: "o=Mozilla-SIPUA 2208 0 IN IP4 0.0.0.0\r\n" michael@0: "s=SIP Call\r\n" michael@0: "t=0 0\r\n" michael@0: "a=ice-pwd:4450d5a4a5f097855c16fa079893be18\r\n" michael@0: "a=fingerprint:sha-256 23:9A:2E:43:94:42:CF:46:68:FC:62:F9:F4:48:61:DB:" michael@0: "2F:8C:C9:FF:6B:25:54:9D:41:09:EF:83:A8:19:FC:B6\r\n" michael@0: "m=audio 56187 RTP/SAVPF 109 0 8 101\r\n" michael@0: "c=IN IP4 77.9.79.167\r\n" michael@0: "a=rtpmap:109 opus/48000/2\r\n" michael@0: "a=ptime:20\r\n" michael@0: "a=rtpmap:0 PCMU/8000\r\n" michael@0: "a=rtpmap:8 PCMA/8000\r\n" michael@0: "a=rtpmap:101 telephone-event/8000\r\n" michael@0: "a=fmtp:101 0-15\r\n" michael@0: "a=sendrecv\r\n" michael@0: "a=candidate:0 1 UDP 2113601791 192.168.178.20 56187 typ host\r\n" michael@0: "a=candidate:1 1 UDP 1694236671 77.9.79.167 56187 typ srflx raddr " michael@0: "192.168.178.20 rport 56187\r\n" michael@0: "a=candidate:0 2 UDP 2113601790 192.168.178.20 52955 typ host\r\n" michael@0: "a=candidate:1 2 UDP 1694236670 77.9.79.167 52955 typ srflx raddr " michael@0: "192.168.178.20 rport 52955\r\n" michael@0: "m=video 49929 RTP/SAVPF 120\r\n" michael@0: "c=IN IP4 77.9.79.167\r\n" michael@0: "a=rtpmap:120 VP8/90000\r\n" michael@0: "a=recvonly\r\n" michael@0: "a=candidate:0 1 UDP 2113601791 192.168.178.20 49929 typ host\r\n" michael@0: "a=candidate:1 1 UDP 1694236671 77.9.79.167 49929 typ srflx raddr " michael@0: "192.168.178.20 rport 49929\r\n" michael@0: "a=candidate:0 2 UDP 2113601790 192.168.178.20 50769 typ host\r\n" michael@0: "a=candidate:1 2 UDP 1694236670 77.9.79.167 50769 typ srflx raddr " michael@0: "192.168.178.20 rport 50769\r\n" michael@0: "m=application 54054 DTLS/SCTP 5000\r\n" michael@0: "c=IN IP4 77.9.79.167\r\n" michael@0: "a=fmtp:HuRUu]Dtcl\\zM,7(OmEU%O$gU]x/z\tD protocol=webrtc-datachannel;" michael@0: "streams=16\r\n" michael@0: "a=sendrecv\r\n"; michael@0: michael@0: // Need to create an offer, since that's currently required by our michael@0: // FSM. This may change in the future. michael@0: a1_->CreateOffer(constraints, OFFER_AV, SHOULD_SENDRECV_AV); michael@0: a1_->SetLocal(TestObserver::OFFER, offer, true); michael@0: // We now detect the missing ICE parameters at SetRemoteDescription michael@0: a2_->SetRemote(TestObserver::OFFER, offer, true, michael@0: PCImplSignalingState::SignalingStable); michael@0: ASSERT_TRUE(a2_->pObserver->state == TestObserver::stateError); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, AudioOnlyCalleeNoRtcpMux) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: michael@0: a1_->CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO); michael@0: a1_->SetLocal(TestObserver::OFFER, a1_->offer(), false); michael@0: ParsedSDP sdpWrapper(a1_->offer()); michael@0: sdpWrapper.DeleteLine("a=rtcp-mux"); michael@0: std::cout << "Modified SDP " << std::endl michael@0: << indent(sdpWrapper.getSdp()) << std::endl; michael@0: a2_->SetRemote(TestObserver::OFFER, sdpWrapper.getSdp(), false); michael@0: a2_->CreateAnswer(constraints, sdpWrapper.getSdp(), michael@0: OFFER_AUDIO | ANSWER_AUDIO); michael@0: a2_->SetLocal(TestObserver::ANSWER, a2_->answer(), false); michael@0: a1_->SetRemote(TestObserver::ANSWER, a2_->answer(), false); michael@0: michael@0: // Answer should not have a=rtcp-mux michael@0: ASSERT_EQ(a2_->getLocalDescription().find("\r\na=rtcp-mux"), michael@0: std::string::npos); michael@0: michael@0: ASSERT_TRUE_WAIT(a1_->IceCompleted() == true, kDefaultTimeout); michael@0: ASSERT_TRUE_WAIT(a2_->IceCompleted() == true, kDefaultTimeout); michael@0: michael@0: // Wait for some data to get written michael@0: ASSERT_TRUE_WAIT(a1_->GetPacketsSent(0) >= 40 && michael@0: a2_->GetPacketsReceived(0) >= 40, kDefaultTimeout * 2); michael@0: michael@0: a1_->CloseSendStreams(); michael@0: a2_->CloseReceiveStreams(); michael@0: michael@0: ASSERT_GE(a1_->GetPacketsSent(0), 40); michael@0: ASSERT_GE(a2_->GetPacketsReceived(0), 40); michael@0: michael@0: // Check the low-level media pipeline michael@0: // for RTP and RTCP flows michael@0: // The first Local pipeline gets stored at 0 michael@0: a1_->CheckMediaPipeline(0, 0, PIPELINE_LOCAL | PIPELINE_SEND); michael@0: michael@0: // The first Remote pipeline gets stored at 1 michael@0: a2_->CheckMediaPipeline(0, 1, 0); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, FullCallAudioNoMuxVideoMux) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: michael@0: a1_->CreateOffer(constraints, OFFER_AV, SHOULD_SENDRECV_AV); michael@0: a1_->SetLocal(TestObserver::OFFER, a1_->offer(), false); michael@0: ParsedSDP sdpWrapper(a1_->offer()); michael@0: sdpWrapper.DeleteLine("a=rtcp-mux"); michael@0: std::cout << "Modified SDP " << std::endl michael@0: << indent(sdpWrapper.getSdp()) << std::endl; michael@0: a2_->SetRemote(TestObserver::OFFER, sdpWrapper.getSdp(), false); michael@0: a2_->CreateAnswer(constraints, sdpWrapper.getSdp(), OFFER_AV | ANSWER_AV); michael@0: a2_->SetLocal(TestObserver::ANSWER, a2_->answer(), false); michael@0: a1_->SetRemote(TestObserver::ANSWER, a2_->answer(), false); michael@0: michael@0: // Answer should have only one a=rtcp-mux line michael@0: size_t match = a2_->getLocalDescription().find("\r\na=rtcp-mux"); michael@0: if (fRtcpMux) { michael@0: ASSERT_NE(match, std::string::npos); michael@0: match = a2_->getLocalDescription().find("\r\na=rtcp-mux", match + 1); michael@0: } michael@0: ASSERT_EQ(match, std::string::npos); michael@0: michael@0: ASSERT_TRUE_WAIT(a1_->IceCompleted() == true, kDefaultTimeout); michael@0: ASSERT_TRUE_WAIT(a2_->IceCompleted() == true, kDefaultTimeout); michael@0: michael@0: // Wait for some data to get written michael@0: ASSERT_TRUE_WAIT(a1_->GetPacketsSent(0) >= 40 && michael@0: a2_->GetPacketsReceived(0) >= 40, kDefaultTimeout * 2); michael@0: michael@0: a1_->CloseSendStreams(); michael@0: a2_->CloseReceiveStreams(); michael@0: michael@0: ASSERT_GE(a1_->GetPacketsSent(0), 40); michael@0: ASSERT_GE(a2_->GetPacketsReceived(0), 40); michael@0: michael@0: // Check the low-level media pipeline michael@0: // for RTP and RTCP flows michael@0: // The first Local pipeline gets stored at 0 michael@0: a1_->CheckMediaPipeline(0, 0, PIPELINE_LOCAL | PIPELINE_SEND); michael@0: michael@0: // Now check video mux. michael@0: a1_->CheckMediaPipeline(0, 1, michael@0: PIPELINE_LOCAL | (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | PIPELINE_SEND | michael@0: PIPELINE_VIDEO); michael@0: michael@0: // The first Remote pipeline gets stored at 1 michael@0: a2_->CheckMediaPipeline(0, 1, 0); michael@0: michael@0: // Now check video mux. michael@0: a2_->CheckMediaPipeline(0, 2, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | michael@0: PIPELINE_VIDEO | PIPELINE_RTCP_NACK, VideoSessionConduit::FrameRequestPli); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, RtcpFbInOffer) michael@0: { michael@0: EnsureInit(); michael@0: sipcc::MediaConstraints constraints; michael@0: a1_->CreateOffer(constraints, OFFER_AV, SHOULD_SENDRECV_AV); michael@0: const char *expected[] = { "nack", "nack pli", "ccm fir" }; michael@0: CheckRtcpFbSdp(a1_->offer(), ARRAY_TO_SET(std::string, expected)); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, RtcpFbInAnswer) michael@0: { michael@0: const char *feedbackTypes[] = { "nack", "nack pli", "ccm fir" }; michael@0: TestRtcpFb(ARRAY_TO_SET(std::string, feedbackTypes), michael@0: PIPELINE_RTCP_NACK, michael@0: VideoSessionConduit::FrameRequestPli); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, RtcpFbNoNackBasic) michael@0: { michael@0: const char *feedbackTypes[] = { "nack pli", "ccm fir" }; michael@0: TestRtcpFb(ARRAY_TO_SET(std::string, feedbackTypes), michael@0: 0, michael@0: VideoSessionConduit::FrameRequestPli); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, RtcpFbNoNackPli) michael@0: { michael@0: const char *feedbackTypes[] = { "nack", "ccm fir" }; michael@0: TestRtcpFb(ARRAY_TO_SET(std::string, feedbackTypes), michael@0: PIPELINE_RTCP_NACK, michael@0: VideoSessionConduit::FrameRequestFir); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, RtcpFbNoCcmFir) michael@0: { michael@0: const char *feedbackTypes[] = { "nack", "nack pli" }; michael@0: TestRtcpFb(ARRAY_TO_SET(std::string, feedbackTypes), michael@0: PIPELINE_RTCP_NACK, michael@0: VideoSessionConduit::FrameRequestPli); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, RtcpFbNoNack) michael@0: { michael@0: const char *feedbackTypes[] = { "ccm fir" }; michael@0: TestRtcpFb(ARRAY_TO_SET(std::string, feedbackTypes), michael@0: 0, michael@0: VideoSessionConduit::FrameRequestFir); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, RtcpFbNoFrameRequest) michael@0: { michael@0: const char *feedbackTypes[] = { "nack" }; michael@0: TestRtcpFb(ARRAY_TO_SET(std::string, feedbackTypes), michael@0: PIPELINE_RTCP_NACK, michael@0: VideoSessionConduit::FrameRequestNone); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, RtcpFbPliOnly) michael@0: { michael@0: const char *feedbackTypes[] = { "nack pli" }; michael@0: TestRtcpFb(ARRAY_TO_SET(std::string, feedbackTypes), michael@0: 0, michael@0: VideoSessionConduit::FrameRequestPli); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, RtcpFbNoFeedback) michael@0: { michael@0: const char *feedbackTypes[] = { }; michael@0: TestRtcpFb(ARRAY_TO_SET(std::string, feedbackTypes), michael@0: 0, michael@0: VideoSessionConduit::FrameRequestNone); michael@0: } michael@0: michael@0: // In this test we will change the offer SDP's a=setup value michael@0: // from actpass to passive. This will make the answer do active. michael@0: TEST_F(SignalingTest, AudioCallForceDtlsRoles) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: size_t match; michael@0: michael@0: a1_->CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO); michael@0: michael@0: // By default the offer should give actpass michael@0: std::string offer(a1_->offer()); michael@0: match = offer.find("\r\na=setup:actpass"); michael@0: ASSERT_NE(match, std::string::npos); michael@0: // Now replace the actpass with passive so that the answer will michael@0: // return active michael@0: offer.replace(match, strlen("\r\na=setup:actpass"), michael@0: "\r\na=setup:passive"); michael@0: std::cout << "Modified SDP " << std::endl michael@0: << indent(offer) << std::endl; michael@0: michael@0: a1_->SetLocal(TestObserver::OFFER, offer.c_str(), false); michael@0: a2_->SetRemote(TestObserver::OFFER, offer.c_str(), false); michael@0: a2_->CreateAnswer(constraints, offer.c_str(), OFFER_AUDIO | ANSWER_AUDIO); michael@0: michael@0: // Now the answer should contain a=setup:active michael@0: std::string answer(a2_->answer()); michael@0: match = answer.find("\r\na=setup:active"); michael@0: ASSERT_NE(match, std::string::npos); michael@0: michael@0: // This should setup the DTLS with the same roles michael@0: // as the regular tests above. michael@0: a2_->SetLocal(TestObserver::ANSWER, a2_->answer(), false); michael@0: a1_->SetRemote(TestObserver::ANSWER, a2_->answer(), false); michael@0: michael@0: ASSERT_TRUE_WAIT(a1_->IceCompleted() == true, kDefaultTimeout); michael@0: ASSERT_TRUE_WAIT(a2_->IceCompleted() == true, kDefaultTimeout); michael@0: michael@0: // Wait for some data to get written michael@0: ASSERT_TRUE_WAIT(a1_->GetPacketsSent(0) >= 40 && michael@0: a2_->GetPacketsReceived(0) >= 40, kDefaultTimeout * 2); michael@0: michael@0: a1_->CloseSendStreams(); michael@0: a2_->CloseReceiveStreams(); michael@0: michael@0: ASSERT_GE(a1_->GetPacketsSent(0), 40); michael@0: ASSERT_GE(a2_->GetPacketsReceived(0), 40); michael@0: } michael@0: michael@0: // In this test we will change the offer SDP's a=setup value michael@0: // from actpass to active. This will make the answer do passive michael@0: TEST_F(SignalingTest, AudioCallReverseDtlsRoles) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: size_t match; michael@0: michael@0: a1_->CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO); michael@0: michael@0: // By default the offer should give actpass michael@0: std::string offer(a1_->offer()); michael@0: match = offer.find("\r\na=setup:actpass"); michael@0: ASSERT_NE(match, std::string::npos); michael@0: // Now replace the actpass with active so that the answer will michael@0: // return passive michael@0: offer.replace(match, strlen("\r\na=setup:actpass"), michael@0: "\r\na=setup:active"); michael@0: std::cout << "Modified SDP " << std::endl michael@0: << indent(offer) << std::endl; michael@0: michael@0: a1_->SetLocal(TestObserver::OFFER, offer.c_str(), false); michael@0: a2_->SetRemote(TestObserver::OFFER, offer.c_str(), false); michael@0: a2_->CreateAnswer(constraints, offer.c_str(), OFFER_AUDIO | ANSWER_AUDIO); michael@0: michael@0: // Now the answer should contain a=setup:passive michael@0: std::string answer(a2_->answer()); michael@0: match = answer.find("\r\na=setup:passive"); michael@0: ASSERT_NE(match, std::string::npos); michael@0: michael@0: // This should setup the DTLS with the opposite roles michael@0: // than the regular tests above. michael@0: a2_->SetLocal(TestObserver::ANSWER, a2_->answer(), false); michael@0: a1_->SetRemote(TestObserver::ANSWER, a2_->answer(), false); michael@0: michael@0: ASSERT_TRUE_WAIT(a1_->IceCompleted() == true, kDefaultTimeout); michael@0: ASSERT_TRUE_WAIT(a2_->IceCompleted() == true, kDefaultTimeout); michael@0: michael@0: // Wait for some data to get written michael@0: ASSERT_TRUE_WAIT(a1_->GetPacketsSent(0) >= 40 && michael@0: a2_->GetPacketsReceived(0) >= 40, kDefaultTimeout * 2); michael@0: michael@0: a1_->CloseSendStreams(); michael@0: a2_->CloseReceiveStreams(); michael@0: michael@0: ASSERT_GE(a1_->GetPacketsSent(0), 40); michael@0: ASSERT_GE(a2_->GetPacketsReceived(0), 40); michael@0: } michael@0: michael@0: // In this test we will change the answer SDP's a=setup value michael@0: // from active to passive. This will make both sides do michael@0: // active and should not connect. michael@0: TEST_F(SignalingTest, AudioCallMismatchDtlsRoles) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: size_t match; michael@0: michael@0: a1_->CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO); michael@0: michael@0: // By default the offer should give actpass michael@0: std::string offer(a1_->offer()); michael@0: match = offer.find("\r\na=setup:actpass"); michael@0: ASSERT_NE(match, std::string::npos); michael@0: a1_->SetLocal(TestObserver::OFFER, offer.c_str(), false); michael@0: a2_->SetRemote(TestObserver::OFFER, offer.c_str(), false); michael@0: a2_->CreateAnswer(constraints, offer.c_str(), OFFER_AUDIO | ANSWER_AUDIO); michael@0: michael@0: // Now the answer should contain a=setup:active michael@0: std::string answer(a2_->answer()); michael@0: match = answer.find("\r\na=setup:active"); michael@0: ASSERT_NE(match, std::string::npos); michael@0: michael@0: // Now replace the active with passive so that the offerer will michael@0: // also do active. michael@0: answer.replace(match, strlen("\r\na=setup:active"), michael@0: "\r\na=setup:passive"); michael@0: std::cout << "Modified SDP " << std::endl michael@0: << indent(answer) << std::endl; michael@0: michael@0: // This should setup the DTLS with both sides playing active michael@0: a2_->SetLocal(TestObserver::ANSWER, answer.c_str(), false); michael@0: a1_->SetRemote(TestObserver::ANSWER, answer.c_str(), false); michael@0: michael@0: ASSERT_TRUE_WAIT(a1_->IceCompleted() == true, kDefaultTimeout); michael@0: ASSERT_TRUE_WAIT(a2_->IceCompleted() == true, kDefaultTimeout); michael@0: michael@0: // Not using ASSERT_TRUE_WAIT here because we expect failure michael@0: PR_Sleep(kDefaultTimeout * 2); // Wait for some data to get written michael@0: michael@0: a1_->CloseSendStreams(); michael@0: a2_->CloseReceiveStreams(); michael@0: michael@0: ASSERT_GE(a1_->GetPacketsSent(0), 40); michael@0: // In this case we should receive nothing. michael@0: ASSERT_EQ(a2_->GetPacketsReceived(0), 0); michael@0: } michael@0: michael@0: // In this test we will change the offer SDP's a=setup value michael@0: // from actpass to garbage. It should ignore the garbage value michael@0: // and respond with setup:active michael@0: TEST_F(SignalingTest, AudioCallGarbageSetup) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: size_t match; michael@0: michael@0: a1_->CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO); michael@0: michael@0: // By default the offer should give actpass michael@0: std::string offer(a1_->offer()); michael@0: match = offer.find("\r\na=setup:actpass"); michael@0: ASSERT_NE(match, std::string::npos); michael@0: // Now replace the actpass with a garbage value michael@0: offer.replace(match, strlen("\r\na=setup:actpass"), michael@0: "\r\na=setup:G4rb4g3V4lu3"); michael@0: std::cout << "Modified SDP " << std::endl michael@0: << indent(offer) << std::endl; michael@0: michael@0: a1_->SetLocal(TestObserver::OFFER, offer.c_str(), false); michael@0: a2_->SetRemote(TestObserver::OFFER, offer.c_str(), false); michael@0: a2_->CreateAnswer(constraints, offer.c_str(), OFFER_AUDIO | ANSWER_AUDIO); michael@0: michael@0: // Now the answer should contain a=setup:active michael@0: std::string answer(a2_->answer()); michael@0: match = answer.find("\r\na=setup:active"); michael@0: ASSERT_NE(match, std::string::npos); michael@0: michael@0: // This should setup the DTLS with the same roles michael@0: // as the regular tests above. michael@0: a2_->SetLocal(TestObserver::ANSWER, a2_->answer(), false); michael@0: a1_->SetRemote(TestObserver::ANSWER, a2_->answer(), false); michael@0: michael@0: ASSERT_TRUE_WAIT(a1_->IceCompleted() == true, kDefaultTimeout); michael@0: ASSERT_TRUE_WAIT(a2_->IceCompleted() == true, kDefaultTimeout); michael@0: michael@0: // Wait for some data to get written michael@0: ASSERT_TRUE_WAIT(a1_->GetPacketsSent(0) >= 40 && michael@0: a2_->GetPacketsReceived(0) >= 40, kDefaultTimeout * 2); michael@0: michael@0: a1_->CloseSendStreams(); michael@0: a2_->CloseReceiveStreams(); michael@0: michael@0: ASSERT_GE(a1_->GetPacketsSent(0), 40); michael@0: ASSERT_GE(a2_->GetPacketsReceived(0), 40); michael@0: } michael@0: michael@0: // In this test we will change the offer SDP to remove the michael@0: // a=setup line. Answer should respond with a=setup:active. michael@0: TEST_F(SignalingTest, AudioCallOfferNoSetupOrConnection) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: size_t match; michael@0: michael@0: a1_->CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO); michael@0: michael@0: // By default the offer should give setup:actpass michael@0: std::string offer(a1_->offer()); michael@0: match = offer.find("\r\na=setup:actpass"); michael@0: ASSERT_NE(match, std::string::npos); michael@0: // Remove the a=setup line michael@0: offer.replace(match, strlen("\r\na=setup:actpass"), ""); michael@0: std::cout << "Modified SDP " << std::endl michael@0: << indent(offer) << std::endl; michael@0: michael@0: a1_->SetLocal(TestObserver::OFFER, offer.c_str(), false); michael@0: a2_->SetRemote(TestObserver::OFFER, offer.c_str(), false); michael@0: a2_->CreateAnswer(constraints, offer.c_str(), OFFER_AUDIO | ANSWER_AUDIO); michael@0: michael@0: // Now the answer should contain a=setup:active michael@0: std::string answer(a2_->answer()); michael@0: match = answer.find("\r\na=setup:active"); michael@0: ASSERT_NE(match, std::string::npos); michael@0: michael@0: // This should setup the DTLS with the same roles michael@0: // as the regular tests above. michael@0: a2_->SetLocal(TestObserver::ANSWER, a2_->answer(), false); michael@0: a1_->SetRemote(TestObserver::ANSWER, a2_->answer(), false); michael@0: michael@0: ASSERT_TRUE_WAIT(a1_->IceCompleted() == true, kDefaultTimeout); michael@0: ASSERT_TRUE_WAIT(a2_->IceCompleted() == true, kDefaultTimeout); michael@0: michael@0: // Wait for some data to get written michael@0: ASSERT_TRUE_WAIT(a1_->GetPacketsSent(0) >= 40 && michael@0: a2_->GetPacketsReceived(0) >= 40, kDefaultTimeout * 2); michael@0: michael@0: a1_->CloseSendStreams(); michael@0: a2_->CloseReceiveStreams(); michael@0: michael@0: ASSERT_GE(a1_->GetPacketsSent(0), 40); michael@0: ASSERT_GE(a2_->GetPacketsReceived(0), 40); michael@0: } michael@0: michael@0: // In this test we will change the answer SDP to remove the michael@0: // a=setup line. ICE should still connect since active will michael@0: // be assumed. michael@0: TEST_F(SignalingTest, AudioCallAnswerNoSetupOrConnection) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: size_t match; michael@0: michael@0: a1_->CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO); michael@0: michael@0: // By default the offer should give setup:actpass michael@0: std::string offer(a1_->offer()); michael@0: match = offer.find("\r\na=setup:actpass"); michael@0: ASSERT_NE(match, std::string::npos); michael@0: michael@0: a1_->SetLocal(TestObserver::OFFER, offer.c_str(), false); michael@0: a2_->SetRemote(TestObserver::OFFER, offer.c_str(), false); michael@0: a2_->CreateAnswer(constraints, offer.c_str(), OFFER_AUDIO | ANSWER_AUDIO); michael@0: michael@0: // Now the answer should contain a=setup:active michael@0: std::string answer(a2_->answer()); michael@0: match = answer.find("\r\na=setup:active"); michael@0: ASSERT_NE(match, std::string::npos); michael@0: // Remove the a=setup line michael@0: answer.replace(match, strlen("\r\na=setup:active"), ""); michael@0: std::cout << "Modified SDP " << std::endl michael@0: << indent(answer) << std::endl; michael@0: michael@0: // This should setup the DTLS with the same roles michael@0: // as the regular tests above. michael@0: a2_->SetLocal(TestObserver::ANSWER, answer, false); michael@0: a1_->SetRemote(TestObserver::ANSWER, answer, false); michael@0: michael@0: ASSERT_TRUE_WAIT(a1_->IceCompleted() == true, kDefaultTimeout); michael@0: ASSERT_TRUE_WAIT(a2_->IceCompleted() == true, kDefaultTimeout); michael@0: michael@0: // Wait for some data to get written michael@0: ASSERT_TRUE_WAIT(a1_->GetPacketsSent(0) >= 40 && michael@0: a2_->GetPacketsReceived(0) >= 40, kDefaultTimeout * 2); michael@0: michael@0: a1_->CloseSendStreams(); michael@0: a2_->CloseReceiveStreams(); michael@0: michael@0: ASSERT_GE(a1_->GetPacketsSent(0), 40); michael@0: ASSERT_GE(a2_->GetPacketsReceived(0), 40); michael@0: } michael@0: michael@0: michael@0: TEST_F(SignalingTest, FullCallRealTrickle) michael@0: { michael@0: wait_for_gather_ = false; michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: OfferAnswer(constraints, constraints, OFFER_AV | ANSWER_AV, michael@0: true, SHOULD_SENDRECV_AV, SHOULD_SENDRECV_AV); michael@0: michael@0: // Wait for some data to get written michael@0: ASSERT_TRUE_WAIT(a1_->GetPacketsSent(0) >= 40 && michael@0: a2_->GetPacketsReceived(0) >= 40, kDefaultTimeout * 2); michael@0: michael@0: a1_->CloseSendStreams(); michael@0: a2_->CloseReceiveStreams(); michael@0: ASSERT_GE(a1_->GetPacketsSent(0), 40); michael@0: ASSERT_GE(a2_->GetPacketsReceived(0), 40); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, FullCallRealTrickleTestServer) michael@0: { michael@0: wait_for_gather_ = false; michael@0: SetTestStunServer(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: OfferAnswer(constraints, constraints, OFFER_AV | ANSWER_AV, michael@0: true, SHOULD_SENDRECV_AV, SHOULD_SENDRECV_AV); michael@0: michael@0: TestStunServer::GetInstance()->SetActive(true); michael@0: michael@0: // Wait for some data to get written michael@0: ASSERT_TRUE_WAIT(a1_->GetPacketsSent(0) >= 40 && michael@0: a2_->GetPacketsReceived(0) >= 40, kDefaultTimeout * 2); michael@0: michael@0: a1_->CloseSendStreams(); michael@0: a2_->CloseReceiveStreams(); michael@0: ASSERT_GE(a1_->GetPacketsSent(0), 40); michael@0: ASSERT_GE(a2_->GetPacketsReceived(0), 40); michael@0: } michael@0: michael@0: TEST_F(SignalingTest, hugeSdp) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: std::string offer = michael@0: "v=0\r\n" michael@0: "o=- 1109973417102828257 2 IN IP4 127.0.0.1\r\n" michael@0: "s=-\r\n" michael@0: "t=0 0\r\n" michael@0: "a=group:BUNDLE audio video\r\n" michael@0: "a=msid-semantic: WMS 1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIP\r\n" michael@0: "m=audio 32952 RTP/SAVPF 111 103 104 0 8 107 106 105 13 126\r\n" michael@0: "c=IN IP4 128.64.32.16\r\n" michael@0: "a=rtcp:32952 IN IP4 128.64.32.16\r\n" michael@0: "a=candidate:77142221 1 udp 2113937151 192.168.137.1 54081 typ host generation 0\r\n" michael@0: "a=candidate:77142221 2 udp 2113937151 192.168.137.1 54081 typ host generation 0\r\n" michael@0: "a=candidate:983072742 1 udp 2113937151 172.22.0.56 54082 typ host generation 0\r\n" michael@0: "a=candidate:983072742 2 udp 2113937151 172.22.0.56 54082 typ host generation 0\r\n" michael@0: "a=candidate:2245074553 1 udp 1845501695 32.64.128.1 62397 typ srflx raddr 192.168.137.1 rport 54081 generation 0\r\n" michael@0: "a=candidate:2245074553 2 udp 1845501695 32.64.128.1 62397 typ srflx raddr 192.168.137.1 rport 54081 generation 0\r\n" michael@0: "a=candidate:2479353907 1 udp 1845501695 32.64.128.1 54082 typ srflx raddr 172.22.0.56 rport 54082 generation 0\r\n" michael@0: "a=candidate:2479353907 2 udp 1845501695 32.64.128.1 54082 typ srflx raddr 172.22.0.56 rport 54082 generation 0\r\n" michael@0: "a=candidate:1243276349 1 tcp 1509957375 192.168.137.1 0 typ host generation 0\r\n" michael@0: "a=candidate:1243276349 2 tcp 1509957375 192.168.137.1 0 typ host generation 0\r\n" michael@0: "a=candidate:1947960086 1 tcp 1509957375 172.22.0.56 0 typ host generation 0\r\n" michael@0: "a=candidate:1947960086 2 tcp 1509957375 172.22.0.56 0 typ host generation 0\r\n" michael@0: "a=candidate:1808221584 1 udp 33562367 128.64.32.16 32952 typ relay raddr 32.64.128.1 rport 62398 generation 0\r\n" michael@0: "a=candidate:1808221584 2 udp 33562367 128.64.32.16 32952 typ relay raddr 32.64.128.1 rport 62398 generation 0\r\n" michael@0: "a=candidate:507872740 1 udp 33562367 128.64.32.16 40975 typ relay raddr 32.64.128.1 rport 54085 generation 0\r\n" michael@0: "a=candidate:507872740 2 udp 33562367 128.64.32.16 40975 typ relay raddr 32.64.128.1 rport 54085 generation 0\r\n" michael@0: "a=ice-ufrag:xQuJwjX3V3eMA81k\r\n" michael@0: "a=ice-pwd:ZUiRmjS2GDhG140p73dAsSVP\r\n" michael@0: "a=ice-options:google-ice\r\n" michael@0: "a=fingerprint:sha-256 59:4A:8B:73:A7:73:53:71:88:D7:4D:58:28:0C:79:72:31:29:9B:05:37:DD:58:43:C2:D4:85:A2:B3:66:38:7A\r\n" michael@0: "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n" michael@0: "a=sendrecv\r\n" michael@0: "a=mid:audio\r\n" michael@0: "a=rtcp-mux\r\n" michael@0: "a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:/U44g3ULdtapeiSg+T3n6dDLBKIjpOhb/NXAL/2b\r\n" michael@0: "a=rtpmap:111 opus/48000/2\r\n" michael@0: "a=fmtp:111 minptime=10\r\n" michael@0: "a=rtpmap:103 ISAC/16000\r\n" michael@0: "a=rtpmap:104 ISAC/32000\r\n" michael@0: "a=rtpmap:0 PCMU/8000\r\n" michael@0: "a=rtpmap:8 PCMA/8000\r\n" michael@0: "a=rtpmap:107 CN/48000\r\n" michael@0: "a=rtpmap:106 CN/32000\r\n" michael@0: "a=rtpmap:105 CN/16000\r\n" michael@0: "a=rtpmap:13 CN/8000\r\n" michael@0: "a=rtpmap:126 telephone-event/8000\r\n" michael@0: "a=maxptime:60\r\n" michael@0: "a=ssrc:2271517329 cname:mKDNt7SQf6pwDlIn\r\n" michael@0: "a=ssrc:2271517329 msid:1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIP 1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIPa0\r\n" michael@0: "a=ssrc:2271517329 mslabel:1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIP\r\n" michael@0: "a=ssrc:2271517329 label:1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIPa0\r\n" michael@0: "m=video 32952 RTP/SAVPF 100 116 117\r\n" michael@0: "c=IN IP4 128.64.32.16\r\n" michael@0: "a=rtcp:32952 IN IP4 128.64.32.16\r\n" michael@0: "a=candidate:77142221 1 udp 2113937151 192.168.137.1 54081 typ host generation 0\r\n" michael@0: "a=candidate:77142221 2 udp 2113937151 192.168.137.1 54081 typ host generation 0\r\n" michael@0: "a=candidate:983072742 1 udp 2113937151 172.22.0.56 54082 typ host generation 0\r\n" michael@0: "a=candidate:983072742 2 udp 2113937151 172.22.0.56 54082 typ host generation 0\r\n" michael@0: "a=candidate:2245074553 1 udp 1845501695 32.64.128.1 62397 typ srflx raddr 192.168.137.1 rport 54081 generation 0\r\n" michael@0: "a=candidate:2245074553 2 udp 1845501695 32.64.128.1 62397 typ srflx raddr 192.168.137.1 rport 54081 generation 0\r\n" michael@0: "a=candidate:2479353907 1 udp 1845501695 32.64.128.1 54082 typ srflx raddr 172.22.0.56 rport 54082 generation 0\r\n" michael@0: "a=candidate:2479353907 2 udp 1845501695 32.64.128.1 54082 typ srflx raddr 172.22.0.56 rport 54082 generation 0\r\n" michael@0: "a=candidate:1243276349 1 tcp 1509957375 192.168.137.1 0 typ host generation 0\r\n" michael@0: "a=candidate:1243276349 2 tcp 1509957375 192.168.137.1 0 typ host generation 0\r\n" michael@0: "a=candidate:1947960086 1 tcp 1509957375 172.22.0.56 0 typ host generation 0\r\n" michael@0: "a=candidate:1947960086 2 tcp 1509957375 172.22.0.56 0 typ host generation 0\r\n" michael@0: "a=candidate:1808221584 1 udp 33562367 128.64.32.16 32952 typ relay raddr 32.64.128.1 rport 62398 generation 0\r\n" michael@0: "a=candidate:1808221584 2 udp 33562367 128.64.32.16 32952 typ relay raddr 32.64.128.1 rport 62398 generation 0\r\n" michael@0: "a=candidate:507872740 1 udp 33562367 128.64.32.16 40975 typ relay raddr 32.64.128.1 rport 54085 generation 0\r\n" michael@0: "a=candidate:507872740 2 udp 33562367 128.64.32.16 40975 typ relay raddr 32.64.128.1 rport 54085 generation 0\r\n" michael@0: "a=ice-ufrag:xQuJwjX3V3eMA81k\r\n" michael@0: "a=ice-pwd:ZUiRmjS2GDhG140p73dAsSVP\r\n" michael@0: "a=ice-options:google-ice\r\n" michael@0: "a=fingerprint:sha-256 59:4A:8B:73:A7:73:53:71:88:D7:4D:58:28:0C:79:72:31:29:9B:05:37:DD:58:43:C2:D4:85:A2:B3:66:38:7A\r\n" michael@0: "a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n" michael@0: "a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n" michael@0: "a=sendrecv\r\n" michael@0: "a=mid:video\r\n" michael@0: "a=rtcp-mux\r\n" michael@0: "a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:/U44g3ULdtapeiSg+T3n6dDLBKIjpOhb/NXAL/2b\r\n" michael@0: "a=rtpmap:100 VP8/90000\r\n" michael@0: "a=rtcp-fb:100 ccm fir\r\n" michael@0: "a=rtcp-fb:100 nack\r\n" michael@0: "a=rtcp-fb:100 goog-remb\r\n" michael@0: "a=rtpmap:116 red/90000\r\n" michael@0: "a=rtpmap:117 ulpfec/90000\r\n" michael@0: "a=ssrc:54724160 cname:mKDNt7SQf6pwDlIn\r\n" michael@0: "a=ssrc:54724160 msid:1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIP 1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIPv0\r\n" michael@0: "a=ssrc:54724160 mslabel:1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIP\r\n" michael@0: "a=ssrc:54724160 label:1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIPv0\r\n"; michael@0: michael@0: a1_->CreateOffer(constraints, OFFER_AV, SHOULD_SENDRECV_AV); michael@0: a1_->SetLocal(TestObserver::OFFER, offer, true); michael@0: michael@0: a2_->SetRemote(TestObserver::OFFER, offer, true); michael@0: ASSERT_GE(a2_->getRemoteDescription().length(), 4096U); michael@0: a2_->CreateAnswer(constraints, offer, OFFER_AV); michael@0: } michael@0: michael@0: // Test max_fs and max_fr prefs have proper impact on SDP offer michael@0: TEST_F(SignalingTest, MaxFsFrInOffer) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: michael@0: nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); michael@0: ASSERT_TRUE(prefs); michael@0: FsFrPrefClearer prefClearer(prefs); michael@0: michael@0: SetMaxFsFr(prefs, 300, 30); michael@0: michael@0: a1_->CreateOffer(constraints, OFFER_AV, SHOULD_CHECK_AV); michael@0: michael@0: // Verify that SDP contains correct max-fs and max-fr michael@0: CheckMaxFsFrSdp(a1_->offer(), 120, 300, 30); michael@0: } michael@0: michael@0: // Test max_fs and max_fr prefs have proper impact on SDP answer michael@0: TEST_F(SignalingTest, MaxFsFrInAnswer) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: michael@0: nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); michael@0: ASSERT_TRUE(prefs); michael@0: FsFrPrefClearer prefClearer(prefs); michael@0: michael@0: // We don't want max_fs and max_fr prefs impact SDP at this moment michael@0: SetMaxFsFr(prefs, 0, 0); michael@0: michael@0: a1_->CreateOffer(constraints, OFFER_AV, SHOULD_CHECK_AV); michael@0: michael@0: // SDP should not contain max-fs and max-fr here michael@0: CheckMaxFsFrSdp(a1_->offer(), 120, 0, 0); michael@0: michael@0: a2_->SetRemote(TestObserver::OFFER, a1_->offer()); michael@0: michael@0: SetMaxFsFr(prefs, 600, 60); michael@0: michael@0: a2_->CreateAnswer(constraints, a1_->offer(), OFFER_AV | ANSWER_AV); michael@0: michael@0: // Verify that SDP contains correct max-fs and max-fr michael@0: CheckMaxFsFrSdp(a2_->answer(), 120, 600, 60); michael@0: } michael@0: michael@0: // Test SDP offer has proper impact on callee's codec configuration michael@0: TEST_F(SignalingTest, MaxFsFrCalleeCodec) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: michael@0: nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); michael@0: ASSERT_TRUE(prefs); michael@0: FsFrPrefClearer prefClearer(prefs); michael@0: michael@0: // We don't want max_fs and max_fr prefs impact SDP at this moment michael@0: SetMaxFsFr(prefs, 0, 0); michael@0: michael@0: a1_->CreateOffer(constraints, OFFER_AV, SHOULD_CHECK_AV); michael@0: michael@0: ParsedSDP sdpWrapper(a1_->offer()); michael@0: michael@0: sdpWrapper.ReplaceLine("a=rtpmap:120", michael@0: "a=rtpmap:120 VP8/90000\r\na=fmtp:120 max-fs=300;max-fr=30\r\n"); michael@0: michael@0: std::cout << "Modified SDP " << std::endl michael@0: << indent(sdpWrapper.getSdp()) << std::endl; michael@0: michael@0: // Double confirm that SDP offer contains correct max-fs and max-fr michael@0: CheckMaxFsFrSdp(sdpWrapper.getSdp(), 120, 300, 30); michael@0: michael@0: a1_->SetLocal(TestObserver::OFFER, sdpWrapper.getSdp()); michael@0: a2_->SetRemote(TestObserver::OFFER, sdpWrapper.getSdp()); michael@0: michael@0: a2_->CreateAnswer(constraints, sdpWrapper.getSdp(), OFFER_AV | ANSWER_AV); michael@0: michael@0: // SDP should not contain max-fs and max-fr here michael@0: CheckMaxFsFrSdp(a2_->answer(), 120, 0, 0); michael@0: michael@0: a2_->SetLocal(TestObserver::ANSWER, a2_->answer()); michael@0: a1_->SetRemote(TestObserver::ANSWER, a2_->answer()); michael@0: michael@0: ASSERT_TRUE_WAIT(a1_->IceCompleted() == true, kDefaultTimeout); michael@0: ASSERT_TRUE_WAIT(a2_->IceCompleted() == true, kDefaultTimeout); michael@0: michael@0: // Checking callee's video sending configuration does respect max-fs and michael@0: // max-fr in SDP offer. michael@0: mozilla::RefPtr pipeline = michael@0: a2_->GetMediaPipeline(1, 0, 1); michael@0: ASSERT_TRUE(pipeline); michael@0: mozilla::MediaSessionConduit *conduit = pipeline->Conduit(); michael@0: ASSERT_TRUE(conduit); michael@0: ASSERT_EQ(conduit->type(), mozilla::MediaSessionConduit::VIDEO); michael@0: mozilla::VideoSessionConduit *video_conduit = michael@0: static_cast(conduit); michael@0: michael@0: ASSERT_EQ(video_conduit->SendingMaxFs(), (unsigned short) 300); michael@0: ASSERT_EQ(video_conduit->SendingMaxFr(), (unsigned short) 30); michael@0: } michael@0: michael@0: // Test SDP answer has proper impact on caller's codec configuration michael@0: TEST_F(SignalingTest, MaxFsFrCallerCodec) michael@0: { michael@0: EnsureInit(); michael@0: michael@0: sipcc::MediaConstraints constraints; michael@0: michael@0: nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); michael@0: ASSERT_TRUE(prefs); michael@0: FsFrPrefClearer prefClearer(prefs); michael@0: michael@0: // We don't want max_fs and max_fr prefs impact SDP at this moment michael@0: SetMaxFsFr(prefs, 0, 0); michael@0: michael@0: a1_->CreateOffer(constraints, OFFER_AV, SHOULD_CHECK_AV); michael@0: a1_->SetLocal(TestObserver::OFFER, a1_->offer()); michael@0: a2_->SetRemote(TestObserver::OFFER, a1_->offer()); michael@0: michael@0: a2_->CreateAnswer(constraints, a1_->offer(), OFFER_AV | ANSWER_AV); michael@0: michael@0: ParsedSDP sdpWrapper(a2_->answer()); michael@0: michael@0: sdpWrapper.ReplaceLine("a=rtpmap:120", michael@0: "a=rtpmap:120 VP8/90000\r\na=fmtp:120 max-fs=600;max-fr=60\r\n"); michael@0: michael@0: std::cout << "Modified SDP " << std::endl michael@0: << indent(sdpWrapper.getSdp()) << std::endl; michael@0: michael@0: // Double confirm that SDP answer contains correct max-fs and max-fr michael@0: CheckMaxFsFrSdp(sdpWrapper.getSdp(), 120, 600, 60); michael@0: michael@0: a2_->SetLocal(TestObserver::ANSWER, sdpWrapper.getSdp()); michael@0: a1_->SetRemote(TestObserver::ANSWER, sdpWrapper.getSdp()); michael@0: michael@0: ASSERT_TRUE_WAIT(a1_->IceCompleted() == true, kDefaultTimeout); michael@0: ASSERT_TRUE_WAIT(a2_->IceCompleted() == true, kDefaultTimeout); michael@0: michael@0: // Checking caller's video sending configuration does respect max-fs and michael@0: // max-fr in SDP answer. michael@0: mozilla::RefPtr pipeline = michael@0: a1_->GetMediaPipeline(1, 0, 1); michael@0: ASSERT_TRUE(pipeline); michael@0: mozilla::MediaSessionConduit *conduit = pipeline->Conduit(); michael@0: ASSERT_TRUE(conduit); michael@0: ASSERT_EQ(conduit->type(), mozilla::MediaSessionConduit::VIDEO); michael@0: mozilla::VideoSessionConduit *video_conduit = michael@0: static_cast(conduit); michael@0: michael@0: ASSERT_EQ(video_conduit->SendingMaxFs(), (unsigned short) 600); michael@0: ASSERT_EQ(video_conduit->SendingMaxFr(), (unsigned short) 60); michael@0: } michael@0: michael@0: } // End namespace test. michael@0: michael@0: bool is_color_terminal(const char *terminal) { michael@0: if (!terminal) { michael@0: return false; michael@0: } michael@0: const char *color_terms[] = { michael@0: "xterm", michael@0: "xterm-color", michael@0: "xterm-256color", michael@0: "screen", michael@0: "linux", michael@0: "cygwin", michael@0: 0 michael@0: }; michael@0: const char **p = color_terms; michael@0: while (*p) { michael@0: if (!strcmp(terminal, *p)) { michael@0: return true; michael@0: } michael@0: p++; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: static std::string get_environment(const char *name) { michael@0: char *value = getenv(name); michael@0: michael@0: if (!value) michael@0: return ""; michael@0: michael@0: return value; michael@0: } michael@0: michael@0: // This exists to send as an event to trigger shutdown. michael@0: static void tests_complete() { michael@0: gTestsComplete = true; michael@0: } michael@0: michael@0: // The GTest thread runs this instead of the main thread so it can michael@0: // do things like ASSERT_TRUE_WAIT which you could not do on the main thread. michael@0: static int gtest_main(int argc, char **argv) { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: michael@0: ::testing::InitGoogleTest(&argc, argv); michael@0: michael@0: for(int i=0; ists_target()->Dispatch( michael@0: WrapRunnableNM(&TestStunServer::ShutdownInstance), NS_DISPATCH_SYNC); michael@0: michael@0: // Set the global shutdown flag and tickle the main thread michael@0: // The main thread did not go through Init() so calling Shutdown() michael@0: // on it will not work. michael@0: gMainThread->Dispatch(WrapRunnableNM(tests_complete), NS_DISPATCH_SYNC); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: michael@0: int main(int argc, char **argv) { michael@0: michael@0: // This test can cause intermittent oranges on the builders michael@0: CHECK_ENVIRONMENT_FLAG("MOZ_WEBRTC_TESTS") michael@0: michael@0: if (isatty(STDOUT_FILENO) && is_color_terminal(getenv("TERM"))) { michael@0: std::string ansiMagenta = "\x1b[35m"; michael@0: std::string ansiCyan = "\x1b[36m"; michael@0: std::string ansiColorOff = "\x1b[0m"; michael@0: callerName = ansiCyan + callerName + ansiColorOff; michael@0: calleeName = ansiMagenta + calleeName + ansiColorOff; michael@0: } michael@0: michael@0: std::string tmp = get_environment("STUN_SERVER_ADDRESS"); michael@0: if (tmp != "") michael@0: g_stun_server_address = tmp; michael@0: michael@0: tmp = get_environment("STUN_SERVER_PORT"); michael@0: if (tmp != "") michael@0: g_stun_server_port = atoi(tmp.c_str()); michael@0: michael@0: test_utils = new MtransportTestUtils(); michael@0: NSS_NoDB_Init(nullptr); michael@0: NSS_SetDomesticPolicy(); michael@0: michael@0: ::testing::TestEventListeners& listeners = michael@0: ::testing::UnitTest::GetInstance()->listeners(); michael@0: // Adds a listener to the end. Google Test takes the ownership. michael@0: listeners.Append(new test::RingbufferDumper(test_utils)); michael@0: test_utils->sts_target()->Dispatch( michael@0: WrapRunnableNM(&TestStunServer::GetInstance), NS_DISPATCH_SYNC); michael@0: michael@0: // Set the main thread global which is this thread. michael@0: nsIThread *thread; michael@0: NS_GetMainThread(&thread); michael@0: gMainThread = thread; michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // Now create the GTest thread and run all of the tests on it michael@0: // When it is complete it will set gTestsComplete michael@0: NS_NewNamedThread("gtest_thread", &thread); michael@0: gGtestThread = thread; michael@0: michael@0: int result; michael@0: gGtestThread->Dispatch( michael@0: WrapRunnableNMRet(gtest_main, argc, argv, &result), NS_DISPATCH_NORMAL); michael@0: michael@0: // Here we handle the event queue for dispatches to the main thread michael@0: // When the GTest thread is complete it will send one more dispatch michael@0: // with gTestsComplete == true. michael@0: while (!gTestsComplete && NS_ProcessNextEvent()); michael@0: michael@0: gGtestThread->Shutdown(); michael@0: michael@0: sipcc::PeerConnectionCtx::Destroy(); michael@0: delete test_utils; michael@0: michael@0: return result; michael@0: }