summaryrefslogtreecommitdiff
path: root/Plugins/jingle/libjingle/talk/p2p/base/session_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'Plugins/jingle/libjingle/talk/p2p/base/session_unittest.cc')
-rw-r--r--Plugins/jingle/libjingle/talk/p2p/base/session_unittest.cc1039
1 files changed, 1039 insertions, 0 deletions
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/session_unittest.cc b/Plugins/jingle/libjingle/talk/p2p/base/session_unittest.cc
new file mode 100644
index 0000000..972ed9d
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/p2p/base/session_unittest.cc
@@ -0,0 +1,1039 @@
+#include <iostream>
+#include <sstream>
+#include <deque>
+#include <map>
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/host.h"
+#include "talk/base/natserver.h"
+#include "talk/base/natsocketfactory.h"
+#include "talk/base/helpers.h"
+#include "talk/xmpp/constants.h"
+#include "talk/p2p/base/constants.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/sessionclient.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/portallocator.h"
+#include "talk/p2p/base/transportchannel.h"
+#include "talk/p2p/base/udpport.h"
+#include "talk/p2p/base/stunport.h"
+#include "talk/p2p/base/relayport.h"
+#include "talk/p2p/base/p2ptransport.h"
+#include "talk/p2p/base/rawtransport.h"
+#include "talk/p2p/base/stunserver.h"
+#include "talk/p2p/base/relayserver.h"
+
+using namespace cricket;
+using namespace buzz;
+
+const std::string kSessionType = "http://oink.splat/session";
+
+const talk_base::SocketAddress kStunServerAddress("127.0.0.1", 7000);
+const talk_base::SocketAddress kStunServerAddress2("127.0.0.1", 7001);
+
+const talk_base::SocketAddress kRelayServerIntAddress("127.0.0.1", 7002);
+const talk_base::SocketAddress kRelayServerExtAddress("127.0.0.1", 7003);
+
+const int kNumPorts = 2;
+
+int gPort = 28653;
+int GetNextPort() {
+ int p = gPort;
+ gPort += 5;
+ return p;
+}
+
+int gID = 0;
+std::string GetNextID() {
+ std::ostringstream ost;
+ ost << gID++;
+ return ost.str();
+}
+
+class TestPortAllocatorSession : public PortAllocatorSession {
+public:
+ TestPortAllocatorSession(talk_base::Thread* worker_thread, talk_base::SocketFactory* factory,
+ const std::string& name, const std::string& session_type)
+ : PortAllocatorSession(0), worker_thread_(worker_thread),
+ factory_(factory), name_(name), ports_(kNumPorts),
+ address_("127.0.0.1", 0), network_("network", address_.ip()),
+ running_(false) {
+ }
+
+ ~TestPortAllocatorSession() {
+ for (int i = 0; i < ports_.size(); i++)
+ delete ports_[i];
+ }
+
+ virtual void GetInitialPorts() {
+ // These are the flags set by the raw transport.
+ uint32 raw_flags = PORTALLOCATOR_DISABLE_UDP | PORTALLOCATOR_DISABLE_TCP;
+
+ // If the client doesn't care, just give them two UDP ports.
+ if (flags() == 0) {
+ for (int i = 0; i < kNumPorts; i++) {
+ ports_[i] = new UDPPort(worker_thread_, factory_, &network_,
+ GetAddress());
+ AddPort(ports_[i]);
+ }
+
+ // If the client requested just stun and relay, we have to oblidge.
+ } else if (flags() == raw_flags) {
+ StunPort* sport = new StunPort(worker_thread_, factory_, &network_,
+ GetAddress(), kStunServerAddress);
+ sport->set_server_addr2(kStunServerAddress2);
+ ports_[0] = sport;
+ AddPort(sport);
+
+ std::string username = CreateRandomString(16);
+ std::string password = CreateRandomString(16);
+ RelayPort* rport = new RelayPort(worker_thread_, factory_, &network_,
+ GetAddress(), username, password, "");
+ rport->AddServerAddress(
+ ProtocolAddress(kRelayServerIntAddress, PROTO_UDP));
+ ports_[1] = rport;
+ AddPort(rport);
+ } else {
+ ASSERT(false);
+ }
+ }
+
+ virtual void StartGetAllPorts() { running_ = true; }
+ virtual void StopGetAllPorts() { running_ = false; }
+ virtual bool IsGettingAllPorts() { return running_; }
+
+ talk_base::SocketAddress GetAddress() const {
+ talk_base::SocketAddress addr(address_);
+ addr.SetPort(GetNextPort());
+ return addr;
+ }
+
+ void AddPort(Port* port) {
+ port->set_name(name_);
+ port->set_preference(1.0);
+ port->set_generation(0);
+ port->SignalDestroyed.connect(
+ this, &TestPortAllocatorSession::OnPortDestroyed);
+ port->SignalAddressReady.connect(
+ this, &TestPortAllocatorSession::OnAddressReady);
+ port->PrepareAddress();
+ SignalPortReady(this, port);
+ }
+
+ void OnPortDestroyed(Port* port) {
+ for (int i = 0; i < ports_.size(); i++) {
+ if (ports_[i] == port)
+ ports_[i] = NULL;
+ }
+ }
+
+ void OnAddressReady(Port* port) {
+ SignalCandidatesReady(this, port->candidates());
+ }
+
+private:
+ talk_base::Thread* worker_thread_;
+ talk_base::SocketFactory* factory_;
+ std::string name_;
+ std::vector<Port*> ports_;
+ talk_base::SocketAddress address_;
+ talk_base::Network network_;
+ bool running_;
+};
+
+class TestPortAllocator : public PortAllocator {
+public:
+ TestPortAllocator(talk_base::Thread* worker_thread, talk_base::SocketFactory* factory)
+ : worker_thread_(worker_thread), factory_(factory) {
+ if (factory_ == NULL)
+ factory_ = worker_thread_->socketserver();
+ }
+
+ virtual PortAllocatorSession *CreateSession(const std::string &name, const std::string &session_type) {
+ return new TestPortAllocatorSession(worker_thread_, factory_, name, session_type);
+ }
+
+private:
+ talk_base::Thread* worker_thread_;
+ talk_base::SocketFactory* factory_;
+};
+
+struct SessionManagerHandler : sigslot::has_slots<> {
+ SessionManagerHandler(SessionManager* m, const std::string& u)
+ : manager(m), username(u), create_count(0), destroy_count(0) {
+ manager->SignalSessionCreate.connect(
+ this, &SessionManagerHandler::OnSessionCreate);
+ manager->SignalSessionDestroy.connect(
+ this, &SessionManagerHandler::OnSessionDestroy);
+ manager->SignalOutgoingMessage.connect(
+ this, &SessionManagerHandler::OnOutgoingMessage);
+ manager->SignalRequestSignaling.connect(
+ this, &SessionManagerHandler::OnRequestSignaling);
+ }
+
+ void OnSessionCreate(Session *session, bool initiate) {
+ create_count += 1;
+ last_id = session->id();
+ }
+
+ void OnSessionDestroy(Session *session) {
+ destroy_count += 1;
+ last_id = session->id();
+ }
+
+ void OnOutgoingMessage(const XmlElement* stanza) {
+ XmlElement* elem = new XmlElement(*stanza);
+ ASSERT(elem->Name() == QN_IQ);
+ ASSERT(elem->HasAttr(QN_TO));
+ ASSERT(!elem->HasAttr(QN_FROM));
+ ASSERT(elem->HasAttr(QN_TYPE));
+ ASSERT((elem->Attr(QN_TYPE) == "set") ||
+ (elem->Attr(QN_TYPE) == "result") ||
+ (elem->Attr(QN_TYPE) == "error"));
+
+ // Add in the appropriate "from".
+ elem->SetAttr(QN_FROM, username);
+
+ // Add in the appropriate IQ ID.
+ if (elem->Attr(QN_TYPE) == "set") {
+ ASSERT(!elem->HasAttr(QN_ID));
+ elem->SetAttr(QN_ID, GetNextID());
+ }
+
+ stanzas_.push_back(elem);
+ }
+
+ void OnRequestSignaling() {
+ manager->OnSignalingReady();
+ }
+
+
+ XmlElement* CheckNextStanza(const std::string& expected) {
+ // Get the next stanza, which should exist.
+ ASSERT(stanzas_.size() > 0);
+ XmlElement* stanza = stanzas_.front();
+ stanzas_.pop_front();
+
+ // Make sure the stanza is correct.
+ std::string actual = stanza->Str();
+ if (actual != expected) {
+ LOG(LERROR) << "Incorrect stanza: expected=\"" << expected
+ << "\" actual=\"" << actual << "\"";
+ ASSERT(actual == expected);
+ }
+
+ return stanza;
+ }
+
+ void CheckNoStanza() {
+ ASSERT(stanzas_.size() == 0);
+ }
+
+ void PrintNextStanza() {
+ ASSERT(stanzas_.size() > 0);
+ printf("Stanza: %s\n", stanzas_.front()->Str().c_str());
+ }
+
+ SessionManager* manager;
+ std::string username;
+ SessionID last_id;
+ uint32 create_count;
+ uint32 destroy_count;
+ std::deque<XmlElement*> stanzas_;
+};
+
+struct SessionHandler : sigslot::has_slots<> {
+ SessionHandler(Session* s) : session(s) {
+ session->SignalState.connect(this, &SessionHandler::OnState);
+ session->SignalError.connect(this, &SessionHandler::OnError);
+ }
+
+ void PrepareTransport() {
+ Transport* transport = session->GetTransport(kNsP2pTransport);
+ if (transport != NULL)
+ transport->set_allow_local_ips(true);
+ }
+
+ void OnState(Session* session, Session::State state) {
+ ASSERT(session == this->session);
+ last_state = state;
+ }
+
+ void OnError(Session* session, Session::Error error) {
+ ASSERT(session == this->session);
+ ASSERT(false); // errors are bad!
+ }
+
+ Session* session;
+ Session::State last_state;
+};
+
+struct MySessionClient: public SessionClient, public sigslot::has_slots<> {
+ MySessionClient() : create_count(0), a(NULL), b(NULL) { }
+
+ void AddManager(SessionManager* manager) {
+ manager->AddClient(kSessionType, this);
+ ASSERT(manager->GetClient(kSessionType) == this);
+ manager->SignalSessionCreate.connect(
+ this, &MySessionClient::OnSessionCreate);
+ }
+
+ const SessionDescription* CreateSessionDescription(
+ const XmlElement* element) {
+ return new SessionDescription();
+ }
+
+ XmlElement* TranslateSessionDescription(
+ const SessionDescription* description) {
+ return new XmlElement(QName(kSessionType, "description"));
+ }
+
+ void OnSessionCreate(Session *session, bool initiate) {
+ create_count += 1;
+ a = session->CreateChannel("a");
+ b = session->CreateChannel("b");
+
+ if (transport_name.size() > 0)
+ session->SetPotentialTransports(&transport_name, 1);
+ }
+
+ void OnSessionDestroy(Session *session)
+ {
+ }
+
+ void SetTransports(bool p2p, bool raw) {
+ if (p2p && raw)
+ return; // this is the default
+
+ if (p2p) {
+ transport_name = kNsP2pTransport;
+ }
+ }
+
+ int create_count;
+ TransportChannel* a;
+ TransportChannel* b;
+ std::string transport_name;
+};
+
+struct ChannelHandler : sigslot::has_slots<> {
+ ChannelHandler(TransportChannel* p)
+ : channel(p), last_readable(false), last_writable(false), data_count(0),
+ last_size(0) {
+ p->SignalReadableState.connect(this, &ChannelHandler::OnReadableState);
+ p->SignalWritableState.connect(this, &ChannelHandler::OnWritableState);
+ p->SignalReadPacket.connect(this, &ChannelHandler::OnReadPacket);
+ }
+
+ void OnReadableState(TransportChannel* p) {
+ ASSERT(p == channel);
+ last_readable = channel->readable();
+ }
+
+ void OnWritableState(TransportChannel* p) {
+ ASSERT(p == channel);
+ last_writable = channel->writable();
+ }
+
+ void OnReadPacket(TransportChannel* p, const char* buf, size_t size) {
+ ASSERT(p == channel);
+ ASSERT(size <= sizeof(last_data));
+ data_count += 1;
+ last_size = size;
+ std::memcpy(last_data, buf, size);
+ }
+
+ void Send(const char* data, size_t size) {
+ int result = channel->SendPacket(data, size);
+ ASSERT(result == static_cast<int>(size));
+ }
+
+ TransportChannel* channel;
+ bool last_readable, last_writable;
+ int data_count;
+ char last_data[4096];
+ size_t last_size;
+};
+
+char* Reverse(const char* str) {
+ int len = strlen(str);
+ char* rev = new char[len+1];
+ for (int i = 0; i < len; i++)
+ rev[i] = str[len-i-1];
+ rev[len] = '\0';
+ return rev;
+}
+
+// Sets up values that should be the same for every test.
+void InitTest() {
+ SetRandomSeed(7);
+ gPort = 28653;
+ gID = 0;
+}
+
+// Tests having client2 accept the session.
+void TestAccept(talk_base::Thread* signaling_thread,
+ Session* session1, Session* session2,
+ SessionHandler* handler1, SessionHandler* handler2,
+ SessionManager* manager1, SessionManager* manager2,
+ SessionManagerHandler* manhandler1,
+ SessionManagerHandler* manhandler2) {
+ // Make sure the IQ ID is 5.
+ ASSERT(gID <= 5);
+ while (gID < 5) GetNextID();
+
+ // Accept the session.
+ SessionDescription* desc2 = new SessionDescription();
+ bool valid = session2->Accept(desc2);
+ ASSERT(valid);
+
+ scoped_ptr<buzz::XmlElement> stanza;
+ stanza.reset(manhandler2->CheckNextStanza(
+ "<cli:iq to=\"foo@baz.com\" type=\"set\" from=\"bar@baz.com\" id=\"5\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"accept\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<ses:description xmlns:ses=\"http://oink.splat/session\"/>"
+ "</session>"
+ "</cli:iq>"));
+ manhandler2->CheckNoStanza();
+
+ // Simulate a tiny delay in sending.
+ signaling_thread->ProcessMessages(10);
+
+ // Delivery the accept.
+ manager1->OnIncomingMessage(stanza.get());
+ stanza.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" id=\"5\" type=\"result\" from=\"foo@baz.com\""
+ " xmlns:cli=\"jabber:client\"/>"));
+ manhandler1->CheckNoStanza();
+
+ // Both sessions should be in progress after a short wait.
+ signaling_thread->ProcessMessages(10);
+ ASSERT(handler1->last_state == Session::STATE_INPROGRESS);
+ ASSERT(handler2->last_state == Session::STATE_INPROGRESS);
+}
+
+// Tests sending data between two clients, over two channels.
+void TestSendRecv(ChannelHandler* chanhandler1a, ChannelHandler* chanhandler1b,
+ ChannelHandler* chanhandler2a, ChannelHandler* chanhandler2b,
+ talk_base::Thread* signaling_thread, bool first_dropped) {
+ const char* dat1a = "spamspamspamspamspamspamspambakedbeansspam";
+ const char* dat1b = "Lobster Thermidor a Crevette with a mornay sauce...";
+ const char* dat2a = Reverse(dat1a);
+ const char* dat2b = Reverse(dat1b);
+
+ // Sending from 2 -> 1 will enable 1 to send to 2 below. That will then
+ // enable 2 to send back to 1. So the code below will just work.
+ if (first_dropped) {
+ chanhandler2a->Send(dat2a, strlen(dat2a));
+ chanhandler2b->Send(dat2b, strlen(dat2b));
+ }
+
+ for (int i = 0; i < 20; i++) {
+ chanhandler1a->Send(dat1a, strlen(dat1a));
+ chanhandler1b->Send(dat1b, strlen(dat1b));
+ chanhandler2a->Send(dat2a, strlen(dat2a));
+ chanhandler2b->Send(dat2b, strlen(dat2b));
+
+ signaling_thread->ProcessMessages(10);
+
+ ASSERT(chanhandler1a->data_count == i + 1);
+ ASSERT(chanhandler1b->data_count == i + 1);
+ ASSERT(chanhandler2a->data_count == i + 1);
+ ASSERT(chanhandler2b->data_count == i + 1);
+
+ ASSERT(chanhandler1a->last_size == strlen(dat2a));
+ ASSERT(chanhandler1b->last_size == strlen(dat2b));
+ ASSERT(chanhandler2a->last_size == strlen(dat1a));
+ ASSERT(chanhandler2b->last_size == strlen(dat1b));
+
+ ASSERT(std::memcmp(chanhandler1a->last_data, dat2a, strlen(dat2a)) == 0);
+ ASSERT(std::memcmp(chanhandler1b->last_data, dat2b, strlen(dat2b)) == 0);
+ ASSERT(std::memcmp(chanhandler2a->last_data, dat1a, strlen(dat1a)) == 0);
+ ASSERT(std::memcmp(chanhandler2b->last_data, dat1b, strlen(dat1b)) == 0);
+ }
+}
+
+// Tests a session between two clients. The inputs indicate whether we should
+// replace each client's output with what we would see from an old client.
+void TestP2PCompatibility(const std::string& test_name, bool old1, bool old2) {
+ InitTest();
+
+ talk_base::Thread* signaling_thread = talk_base::Thread::Current();
+ scoped_ptr<talk_base::Thread> worker_thread(new talk_base::Thread());
+ worker_thread->Start();
+
+ scoped_ptr<PortAllocator> allocator(
+ new TestPortAllocator(worker_thread.get(), NULL));
+ scoped_ptr<MySessionClient> client(new MySessionClient());
+ client->SetTransports(true, false);
+
+ scoped_ptr<SessionManager> manager1(
+ new SessionManager(allocator.get(), worker_thread.get()));
+ scoped_ptr<SessionManagerHandler> manhandler1(
+ new SessionManagerHandler(manager1.get(), "foo@baz.com"));
+ client->AddManager(manager1.get());
+
+ Session* session1 = manager1->CreateSession("foo@baz.com", kSessionType);
+ ASSERT(manhandler1->create_count == 1);
+ ASSERT(manhandler1->last_id == session1->id());
+ scoped_ptr<SessionHandler> handler1(new SessionHandler(session1));
+
+ ASSERT(client->create_count == 1);
+ TransportChannel* chan1a = client->a;
+ ASSERT(chan1a->name() == "a");
+ ASSERT(session1->GetChannel("a") == chan1a);
+ scoped_ptr<ChannelHandler> chanhandler1a(new ChannelHandler(chan1a));
+ TransportChannel* chan1b = client->b;
+ ASSERT(chan1b->name() == "b");
+ ASSERT(session1->GetChannel("b") == chan1b);
+ scoped_ptr<ChannelHandler> chanhandler1b(new ChannelHandler(chan1b));
+
+ SessionDescription* desc1 = new SessionDescription();
+ ASSERT(session1->state() == Session::STATE_INIT);
+ bool valid = session1->Initiate("bar@baz.com", NULL, desc1);
+ ASSERT(valid);
+ handler1->PrepareTransport();
+
+ signaling_thread->ProcessMessages(100);
+
+ ASSERT(handler1->last_state == Session::STATE_SENTINITIATE);
+ scoped_ptr<XmlElement> stanza1, stanza2;
+ stanza1.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"0\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"initiate\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<ses:description xmlns:ses=\"http://oink.splat/session\"/>"
+ "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\"/>"
+ "</session>"
+ "</cli:iq>"));
+ stanza2.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"1\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"transport-info\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\">"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28653\""
+ " preference=\"1\" username=\"h0ISP4S5SJKH/9EY\" protocol=\"udp\""
+ " generation=\"0\" password=\"UhnAmO5C89dD2dZ+\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28658\""
+ " preference=\"1\" username=\"yid4vfB3zXPvrRB9\" protocol=\"udp\""
+ " generation=\"0\" password=\"SqLXTvcEyriIo+Mj\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28663\""
+ " preference=\"1\" username=\"NvT78D7WxPWM1KL8\" protocol=\"udp\""
+ " generation=\"0\" password=\"+mV/QhOapXu4caPX\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28668\""
+ " preference=\"1\" username=\"8EzB7MH+TYpIlSp/\" protocol=\"udp\""
+ " generation=\"0\" password=\"h+MelLXupoK5aYqC\" type=\"local\""
+ " network=\"network\"/>"
+ "</p:transport>"
+ "</session>"
+ "</cli:iq>"));
+ manhandler1->CheckNoStanza();
+
+ // If the first client were old, the initiate would have no transports and
+ // the candidates would be sent in a candidates message.
+ if (old1) {
+ stanza1.reset(XmlElement::ForStr(
+ "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"0\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"initiate\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<ses:description xmlns:ses=\"http://oink.splat/session\"/>"
+ "</session>"
+ "</cli:iq>"));
+ stanza2.reset(XmlElement::ForStr(
+ "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"1\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"candidates\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28653\""
+ " preference=\"1\" username=\"h0ISP4S5SJKH/9EY\" protocol=\"udp\""
+ " generation=\"0\" password=\"UhnAmO5C89dD2dZ+\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28658\""
+ " preference=\"1\" username=\"yid4vfB3zXPvrRB9\" protocol=\"udp\""
+ " generation=\"0\" password=\"SqLXTvcEyriIo+Mj\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28663\""
+ " preference=\"1\" username=\"NvT78D7WxPWM1KL8\" protocol=\"udp\""
+ " generation=\"0\" password=\"+mV/QhOapXu4caPX\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28668\""
+ " preference=\"1\" username=\"8EzB7MH+TYpIlSp/\" protocol=\"udp\""
+ " generation=\"0\" password=\"h+MelLXupoK5aYqC\" type=\"local\""
+ " network=\"network\"/>"
+ "</session>"
+ "</cli:iq>"));
+ }
+
+ scoped_ptr<SessionManager> manager2(
+ new SessionManager(allocator.get(), worker_thread.get()));
+ scoped_ptr<SessionManagerHandler> manhandler2(
+ new SessionManagerHandler(manager2.get(), "bar@baz.com"));
+ client->AddManager(manager2.get());
+
+ // Deliver the initiate.
+ manager2->OnIncomingMessage(stanza1.get());
+ stanza1.reset(manhandler2->CheckNextStanza(
+ "<cli:iq to=\"foo@baz.com\" id=\"0\" type=\"result\" from=\"bar@baz.com\""
+ " xmlns:cli=\"jabber:client\"/>"));
+
+ // If client1 is old, we will not see a transport-accept. If client2 is old,
+ // then we should act as if it did not send one.
+ if (!old1) {
+ stanza1.reset(manhandler2->CheckNextStanza(
+ "<cli:iq to=\"foo@baz.com\" type=\"set\" from=\"bar@baz.com\" id=\"2\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\""
+ " type=\"transport-accept\" id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\"/>"
+ "</session>"
+ "</cli:iq>"));
+ } else {
+ GetNextID(); // Advance the ID count to be the same in all cases.
+ stanza1.reset(NULL);
+ }
+ if (old2) {
+ stanza1.reset(NULL);
+ }
+ manhandler2->CheckNoStanza();
+ ASSERT(manhandler2->create_count == 1);
+ ASSERT(manhandler2->last_id == session1->id());
+
+ Session* session2 = manager2->GetSession(session1->id());
+ ASSERT(session2);
+ ASSERT(session1->id() == session2->id());
+ ASSERT(manhandler2->last_id == session2->id());
+ ASSERT(session2->state() == Session::STATE_RECEIVEDINITIATE);
+ scoped_ptr<SessionHandler> handler2(new SessionHandler(session2));
+ handler2->PrepareTransport();
+
+ ASSERT(session2->name() == session1->remote_name());
+ ASSERT(session1->name() == session2->remote_name());
+
+ ASSERT(session2->transport() != NULL);
+ ASSERT(session2->transport()->name() == kNsP2pTransport);
+
+ ASSERT(client->create_count == 2);
+ TransportChannel* chan2a = client->a;
+ scoped_ptr<ChannelHandler> chanhandler2a(new ChannelHandler(chan2a));
+ TransportChannel* chan2b = client->b;
+ scoped_ptr<ChannelHandler> chanhandler2b(new ChannelHandler(chan2b));
+
+ // Deliver the candidates.
+ manager2->OnIncomingMessage(stanza2.get());
+ stanza2.reset(manhandler2->CheckNextStanza(
+ "<cli:iq to=\"foo@baz.com\" id=\"1\" type=\"result\" from=\"bar@baz.com\""
+ " xmlns:cli=\"jabber:client\"/>"));
+
+ signaling_thread->ProcessMessages(10);
+
+ // If client1 is old, we should see a candidates message instead of a
+ // transport-info. If client2 is old, we should act as if we did.
+ const char* kCandidates2 =
+ "<cli:iq to=\"foo@baz.com\" type=\"set\" from=\"bar@baz.com\" id=\"3\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"candidates\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28673\""
+ " preference=\"1\" username=\"FJDz3iuXjbQJDRjs\" protocol=\"udp\""
+ " generation=\"0\" password=\"Ca5daV9m6G91qhlM\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28678\""
+ " preference=\"1\" username=\"xlN53r3Jn/R5XuCt\" protocol=\"udp\""
+ " generation=\"0\" password=\"rgik2pKsjaPSUdJd\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28683\""
+ " preference=\"1\" username=\"IBZ8CSq8ot2+pSMp\" protocol=\"udp\""
+ " generation=\"0\" password=\"i7RcDsGntMI6fzdd\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28688\""
+ " preference=\"1\" username=\"SEtih9PYtMHCAlMI\" protocol=\"udp\""
+ " generation=\"0\" password=\"wROrHJ3+gDxUUMp1\" type=\"local\""
+ " network=\"network\"/>"
+ "</session>"
+ "</cli:iq>";
+ if (old1) {
+ stanza2.reset(manhandler2->CheckNextStanza(kCandidates2));
+ } else {
+ stanza2.reset(manhandler2->CheckNextStanza(
+ "<cli:iq to=\"foo@baz.com\" type=\"set\" from=\"bar@baz.com\" id=\"3\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"transport-info\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\">"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28673\""
+ " preference=\"1\" username=\"FJDz3iuXjbQJDRjs\" protocol=\"udp\""
+ " generation=\"0\" password=\"Ca5daV9m6G91qhlM\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28678\""
+ " preference=\"1\" username=\"xlN53r3Jn/R5XuCt\" protocol=\"udp\""
+ " generation=\"0\" password=\"rgik2pKsjaPSUdJd\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28683\""
+ " preference=\"1\" username=\"IBZ8CSq8ot2+pSMp\" protocol=\"udp\""
+ " generation=\"0\" password=\"i7RcDsGntMI6fzdd\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28688\""
+ " preference=\"1\" username=\"SEtih9PYtMHCAlMI\" protocol=\"udp\""
+ " generation=\"0\" password=\"wROrHJ3+gDxUUMp1\" type=\"local\""
+ " network=\"network\"/>"
+ "</p:transport>"
+ "</session>"
+ "</cli:iq>"));
+ }
+ if (old2) {
+ stanza2.reset(XmlElement::ForStr(kCandidates2));
+ }
+ manhandler2->CheckNoStanza();
+
+ // Deliver the transport-accept if one exists.
+ if (stanza1.get() != NULL) {
+ manager1->OnIncomingMessage(stanza1.get());
+ stanza1.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" id=\"2\" type=\"result\" from=\"foo@baz.com\""
+ " xmlns:cli=\"jabber:client\"/>"));
+ manhandler1->CheckNoStanza();
+
+ // The first session should now have a transport.
+ ASSERT(session1->transport() != NULL);
+ ASSERT(session1->transport()->name() == kNsP2pTransport);
+ }
+
+ // Deliver the candidates. If client2 is old (or is acting old because
+ // client1 is), then client1 will correct its earlier mistake of sending
+ // transport-info by sending a candidates message. If client1 is supposed to
+ // be old, then it sent candidates earlier, so we drop this.
+ manager1->OnIncomingMessage(stanza2.get());
+ if (old1 || old2) {
+ stanza2.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"4\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"candidates\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28653\""
+ " preference=\"1\" username=\"h0ISP4S5SJKH/9EY\" protocol=\"udp\""
+ " generation=\"0\" password=\"UhnAmO5C89dD2dZ+\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28658\""
+ " preference=\"1\" username=\"yid4vfB3zXPvrRB9\" protocol=\"udp\""
+ " generation=\"0\" password=\"SqLXTvcEyriIo+Mj\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28663\""
+ " preference=\"1\" username=\"NvT78D7WxPWM1KL8\" protocol=\"udp\""
+ " generation=\"0\" password=\"+mV/QhOapXu4caPX\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28668\""
+ " preference=\"1\" username=\"8EzB7MH+TYpIlSp/\" protocol=\"udp\""
+ " generation=\"0\" password=\"h+MelLXupoK5aYqC\" type=\"local\""
+ " network=\"network\"/>"
+ "</session>"
+ "</cli:iq>"));
+ } else {
+ GetNextID(); // Advance the ID count to be the same in all cases.
+ stanza2.reset(NULL);
+ }
+ if (old1) {
+ stanza2.reset(NULL);
+ }
+ stanza1.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" id=\"3\" type=\"result\" from=\"foo@baz.com\""
+ " xmlns:cli=\"jabber:client\"/>"));
+ manhandler1->CheckNoStanza();
+
+ // The first session must have a transport in either case now.
+ ASSERT(session1->transport() != NULL);
+ ASSERT(session1->transport()->name() == kNsP2pTransport);
+
+ // If client1 just generated a candidates message, then we must deliver it.
+ if (stanza2.get() != NULL) {
+ manager2->OnIncomingMessage(stanza2.get());
+ stanza2.reset(manhandler2->CheckNextStanza(
+ "<cli:iq to=\"foo@baz.com\" id=\"4\" type=\"result\" from=\"bar@baz.com\""
+ " xmlns:cli=\"jabber:client\"/>"));
+ manhandler2->CheckNoStanza();
+ }
+
+ // The channels should be able to become writable at this point. This
+ // requires pinging, so it may take a little while.
+ signaling_thread->ProcessMessages(500);
+ ASSERT(chan1a->writable() && chan1a->readable());
+ ASSERT(chan1b->writable() && chan1b->readable());
+ ASSERT(chan2a->writable() && chan2a->readable());
+ ASSERT(chan2b->writable() && chan2b->readable());
+ ASSERT(chanhandler1a->last_writable);
+ ASSERT(chanhandler1b->last_writable);
+ ASSERT(chanhandler2a->last_writable);
+ ASSERT(chanhandler2b->last_writable);
+
+ // Accept the session.
+ TestAccept(signaling_thread, session1, session2,
+ handler1.get(), handler2.get(),
+ manager1.get(), manager2.get(),
+ manhandler1.get(), manhandler2.get());
+
+ // Send a bunch of data between them.
+ TestSendRecv(chanhandler1a.get(), chanhandler1b.get(), chanhandler2a.get(),
+ chanhandler2b.get(), signaling_thread, false);
+
+ manager1->DestroySession(session1);
+ manager2->DestroySession(session2);
+
+ ASSERT(manhandler1->create_count == 1);
+ ASSERT(manhandler2->create_count == 1);
+ ASSERT(manhandler1->destroy_count == 1);
+ ASSERT(manhandler2->destroy_count == 1);
+
+ worker_thread->Stop();
+
+ std::cout << "P2P Compatibility: " << test_name << ": PASS" << std::endl;
+}
+
+// Tests the P2P transport. The flags indicate whether they clients will
+// advertise support for raw as well.
+void TestP2P(const std::string& test_name, bool raw1, bool raw2) {
+ InitTest();
+
+ talk_base::Thread* signaling_thread = talk_base::Thread::Current();
+ scoped_ptr<talk_base::Thread> worker_thread(new talk_base::Thread());
+ worker_thread->Start();
+
+ scoped_ptr<PortAllocator> allocator(
+ new TestPortAllocator(worker_thread.get(), NULL));
+ scoped_ptr<MySessionClient> client1(new MySessionClient());
+ client1->SetTransports(true, raw1);
+ scoped_ptr<MySessionClient> client2(new MySessionClient());
+ client2->SetTransports(true, raw2);
+
+ scoped_ptr<SessionManager> manager1(
+ new SessionManager(allocator.get(), worker_thread.get()));
+ scoped_ptr<SessionManagerHandler> manhandler1(
+ new SessionManagerHandler(manager1.get(), "foo@baz.com"));
+ client1->AddManager(manager1.get());
+
+ Session* session1 = manager1->CreateSession("foo@baz.com", kSessionType);
+ ASSERT(manhandler1->create_count == 1);
+ ASSERT(manhandler1->last_id == session1->id());
+ scoped_ptr<SessionHandler> handler1(new SessionHandler(session1));
+
+ ASSERT(client1->create_count == 1);
+ TransportChannel* chan1a = client1->a;
+ ASSERT(chan1a->name() == "a");
+ ASSERT(session1->GetChannel("a") == chan1a);
+ scoped_ptr<ChannelHandler> chanhandler1a(new ChannelHandler(chan1a));
+ TransportChannel* chan1b = client1->b;
+ ASSERT(chan1b->name() == "b");
+ ASSERT(session1->GetChannel("b") == chan1b);
+ scoped_ptr<ChannelHandler> chanhandler1b(new ChannelHandler(chan1b));
+
+ SessionDescription* desc1 = new SessionDescription();
+ ASSERT(session1->state() == Session::STATE_INIT);
+ bool valid = session1->Initiate("bar@baz.com", NULL, desc1);
+ ASSERT(valid);
+ handler1->PrepareTransport();
+
+ signaling_thread->ProcessMessages(100);
+
+ ASSERT(handler1->last_state == Session::STATE_SENTINITIATE);
+ scoped_ptr<XmlElement> stanza1, stanza2;
+ if (raw1) {
+ stanza1.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"0\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"initiate\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<ses:description xmlns:ses=\"http://oink.splat/session\"/>"
+ "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\"/>"
+ "<raw:transport xmlns:raw=\"http://www.google.com/transport/raw\"/>"
+ "</session>"
+ "</cli:iq>"));
+ } else {
+ stanza1.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"0\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"initiate\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<ses:description xmlns:ses=\"http://oink.splat/session\"/>"
+ "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\"/>"
+ "</session>"
+ "</cli:iq>"));
+ }
+ stanza2.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"1\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"transport-info\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\">"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28653\""
+ " preference=\"1\" username=\"h0ISP4S5SJKH/9EY\" protocol=\"udp\""
+ " generation=\"0\" password=\"UhnAmO5C89dD2dZ+\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28658\""
+ " preference=\"1\" username=\"yid4vfB3zXPvrRB9\" protocol=\"udp\""
+ " generation=\"0\" password=\"SqLXTvcEyriIo+Mj\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28663\""
+ " preference=\"1\" username=\"NvT78D7WxPWM1KL8\" protocol=\"udp\""
+ " generation=\"0\" password=\"+mV/QhOapXu4caPX\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28668\""
+ " preference=\"1\" username=\"8EzB7MH+TYpIlSp/\" protocol=\"udp\""
+ " generation=\"0\" password=\"h+MelLXupoK5aYqC\" type=\"local\""
+ " network=\"network\"/>"
+ "</p:transport>"
+ "</session>"
+ "</cli:iq>"));
+ manhandler1->CheckNoStanza();
+
+ scoped_ptr<SessionManager> manager2(
+ new SessionManager(allocator.get(), worker_thread.get()));
+ scoped_ptr<SessionManagerHandler> manhandler2(
+ new SessionManagerHandler(manager2.get(), "bar@baz.com"));
+ client2->AddManager(manager2.get());
+
+ // Deliver the initiate.
+ manager2->OnIncomingMessage(stanza1.get());
+ stanza1.reset(manhandler2->CheckNextStanza(
+ "<cli:iq to=\"foo@baz.com\" id=\"0\" type=\"result\" from=\"bar@baz.com\""
+ " xmlns:cli=\"jabber:client\"/>"));
+ stanza1.reset(manhandler2->CheckNextStanza(
+ "<cli:iq to=\"foo@baz.com\" type=\"set\" from=\"bar@baz.com\" id=\"2\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\""
+ " type=\"transport-accept\" id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\"/>"
+ "</session>"
+ "</cli:iq>"));
+ manhandler2->CheckNoStanza();
+ ASSERT(manhandler2->create_count == 1);
+ ASSERT(manhandler2->last_id == session1->id());
+
+ Session* session2 = manager2->GetSession(session1->id());
+ ASSERT(session2);
+ ASSERT(session1->id() == session2->id());
+ ASSERT(manhandler2->last_id == session2->id());
+ ASSERT(session2->state() == Session::STATE_RECEIVEDINITIATE);
+ scoped_ptr<SessionHandler> handler2(new SessionHandler(session2));
+ handler2->PrepareTransport();
+
+ ASSERT(session2->name() == session1->remote_name());
+ ASSERT(session1->name() == session2->remote_name());
+
+ ASSERT(session2->transport() != NULL);
+ ASSERT(session2->transport()->name() == kNsP2pTransport);
+
+ ASSERT(client2->create_count == 1);
+ TransportChannel* chan2a = client2->a;
+ scoped_ptr<ChannelHandler> chanhandler2a(new ChannelHandler(chan2a));
+ TransportChannel* chan2b = client2->b;
+ scoped_ptr<ChannelHandler> chanhandler2b(new ChannelHandler(chan2b));
+
+ // Deliver the candidates.
+ manager2->OnIncomingMessage(stanza2.get());
+ stanza2.reset(manhandler2->CheckNextStanza(
+ "<cli:iq to=\"foo@baz.com\" id=\"1\" type=\"result\" from=\"bar@baz.com\""
+ " xmlns:cli=\"jabber:client\"/>"));
+
+ signaling_thread->ProcessMessages(10);
+
+ stanza2.reset(manhandler2->CheckNextStanza(
+ "<cli:iq to=\"foo@baz.com\" type=\"set\" from=\"bar@baz.com\" id=\"3\""
+ " xmlns:cli=\"jabber:client\">"
+ "<session xmlns=\"http://www.google.com/session\" type=\"transport-info\""
+ " id=\"2154761789\" initiator=\"foo@baz.com\">"
+ "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\">"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28673\""
+ " preference=\"1\" username=\"FJDz3iuXjbQJDRjs\" protocol=\"udp\""
+ " generation=\"0\" password=\"Ca5daV9m6G91qhlM\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28678\""
+ " preference=\"1\" username=\"xlN53r3Jn/R5XuCt\" protocol=\"udp\""
+ " generation=\"0\" password=\"rgik2pKsjaPSUdJd\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28683\""
+ " preference=\"1\" username=\"IBZ8CSq8ot2+pSMp\" protocol=\"udp\""
+ " generation=\"0\" password=\"i7RcDsGntMI6fzdd\" type=\"local\""
+ " network=\"network\"/>"
+ "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28688\""
+ " preference=\"1\" username=\"SEtih9PYtMHCAlMI\" protocol=\"udp\""
+ " generation=\"0\" password=\"wROrHJ3+gDxUUMp1\" type=\"local\""
+ " network=\"network\"/>"
+ "</p:transport>"
+ "</session>"
+ "</cli:iq>"));
+ manhandler2->CheckNoStanza();
+
+ // Deliver the transport-accept.
+ manager1->OnIncomingMessage(stanza1.get());
+ stanza1.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" id=\"2\" type=\"result\" from=\"foo@baz.com\""
+ " xmlns:cli=\"jabber:client\"/>"));
+ manhandler1->CheckNoStanza();
+
+ // The first session should now have a transport.
+ ASSERT(session1->transport() != NULL);
+ ASSERT(session1->transport()->name() == kNsP2pTransport);
+
+ // Deliver the candidates.
+ manager1->OnIncomingMessage(stanza2.get());
+ stanza1.reset(manhandler1->CheckNextStanza(
+ "<cli:iq to=\"bar@baz.com\" id=\"3\" type=\"result\" from=\"foo@baz.com\""
+ " xmlns:cli=\"jabber:client\"/>"));
+ manhandler1->CheckNoStanza();
+
+ // The channels should be able to become writable at this point. This
+ // requires pinging, so it may take a little while.
+ signaling_thread->ProcessMessages(500);
+ ASSERT(chan1a->writable() && chan1a->readable());
+ ASSERT(chan1b->writable() && chan1b->readable());
+ ASSERT(chan2a->writable() && chan2a->readable());
+ ASSERT(chan2b->writable() && chan2b->readable());
+ ASSERT(chanhandler1a->last_writable);
+ ASSERT(chanhandler1b->last_writable);
+ ASSERT(chanhandler2a->last_writable);
+ ASSERT(chanhandler2b->last_writable);
+
+ // Accept the session.
+ TestAccept(signaling_thread, session1, session2,
+ handler1.get(), handler2.get(),
+ manager1.get(), manager2.get(),
+ manhandler1.get(), manhandler2.get());
+
+ // Send a bunch of data between them.
+ TestSendRecv(chanhandler1a.get(), chanhandler1b.get(), chanhandler2a.get(),
+ chanhandler2b.get(), signaling_thread, false);
+
+ manager1->DestroySession(session1);
+ manager2->DestroySession(session2);
+
+ ASSERT(manhandler1->create_count == 1);
+ ASSERT(manhandler2->create_count == 1);
+ ASSERT(manhandler1->destroy_count == 1);
+ ASSERT(manhandler2->destroy_count == 1);
+
+ worker_thread->Stop();
+
+ std::cout << "P2P: " << test_name << ": PASS" << std::endl;
+}
+//
+int main(int argc, char* argv[]) {
+ talk_base::LogMessage::LogToDebug(talk_base::LS_WARNING);
+
+ TestP2P("{p2p} => {p2p}", false, false);
+ TestP2P("{p2p} => {p2p,raw}", false, true);
+ TestP2P("{p2p,raw} => {p2p}", true, false);
+ TestP2P("{p2p,raw} => {p2p,raw}", true, true);
+ TestP2PCompatibility("New => New", false, false);
+ TestP2PCompatibility("Old => New", true, false);
+ TestP2PCompatibility("New => Old", false, true);
+ TestP2PCompatibility("Old => Old", true, true);
+
+ return 0;
+}