| #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; |
| } |