4a5061fbff
BUG=N/A R=pbos@webrtc.org Review URL: https://webrtc-codereview.appspot.com/30419004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@7212 4adac7df-926f-26a2-2b94-8c16560cd09d
2448 lines
91 KiB
C++
2448 lines
91 KiB
C++
/*
|
|
* libjingle
|
|
* Copyright 2004 Google Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
|
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include <deque>
|
|
#include <map>
|
|
#include <sstream>
|
|
|
|
#include "talk/p2p/base/basicpacketsocketfactory.h"
|
|
#include "talk/p2p/base/constants.h"
|
|
#include "talk/p2p/base/p2ptransport.h"
|
|
#include "talk/p2p/base/parsing.h"
|
|
#include "talk/p2p/base/portallocator.h"
|
|
#include "talk/p2p/base/relayport.h"
|
|
#include "talk/p2p/base/relayserver.h"
|
|
#include "talk/p2p/base/session.h"
|
|
#include "talk/p2p/base/sessionclient.h"
|
|
#include "talk/p2p/base/sessionmanager.h"
|
|
#include "talk/p2p/base/stunport.h"
|
|
#include "talk/p2p/base/stunserver.h"
|
|
#include "talk/p2p/base/transportchannel.h"
|
|
#include "talk/p2p/base/transportchannelproxy.h"
|
|
#include "talk/p2p/base/udpport.h"
|
|
#include "talk/xmpp/constants.h"
|
|
#include "webrtc/base/base64.h"
|
|
#include "webrtc/base/common.h"
|
|
#include "webrtc/base/gunit.h"
|
|
#include "webrtc/base/helpers.h"
|
|
#include "webrtc/base/logging.h"
|
|
#include "webrtc/base/natserver.h"
|
|
#include "webrtc/base/natsocketfactory.h"
|
|
#include "webrtc/base/stringencode.h"
|
|
|
|
using cricket::SignalingProtocol;
|
|
using cricket::PROTOCOL_HYBRID;
|
|
using cricket::PROTOCOL_JINGLE;
|
|
using cricket::PROTOCOL_GINGLE;
|
|
|
|
static const std::string kInitiator = "init@init.com";
|
|
static const std::string kResponder = "resp@resp.com";
|
|
// Expected from test random number generator.
|
|
static const std::string kSessionId = "9254631414740579489";
|
|
// TODO: When we need to test more than one transport type,
|
|
// allow this to be injected like the content types are.
|
|
static const std::string kTransportType = "http://www.google.com/transport/p2p";
|
|
|
|
// Controls how long we wait for a session to send messages that we
|
|
// expect, in milliseconds. We put it high to avoid flaky tests.
|
|
static const int kEventTimeout = 5000;
|
|
|
|
static const int kNumPorts = 2;
|
|
static const int kPort0 = 28653;
|
|
static const int kPortStep = 5;
|
|
|
|
int GetPort(int port_index) {
|
|
return kPort0 + (port_index * kPortStep);
|
|
}
|
|
|
|
std::string GetPortString(int port_index) {
|
|
return rtc::ToString(GetPort(port_index));
|
|
}
|
|
|
|
// Only works for port_index < 10, which is fine for our purposes.
|
|
std::string GetUsername(int port_index) {
|
|
return "username" + std::string(8, rtc::ToString(port_index)[0]);
|
|
}
|
|
|
|
// Only works for port_index < 10, which is fine for our purposes.
|
|
std::string GetPassword(int port_index) {
|
|
return "password" + std::string(8, rtc::ToString(port_index)[0]);
|
|
}
|
|
|
|
std::string IqAck(const std::string& id,
|
|
const std::string& from,
|
|
const std::string& to) {
|
|
return "<cli:iq"
|
|
" to=\"" + to + "\""
|
|
" id=\"" + id + "\""
|
|
" type=\"result\""
|
|
" from=\"" + from + "\""
|
|
" xmlns:cli=\"jabber:client\""
|
|
"/>";
|
|
}
|
|
|
|
std::string IqSet(const std::string& id,
|
|
const std::string& from,
|
|
const std::string& to,
|
|
const std::string& content) {
|
|
return "<cli:iq"
|
|
" to=\"" + to + "\""
|
|
" type=\"set\""
|
|
" from=\"" + from + "\""
|
|
" id=\"" + id + "\""
|
|
" xmlns:cli=\"jabber:client\""
|
|
">"
|
|
+ content +
|
|
"</cli:iq>";
|
|
}
|
|
|
|
std::string IqError(const std::string& id,
|
|
const std::string& from,
|
|
const std::string& to,
|
|
const std::string& content) {
|
|
return "<cli:error"
|
|
" to=\"" + to + "\""
|
|
" type=\"error\""
|
|
" from=\"" + from + "\""
|
|
" id=\"" + id + "\""
|
|
" xmlns:cli=\"jabber:client\""
|
|
">"
|
|
+ content +
|
|
"</cli:error>";
|
|
}
|
|
|
|
std::string GingleSessionXml(const std::string& type,
|
|
const std::string& content) {
|
|
return "<session"
|
|
" xmlns=\"http://www.google.com/session\""
|
|
" type=\"" + type + "\""
|
|
" id=\"" + kSessionId + "\""
|
|
" initiator=\"" + kInitiator + "\""
|
|
">"
|
|
+ content +
|
|
"</session>";
|
|
}
|
|
|
|
std::string GingleDescriptionXml(const std::string& content_type) {
|
|
return "<description"
|
|
" xmlns=\"" + content_type + "\""
|
|
"/>";
|
|
}
|
|
|
|
std::string P2pCandidateXml(const std::string& name, int port_index) {
|
|
// Port will update the rtcp username by +1 on the last character. So we need
|
|
// to compensate here. See Port::username_fragment() for detail.
|
|
std::string username = GetUsername(port_index);
|
|
// TODO: Use the component id instead of the channel name to
|
|
// determinte if we need to covert the username here.
|
|
if (name == "rtcp" || name == "video_rtcp" || name == "chanb") {
|
|
char next_ch = username[username.size() - 1];
|
|
ASSERT(username.size() > 0);
|
|
rtc::Base64::GetNextBase64Char(next_ch, &next_ch);
|
|
username[username.size() - 1] = next_ch;
|
|
}
|
|
return "<candidate"
|
|
" name=\"" + name + "\""
|
|
" address=\"127.0.0.1\""
|
|
" port=\"" + GetPortString(port_index) + "\""
|
|
" preference=\"0.99\""
|
|
" username=\"" + username + "\""
|
|
" protocol=\"udp\""
|
|
" generation=\"0\""
|
|
" password=\"" + GetPassword(port_index) + "\""
|
|
" type=\"local\""
|
|
" network=\"network\""
|
|
"/>";
|
|
}
|
|
|
|
std::string JingleActionXml(const std::string& action,
|
|
const std::string& content) {
|
|
return "<jingle"
|
|
" xmlns=\"urn:xmpp:jingle:1\""
|
|
" action=\"" + action + "\""
|
|
" sid=\"" + kSessionId + "\""
|
|
">"
|
|
+ content +
|
|
"</jingle>";
|
|
}
|
|
|
|
std::string JingleInitiateActionXml(const std::string& content) {
|
|
return "<jingle"
|
|
" xmlns=\"urn:xmpp:jingle:1\""
|
|
" action=\"session-initiate\""
|
|
" sid=\"" + kSessionId + "\""
|
|
" initiator=\"" + kInitiator + "\""
|
|
">"
|
|
+ content +
|
|
"</jingle>";
|
|
}
|
|
|
|
std::string JingleGroupInfoXml(const std::string& content_name_a,
|
|
const std::string& content_name_b) {
|
|
std::string group_info = "<jin:group"
|
|
" type=\"BUNDLE\""
|
|
" xmlns:jin=\"google:jingle\""
|
|
">";
|
|
if (!content_name_a.empty())
|
|
group_info += "<content name=\"" + content_name_a + "\""
|
|
"/>";
|
|
if (!content_name_b.empty())
|
|
group_info += "<content name=\"" + content_name_b + "\""
|
|
"/>";
|
|
group_info += "</jin:group>";
|
|
return group_info;
|
|
}
|
|
|
|
|
|
std::string JingleEmptyContentXml(const std::string& content_name,
|
|
const std::string& content_type,
|
|
const std::string& transport_type) {
|
|
return "<content"
|
|
" name=\"" + content_name + "\""
|
|
" creator=\"initiator\""
|
|
">"
|
|
"<description"
|
|
" xmlns=\"" + content_type + "\""
|
|
"/>"
|
|
"<transport"
|
|
" xmlns=\"" + transport_type + "\""
|
|
"/>"
|
|
"</content>";
|
|
}
|
|
|
|
std::string JingleContentXml(const std::string& content_name,
|
|
const std::string& content_type,
|
|
const std::string& transport_type,
|
|
const std::string& transport_main) {
|
|
std::string transport = transport_type.empty() ? "" :
|
|
"<transport"
|
|
" xmlns=\"" + transport_type + "\""
|
|
">"
|
|
+ transport_main +
|
|
"</transport>";
|
|
|
|
return"<content"
|
|
" name=\"" + content_name + "\""
|
|
" creator=\"initiator\""
|
|
">"
|
|
"<description"
|
|
" xmlns=\"" + content_type + "\""
|
|
"/>"
|
|
+ transport +
|
|
"</content>";
|
|
}
|
|
|
|
std::string JingleTransportContentXml(const std::string& content_name,
|
|
const std::string& transport_type,
|
|
const std::string& content) {
|
|
return "<content"
|
|
" name=\"" + content_name + "\""
|
|
" creator=\"initiator\""
|
|
">"
|
|
"<transport"
|
|
" xmlns=\"" + transport_type + "\""
|
|
">"
|
|
+ content +
|
|
"</transport>"
|
|
"</content>";
|
|
}
|
|
|
|
std::string GingleInitiateXml(const std::string& content_type) {
|
|
return GingleSessionXml(
|
|
"initiate",
|
|
GingleDescriptionXml(content_type));
|
|
}
|
|
|
|
std::string JingleInitiateXml(const std::string& content_name_a,
|
|
const std::string& content_type_a,
|
|
const std::string& content_name_b,
|
|
const std::string& content_type_b,
|
|
bool bundle = false) {
|
|
std::string content_xml;
|
|
if (content_name_b.empty()) {
|
|
content_xml = JingleEmptyContentXml(
|
|
content_name_a, content_type_a, kTransportType);
|
|
} else {
|
|
content_xml = JingleEmptyContentXml(
|
|
content_name_a, content_type_a, kTransportType) +
|
|
JingleEmptyContentXml(
|
|
content_name_b, content_type_b, kTransportType);
|
|
if (bundle) {
|
|
content_xml += JingleGroupInfoXml(content_name_a, content_name_b);
|
|
}
|
|
}
|
|
return JingleInitiateActionXml(content_xml);
|
|
}
|
|
|
|
std::string GingleAcceptXml(const std::string& content_type) {
|
|
return GingleSessionXml(
|
|
"accept",
|
|
GingleDescriptionXml(content_type));
|
|
}
|
|
|
|
std::string JingleAcceptXml(const std::string& content_name_a,
|
|
const std::string& content_type_a,
|
|
const std::string& content_name_b,
|
|
const std::string& content_type_b,
|
|
bool bundle = false) {
|
|
std::string content_xml;
|
|
if (content_name_b.empty()) {
|
|
content_xml = JingleEmptyContentXml(
|
|
content_name_a, content_type_a, kTransportType);
|
|
} else {
|
|
content_xml = JingleEmptyContentXml(
|
|
content_name_a, content_type_a, kTransportType) +
|
|
JingleEmptyContentXml(
|
|
content_name_b, content_type_b, kTransportType);
|
|
}
|
|
if (bundle) {
|
|
content_xml += JingleGroupInfoXml(content_name_a, content_name_b);
|
|
}
|
|
|
|
return JingleActionXml("session-accept", content_xml);
|
|
}
|
|
|
|
std::string Gingle2CandidatesXml(const std::string& channel_name,
|
|
int port_index0,
|
|
int port_index1) {
|
|
return GingleSessionXml(
|
|
"candidates",
|
|
P2pCandidateXml(channel_name, port_index0) +
|
|
P2pCandidateXml(channel_name, port_index1));
|
|
}
|
|
|
|
std::string Gingle4CandidatesXml(const std::string& channel_name_a,
|
|
int port_index0,
|
|
int port_index1,
|
|
const std::string& channel_name_b,
|
|
int port_index2,
|
|
int port_index3) {
|
|
return GingleSessionXml(
|
|
"candidates",
|
|
P2pCandidateXml(channel_name_a, port_index0) +
|
|
P2pCandidateXml(channel_name_a, port_index1) +
|
|
P2pCandidateXml(channel_name_b, port_index2) +
|
|
P2pCandidateXml(channel_name_b, port_index3));
|
|
}
|
|
|
|
std::string Jingle2TransportInfoXml(const std::string& content_name,
|
|
const std::string& channel_name,
|
|
int port_index0,
|
|
int port_index1) {
|
|
return JingleActionXml(
|
|
"transport-info",
|
|
JingleTransportContentXml(
|
|
content_name, kTransportType,
|
|
P2pCandidateXml(channel_name, port_index0) +
|
|
P2pCandidateXml(channel_name, port_index1)));
|
|
}
|
|
|
|
std::string Jingle4TransportInfoXml(const std::string& content_name,
|
|
const std::string& channel_name_a,
|
|
int port_index0,
|
|
int port_index1,
|
|
const std::string& channel_name_b,
|
|
int port_index2,
|
|
int port_index3) {
|
|
return JingleActionXml(
|
|
"transport-info",
|
|
JingleTransportContentXml(
|
|
content_name, kTransportType,
|
|
P2pCandidateXml(channel_name_a, port_index0) +
|
|
P2pCandidateXml(channel_name_a, port_index1) +
|
|
P2pCandidateXml(channel_name_b, port_index2) +
|
|
P2pCandidateXml(channel_name_b, port_index3)));
|
|
}
|
|
|
|
std::string JingleDescriptionInfoXml(const std::string& content_name,
|
|
const std::string& content_type) {
|
|
return JingleActionXml(
|
|
"description-info",
|
|
JingleContentXml(content_name, content_type, "", ""));
|
|
}
|
|
|
|
std::string GingleRejectXml(const std::string& reason) {
|
|
return GingleSessionXml(
|
|
"reject",
|
|
"<" + reason + "/>");
|
|
}
|
|
|
|
std::string JingleTerminateXml(const std::string& reason) {
|
|
return JingleActionXml(
|
|
"session-terminate",
|
|
"<reason><" + reason + "/></reason>");
|
|
}
|
|
|
|
std::string GingleTerminateXml(const std::string& reason) {
|
|
return GingleSessionXml(
|
|
"terminate",
|
|
"<" + reason + "/>");
|
|
}
|
|
|
|
std::string GingleRedirectXml(const std::string& intitiate,
|
|
const std::string& target) {
|
|
return intitiate +
|
|
"<error code=\"302\" type=\"modify\">"
|
|
"<redirect xmlns=\"http://www.google.com/session\">"
|
|
"xmpp:" + target +
|
|
"</redirect>"
|
|
"</error>";
|
|
}
|
|
|
|
std::string JingleRedirectXml(const std::string& intitiate,
|
|
const std::string& target) {
|
|
return intitiate +
|
|
"<error code=\"302\" type=\"modify\">"
|
|
"<redirect xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\">"
|
|
"xmpp:" + target +
|
|
"</redirect>"
|
|
"</error>";
|
|
}
|
|
|
|
std::string InitiateXml(SignalingProtocol protocol,
|
|
const std::string& gingle_content_type,
|
|
const std::string& content_name_a,
|
|
const std::string& content_type_a,
|
|
const std::string& content_name_b,
|
|
const std::string& content_type_b,
|
|
bool bundle = false) {
|
|
switch (protocol) {
|
|
case PROTOCOL_JINGLE:
|
|
return JingleInitiateXml(content_name_a, content_type_a,
|
|
content_name_b, content_type_b,
|
|
bundle);
|
|
case PROTOCOL_GINGLE:
|
|
return GingleInitiateXml(gingle_content_type);
|
|
case PROTOCOL_HYBRID:
|
|
return JingleInitiateXml(content_name_a, content_type_a,
|
|
content_name_b, content_type_b) +
|
|
GingleInitiateXml(gingle_content_type);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
std::string InitiateXml(SignalingProtocol protocol,
|
|
const std::string& content_name,
|
|
const std::string& content_type) {
|
|
return InitiateXml(protocol,
|
|
content_type,
|
|
content_name, content_type,
|
|
"", "");
|
|
}
|
|
|
|
std::string AcceptXml(SignalingProtocol protocol,
|
|
const std::string& gingle_content_type,
|
|
const std::string& content_name_a,
|
|
const std::string& content_type_a,
|
|
const std::string& content_name_b,
|
|
const std::string& content_type_b,
|
|
bool bundle = false) {
|
|
switch (protocol) {
|
|
case PROTOCOL_JINGLE:
|
|
return JingleAcceptXml(content_name_a, content_type_a,
|
|
content_name_b, content_type_b, bundle);
|
|
case PROTOCOL_GINGLE:
|
|
return GingleAcceptXml(gingle_content_type);
|
|
case PROTOCOL_HYBRID:
|
|
return
|
|
JingleAcceptXml(content_name_a, content_type_a,
|
|
content_name_b, content_type_b) +
|
|
GingleAcceptXml(gingle_content_type);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
|
|
std::string AcceptXml(SignalingProtocol protocol,
|
|
const std::string& content_name,
|
|
const std::string& content_type,
|
|
bool bundle = false) {
|
|
return AcceptXml(protocol,
|
|
content_type,
|
|
content_name, content_type,
|
|
"", "");
|
|
}
|
|
|
|
std::string TransportInfo2Xml(SignalingProtocol protocol,
|
|
const std::string& content_name,
|
|
const std::string& channel_name,
|
|
int port_index0,
|
|
int port_index1) {
|
|
switch (protocol) {
|
|
case PROTOCOL_JINGLE:
|
|
return Jingle2TransportInfoXml(
|
|
content_name,
|
|
channel_name, port_index0, port_index1);
|
|
case PROTOCOL_GINGLE:
|
|
return Gingle2CandidatesXml(
|
|
channel_name, port_index0, port_index1);
|
|
case PROTOCOL_HYBRID:
|
|
return
|
|
Jingle2TransportInfoXml(
|
|
content_name,
|
|
channel_name, port_index0, port_index1) +
|
|
Gingle2CandidatesXml(
|
|
channel_name, port_index0, port_index1);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
std::string TransportInfo4Xml(SignalingProtocol protocol,
|
|
const std::string& content_name,
|
|
const std::string& channel_name_a,
|
|
int port_index0,
|
|
int port_index1,
|
|
const std::string& channel_name_b,
|
|
int port_index2,
|
|
int port_index3) {
|
|
switch (protocol) {
|
|
case PROTOCOL_JINGLE:
|
|
return Jingle4TransportInfoXml(
|
|
content_name,
|
|
channel_name_a, port_index0, port_index1,
|
|
channel_name_b, port_index2, port_index3);
|
|
case PROTOCOL_GINGLE:
|
|
return Gingle4CandidatesXml(
|
|
channel_name_a, port_index0, port_index1,
|
|
channel_name_b, port_index2, port_index3);
|
|
case PROTOCOL_HYBRID:
|
|
return
|
|
Jingle4TransportInfoXml(
|
|
content_name,
|
|
channel_name_a, port_index0, port_index1,
|
|
channel_name_b, port_index2, port_index3) +
|
|
Gingle4CandidatesXml(
|
|
channel_name_a, port_index0, port_index1,
|
|
channel_name_b, port_index2, port_index3);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
std::string RejectXml(SignalingProtocol protocol,
|
|
const std::string& reason) {
|
|
switch (protocol) {
|
|
case PROTOCOL_JINGLE:
|
|
return JingleTerminateXml(reason);
|
|
case PROTOCOL_GINGLE:
|
|
return GingleRejectXml(reason);
|
|
case PROTOCOL_HYBRID:
|
|
return JingleTerminateXml(reason) +
|
|
GingleRejectXml(reason);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
std::string TerminateXml(SignalingProtocol protocol,
|
|
const std::string& reason) {
|
|
switch (protocol) {
|
|
case PROTOCOL_JINGLE:
|
|
return JingleTerminateXml(reason);
|
|
case PROTOCOL_GINGLE:
|
|
return GingleTerminateXml(reason);
|
|
case PROTOCOL_HYBRID:
|
|
return JingleTerminateXml(reason) +
|
|
GingleTerminateXml(reason);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
std::string RedirectXml(SignalingProtocol protocol,
|
|
const std::string& initiate,
|
|
const std::string& target) {
|
|
switch (protocol) {
|
|
case PROTOCOL_JINGLE:
|
|
return JingleRedirectXml(initiate, target);
|
|
case PROTOCOL_GINGLE:
|
|
return GingleRedirectXml(initiate, target);
|
|
default:
|
|
break;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
// TODO: Break out and join with fakeportallocator.h
|
|
class TestPortAllocatorSession : public cricket::PortAllocatorSession {
|
|
public:
|
|
TestPortAllocatorSession(const std::string& content_name,
|
|
int component,
|
|
const std::string& ice_ufrag,
|
|
const std::string& ice_pwd,
|
|
const int port_offset)
|
|
: PortAllocatorSession(content_name, component, ice_ufrag, ice_pwd, 0),
|
|
port_offset_(port_offset),
|
|
ports_(kNumPorts),
|
|
address_("127.0.0.1", 0),
|
|
network_("network", "unittest",
|
|
rtc::IPAddress(INADDR_LOOPBACK), 8),
|
|
socket_factory_(rtc::Thread::Current()),
|
|
running_(false) {
|
|
network_.AddIP(address_.ipaddr());
|
|
}
|
|
|
|
~TestPortAllocatorSession() {
|
|
for (size_t i = 0; i < ports_.size(); i++)
|
|
delete ports_[i];
|
|
}
|
|
|
|
virtual void StartGettingPorts() {
|
|
for (int i = 0; i < kNumPorts; i++) {
|
|
int index = port_offset_ + i;
|
|
ports_[i] = cricket::UDPPort::Create(
|
|
rtc::Thread::Current(), &socket_factory_,
|
|
&network_, address_.ipaddr(), GetPort(index), GetPort(index),
|
|
GetUsername(index), GetPassword(index));
|
|
AddPort(ports_[i]);
|
|
}
|
|
running_ = true;
|
|
}
|
|
|
|
virtual void StopGettingPorts() { running_ = false; }
|
|
virtual bool IsGettingPorts() { return running_; }
|
|
|
|
void AddPort(cricket::Port* port) {
|
|
port->set_component(component_);
|
|
port->set_generation(0);
|
|
port->SignalDestroyed.connect(
|
|
this, &TestPortAllocatorSession::OnPortDestroyed);
|
|
port->SignalPortComplete.connect(
|
|
this, &TestPortAllocatorSession::OnPortComplete);
|
|
port->PrepareAddress();
|
|
SignalPortReady(this, port);
|
|
}
|
|
|
|
void OnPortDestroyed(cricket::PortInterface* port) {
|
|
for (size_t i = 0; i < ports_.size(); i++) {
|
|
if (ports_[i] == port)
|
|
ports_[i] = NULL;
|
|
}
|
|
}
|
|
|
|
void OnPortComplete(cricket::Port* port) {
|
|
SignalCandidatesReady(this, port->Candidates());
|
|
}
|
|
|
|
private:
|
|
int port_offset_;
|
|
std::vector<cricket::Port*> ports_;
|
|
rtc::SocketAddress address_;
|
|
rtc::Network network_;
|
|
rtc::BasicPacketSocketFactory socket_factory_;
|
|
bool running_;
|
|
};
|
|
|
|
class TestPortAllocator : public cricket::PortAllocator {
|
|
public:
|
|
TestPortAllocator() : port_offset_(0) {}
|
|
|
|
virtual cricket::PortAllocatorSession*
|
|
CreateSessionInternal(
|
|
const std::string& content_name,
|
|
int component,
|
|
const std::string& ice_ufrag,
|
|
const std::string& ice_pwd) {
|
|
port_offset_ += 2;
|
|
return new TestPortAllocatorSession(content_name, component,
|
|
ice_ufrag, ice_pwd, port_offset_ - 2);
|
|
}
|
|
|
|
int port_offset_;
|
|
};
|
|
|
|
class TestContentDescription : public cricket::ContentDescription {
|
|
public:
|
|
explicit TestContentDescription(const std::string& gingle_content_type,
|
|
const std::string& content_type)
|
|
: gingle_content_type(gingle_content_type),
|
|
content_type(content_type) {
|
|
}
|
|
virtual ContentDescription* Copy() const {
|
|
return new TestContentDescription(*this);
|
|
}
|
|
|
|
std::string gingle_content_type;
|
|
std::string content_type;
|
|
};
|
|
|
|
cricket::SessionDescription* NewTestSessionDescription(
|
|
const std::string gingle_content_type,
|
|
const std::string& content_name_a, const std::string& content_type_a,
|
|
const std::string& content_name_b, const std::string& content_type_b) {
|
|
|
|
cricket::SessionDescription* offer = new cricket::SessionDescription();
|
|
offer->AddContent(content_name_a, content_type_a,
|
|
new TestContentDescription(gingle_content_type,
|
|
content_type_a));
|
|
cricket::TransportDescription desc(cricket::NS_GINGLE_P2P,
|
|
std::string(), std::string());
|
|
offer->AddTransportInfo(cricket::TransportInfo(content_name_a, desc));
|
|
|
|
if (content_name_a != content_name_b) {
|
|
offer->AddContent(content_name_b, content_type_b,
|
|
new TestContentDescription(gingle_content_type,
|
|
content_type_b));
|
|
offer->AddTransportInfo(cricket::TransportInfo(content_name_b, desc));
|
|
}
|
|
return offer;
|
|
}
|
|
|
|
cricket::SessionDescription* NewTestSessionDescription(
|
|
const std::string& content_name, const std::string& content_type) {
|
|
|
|
cricket::SessionDescription* offer = new cricket::SessionDescription();
|
|
offer->AddContent(content_name, content_type,
|
|
new TestContentDescription(content_type,
|
|
content_type));
|
|
offer->AddTransportInfo(cricket::TransportInfo
|
|
(content_name, cricket::TransportDescription(
|
|
cricket::NS_GINGLE_P2P,
|
|
std::string(), std::string())));
|
|
return offer;
|
|
}
|
|
|
|
struct TestSessionClient: public cricket::SessionClient,
|
|
public sigslot::has_slots<> {
|
|
public:
|
|
TestSessionClient() {
|
|
}
|
|
|
|
~TestSessionClient() {
|
|
}
|
|
|
|
virtual bool ParseContent(SignalingProtocol protocol,
|
|
const buzz::XmlElement* elem,
|
|
cricket::ContentDescription** content,
|
|
cricket::ParseError* error) {
|
|
std::string content_type;
|
|
std::string gingle_content_type;
|
|
if (protocol == PROTOCOL_GINGLE) {
|
|
gingle_content_type = elem->Name().Namespace();
|
|
} else {
|
|
content_type = elem->Name().Namespace();
|
|
}
|
|
|
|
*content = new TestContentDescription(gingle_content_type, content_type);
|
|
return true;
|
|
}
|
|
|
|
virtual bool WriteContent(SignalingProtocol protocol,
|
|
const cricket::ContentDescription* untyped_content,
|
|
buzz::XmlElement** elem,
|
|
cricket::WriteError* error) {
|
|
const TestContentDescription* content =
|
|
static_cast<const TestContentDescription*>(untyped_content);
|
|
std::string content_type = (protocol == PROTOCOL_GINGLE ?
|
|
content->gingle_content_type :
|
|
content->content_type);
|
|
*elem = new buzz::XmlElement(
|
|
buzz::QName(content_type, "description"), true);
|
|
return true;
|
|
}
|
|
|
|
void OnSessionCreate(cricket::Session* session, bool initiate) {
|
|
}
|
|
|
|
void OnSessionDestroy(cricket::Session* session) {
|
|
}
|
|
};
|
|
|
|
struct ChannelHandler : sigslot::has_slots<> {
|
|
explicit ChannelHandler(cricket::TransportChannel* p, const std::string& name)
|
|
: channel(p), last_readable(false), last_writable(false), data_count(0),
|
|
last_size(0), name(name) {
|
|
p->SignalReadableState.connect(this, &ChannelHandler::OnReadableState);
|
|
p->SignalWritableState.connect(this, &ChannelHandler::OnWritableState);
|
|
p->SignalReadPacket.connect(this, &ChannelHandler::OnReadPacket);
|
|
}
|
|
|
|
bool writable() const {
|
|
return last_writable && channel->writable();
|
|
}
|
|
|
|
bool readable() const {
|
|
return last_readable && channel->readable();
|
|
}
|
|
|
|
void OnReadableState(cricket::TransportChannel* p) {
|
|
EXPECT_EQ(channel, p);
|
|
last_readable = channel->readable();
|
|
}
|
|
|
|
void OnWritableState(cricket::TransportChannel* p) {
|
|
EXPECT_EQ(channel, p);
|
|
last_writable = channel->writable();
|
|
}
|
|
|
|
void OnReadPacket(cricket::TransportChannel* p, const char* buf,
|
|
size_t size, const rtc::PacketTime& time, int flags) {
|
|
if (memcmp(buf, name.c_str(), name.size()) != 0)
|
|
return; // drop packet if packet doesn't belong to this channel. This
|
|
// can happen when transport channels are muxed together.
|
|
buf += name.size(); // Remove channel name from the message.
|
|
size -= name.size(); // Decrement size by channel name string size.
|
|
EXPECT_EQ(channel, p);
|
|
EXPECT_LE(size, sizeof(last_data));
|
|
data_count += 1;
|
|
last_size = size;
|
|
memcpy(last_data, buf, size);
|
|
}
|
|
|
|
void Send(const char* data, size_t size) {
|
|
rtc::PacketOptions options;
|
|
std::string data_with_id(name);
|
|
data_with_id += data;
|
|
int result = channel->SendPacket(data_with_id.c_str(), data_with_id.size(),
|
|
options, 0);
|
|
EXPECT_EQ(static_cast<int>(data_with_id.size()), result);
|
|
}
|
|
|
|
cricket::TransportChannel* channel;
|
|
bool last_readable, last_writable;
|
|
int data_count;
|
|
char last_data[4096];
|
|
size_t last_size;
|
|
std::string name;
|
|
};
|
|
|
|
void PrintStanza(const std::string& message,
|
|
const buzz::XmlElement* stanza) {
|
|
printf("%s: %s\n", message.c_str(), stanza->Str().c_str());
|
|
}
|
|
|
|
class TestClient : public sigslot::has_slots<> {
|
|
public:
|
|
// TODO: Add channel_component_a/b as inputs to the ctor.
|
|
TestClient(cricket::PortAllocator* port_allocator,
|
|
int* next_message_id,
|
|
const std::string& local_name,
|
|
SignalingProtocol start_protocol,
|
|
const std::string& content_type,
|
|
const std::string& content_name_a,
|
|
const std::string& channel_name_a,
|
|
const std::string& content_name_b,
|
|
const std::string& channel_name_b) {
|
|
Construct(port_allocator, next_message_id, local_name, start_protocol,
|
|
content_type, content_name_a, channel_name_a,
|
|
content_name_b, channel_name_b);
|
|
}
|
|
|
|
~TestClient() {
|
|
if (session) {
|
|
session_manager->DestroySession(session);
|
|
EXPECT_EQ(1U, session_destroyed_count);
|
|
}
|
|
delete session_manager;
|
|
delete client;
|
|
for (std::deque<buzz::XmlElement*>::iterator it = sent_stanzas.begin();
|
|
it != sent_stanzas.end(); ++it) {
|
|
delete *it;
|
|
}
|
|
}
|
|
|
|
void Construct(cricket::PortAllocator* pa,
|
|
int* message_id,
|
|
const std::string& lname,
|
|
SignalingProtocol protocol,
|
|
const std::string& cont_type,
|
|
const std::string& cont_name_a,
|
|
const std::string& chan_name_a,
|
|
const std::string& cont_name_b,
|
|
const std::string& chan_name_b) {
|
|
port_allocator_ = pa;
|
|
next_message_id = message_id;
|
|
local_name = lname;
|
|
start_protocol = protocol;
|
|
content_type = cont_type;
|
|
content_name_a = cont_name_a;
|
|
channel_name_a = chan_name_a;
|
|
content_name_b = cont_name_b;
|
|
channel_name_b = chan_name_b;
|
|
session_created_count = 0;
|
|
session_destroyed_count = 0;
|
|
session_remote_description_update_count = 0;
|
|
new_local_description = false;
|
|
new_remote_description = false;
|
|
last_content_action = cricket::CA_OFFER;
|
|
last_content_source = cricket::CS_LOCAL;
|
|
session = NULL;
|
|
last_session_state = cricket::BaseSession::STATE_INIT;
|
|
blow_up_on_error = true;
|
|
error_count = 0;
|
|
|
|
session_manager = new cricket::SessionManager(port_allocator_);
|
|
session_manager->SignalSessionCreate.connect(
|
|
this, &TestClient::OnSessionCreate);
|
|
session_manager->SignalSessionDestroy.connect(
|
|
this, &TestClient::OnSessionDestroy);
|
|
session_manager->SignalOutgoingMessage.connect(
|
|
this, &TestClient::OnOutgoingMessage);
|
|
|
|
client = new TestSessionClient();
|
|
session_manager->AddClient(content_type, client);
|
|
EXPECT_EQ(client, session_manager->GetClient(content_type));
|
|
}
|
|
|
|
uint32 sent_stanza_count() const {
|
|
return static_cast<uint32>(sent_stanzas.size());
|
|
}
|
|
|
|
const buzz::XmlElement* stanza() const {
|
|
return last_expected_sent_stanza.get();
|
|
}
|
|
|
|
cricket::BaseSession::State session_state() const {
|
|
EXPECT_EQ(last_session_state, session->state());
|
|
return session->state();
|
|
}
|
|
|
|
void SetSessionState(cricket::BaseSession::State state) {
|
|
session->SetState(state);
|
|
EXPECT_EQ_WAIT(last_session_state, session->state(), kEventTimeout);
|
|
}
|
|
|
|
void CreateSession() {
|
|
session_manager->CreateSession(local_name, content_type);
|
|
}
|
|
|
|
void DeliverStanza(const buzz::XmlElement* stanza) {
|
|
session_manager->OnIncomingMessage(stanza);
|
|
}
|
|
|
|
void DeliverStanza(const std::string& str) {
|
|
buzz::XmlElement* stanza = buzz::XmlElement::ForStr(str);
|
|
session_manager->OnIncomingMessage(stanza);
|
|
delete stanza;
|
|
}
|
|
|
|
void DeliverAckToLastStanza() {
|
|
const buzz::XmlElement* orig_stanza = stanza();
|
|
const buzz::XmlElement* response_stanza =
|
|
buzz::XmlElement::ForStr(IqAck(orig_stanza->Attr(buzz::QN_IQ), "", ""));
|
|
session_manager->OnIncomingResponse(orig_stanza, response_stanza);
|
|
delete response_stanza;
|
|
}
|
|
|
|
void ExpectSentStanza(const std::string& expected) {
|
|
EXPECT_TRUE(!sent_stanzas.empty()) <<
|
|
"Found no stanza when expected " << expected;
|
|
|
|
last_expected_sent_stanza.reset(sent_stanzas.front());
|
|
sent_stanzas.pop_front();
|
|
|
|
std::string actual = last_expected_sent_stanza->Str();
|
|
EXPECT_EQ(expected, actual);
|
|
}
|
|
|
|
void SkipUnsentStanza() {
|
|
GetNextOutgoingMessageID();
|
|
}
|
|
|
|
bool HasTransport(const std::string& content_name) const {
|
|
ASSERT(session != NULL);
|
|
const cricket::Transport* transport = session->GetTransport(content_name);
|
|
return transport != NULL && (kTransportType == transport->type());
|
|
}
|
|
|
|
bool HasChannel(const std::string& content_name,
|
|
int component) const {
|
|
ASSERT(session != NULL);
|
|
const cricket::TransportChannel* channel =
|
|
session->GetChannel(content_name, component);
|
|
return channel != NULL && (component == channel->component());
|
|
}
|
|
|
|
cricket::TransportChannel* GetChannel(const std::string& content_name,
|
|
int component) const {
|
|
ASSERT(session != NULL);
|
|
return session->GetChannel(content_name, component);
|
|
}
|
|
|
|
void OnSessionCreate(cricket::Session* created_session, bool initiate) {
|
|
session_created_count += 1;
|
|
|
|
session = created_session;
|
|
session->set_current_protocol(start_protocol);
|
|
session->SignalState.connect(this, &TestClient::OnSessionState);
|
|
session->SignalError.connect(this, &TestClient::OnSessionError);
|
|
session->SignalRemoteDescriptionUpdate.connect(
|
|
this, &TestClient::OnSessionRemoteDescriptionUpdate);
|
|
session->SignalNewLocalDescription.connect(
|
|
this, &TestClient::OnNewLocalDescription);
|
|
session->SignalNewRemoteDescription.connect(
|
|
this, &TestClient::OnNewRemoteDescription);
|
|
|
|
CreateChannels();
|
|
}
|
|
|
|
void OnSessionDestroy(cricket::Session *session) {
|
|
session_destroyed_count += 1;
|
|
}
|
|
|
|
void OnSessionState(cricket::BaseSession* session,
|
|
cricket::BaseSession::State state) {
|
|
// EXPECT_EQ does not allow use of this, hence the tmp variable.
|
|
cricket::BaseSession* tmp = this->session;
|
|
EXPECT_EQ(tmp, session);
|
|
last_session_state = state;
|
|
}
|
|
|
|
void OnSessionError(cricket::BaseSession* session,
|
|
cricket::BaseSession::Error error) {
|
|
// EXPECT_EQ does not allow use of this, hence the tmp variable.
|
|
cricket::BaseSession* tmp = this->session;
|
|
EXPECT_EQ(tmp, session);
|
|
if (blow_up_on_error) {
|
|
EXPECT_TRUE(false);
|
|
} else {
|
|
error_count++;
|
|
}
|
|
}
|
|
|
|
void OnSessionRemoteDescriptionUpdate(cricket::BaseSession* session,
|
|
const cricket::ContentInfos& contents) {
|
|
session_remote_description_update_count++;
|
|
}
|
|
|
|
void OnNewLocalDescription(cricket::BaseSession* session,
|
|
cricket::ContentAction action) {
|
|
new_local_description = true;
|
|
last_content_action = action;
|
|
last_content_source = cricket::CS_LOCAL;
|
|
}
|
|
|
|
void OnNewRemoteDescription(cricket::BaseSession* session,
|
|
cricket::ContentAction action) {
|
|
new_remote_description = true;
|
|
last_content_action = action;
|
|
last_content_source = cricket::CS_REMOTE;
|
|
}
|
|
|
|
void PrepareCandidates() {
|
|
session_manager->OnSignalingReady();
|
|
}
|
|
|
|
void OnOutgoingMessage(cricket::SessionManager* manager,
|
|
const buzz::XmlElement* stanza) {
|
|
buzz::XmlElement* elem = new buzz::XmlElement(*stanza);
|
|
EXPECT_TRUE(elem->Name() == buzz::QN_IQ);
|
|
EXPECT_TRUE(elem->HasAttr(buzz::QN_TO));
|
|
EXPECT_FALSE(elem->HasAttr(buzz::QN_FROM));
|
|
EXPECT_TRUE(elem->HasAttr(buzz::QN_TYPE));
|
|
EXPECT_TRUE((elem->Attr(buzz::QN_TYPE) == "set") ||
|
|
(elem->Attr(buzz::QN_TYPE) == "result") ||
|
|
(elem->Attr(buzz::QN_TYPE) == "error"));
|
|
|
|
elem->SetAttr(buzz::QN_FROM, local_name);
|
|
if (elem->Attr(buzz::QN_TYPE) == "set") {
|
|
EXPECT_FALSE(elem->HasAttr(buzz::QN_ID));
|
|
elem->SetAttr(buzz::QN_ID, GetNextOutgoingMessageID());
|
|
}
|
|
|
|
// Uncommenting this is useful for debugging.
|
|
// PrintStanza("OutgoingMessage", elem);
|
|
sent_stanzas.push_back(elem);
|
|
}
|
|
|
|
std::string GetNextOutgoingMessageID() {
|
|
int message_id = (*next_message_id)++;
|
|
std::ostringstream ost;
|
|
ost << message_id;
|
|
return ost.str();
|
|
}
|
|
|
|
void CreateChannels() {
|
|
ASSERT(session != NULL);
|
|
// We either have a single content with multiple components (RTP/RTCP), or
|
|
// multiple contents with single components, but not both.
|
|
int component_a = 1;
|
|
int component_b = (content_name_a == content_name_b) ? 2 : 1;
|
|
chan_a.reset(new ChannelHandler(
|
|
session->CreateChannel(content_name_a, channel_name_a, component_a),
|
|
channel_name_a));
|
|
chan_b.reset(new ChannelHandler(
|
|
session->CreateChannel(content_name_b, channel_name_b, component_b),
|
|
channel_name_b));
|
|
}
|
|
|
|
int* next_message_id;
|
|
std::string local_name;
|
|
SignalingProtocol start_protocol;
|
|
std::string content_type;
|
|
std::string content_name_a;
|
|
std::string channel_name_a;
|
|
std::string content_name_b;
|
|
std::string channel_name_b;
|
|
|
|
uint32 session_created_count;
|
|
uint32 session_destroyed_count;
|
|
uint32 session_remote_description_update_count;
|
|
bool new_local_description;
|
|
bool new_remote_description;
|
|
cricket::ContentAction last_content_action;
|
|
cricket::ContentSource last_content_source;
|
|
std::deque<buzz::XmlElement*> sent_stanzas;
|
|
rtc::scoped_ptr<buzz::XmlElement> last_expected_sent_stanza;
|
|
|
|
cricket::SessionManager* session_manager;
|
|
TestSessionClient* client;
|
|
cricket::PortAllocator* port_allocator_;
|
|
cricket::Session* session;
|
|
cricket::BaseSession::State last_session_state;
|
|
rtc::scoped_ptr<ChannelHandler> chan_a;
|
|
rtc::scoped_ptr<ChannelHandler> chan_b;
|
|
bool blow_up_on_error;
|
|
int error_count;
|
|
};
|
|
|
|
class SessionTest : public testing::Test {
|
|
protected:
|
|
virtual void SetUp() {
|
|
// Seed needed for each test to satisfy expectations.
|
|
rtc::SetRandomTestMode(true);
|
|
}
|
|
|
|
virtual void TearDown() {
|
|
rtc::SetRandomTestMode(false);
|
|
}
|
|
|
|
// Tests sending data between two clients, over two channels.
|
|
void TestSendRecv(ChannelHandler* chan1a,
|
|
ChannelHandler* chan1b,
|
|
ChannelHandler* chan2a,
|
|
ChannelHandler* chan2b) {
|
|
const char* dat1a = "spamspamspamspamspamspamspambakedbeansspam";
|
|
const char* dat2a = "mapssnaebdekabmapsmapsmapsmapsmapsmapsmaps";
|
|
const char* dat1b = "Lobster Thermidor a Crevette with a mornay sauce...";
|
|
const char* dat2b = "...ecuas yanrom a htiw etteverC a rodimrehT retsboL";
|
|
|
|
for (int i = 0; i < 20; i++) {
|
|
chan1a->Send(dat1a, strlen(dat1a));
|
|
chan1b->Send(dat1b, strlen(dat1b));
|
|
chan2a->Send(dat2a, strlen(dat2a));
|
|
chan2b->Send(dat2b, strlen(dat2b));
|
|
|
|
EXPECT_EQ_WAIT(i + 1, chan1a->data_count, kEventTimeout);
|
|
EXPECT_EQ_WAIT(i + 1, chan1b->data_count, kEventTimeout);
|
|
EXPECT_EQ_WAIT(i + 1, chan2a->data_count, kEventTimeout);
|
|
EXPECT_EQ_WAIT(i + 1, chan2b->data_count, kEventTimeout);
|
|
|
|
EXPECT_EQ(strlen(dat2a), chan1a->last_size);
|
|
EXPECT_EQ(strlen(dat2b), chan1b->last_size);
|
|
EXPECT_EQ(strlen(dat1a), chan2a->last_size);
|
|
EXPECT_EQ(strlen(dat1b), chan2b->last_size);
|
|
|
|
EXPECT_EQ(0, memcmp(chan1a->last_data, dat2a, strlen(dat2a)));
|
|
EXPECT_EQ(0, memcmp(chan1b->last_data, dat2b, strlen(dat2b)));
|
|
EXPECT_EQ(0, memcmp(chan2a->last_data, dat1a, strlen(dat1a)));
|
|
EXPECT_EQ(0, memcmp(chan2b->last_data, dat1b, strlen(dat1b)));
|
|
}
|
|
}
|
|
|
|
// Test an initiate from one client to another, each with
|
|
// independent initial protocols. Checks for the correct initiates,
|
|
// candidates, and accept messages, and tests that working network
|
|
// channels are established.
|
|
void TestSession(SignalingProtocol initiator_protocol,
|
|
SignalingProtocol responder_protocol,
|
|
SignalingProtocol resulting_protocol,
|
|
const std::string& gingle_content_type,
|
|
const std::string& content_type,
|
|
const std::string& content_name_a,
|
|
const std::string& channel_name_a,
|
|
const std::string& content_name_b,
|
|
const std::string& channel_name_b,
|
|
const std::string& initiate_xml,
|
|
const std::string& transport_info_a_xml,
|
|
const std::string& transport_info_b_xml,
|
|
const std::string& transport_info_reply_a_xml,
|
|
const std::string& transport_info_reply_b_xml,
|
|
const std::string& accept_xml,
|
|
bool bundle = false) {
|
|
rtc::scoped_ptr<cricket::PortAllocator> allocator(
|
|
new TestPortAllocator());
|
|
int next_message_id = 0;
|
|
|
|
rtc::scoped_ptr<TestClient> initiator(
|
|
new TestClient(allocator.get(), &next_message_id,
|
|
kInitiator, initiator_protocol,
|
|
content_type,
|
|
content_name_a, channel_name_a,
|
|
content_name_b, channel_name_b));
|
|
rtc::scoped_ptr<TestClient> responder(
|
|
new TestClient(allocator.get(), &next_message_id,
|
|
kResponder, responder_protocol,
|
|
content_type,
|
|
content_name_a, channel_name_a,
|
|
content_name_b, channel_name_b));
|
|
|
|
// Create Session and check channels and state.
|
|
initiator->CreateSession();
|
|
EXPECT_EQ(1U, initiator->session_created_count);
|
|
EXPECT_EQ(kSessionId, initiator->session->id());
|
|
EXPECT_EQ(initiator->session->local_name(), kInitiator);
|
|
EXPECT_EQ(cricket::BaseSession::STATE_INIT,
|
|
initiator->session_state());
|
|
|
|
// See comment in CreateChannels about how we choose component IDs.
|
|
int component_a = 1;
|
|
int component_b = (content_name_a == content_name_b) ? 2 : 1;
|
|
EXPECT_TRUE(initiator->HasTransport(content_name_a));
|
|
EXPECT_TRUE(initiator->HasChannel(content_name_a, component_a));
|
|
EXPECT_TRUE(initiator->HasTransport(content_name_b));
|
|
EXPECT_TRUE(initiator->HasChannel(content_name_b, component_b));
|
|
|
|
// Initiate and expect initiate message sent.
|
|
cricket::SessionDescription* offer = NewTestSessionDescription(
|
|
gingle_content_type,
|
|
content_name_a, content_type,
|
|
content_name_b, content_type);
|
|
if (bundle) {
|
|
cricket::ContentGroup group(cricket::GROUP_TYPE_BUNDLE);
|
|
group.AddContentName(content_name_a);
|
|
group.AddContentName(content_name_b);
|
|
EXPECT_TRUE(group.HasContentName(content_name_a));
|
|
EXPECT_TRUE(group.HasContentName(content_name_b));
|
|
offer->AddGroup(group);
|
|
}
|
|
EXPECT_TRUE(initiator->session->Initiate(kResponder, offer));
|
|
EXPECT_EQ(initiator->session->remote_name(), kResponder);
|
|
EXPECT_EQ(initiator->session->local_description(), offer);
|
|
|
|
EXPECT_TRUE_WAIT(initiator->sent_stanza_count() > 0, kEventTimeout);
|
|
EXPECT_EQ(cricket::BaseSession::STATE_SENTINITIATE,
|
|
initiator->session_state());
|
|
|
|
initiator->ExpectSentStanza(
|
|
IqSet("0", kInitiator, kResponder, initiate_xml));
|
|
|
|
// Deliver the initiate. Expect ack and session created with
|
|
// transports.
|
|
responder->DeliverStanza(initiator->stanza());
|
|
responder->ExpectSentStanza(
|
|
IqAck("0", kResponder, kInitiator));
|
|
EXPECT_EQ(0U, responder->sent_stanza_count());
|
|
|
|
EXPECT_EQ(1U, responder->session_created_count);
|
|
EXPECT_EQ(kSessionId, responder->session->id());
|
|
EXPECT_EQ(responder->session->local_name(), kResponder);
|
|
EXPECT_EQ(responder->session->remote_name(), kInitiator);
|
|
EXPECT_EQ(cricket::BaseSession::STATE_RECEIVEDINITIATE,
|
|
responder->session_state());
|
|
|
|
EXPECT_TRUE(responder->HasTransport(content_name_a));
|
|
EXPECT_TRUE(responder->HasChannel(content_name_a, component_a));
|
|
EXPECT_TRUE(responder->HasTransport(content_name_b));
|
|
EXPECT_TRUE(responder->HasChannel(content_name_b, component_b));
|
|
|
|
// Expect transport-info message from initiator.
|
|
// But don't send candidates until initiate ack is received.
|
|
initiator->PrepareCandidates();
|
|
WAIT(initiator->sent_stanza_count() > 0, 100);
|
|
EXPECT_EQ(0U, initiator->sent_stanza_count());
|
|
initiator->DeliverAckToLastStanza();
|
|
EXPECT_TRUE_WAIT(initiator->sent_stanza_count() > 0, kEventTimeout);
|
|
initiator->ExpectSentStanza(
|
|
IqSet("1", kInitiator, kResponder, transport_info_a_xml));
|
|
|
|
// Deliver transport-info and expect ack.
|
|
responder->DeliverStanza(initiator->stanza());
|
|
responder->ExpectSentStanza(
|
|
IqAck("1", kResponder, kInitiator));
|
|
|
|
if (!transport_info_b_xml.empty()) {
|
|
// Expect second transport-info message from initiator.
|
|
EXPECT_TRUE_WAIT(initiator->sent_stanza_count() > 0, kEventTimeout);
|
|
initiator->ExpectSentStanza(
|
|
IqSet("2", kInitiator, kResponder, transport_info_b_xml));
|
|
EXPECT_EQ(0U, initiator->sent_stanza_count());
|
|
|
|
// Deliver second transport-info message and expect ack.
|
|
responder->DeliverStanza(initiator->stanza());
|
|
responder->ExpectSentStanza(
|
|
IqAck("2", kResponder, kInitiator));
|
|
} else {
|
|
EXPECT_EQ(0U, initiator->sent_stanza_count());
|
|
EXPECT_EQ(0U, responder->sent_stanza_count());
|
|
initiator->SkipUnsentStanza();
|
|
}
|
|
|
|
// Expect reply transport-info message from responder.
|
|
responder->PrepareCandidates();
|
|
EXPECT_TRUE_WAIT(responder->sent_stanza_count() > 0, kEventTimeout);
|
|
responder->ExpectSentStanza(
|
|
IqSet("3", kResponder, kInitiator, transport_info_reply_a_xml));
|
|
|
|
// Deliver reply transport-info and expect ack.
|
|
initiator->DeliverStanza(responder->stanza());
|
|
initiator->ExpectSentStanza(
|
|
IqAck("3", kInitiator, kResponder));
|
|
|
|
if (!transport_info_reply_b_xml.empty()) {
|
|
// Expect second reply transport-info message from responder.
|
|
EXPECT_TRUE_WAIT(responder->sent_stanza_count() > 0, kEventTimeout);
|
|
responder->ExpectSentStanza(
|
|
IqSet("4", kResponder, kInitiator, transport_info_reply_b_xml));
|
|
EXPECT_EQ(0U, responder->sent_stanza_count());
|
|
|
|
// Deliver second reply transport-info message and expect ack.
|
|
initiator->DeliverStanza(responder->stanza());
|
|
initiator->ExpectSentStanza(
|
|
IqAck("4", kInitiator, kResponder));
|
|
EXPECT_EQ(0U, initiator->sent_stanza_count());
|
|
} else {
|
|
EXPECT_EQ(0U, initiator->sent_stanza_count());
|
|
EXPECT_EQ(0U, responder->sent_stanza_count());
|
|
responder->SkipUnsentStanza();
|
|
}
|
|
|
|
// The channels should be able to become writable at this point. This
|
|
// requires pinging, so it may take a little while.
|
|
EXPECT_TRUE_WAIT(initiator->chan_a->writable() &&
|
|
initiator->chan_a->readable(), kEventTimeout);
|
|
EXPECT_TRUE_WAIT(initiator->chan_b->writable() &&
|
|
initiator->chan_b->readable(), kEventTimeout);
|
|
EXPECT_TRUE_WAIT(responder->chan_a->writable() &&
|
|
responder->chan_a->readable(), kEventTimeout);
|
|
EXPECT_TRUE_WAIT(responder->chan_b->writable() &&
|
|
responder->chan_b->readable(), kEventTimeout);
|
|
|
|
// Accept the session and expect accept stanza.
|
|
cricket::SessionDescription* answer = NewTestSessionDescription(
|
|
gingle_content_type,
|
|
content_name_a, content_type,
|
|
content_name_b, content_type);
|
|
if (bundle) {
|
|
cricket::ContentGroup group(cricket::GROUP_TYPE_BUNDLE);
|
|
group.AddContentName(content_name_a);
|
|
group.AddContentName(content_name_b);
|
|
EXPECT_TRUE(group.HasContentName(content_name_a));
|
|
EXPECT_TRUE(group.HasContentName(content_name_b));
|
|
answer->AddGroup(group);
|
|
}
|
|
EXPECT_TRUE(responder->session->Accept(answer));
|
|
EXPECT_EQ(responder->session->local_description(), answer);
|
|
|
|
responder->ExpectSentStanza(
|
|
IqSet("5", kResponder, kInitiator, accept_xml));
|
|
|
|
EXPECT_EQ(0U, responder->sent_stanza_count());
|
|
|
|
// Deliver the accept message and expect an ack.
|
|
initiator->DeliverStanza(responder->stanza());
|
|
EXPECT_TRUE_WAIT(initiator->sent_stanza_count() > 0, kEventTimeout);
|
|
initiator->ExpectSentStanza(
|
|
IqAck("5", kInitiator, kResponder));
|
|
EXPECT_EQ(0U, initiator->sent_stanza_count());
|
|
|
|
// Both sessions should be in progress and have functioning
|
|
// channels.
|
|
EXPECT_EQ(resulting_protocol, initiator->session->current_protocol());
|
|
EXPECT_EQ(resulting_protocol, responder->session->current_protocol());
|
|
EXPECT_EQ_WAIT(cricket::BaseSession::STATE_INPROGRESS,
|
|
initiator->session_state(), kEventTimeout);
|
|
EXPECT_EQ_WAIT(cricket::BaseSession::STATE_INPROGRESS,
|
|
responder->session_state(), kEventTimeout);
|
|
if (bundle) {
|
|
cricket::TransportChannel* initiator_chan_a = initiator->chan_a->channel;
|
|
cricket::TransportChannel* initiator_chan_b = initiator->chan_b->channel;
|
|
|
|
// Since we know these are TransportChannelProxy, type cast it.
|
|
cricket::TransportChannelProxy* initiator_proxy_chan_a =
|
|
static_cast<cricket::TransportChannelProxy*>(initiator_chan_a);
|
|
cricket::TransportChannelProxy* initiator_proxy_chan_b =
|
|
static_cast<cricket::TransportChannelProxy*>(initiator_chan_b);
|
|
EXPECT_TRUE(initiator_proxy_chan_a->impl() != NULL);
|
|
EXPECT_TRUE(initiator_proxy_chan_b->impl() != NULL);
|
|
EXPECT_EQ(initiator_proxy_chan_a->impl(), initiator_proxy_chan_b->impl());
|
|
|
|
cricket::TransportChannel* responder_chan_a = responder->chan_a->channel;
|
|
cricket::TransportChannel* responder_chan_b = responder->chan_b->channel;
|
|
|
|
// Since we know these are TransportChannelProxy, type cast it.
|
|
cricket::TransportChannelProxy* responder_proxy_chan_a =
|
|
static_cast<cricket::TransportChannelProxy*>(responder_chan_a);
|
|
cricket::TransportChannelProxy* responder_proxy_chan_b =
|
|
static_cast<cricket::TransportChannelProxy*>(responder_chan_b);
|
|
EXPECT_TRUE(responder_proxy_chan_a->impl() != NULL);
|
|
EXPECT_TRUE(responder_proxy_chan_b->impl() != NULL);
|
|
EXPECT_EQ(responder_proxy_chan_a->impl(), responder_proxy_chan_b->impl());
|
|
}
|
|
TestSendRecv(initiator->chan_a.get(), initiator->chan_b.get(),
|
|
responder->chan_a.get(), responder->chan_b.get());
|
|
|
|
if (resulting_protocol == PROTOCOL_JINGLE) {
|
|
// Deliver a description-info message to the initiator and check if the
|
|
// content description changes.
|
|
EXPECT_EQ(0U, initiator->session_remote_description_update_count);
|
|
|
|
const cricket::SessionDescription* old_session_desc =
|
|
initiator->session->remote_description();
|
|
const cricket::ContentInfo* old_content_a =
|
|
old_session_desc->GetContentByName(content_name_a);
|
|
const cricket::ContentDescription* old_content_desc_a =
|
|
old_content_a->description;
|
|
const cricket::ContentInfo* old_content_b =
|
|
old_session_desc->GetContentByName(content_name_b);
|
|
const cricket::ContentDescription* old_content_desc_b =
|
|
old_content_b->description;
|
|
EXPECT_TRUE(old_content_desc_a != NULL);
|
|
EXPECT_TRUE(old_content_desc_b != NULL);
|
|
|
|
LOG(LS_INFO) << "A " << old_content_a->name;
|
|
LOG(LS_INFO) << "B " << old_content_b->name;
|
|
|
|
std::string description_info_xml =
|
|
JingleDescriptionInfoXml(content_name_a, content_type);
|
|
initiator->DeliverStanza(
|
|
IqSet("6", kResponder, kInitiator, description_info_xml));
|
|
responder->SkipUnsentStanza();
|
|
EXPECT_EQ(1U, initiator->session_remote_description_update_count);
|
|
|
|
const cricket::SessionDescription* new_session_desc =
|
|
initiator->session->remote_description();
|
|
const cricket::ContentInfo* new_content_a =
|
|
new_session_desc->GetContentByName(content_name_a);
|
|
const cricket::ContentDescription* new_content_desc_a =
|
|
new_content_a->description;
|
|
const cricket::ContentInfo* new_content_b =
|
|
new_session_desc->GetContentByName(content_name_b);
|
|
const cricket::ContentDescription* new_content_desc_b =
|
|
new_content_b->description;
|
|
EXPECT_TRUE(new_content_desc_a != NULL);
|
|
EXPECT_TRUE(new_content_desc_b != NULL);
|
|
|
|
// TODO: We used to replace contents from an update, but
|
|
// that no longer works with partial updates. We need to figure out
|
|
// a way to merge patial updates into contents. For now, users of
|
|
// Session should listen to SignalRemoteDescriptionUpdate and handle
|
|
// updates. They should not expect remote_description to be the
|
|
// latest value.
|
|
// See session.cc OnDescriptionInfoMessage.
|
|
|
|
// EXPECT_NE(old_content_desc_a, new_content_desc_a);
|
|
|
|
// if (content_name_a != content_name_b) {
|
|
// // If content_name_a != content_name_b, then b's content description
|
|
// // should not have changed since the description-info message only
|
|
// // contained an update for content_name_a.
|
|
// EXPECT_EQ(old_content_desc_b, new_content_desc_b);
|
|
// }
|
|
|
|
EXPECT_TRUE_WAIT(initiator->sent_stanza_count() > 0, kEventTimeout);
|
|
initiator->ExpectSentStanza(
|
|
IqAck("6", kInitiator, kResponder));
|
|
EXPECT_EQ(0U, initiator->sent_stanza_count());
|
|
} else {
|
|
responder->SkipUnsentStanza();
|
|
}
|
|
|
|
initiator->session->Terminate();
|
|
initiator->ExpectSentStanza(
|
|
IqSet("7", kInitiator, kResponder,
|
|
TerminateXml(resulting_protocol,
|
|
cricket::STR_TERMINATE_SUCCESS)));
|
|
|
|
responder->DeliverStanza(initiator->stanza());
|
|
responder->ExpectSentStanza(
|
|
IqAck("7", kResponder, kInitiator));
|
|
EXPECT_EQ(cricket::BaseSession::STATE_SENTTERMINATE,
|
|
initiator->session_state());
|
|
EXPECT_EQ(cricket::BaseSession::STATE_RECEIVEDTERMINATE,
|
|
responder->session_state());
|
|
}
|
|
|
|
// Test an initiate with other content, called "main".
|
|
void TestOtherContent(SignalingProtocol initiator_protocol,
|
|
SignalingProtocol responder_protocol,
|
|
SignalingProtocol resulting_protocol) {
|
|
std::string content_name = "main";
|
|
std::string content_type = "http://oink.splat/session";
|
|
std::string content_name_a = content_name;
|
|
std::string channel_name_a = "rtp";
|
|
std::string content_name_b = content_name;
|
|
std::string channel_name_b = "rtcp";
|
|
std::string initiate_xml = InitiateXml(
|
|
initiator_protocol,
|
|
content_name_a, content_type);
|
|
std::string transport_info_a_xml = TransportInfo4Xml(
|
|
initiator_protocol, content_name,
|
|
channel_name_a, 0, 1,
|
|
channel_name_b, 2, 3);
|
|
std::string transport_info_b_xml = "";
|
|
std::string transport_info_reply_a_xml = TransportInfo4Xml(
|
|
resulting_protocol, content_name,
|
|
channel_name_a, 4, 5,
|
|
channel_name_b, 6, 7);
|
|
std::string transport_info_reply_b_xml = "";
|
|
std::string accept_xml = AcceptXml(
|
|
resulting_protocol,
|
|
content_name_a, content_type);
|
|
|
|
|
|
TestSession(initiator_protocol, responder_protocol, resulting_protocol,
|
|
content_type,
|
|
content_type,
|
|
content_name_a, channel_name_a,
|
|
content_name_b, channel_name_b,
|
|
initiate_xml,
|
|
transport_info_a_xml, transport_info_b_xml,
|
|
transport_info_reply_a_xml, transport_info_reply_b_xml,
|
|
accept_xml);
|
|
}
|
|
|
|
// Test an initiate with audio content.
|
|
void TestAudioContent(SignalingProtocol initiator_protocol,
|
|
SignalingProtocol responder_protocol,
|
|
SignalingProtocol resulting_protocol) {
|
|
std::string gingle_content_type = cricket::NS_GINGLE_AUDIO;
|
|
std::string content_name = cricket::CN_AUDIO;
|
|
std::string content_type = cricket::NS_JINGLE_RTP;
|
|
std::string channel_name_a = "rtp";
|
|
std::string channel_name_b = "rtcp";
|
|
std::string initiate_xml = InitiateXml(
|
|
initiator_protocol,
|
|
gingle_content_type,
|
|
content_name, content_type,
|
|
"", "");
|
|
std::string transport_info_a_xml = TransportInfo4Xml(
|
|
initiator_protocol, content_name,
|
|
channel_name_a, 0, 1,
|
|
channel_name_b, 2, 3);
|
|
std::string transport_info_b_xml = "";
|
|
std::string transport_info_reply_a_xml = TransportInfo4Xml(
|
|
resulting_protocol, content_name,
|
|
channel_name_a, 4, 5,
|
|
channel_name_b, 6, 7);
|
|
std::string transport_info_reply_b_xml = "";
|
|
std::string accept_xml = AcceptXml(
|
|
resulting_protocol,
|
|
gingle_content_type,
|
|
content_name, content_type,
|
|
"", "");
|
|
|
|
|
|
TestSession(initiator_protocol, responder_protocol, resulting_protocol,
|
|
gingle_content_type,
|
|
content_type,
|
|
content_name, channel_name_a,
|
|
content_name, channel_name_b,
|
|
initiate_xml,
|
|
transport_info_a_xml, transport_info_b_xml,
|
|
transport_info_reply_a_xml, transport_info_reply_b_xml,
|
|
accept_xml);
|
|
}
|
|
|
|
// Since media content is "split" into two contents (audio and
|
|
// video), we need to treat it special.
|
|
void TestVideoContents(SignalingProtocol initiator_protocol,
|
|
SignalingProtocol responder_protocol,
|
|
SignalingProtocol resulting_protocol) {
|
|
std::string content_type = cricket::NS_JINGLE_RTP;
|
|
std::string gingle_content_type = cricket::NS_GINGLE_VIDEO;
|
|
std::string content_name_a = cricket::CN_AUDIO;
|
|
std::string channel_name_a = "rtp";
|
|
std::string content_name_b = cricket::CN_VIDEO;
|
|
std::string channel_name_b = "video_rtp";
|
|
|
|
std::string initiate_xml = InitiateXml(
|
|
initiator_protocol,
|
|
gingle_content_type,
|
|
content_name_a, content_type,
|
|
content_name_b, content_type);
|
|
std::string transport_info_a_xml = TransportInfo2Xml(
|
|
initiator_protocol, content_name_a,
|
|
channel_name_a, 0, 1);
|
|
std::string transport_info_b_xml = TransportInfo2Xml(
|
|
initiator_protocol, content_name_b,
|
|
channel_name_b, 2, 3);
|
|
std::string transport_info_reply_a_xml = TransportInfo2Xml(
|
|
resulting_protocol, content_name_a,
|
|
channel_name_a, 4, 5);
|
|
std::string transport_info_reply_b_xml = TransportInfo2Xml(
|
|
resulting_protocol, content_name_b,
|
|
channel_name_b, 6, 7);
|
|
std::string accept_xml = AcceptXml(
|
|
resulting_protocol,
|
|
gingle_content_type,
|
|
content_name_a, content_type,
|
|
content_name_b, content_type);
|
|
|
|
TestSession(initiator_protocol, responder_protocol, resulting_protocol,
|
|
gingle_content_type,
|
|
content_type,
|
|
content_name_a, channel_name_a,
|
|
content_name_b, channel_name_b,
|
|
initiate_xml,
|
|
transport_info_a_xml, transport_info_b_xml,
|
|
transport_info_reply_a_xml, transport_info_reply_b_xml,
|
|
accept_xml);
|
|
}
|
|
|
|
void TestBadRedirect(SignalingProtocol protocol) {
|
|
std::string content_name = "main";
|
|
std::string content_type = "http://oink.splat/session";
|
|
std::string channel_name_a = "chana";
|
|
std::string channel_name_b = "chanb";
|
|
std::string initiate_xml = InitiateXml(
|
|
protocol, content_name, content_type);
|
|
std::string transport_info_xml = TransportInfo4Xml(
|
|
protocol, content_name,
|
|
channel_name_a, 0, 1,
|
|
channel_name_b, 2, 3);
|
|
std::string transport_info_reply_xml = TransportInfo4Xml(
|
|
protocol, content_name,
|
|
channel_name_a, 4, 5,
|
|
channel_name_b, 6, 7);
|
|
std::string accept_xml = AcceptXml(
|
|
protocol, content_name, content_type);
|
|
std::string responder_full = kResponder + "/full";
|
|
|
|
rtc::scoped_ptr<cricket::PortAllocator> allocator(
|
|
new TestPortAllocator());
|
|
int next_message_id = 0;
|
|
|
|
rtc::scoped_ptr<TestClient> initiator(
|
|
new TestClient(allocator.get(), &next_message_id,
|
|
kInitiator, protocol,
|
|
content_type,
|
|
content_name, channel_name_a,
|
|
content_name, channel_name_b));
|
|
|
|
rtc::scoped_ptr<TestClient> responder(
|
|
new TestClient(allocator.get(), &next_message_id,
|
|
responder_full, protocol,
|
|
content_type,
|
|
content_name, channel_name_a,
|
|
content_name, channel_name_b));
|
|
|
|
// Create Session and check channels and state.
|
|
initiator->CreateSession();
|
|
EXPECT_EQ(1U, initiator->session_created_count);
|
|
EXPECT_EQ(kSessionId, initiator->session->id());
|
|
EXPECT_EQ(initiator->session->local_name(), kInitiator);
|
|
EXPECT_EQ(cricket::BaseSession::STATE_INIT,
|
|
initiator->session_state());
|
|
|
|
EXPECT_TRUE(initiator->HasChannel(content_name, 1));
|
|
EXPECT_TRUE(initiator->HasChannel(content_name, 2));
|
|
|
|
// Initiate and expect initiate message sent.
|
|
cricket::SessionDescription* offer = NewTestSessionDescription(
|
|
content_name, content_type);
|
|
EXPECT_TRUE(initiator->session->Initiate(kResponder, offer));
|
|
EXPECT_EQ(initiator->session->remote_name(), kResponder);
|
|
EXPECT_EQ(initiator->session->local_description(), offer);
|
|
|
|
EXPECT_TRUE_WAIT(initiator->sent_stanza_count() > 0, kEventTimeout);
|
|
EXPECT_EQ(cricket::BaseSession::STATE_SENTINITIATE,
|
|
initiator->session_state());
|
|
initiator->ExpectSentStanza(
|
|
IqSet("0", kInitiator, kResponder, initiate_xml));
|
|
|
|
// Expect transport-info message from initiator.
|
|
initiator->DeliverAckToLastStanza();
|
|
initiator->PrepareCandidates();
|
|
EXPECT_TRUE_WAIT(initiator->sent_stanza_count() > 0, kEventTimeout);
|
|
initiator->ExpectSentStanza(
|
|
IqSet("1", kInitiator, kResponder, transport_info_xml));
|
|
|
|
// Send an unauthorized redirect to the initiator and expect it be ignored.
|
|
initiator->blow_up_on_error = false;
|
|
const buzz::XmlElement* initiate_stanza = initiator->stanza();
|
|
rtc::scoped_ptr<buzz::XmlElement> redirect_stanza(
|
|
buzz::XmlElement::ForStr(
|
|
IqError("ER", kResponder, kInitiator,
|
|
RedirectXml(protocol, initiate_xml, "not@allowed.com"))));
|
|
initiator->session_manager->OnFailedSend(
|
|
initiate_stanza, redirect_stanza.get());
|
|
EXPECT_EQ(initiator->session->remote_name(), kResponder);
|
|
initiator->blow_up_on_error = true;
|
|
EXPECT_EQ(initiator->error_count, 1);
|
|
}
|
|
|
|
void TestGoodRedirect(SignalingProtocol protocol) {
|
|
std::string content_name = "main";
|
|
std::string content_type = "http://oink.splat/session";
|
|
std::string channel_name_a = "chana";
|
|
std::string channel_name_b = "chanb";
|
|
std::string initiate_xml = InitiateXml(
|
|
protocol, content_name, content_type);
|
|
std::string transport_info_xml = TransportInfo4Xml(
|
|
protocol, content_name,
|
|
channel_name_a, 0, 1,
|
|
channel_name_b, 2, 3);
|
|
std::string transport_info_reply_xml = TransportInfo4Xml(
|
|
protocol, content_name,
|
|
channel_name_a, 4, 5,
|
|
channel_name_b, 6, 7);
|
|
std::string accept_xml = AcceptXml(
|
|
protocol, content_name, content_type);
|
|
std::string responder_full = kResponder + "/full";
|
|
|
|
rtc::scoped_ptr<cricket::PortAllocator> allocator(
|
|
new TestPortAllocator());
|
|
int next_message_id = 0;
|
|
|
|
rtc::scoped_ptr<TestClient> initiator(
|
|
new TestClient(allocator.get(), &next_message_id,
|
|
kInitiator, protocol,
|
|
content_type,
|
|
content_name, channel_name_a,
|
|
content_name, channel_name_b));
|
|
|
|
rtc::scoped_ptr<TestClient> responder(
|
|
new TestClient(allocator.get(), &next_message_id,
|
|
responder_full, protocol,
|
|
content_type,
|
|
content_name, channel_name_a,
|
|
content_name, channel_name_b));
|
|
|
|
// Create Session and check channels and state.
|
|
initiator->CreateSession();
|
|
EXPECT_EQ(1U, initiator->session_created_count);
|
|
EXPECT_EQ(kSessionId, initiator->session->id());
|
|
EXPECT_EQ(initiator->session->local_name(), kInitiator);
|
|
EXPECT_EQ(cricket::BaseSession::STATE_INIT,
|
|
initiator->session_state());
|
|
|
|
EXPECT_TRUE(initiator->HasChannel(content_name, 1));
|
|
EXPECT_TRUE(initiator->HasChannel(content_name, 2));
|
|
|
|
// Initiate and expect initiate message sent.
|
|
cricket::SessionDescription* offer = NewTestSessionDescription(
|
|
content_name, content_type);
|
|
EXPECT_TRUE(initiator->session->Initiate(kResponder, offer));
|
|
EXPECT_EQ(initiator->session->remote_name(), kResponder);
|
|
EXPECT_EQ(initiator->session->local_description(), offer);
|
|
|
|
EXPECT_TRUE_WAIT(initiator->sent_stanza_count() > 0, kEventTimeout);
|
|
EXPECT_EQ(cricket::BaseSession::STATE_SENTINITIATE,
|
|
initiator->session_state());
|
|
initiator->ExpectSentStanza(
|
|
IqSet("0", kInitiator, kResponder, initiate_xml));
|
|
|
|
// Expect transport-info message from initiator.
|
|
initiator->DeliverAckToLastStanza();
|
|
initiator->PrepareCandidates();
|
|
EXPECT_TRUE_WAIT(initiator->sent_stanza_count() > 0, kEventTimeout);
|
|
initiator->ExpectSentStanza(
|
|
IqSet("1", kInitiator, kResponder, transport_info_xml));
|
|
|
|
// Send a redirect to the initiator and expect all of the message
|
|
// to be resent.
|
|
const buzz::XmlElement* initiate_stanza = initiator->stanza();
|
|
rtc::scoped_ptr<buzz::XmlElement> redirect_stanza(
|
|
buzz::XmlElement::ForStr(
|
|
IqError("ER2", kResponder, kInitiator,
|
|
RedirectXml(protocol, initiate_xml, responder_full))));
|
|
initiator->session_manager->OnFailedSend(
|
|
initiate_stanza, redirect_stanza.get());
|
|
EXPECT_EQ(initiator->session->remote_name(), responder_full);
|
|
|
|
EXPECT_TRUE_WAIT(initiator->sent_stanza_count() > 0, kEventTimeout);
|
|
initiator->ExpectSentStanza(
|
|
IqSet("2", kInitiator, responder_full, initiate_xml));
|
|
initiator->ExpectSentStanza(
|
|
IqSet("3", kInitiator, responder_full, transport_info_xml));
|
|
|
|
// Deliver the initiate. Expect ack and session created with
|
|
// transports.
|
|
responder->DeliverStanza(
|
|
IqSet("2", kInitiator, responder_full, initiate_xml));
|
|
responder->ExpectSentStanza(
|
|
IqAck("2", responder_full, kInitiator));
|
|
EXPECT_EQ(0U, responder->sent_stanza_count());
|
|
|
|
EXPECT_EQ(1U, responder->session_created_count);
|
|
EXPECT_EQ(kSessionId, responder->session->id());
|
|
EXPECT_EQ(responder->session->local_name(), responder_full);
|
|
EXPECT_EQ(responder->session->remote_name(), kInitiator);
|
|
EXPECT_EQ(cricket::BaseSession::STATE_RECEIVEDINITIATE,
|
|
responder->session_state());
|
|
|
|
EXPECT_TRUE(responder->HasChannel(content_name, 1));
|
|
EXPECT_TRUE(responder->HasChannel(content_name, 2));
|
|
|
|
// Deliver transport-info and expect ack.
|
|
responder->DeliverStanza(
|
|
IqSet("3", kInitiator, responder_full, transport_info_xml));
|
|
responder->ExpectSentStanza(
|
|
IqAck("3", responder_full, kInitiator));
|
|
|
|
// Expect reply transport-infos sent to new remote JID
|
|
responder->PrepareCandidates();
|
|
EXPECT_TRUE_WAIT(responder->sent_stanza_count() > 0, kEventTimeout);
|
|
responder->ExpectSentStanza(
|
|
IqSet("4", responder_full, kInitiator, transport_info_reply_xml));
|
|
|
|
initiator->DeliverStanza(responder->stanza());
|
|
initiator->ExpectSentStanza(
|
|
IqAck("4", kInitiator, responder_full));
|
|
|
|
// The channels should be able to become writable at this point. This
|
|
// requires pinging, so it may take a little while.
|
|
EXPECT_TRUE_WAIT(initiator->chan_a->writable() &&
|
|
initiator->chan_a->readable(), kEventTimeout);
|
|
EXPECT_TRUE_WAIT(initiator->chan_b->writable() &&
|
|
initiator->chan_b->readable(), kEventTimeout);
|
|
EXPECT_TRUE_WAIT(responder->chan_a->writable() &&
|
|
responder->chan_a->readable(), kEventTimeout);
|
|
EXPECT_TRUE_WAIT(responder->chan_b->writable() &&
|
|
responder->chan_b->readable(), kEventTimeout);
|
|
|
|
// Accept the session and expect accept stanza.
|
|
cricket::SessionDescription* answer = NewTestSessionDescription(
|
|
content_name, content_type);
|
|
EXPECT_TRUE(responder->session->Accept(answer));
|
|
EXPECT_EQ(responder->session->local_description(), answer);
|
|
|
|
responder->ExpectSentStanza(
|
|
IqSet("5", responder_full, kInitiator, accept_xml));
|
|
EXPECT_EQ(0U, responder->sent_stanza_count());
|
|
|
|
// Deliver the accept message and expect an ack.
|
|
initiator->DeliverStanza(responder->stanza());
|
|
EXPECT_TRUE_WAIT(initiator->sent_stanza_count() > 0, kEventTimeout);
|
|
initiator->ExpectSentStanza(
|
|
IqAck("5", kInitiator, responder_full));
|
|
EXPECT_EQ(0U, initiator->sent_stanza_count());
|
|
|
|
// Both sessions should be in progress and have functioning
|
|
// channels.
|
|
EXPECT_EQ_WAIT(cricket::BaseSession::STATE_INPROGRESS,
|
|
initiator->session_state(), kEventTimeout);
|
|
EXPECT_EQ_WAIT(cricket::BaseSession::STATE_INPROGRESS,
|
|
responder->session_state(), kEventTimeout);
|
|
TestSendRecv(initiator->chan_a.get(), initiator->chan_b.get(),
|
|
responder->chan_a.get(), responder->chan_b.get());
|
|
}
|
|
|
|
void TestCandidatesInInitiateAndAccept(const std::string& test_name) {
|
|
std::string content_name = "main";
|
|
std::string content_type = "http://oink.splat/session";
|
|
std::string channel_name_a = "rtp";
|
|
std::string channel_name_b = "rtcp";
|
|
cricket::SignalingProtocol protocol = PROTOCOL_JINGLE;
|
|
|
|
rtc::scoped_ptr<cricket::PortAllocator> allocator(
|
|
new TestPortAllocator());
|
|
int next_message_id = 0;
|
|
|
|
rtc::scoped_ptr<TestClient> initiator(
|
|
new TestClient(allocator.get(), &next_message_id,
|
|
kInitiator, protocol,
|
|
content_type,
|
|
content_name, channel_name_a,
|
|
content_name, channel_name_b));
|
|
|
|
rtc::scoped_ptr<TestClient> responder(
|
|
new TestClient(allocator.get(), &next_message_id,
|
|
kResponder, protocol,
|
|
content_type,
|
|
content_name, channel_name_a,
|
|
content_name, channel_name_b));
|
|
|
|
// Create Session and check channels and state.
|
|
initiator->CreateSession();
|
|
EXPECT_TRUE(initiator->HasTransport(content_name));
|
|
EXPECT_TRUE(initiator->HasChannel(content_name, 1));
|
|
EXPECT_TRUE(initiator->HasTransport(content_name));
|
|
EXPECT_TRUE(initiator->HasChannel(content_name, 2));
|
|
|
|
// Initiate and expect initiate message sent.
|
|
cricket::SessionDescription* offer = NewTestSessionDescription(
|
|
content_name, content_type);
|
|
EXPECT_TRUE(initiator->session->Initiate(kResponder, offer));
|
|
|
|
EXPECT_TRUE_WAIT(initiator->sent_stanza_count() > 0, kEventTimeout);
|
|
EXPECT_EQ(cricket::BaseSession::STATE_SENTINITIATE,
|
|
initiator->session_state());
|
|
initiator->ExpectSentStanza(
|
|
IqSet("0", kInitiator, kResponder,
|
|
InitiateXml(protocol, content_name, content_type)));
|
|
|
|
// Fake the delivery the initiate and candidates together.
|
|
responder->DeliverStanza(
|
|
IqSet("A", kInitiator, kResponder,
|
|
JingleInitiateActionXml(
|
|
JingleContentXml(
|
|
content_name, content_type, kTransportType,
|
|
P2pCandidateXml(channel_name_a, 0) +
|
|
P2pCandidateXml(channel_name_a, 1) +
|
|
P2pCandidateXml(channel_name_b, 2) +
|
|
P2pCandidateXml(channel_name_b, 3)))));
|
|
responder->ExpectSentStanza(
|
|
IqAck("A", kResponder, kInitiator));
|
|
EXPECT_EQ(0U, responder->sent_stanza_count());
|
|
|
|
EXPECT_EQ(1U, responder->session_created_count);
|
|
EXPECT_EQ(kSessionId, responder->session->id());
|
|
EXPECT_EQ(responder->session->local_name(), kResponder);
|
|
EXPECT_EQ(responder->session->remote_name(), kInitiator);
|
|
EXPECT_EQ(cricket::BaseSession::STATE_RECEIVEDINITIATE,
|
|
responder->session_state());
|
|
|
|
EXPECT_TRUE(responder->HasTransport(content_name));
|
|
EXPECT_TRUE(responder->HasChannel(content_name, 1));
|
|
EXPECT_TRUE(responder->HasTransport(content_name));
|
|
EXPECT_TRUE(responder->HasChannel(content_name, 2));
|
|
|
|
// Expect transport-info message from initiator.
|
|
// But don't send candidates until initiate ack is received.
|
|
initiator->DeliverAckToLastStanza();
|
|
initiator->PrepareCandidates();
|
|
EXPECT_TRUE_WAIT(initiator->sent_stanza_count() > 0, kEventTimeout);
|
|
initiator->ExpectSentStanza(
|
|
IqSet("1", kInitiator, kResponder,
|
|
TransportInfo4Xml(protocol, content_name,
|
|
channel_name_a, 0, 1,
|
|
channel_name_b, 2, 3)));
|
|
|
|
responder->PrepareCandidates();
|
|
EXPECT_TRUE_WAIT(responder->sent_stanza_count() > 0, kEventTimeout);
|
|
responder->ExpectSentStanza(
|
|
IqSet("2", kResponder, kInitiator,
|
|
TransportInfo4Xml(protocol, content_name,
|
|
channel_name_a, 4, 5,
|
|
channel_name_b, 6, 7)));
|
|
|
|
// Accept the session and expect accept stanza.
|
|
cricket::SessionDescription* answer = NewTestSessionDescription(
|
|
content_name, content_type);
|
|
EXPECT_TRUE(responder->session->Accept(answer));
|
|
|
|
responder->ExpectSentStanza(
|
|
IqSet("3", kResponder, kInitiator,
|
|
AcceptXml(protocol, content_name, content_type)));
|
|
EXPECT_EQ(0U, responder->sent_stanza_count());
|
|
|
|
// Fake the delivery the accept and candidates together.
|
|
initiator->DeliverStanza(
|
|
IqSet("B", kResponder, kInitiator,
|
|
JingleActionXml("session-accept",
|
|
JingleContentXml(
|
|
content_name, content_type, kTransportType,
|
|
P2pCandidateXml(channel_name_a, 4) +
|
|
P2pCandidateXml(channel_name_a, 5) +
|
|
P2pCandidateXml(channel_name_b, 6) +
|
|
P2pCandidateXml(channel_name_b, 7)))));
|
|
EXPECT_TRUE_WAIT(initiator->sent_stanza_count() > 0, kEventTimeout);
|
|
initiator->ExpectSentStanza(
|
|
IqAck("B", kInitiator, kResponder));
|
|
EXPECT_EQ(0U, initiator->sent_stanza_count());
|
|
|
|
// The channels should be able to become writable at this point. This
|
|
// requires pinging, so it may take a little while.
|
|
EXPECT_TRUE_WAIT(initiator->chan_a->writable() &&
|
|
initiator->chan_a->readable(), kEventTimeout);
|
|
EXPECT_TRUE_WAIT(initiator->chan_b->writable() &&
|
|
initiator->chan_b->readable(), kEventTimeout);
|
|
EXPECT_TRUE_WAIT(responder->chan_a->writable() &&
|
|
responder->chan_a->readable(), kEventTimeout);
|
|
EXPECT_TRUE_WAIT(responder->chan_b->writable() &&
|
|
responder->chan_b->readable(), kEventTimeout);
|
|
|
|
|
|
// Both sessions should be in progress and have functioning
|
|
// channels.
|
|
EXPECT_EQ(protocol, initiator->session->current_protocol());
|
|
EXPECT_EQ(protocol, responder->session->current_protocol());
|
|
EXPECT_EQ_WAIT(cricket::BaseSession::STATE_INPROGRESS,
|
|
initiator->session_state(), kEventTimeout);
|
|
EXPECT_EQ_WAIT(cricket::BaseSession::STATE_INPROGRESS,
|
|
responder->session_state(), kEventTimeout);
|
|
TestSendRecv(initiator->chan_a.get(), initiator->chan_b.get(),
|
|
responder->chan_a.get(), responder->chan_b.get());
|
|
}
|
|
|
|
// Tests that when an initiator terminates right after initiate,
|
|
// everything behaves correctly.
|
|
void TestEarlyTerminationFromInitiator(SignalingProtocol protocol) {
|
|
std::string content_name = "main";
|
|
std::string content_type = "http://oink.splat/session";
|
|
|
|
rtc::scoped_ptr<cricket::PortAllocator> allocator(
|
|
new TestPortAllocator());
|
|
int next_message_id = 0;
|
|
|
|
rtc::scoped_ptr<TestClient> initiator(
|
|
new TestClient(allocator.get(), &next_message_id,
|
|
kInitiator, protocol,
|
|
content_type,
|
|
content_name, "a",
|
|
content_name, "b"));
|
|
|
|
rtc::scoped_ptr<TestClient> responder(
|
|
new TestClient(allocator.get(), &next_message_id,
|
|
kResponder, protocol,
|
|
content_type,
|
|
content_name, "a",
|
|
content_name, "b"));
|
|
|
|
// Send initiate
|
|
initiator->CreateSession();
|
|
EXPECT_TRUE(initiator->session->Initiate(
|
|
kResponder, NewTestSessionDescription(content_name, content_type)));
|
|
initiator->ExpectSentStanza(
|
|
IqSet("0", kInitiator, kResponder,
|
|
InitiateXml(protocol, content_name, content_type)));
|
|
EXPECT_EQ(cricket::BaseSession::STATE_SENTINITIATE,
|
|
initiator->session_state());
|
|
|
|
responder->DeliverStanza(initiator->stanza());
|
|
responder->ExpectSentStanza(
|
|
IqAck("0", kResponder, kInitiator));
|
|
EXPECT_EQ(cricket::BaseSession::STATE_RECEIVEDINITIATE,
|
|
responder->session_state());
|
|
|
|
initiator->session->TerminateWithReason(cricket::STR_TERMINATE_ERROR);
|
|
initiator->ExpectSentStanza(
|
|
IqSet("1", kInitiator, kResponder,
|
|
TerminateXml(protocol, cricket::STR_TERMINATE_ERROR)));
|
|
EXPECT_EQ(cricket::BaseSession::STATE_SENTTERMINATE,
|
|
initiator->session_state());
|
|
|
|
responder->DeliverStanza(initiator->stanza());
|
|
responder->ExpectSentStanza(
|
|
IqAck("1", kResponder, kInitiator));
|
|
EXPECT_EQ(cricket::BaseSession::STATE_RECEIVEDTERMINATE,
|
|
responder->session_state());
|
|
}
|
|
|
|
// Tests that when the responder rejects, everything behaves
|
|
// correctly.
|
|
void TestRejection(SignalingProtocol protocol) {
|
|
std::string content_name = "main";
|
|
std::string content_type = "http://oink.splat/session";
|
|
|
|
rtc::scoped_ptr<cricket::PortAllocator> allocator(
|
|
new TestPortAllocator());
|
|
int next_message_id = 0;
|
|
|
|
rtc::scoped_ptr<TestClient> initiator(
|
|
new TestClient(allocator.get(), &next_message_id,
|
|
kInitiator, protocol,
|
|
content_type,
|
|
content_name, "a",
|
|
content_name, "b"));
|
|
|
|
// Send initiate
|
|
initiator->CreateSession();
|
|
EXPECT_TRUE(initiator->session->Initiate(
|
|
kResponder, NewTestSessionDescription(content_name, content_type)));
|
|
initiator->ExpectSentStanza(
|
|
IqSet("0", kInitiator, kResponder,
|
|
InitiateXml(protocol, content_name, content_type)));
|
|
EXPECT_EQ(cricket::BaseSession::STATE_SENTINITIATE,
|
|
initiator->session_state());
|
|
|
|
initiator->DeliverStanza(
|
|
IqSet("1", kResponder, kInitiator,
|
|
RejectXml(protocol, cricket::STR_TERMINATE_ERROR)));
|
|
initiator->ExpectSentStanza(
|
|
IqAck("1", kInitiator, kResponder));
|
|
if (protocol == PROTOCOL_JINGLE) {
|
|
EXPECT_EQ(cricket::BaseSession::STATE_RECEIVEDTERMINATE,
|
|
initiator->session_state());
|
|
} else {
|
|
EXPECT_EQ(cricket::BaseSession::STATE_RECEIVEDREJECT,
|
|
initiator->session_state());
|
|
}
|
|
}
|
|
|
|
void TestTransportMux() {
|
|
SignalingProtocol initiator_protocol = PROTOCOL_JINGLE;
|
|
SignalingProtocol responder_protocol = PROTOCOL_JINGLE;
|
|
SignalingProtocol resulting_protocol = PROTOCOL_JINGLE;
|
|
std::string content_type = cricket::NS_JINGLE_RTP;
|
|
std::string gingle_content_type = cricket::NS_GINGLE_VIDEO;
|
|
std::string content_name_a = cricket::CN_AUDIO;
|
|
std::string channel_name_a = "rtp";
|
|
std::string content_name_b = cricket::CN_VIDEO;
|
|
std::string channel_name_b = "video_rtp";
|
|
|
|
std::string initiate_xml = InitiateXml(
|
|
initiator_protocol,
|
|
gingle_content_type,
|
|
content_name_a, content_type,
|
|
content_name_b, content_type, true);
|
|
std::string transport_info_a_xml = TransportInfo2Xml(
|
|
initiator_protocol, content_name_a,
|
|
channel_name_a, 0, 1);
|
|
std::string transport_info_b_xml = TransportInfo2Xml(
|
|
initiator_protocol, content_name_b,
|
|
channel_name_b, 2, 3);
|
|
std::string transport_info_reply_a_xml = TransportInfo2Xml(
|
|
resulting_protocol, content_name_a,
|
|
channel_name_a, 4, 5);
|
|
std::string transport_info_reply_b_xml = TransportInfo2Xml(
|
|
resulting_protocol, content_name_b,
|
|
channel_name_b, 6, 7);
|
|
std::string accept_xml = AcceptXml(
|
|
resulting_protocol,
|
|
gingle_content_type,
|
|
content_name_a, content_type,
|
|
content_name_b, content_type, true);
|
|
|
|
TestSession(initiator_protocol, responder_protocol, resulting_protocol,
|
|
gingle_content_type,
|
|
content_type,
|
|
content_name_a, channel_name_a,
|
|
content_name_b, channel_name_b,
|
|
initiate_xml,
|
|
transport_info_a_xml, transport_info_b_xml,
|
|
transport_info_reply_a_xml, transport_info_reply_b_xml,
|
|
accept_xml,
|
|
true);
|
|
}
|
|
|
|
void TestSendDescriptionInfo() {
|
|
rtc::scoped_ptr<cricket::PortAllocator> allocator(
|
|
new TestPortAllocator());
|
|
int next_message_id = 0;
|
|
|
|
std::string content_name = "content-name";
|
|
std::string content_type = "content-type";
|
|
rtc::scoped_ptr<TestClient> initiator(
|
|
new TestClient(allocator.get(), &next_message_id,
|
|
kInitiator, PROTOCOL_JINGLE,
|
|
content_type,
|
|
content_name, "",
|
|
"", ""));
|
|
|
|
initiator->CreateSession();
|
|
cricket::SessionDescription* offer = NewTestSessionDescription(
|
|
content_name, content_type);
|
|
std::string initiate_xml = InitiateXml(
|
|
PROTOCOL_JINGLE, content_name, content_type);
|
|
|
|
cricket::ContentInfos contents;
|
|
TestContentDescription content(content_type, content_type);
|
|
contents.push_back(
|
|
cricket::ContentInfo(content_name, content_type, &content));
|
|
std::string description_info_xml = JingleDescriptionInfoXml(
|
|
content_name, content_type);
|
|
|
|
EXPECT_TRUE(initiator->session->Initiate(kResponder, offer));
|
|
initiator->ExpectSentStanza(
|
|
IqSet("0", kInitiator, kResponder, initiate_xml));
|
|
|
|
EXPECT_TRUE(initiator->session->SendDescriptionInfoMessage(contents));
|
|
initiator->ExpectSentStanza(
|
|
IqSet("1", kInitiator, kResponder, description_info_xml));
|
|
}
|
|
|
|
void DoTestSignalNewDescription(
|
|
TestClient* client,
|
|
cricket::BaseSession::State state,
|
|
cricket::ContentAction expected_content_action,
|
|
cricket::ContentSource expected_content_source) {
|
|
// Clean up before the new test.
|
|
client->new_local_description = false;
|
|
client->new_remote_description = false;
|
|
|
|
client->SetSessionState(state);
|
|
EXPECT_EQ((expected_content_source == cricket::CS_LOCAL),
|
|
client->new_local_description);
|
|
EXPECT_EQ((expected_content_source == cricket::CS_REMOTE),
|
|
client->new_remote_description);
|
|
EXPECT_EQ(expected_content_action, client->last_content_action);
|
|
EXPECT_EQ(expected_content_source, client->last_content_source);
|
|
}
|
|
|
|
void TestCallerSignalNewDescription() {
|
|
rtc::scoped_ptr<cricket::PortAllocator> allocator(
|
|
new TestPortAllocator());
|
|
int next_message_id = 0;
|
|
|
|
std::string content_name = "content-name";
|
|
std::string content_type = "content-type";
|
|
rtc::scoped_ptr<TestClient> initiator(
|
|
new TestClient(allocator.get(), &next_message_id,
|
|
kInitiator, PROTOCOL_JINGLE,
|
|
content_type,
|
|
content_name, "",
|
|
"", ""));
|
|
|
|
initiator->CreateSession();
|
|
|
|
// send offer -> send update offer ->
|
|
// receive pr answer -> receive update pr answer ->
|
|
// receive answer
|
|
DoTestSignalNewDescription(
|
|
initiator.get(), cricket::BaseSession::STATE_SENTINITIATE,
|
|
cricket::CA_OFFER, cricket::CS_LOCAL);
|
|
|
|
DoTestSignalNewDescription(
|
|
initiator.get(), cricket::BaseSession::STATE_SENTINITIATE,
|
|
cricket::CA_OFFER, cricket::CS_LOCAL);
|
|
|
|
DoTestSignalNewDescription(
|
|
initiator.get(), cricket::BaseSession::STATE_RECEIVEDPRACCEPT,
|
|
cricket::CA_PRANSWER, cricket::CS_REMOTE);
|
|
|
|
DoTestSignalNewDescription(
|
|
initiator.get(), cricket::BaseSession::STATE_RECEIVEDPRACCEPT,
|
|
cricket::CA_PRANSWER, cricket::CS_REMOTE);
|
|
|
|
DoTestSignalNewDescription(
|
|
initiator.get(), cricket::BaseSession::STATE_RECEIVEDACCEPT,
|
|
cricket::CA_ANSWER, cricket::CS_REMOTE);
|
|
}
|
|
|
|
void TestCalleeSignalNewDescription() {
|
|
rtc::scoped_ptr<cricket::PortAllocator> allocator(
|
|
new TestPortAllocator());
|
|
int next_message_id = 0;
|
|
|
|
std::string content_name = "content-name";
|
|
std::string content_type = "content-type";
|
|
rtc::scoped_ptr<TestClient> initiator(
|
|
new TestClient(allocator.get(), &next_message_id,
|
|
kInitiator, PROTOCOL_JINGLE,
|
|
content_type,
|
|
content_name, "",
|
|
"", ""));
|
|
|
|
initiator->CreateSession();
|
|
|
|
// receive offer -> receive update offer ->
|
|
// send pr answer -> send update pr answer ->
|
|
// send answer
|
|
DoTestSignalNewDescription(
|
|
initiator.get(), cricket::BaseSession::STATE_RECEIVEDINITIATE,
|
|
cricket::CA_OFFER, cricket::CS_REMOTE);
|
|
|
|
DoTestSignalNewDescription(
|
|
initiator.get(), cricket::BaseSession::STATE_RECEIVEDINITIATE,
|
|
cricket::CA_OFFER, cricket::CS_REMOTE);
|
|
|
|
DoTestSignalNewDescription(
|
|
initiator.get(), cricket::BaseSession::STATE_SENTPRACCEPT,
|
|
cricket::CA_PRANSWER, cricket::CS_LOCAL);
|
|
|
|
DoTestSignalNewDescription(
|
|
initiator.get(), cricket::BaseSession::STATE_SENTPRACCEPT,
|
|
cricket::CA_PRANSWER, cricket::CS_LOCAL);
|
|
|
|
DoTestSignalNewDescription(
|
|
initiator.get(), cricket::BaseSession::STATE_SENTACCEPT,
|
|
cricket::CA_ANSWER, cricket::CS_LOCAL);
|
|
}
|
|
|
|
void TestGetTransportStats() {
|
|
rtc::scoped_ptr<cricket::PortAllocator> allocator(
|
|
new TestPortAllocator());
|
|
int next_message_id = 0;
|
|
|
|
std::string content_name = "content-name";
|
|
std::string content_type = "content-type";
|
|
rtc::scoped_ptr<TestClient> initiator(
|
|
new TestClient(allocator.get(), &next_message_id,
|
|
kInitiator, PROTOCOL_JINGLE,
|
|
content_type,
|
|
content_name, "",
|
|
"", ""));
|
|
initiator->CreateSession();
|
|
|
|
cricket::SessionStats stats;
|
|
EXPECT_TRUE(initiator->session->GetStats(&stats));
|
|
// At initiation, there are 2 transports.
|
|
EXPECT_EQ(2ul, stats.proxy_to_transport.size());
|
|
EXPECT_EQ(2ul, stats.transport_stats.size());
|
|
}
|
|
};
|
|
|
|
// For each of these, "X => Y = Z" means "if a client with protocol X
|
|
// initiates to a client with protocol Y, they end up speaking protocol Z.
|
|
|
|
// Gingle => Gingle = Gingle (with other content)
|
|
TEST_F(SessionTest, GingleToGingleOtherContent) {
|
|
TestOtherContent(PROTOCOL_GINGLE, PROTOCOL_GINGLE, PROTOCOL_GINGLE);
|
|
}
|
|
|
|
// Gingle => Gingle = Gingle (with audio content)
|
|
TEST_F(SessionTest, GingleToGingleAudioContent) {
|
|
TestAudioContent(PROTOCOL_GINGLE, PROTOCOL_GINGLE, PROTOCOL_GINGLE);
|
|
}
|
|
|
|
// Gingle => Gingle = Gingle (with video contents)
|
|
TEST_F(SessionTest, GingleToGingleVideoContents) {
|
|
TestVideoContents(PROTOCOL_GINGLE, PROTOCOL_GINGLE, PROTOCOL_GINGLE);
|
|
}
|
|
|
|
// Jingle => Jingle = Jingle (with other content)
|
|
TEST_F(SessionTest, JingleToJingleOtherContent) {
|
|
TestOtherContent(PROTOCOL_JINGLE, PROTOCOL_JINGLE, PROTOCOL_JINGLE);
|
|
}
|
|
|
|
// Jingle => Jingle = Jingle (with audio content)
|
|
TEST_F(SessionTest, JingleToJingleAudioContent) {
|
|
TestAudioContent(PROTOCOL_JINGLE, PROTOCOL_JINGLE, PROTOCOL_JINGLE);
|
|
}
|
|
|
|
// Jingle => Jingle = Jingle (with video contents)
|
|
TEST_F(SessionTest, JingleToJingleVideoContents) {
|
|
TestVideoContents(PROTOCOL_JINGLE, PROTOCOL_JINGLE, PROTOCOL_JINGLE);
|
|
}
|
|
|
|
// Hybrid => Hybrid = Jingle (with other content)
|
|
TEST_F(SessionTest, HybridToHybridOtherContent) {
|
|
TestOtherContent(PROTOCOL_HYBRID, PROTOCOL_HYBRID, PROTOCOL_JINGLE);
|
|
}
|
|
|
|
// Hybrid => Hybrid = Jingle (with audio content)
|
|
TEST_F(SessionTest, HybridToHybridAudioContent) {
|
|
TestAudioContent(PROTOCOL_HYBRID, PROTOCOL_HYBRID, PROTOCOL_JINGLE);
|
|
}
|
|
|
|
// Hybrid => Hybrid = Jingle (with video contents)
|
|
TEST_F(SessionTest, HybridToHybridVideoContents) {
|
|
TestVideoContents(PROTOCOL_HYBRID, PROTOCOL_HYBRID, PROTOCOL_JINGLE);
|
|
}
|
|
|
|
// Gingle => Hybrid = Gingle (with other content)
|
|
TEST_F(SessionTest, GingleToHybridOtherContent) {
|
|
TestOtherContent(PROTOCOL_GINGLE, PROTOCOL_HYBRID, PROTOCOL_GINGLE);
|
|
}
|
|
|
|
// Gingle => Hybrid = Gingle (with audio content)
|
|
TEST_F(SessionTest, GingleToHybridAudioContent) {
|
|
TestAudioContent(PROTOCOL_GINGLE, PROTOCOL_HYBRID, PROTOCOL_GINGLE);
|
|
}
|
|
|
|
// Gingle => Hybrid = Gingle (with video contents)
|
|
TEST_F(SessionTest, GingleToHybridVideoContents) {
|
|
TestVideoContents(PROTOCOL_GINGLE, PROTOCOL_HYBRID, PROTOCOL_GINGLE);
|
|
}
|
|
|
|
// Jingle => Hybrid = Jingle (with other content)
|
|
TEST_F(SessionTest, JingleToHybridOtherContent) {
|
|
TestOtherContent(PROTOCOL_JINGLE, PROTOCOL_HYBRID, PROTOCOL_JINGLE);
|
|
}
|
|
|
|
// Jingle => Hybrid = Jingle (with audio content)
|
|
TEST_F(SessionTest, JingleToHybridAudioContent) {
|
|
TestAudioContent(PROTOCOL_JINGLE, PROTOCOL_HYBRID, PROTOCOL_JINGLE);
|
|
}
|
|
|
|
// Jingle => Hybrid = Jingle (with video contents)
|
|
TEST_F(SessionTest, JingleToHybridVideoContents) {
|
|
TestVideoContents(PROTOCOL_JINGLE, PROTOCOL_HYBRID, PROTOCOL_JINGLE);
|
|
}
|
|
|
|
// Hybrid => Gingle = Gingle (with other content)
|
|
TEST_F(SessionTest, HybridToGingleOtherContent) {
|
|
TestOtherContent(PROTOCOL_HYBRID, PROTOCOL_GINGLE, PROTOCOL_GINGLE);
|
|
}
|
|
|
|
// Hybrid => Gingle = Gingle (with audio content)
|
|
TEST_F(SessionTest, HybridToGingleAudioContent) {
|
|
TestAudioContent(PROTOCOL_HYBRID, PROTOCOL_GINGLE, PROTOCOL_GINGLE);
|
|
}
|
|
|
|
// Hybrid => Gingle = Gingle (with video contents)
|
|
TEST_F(SessionTest, HybridToGingleVideoContents) {
|
|
TestVideoContents(PROTOCOL_HYBRID, PROTOCOL_GINGLE, PROTOCOL_GINGLE);
|
|
}
|
|
|
|
// Hybrid => Jingle = Jingle (with other content)
|
|
TEST_F(SessionTest, HybridToJingleOtherContent) {
|
|
TestOtherContent(PROTOCOL_HYBRID, PROTOCOL_JINGLE, PROTOCOL_JINGLE);
|
|
}
|
|
|
|
// Hybrid => Jingle = Jingle (with audio content)
|
|
TEST_F(SessionTest, HybridToJingleAudioContent) {
|
|
TestAudioContent(PROTOCOL_HYBRID, PROTOCOL_JINGLE, PROTOCOL_JINGLE);
|
|
}
|
|
|
|
// Hybrid => Jingle = Jingle (with video contents)
|
|
TEST_F(SessionTest, HybridToJingleVideoContents) {
|
|
TestVideoContents(PROTOCOL_HYBRID, PROTOCOL_JINGLE, PROTOCOL_JINGLE);
|
|
}
|
|
|
|
TEST_F(SessionTest, GingleEarlyTerminationFromInitiator) {
|
|
TestEarlyTerminationFromInitiator(PROTOCOL_GINGLE);
|
|
}
|
|
|
|
TEST_F(SessionTest, JingleEarlyTerminationFromInitiator) {
|
|
TestEarlyTerminationFromInitiator(PROTOCOL_JINGLE);
|
|
}
|
|
|
|
TEST_F(SessionTest, HybridEarlyTerminationFromInitiator) {
|
|
TestEarlyTerminationFromInitiator(PROTOCOL_HYBRID);
|
|
}
|
|
|
|
TEST_F(SessionTest, GingleRejection) {
|
|
TestRejection(PROTOCOL_GINGLE);
|
|
}
|
|
|
|
TEST_F(SessionTest, JingleRejection) {
|
|
TestRejection(PROTOCOL_JINGLE);
|
|
}
|
|
|
|
TEST_F(SessionTest, GingleGoodRedirect) {
|
|
TestGoodRedirect(PROTOCOL_GINGLE);
|
|
}
|
|
|
|
TEST_F(SessionTest, JingleGoodRedirect) {
|
|
TestGoodRedirect(PROTOCOL_JINGLE);
|
|
}
|
|
|
|
TEST_F(SessionTest, GingleBadRedirect) {
|
|
TestBadRedirect(PROTOCOL_GINGLE);
|
|
}
|
|
|
|
TEST_F(SessionTest, JingleBadRedirect) {
|
|
TestBadRedirect(PROTOCOL_JINGLE);
|
|
}
|
|
|
|
TEST_F(SessionTest, TestCandidatesInInitiateAndAccept) {
|
|
TestCandidatesInInitiateAndAccept("Candidates in initiate/accept");
|
|
}
|
|
|
|
TEST_F(SessionTest, TestTransportMux) {
|
|
TestTransportMux();
|
|
}
|
|
|
|
TEST_F(SessionTest, TestSendDescriptionInfo) {
|
|
TestSendDescriptionInfo();
|
|
}
|
|
|
|
TEST_F(SessionTest, TestCallerSignalNewDescription) {
|
|
TestCallerSignalNewDescription();
|
|
}
|
|
|
|
TEST_F(SessionTest, TestCalleeSignalNewDescription) {
|
|
TestCalleeSignalNewDescription();
|
|
}
|
|
|
|
TEST_F(SessionTest, TestGetTransportStats) {
|
|
TestGetTransportStats();
|
|
}
|