1 /* 2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include <iostream> 12 #include <sstream> 13 #include <string> 14 15 #include "webrtc/libjingle/xmllite/xmlelement.h" 16 #include "webrtc/libjingle/xmpp/constants.h" 17 #include "webrtc/libjingle/xmpp/rostermodule.h" 18 #include "webrtc/libjingle/xmpp/util_unittest.h" 19 #include "webrtc/libjingle/xmpp/xmppengine.h" 20 #include "webrtc/base/gunit.h" 21 #include "webrtc/base/scoped_ptr.h" 22 23 #define TEST_OK(x) EXPECT_EQ((x),XMPP_RETURN_OK) 24 #define TEST_BADARGUMENT(x) EXPECT_EQ((x),XMPP_RETURN_BADARGUMENT) 25 26 27 namespace buzz { 28 29 class RosterModuleTest; 30 31 static void 32 WriteString(std::ostream& os, const std::string& str) { 33 os<<str; 34 } 35 36 static void 37 WriteSubscriptionState(std::ostream& os, XmppSubscriptionState state) 38 { 39 switch (state) { 40 case XMPP_SUBSCRIPTION_NONE: 41 os<<"none"; 42 break; 43 case XMPP_SUBSCRIPTION_NONE_ASKED: 44 os<<"none_asked"; 45 break; 46 case XMPP_SUBSCRIPTION_TO: 47 os<<"to"; 48 break; 49 case XMPP_SUBSCRIPTION_FROM: 50 os<<"from"; 51 break; 52 case XMPP_SUBSCRIPTION_FROM_ASKED: 53 os<<"from_asked"; 54 break; 55 case XMPP_SUBSCRIPTION_BOTH: 56 os<<"both"; 57 break; 58 default: 59 os<<"unknown"; 60 break; 61 } 62 } 63 64 static void 65 WriteSubscriptionRequestType(std::ostream& os, 66 XmppSubscriptionRequestType type) { 67 switch(type) { 68 case XMPP_REQUEST_SUBSCRIBE: 69 os<<"subscribe"; 70 break; 71 case XMPP_REQUEST_UNSUBSCRIBE: 72 os<<"unsubscribe"; 73 break; 74 case XMPP_REQUEST_SUBSCRIBED: 75 os<<"subscribed"; 76 break; 77 case XMPP_REQUEST_UNSUBSCRIBED: 78 os<<"unsubscribe"; 79 break; 80 default: 81 os<<"unknown"; 82 break; 83 } 84 } 85 86 static void 87 WritePresenceShow(std::ostream& os, XmppPresenceShow show) { 88 switch(show) { 89 case XMPP_PRESENCE_AWAY: 90 os<<"away"; 91 break; 92 case XMPP_PRESENCE_CHAT: 93 os<<"chat"; 94 break; 95 case XMPP_PRESENCE_DND: 96 os<<"dnd"; 97 break; 98 case XMPP_PRESENCE_XA: 99 os<<"xa"; 100 break; 101 case XMPP_PRESENCE_DEFAULT: 102 os<<"[default]"; 103 break; 104 default: 105 os<<"[unknown]"; 106 break; 107 } 108 } 109 110 static void 111 WritePresence(std::ostream& os, const XmppPresence* presence) { 112 if (presence == NULL) { 113 os<<"NULL"; 114 return; 115 } 116 117 os<<"[Presence jid:"; 118 WriteString(os, presence->jid().Str()); 119 os<<" available:"<<presence->available(); 120 os<<" presence_show:"; 121 WritePresenceShow(os, presence->presence_show()); 122 os<<" priority:"<<presence->priority(); 123 os<<" status:"; 124 WriteString(os, presence->status()); 125 os<<"]"<<presence->raw_xml()->Str(); 126 } 127 128 static void 129 WriteContact(std::ostream& os, const XmppRosterContact* contact) { 130 if (contact == NULL) { 131 os<<"NULL"; 132 return; 133 } 134 135 os<<"[Contact jid:"; 136 WriteString(os, contact->jid().Str()); 137 os<<" name:"; 138 WriteString(os, contact->name()); 139 os<<" subscription_state:"; 140 WriteSubscriptionState(os, contact->subscription_state()); 141 os<<" groups:["; 142 for(size_t i=0; i < contact->GetGroupCount(); ++i) { 143 os<<(i==0?"":", "); 144 WriteString(os, contact->GetGroup(i)); 145 } 146 os<<"]]"<<contact->raw_xml()->Str(); 147 } 148 149 //! This session handler saves all calls to a string. These are events and 150 //! data delivered form the engine to application code. 151 class XmppTestRosterHandler : public XmppRosterHandler { 152 public: 153 XmppTestRosterHandler() {} 154 virtual ~XmppTestRosterHandler() {} 155 156 virtual void SubscriptionRequest(XmppRosterModule*, 157 const Jid& requesting_jid, 158 XmppSubscriptionRequestType type, 159 const XmlElement* raw_xml) { 160 ss_<<"[SubscriptionRequest Jid:" << requesting_jid.Str()<<" type:"; 161 WriteSubscriptionRequestType(ss_, type); 162 ss_<<"]"<<raw_xml->Str(); 163 } 164 165 //! Some type of presence error has occured 166 virtual void SubscriptionError(XmppRosterModule*, 167 const Jid& from, 168 const XmlElement* raw_xml) { 169 ss_<<"[SubscriptionError from:"<<from.Str()<<"]"<<raw_xml->Str(); 170 } 171 172 virtual void RosterError(XmppRosterModule*, 173 const XmlElement* raw_xml) { 174 ss_<<"[RosterError]"<<raw_xml->Str(); 175 } 176 177 //! New presence information has come in 178 //! The user is notified with the presence object directly. This info is also 179 //! added to the store accessable from the engine. 180 virtual void IncomingPresenceChanged(XmppRosterModule*, 181 const XmppPresence* presence) { 182 ss_<<"[IncomingPresenceChanged presence:"; 183 WritePresence(ss_, presence); 184 ss_<<"]"; 185 } 186 187 //! A contact has changed 188 //! This indicates that the data for a contact may have changed. No 189 //! contacts have been added or removed. 190 virtual void ContactChanged(XmppRosterModule* roster, 191 const XmppRosterContact* old_contact, 192 size_t index) { 193 ss_<<"[ContactChanged old_contact:"; 194 WriteContact(ss_, old_contact); 195 ss_<<" index:"<<index<<" new_contact:"; 196 WriteContact(ss_, roster->GetRosterContact(index)); 197 ss_<<"]"; 198 } 199 200 //! A set of contacts have been added 201 //! These contacts may have been added in response to the original roster 202 //! request or due to a "roster push" from the server. 203 virtual void ContactsAdded(XmppRosterModule* roster, 204 size_t index, size_t number) { 205 ss_<<"[ContactsAdded index:"<<index<<" number:"<<number; 206 for (size_t i = 0; i < number; ++i) { 207 ss_<<" "<<(index+i)<<":"; 208 WriteContact(ss_, roster->GetRosterContact(index+i)); 209 } 210 ss_<<"]"; 211 } 212 213 //! A contact has been removed 214 //! This contact has been removed form the list. 215 virtual void ContactRemoved(XmppRosterModule*, 216 const XmppRosterContact* removed_contact, 217 size_t index) { 218 ss_<<"[ContactRemoved old_contact:"; 219 WriteContact(ss_, removed_contact); 220 ss_<<" index:"<<index<<"]"; 221 } 222 223 std::string Str() { 224 return ss_.str(); 225 } 226 227 std::string StrClear() { 228 std::string result = ss_.str(); 229 ss_.str(""); 230 return result; 231 } 232 233 private: 234 std::stringstream ss_; 235 }; 236 237 //! This is the class that holds all of the unit test code for the 238 //! roster module 239 class RosterModuleTest : public testing::Test { 240 public: 241 RosterModuleTest() {} 242 static void RunLogin(RosterModuleTest* obj, XmppEngine* engine, 243 XmppTestHandler* handler) { 244 // Needs to be similar to XmppEngineTest::RunLogin 245 } 246 }; 247 248 TEST_F(RosterModuleTest, TestPresence) { 249 XmlElement* status = new XmlElement(QN_GOOGLE_PSTN_CONFERENCE_STATUS); 250 status->AddAttr(QN_STATUS, STR_PSTN_CONFERENCE_STATUS_CONNECTING); 251 XmlElement presence_xml(QN_PRESENCE); 252 presence_xml.AddElement(status); 253 rtc::scoped_ptr<XmppPresence> presence(XmppPresence::Create()); 254 presence->set_raw_xml(&presence_xml); 255 EXPECT_EQ(presence->connection_status(), XMPP_CONNECTION_STATUS_CONNECTING); 256 } 257 258 TEST_F(RosterModuleTest, TestOutgoingPresence) { 259 std::stringstream dump; 260 261 rtc::scoped_ptr<XmppEngine> engine(XmppEngine::Create()); 262 XmppTestHandler handler(engine.get()); 263 XmppTestRosterHandler roster_handler; 264 265 rtc::scoped_ptr<XmppRosterModule> roster(XmppRosterModule::Create()); 266 roster->set_roster_handler(&roster_handler); 267 268 // Configure the roster module 269 roster->RegisterEngine(engine.get()); 270 271 // Set up callbacks 272 engine->SetOutputHandler(&handler); 273 engine->AddStanzaHandler(&handler); 274 engine->SetSessionHandler(&handler); 275 276 // Set up minimal login info 277 engine->SetUser(Jid("david@my-server")); 278 // engine->SetPassword("david"); 279 280 // Do the whole login handshake 281 RunLogin(this, engine.get(), &handler); 282 EXPECT_EQ("", handler.OutputActivity()); 283 284 // Set some presence and broadcast it 285 TEST_OK(roster->outgoing_presence()-> 286 set_available(XMPP_PRESENCE_AVAILABLE)); 287 TEST_OK(roster->outgoing_presence()->set_priority(-37)); 288 TEST_OK(roster->outgoing_presence()->set_presence_show(XMPP_PRESENCE_DND)); 289 TEST_OK(roster->outgoing_presence()-> 290 set_status("I'm off to the races!<>&")); 291 TEST_OK(roster->BroadcastPresence()); 292 293 EXPECT_EQ(roster_handler.StrClear(), ""); 294 EXPECT_EQ(handler.OutputActivity(), 295 "<presence>" 296 "<priority>-37</priority>" 297 "<show>dnd</show>" 298 "<status>I'm off to the races!<>&</status>" 299 "</presence>"); 300 EXPECT_EQ(handler.SessionActivity(), ""); 301 302 // Try some more 303 TEST_OK(roster->outgoing_presence()-> 304 set_available(XMPP_PRESENCE_UNAVAILABLE)); 305 TEST_OK(roster->outgoing_presence()->set_priority(0)); 306 TEST_OK(roster->outgoing_presence()->set_presence_show(XMPP_PRESENCE_XA)); 307 TEST_OK(roster->outgoing_presence()->set_status("Gone fishin'")); 308 TEST_OK(roster->BroadcastPresence()); 309 310 EXPECT_EQ(roster_handler.StrClear(), ""); 311 EXPECT_EQ(handler.OutputActivity(), 312 "<presence type=\"unavailable\">" 313 "<show>xa</show>" 314 "<status>Gone fishin'</status>" 315 "</presence>"); 316 EXPECT_EQ(handler.SessionActivity(), ""); 317 318 // Okay -- we are back on 319 TEST_OK(roster->outgoing_presence()-> 320 set_available(XMPP_PRESENCE_AVAILABLE)); 321 TEST_BADARGUMENT(roster->outgoing_presence()->set_priority(128)); 322 TEST_OK(roster->outgoing_presence()-> 323 set_presence_show(XMPP_PRESENCE_DEFAULT)); 324 TEST_OK(roster->outgoing_presence()->set_status("Cookin' wit gas")); 325 TEST_OK(roster->BroadcastPresence()); 326 327 EXPECT_EQ(roster_handler.StrClear(), ""); 328 EXPECT_EQ(handler.OutputActivity(), 329 "<presence>" 330 "<status>Cookin' wit gas</status>" 331 "</presence>"); 332 EXPECT_EQ(handler.SessionActivity(), ""); 333 334 // Set it via XML 335 XmlElement presence_input(QN_PRESENCE); 336 presence_input.AddAttr(QN_TYPE, "unavailable"); 337 presence_input.AddElement(new XmlElement(QN_PRIORITY)); 338 presence_input.AddText("42", 1); 339 presence_input.AddElement(new XmlElement(QN_STATUS)); 340 presence_input.AddAttr(QN_XML_LANG, "es", 1); 341 presence_input.AddText("Hola Amigos!", 1); 342 presence_input.AddElement(new XmlElement(QN_STATUS)); 343 presence_input.AddText("Hey there, friend!", 1); 344 TEST_OK(roster->outgoing_presence()->set_raw_xml(&presence_input)); 345 TEST_OK(roster->BroadcastPresence()); 346 347 WritePresence(dump, roster->outgoing_presence()); 348 EXPECT_EQ(dump.str(), 349 "[Presence jid: available:0 presence_show:[default] " 350 "priority:42 status:Hey there, friend!]" 351 "<cli:presence type=\"unavailable\" xmlns:cli=\"jabber:client\">" 352 "<cli:priority>42</cli:priority>" 353 "<cli:status xml:lang=\"es\">Hola Amigos!</cli:status>" 354 "<cli:status>Hey there, friend!</cli:status>" 355 "</cli:presence>"); 356 dump.str(""); 357 EXPECT_EQ(roster_handler.StrClear(), ""); 358 EXPECT_EQ(handler.OutputActivity(), 359 "<presence type=\"unavailable\">" 360 "<priority>42</priority>" 361 "<status xml:lang=\"es\">Hola Amigos!</status>" 362 "<status>Hey there, friend!</status>" 363 "</presence>"); 364 EXPECT_EQ(handler.SessionActivity(), ""); 365 366 // Construct a directed presence 367 rtc::scoped_ptr<XmppPresence> directed_presence(XmppPresence::Create()); 368 TEST_OK(directed_presence->set_available(XMPP_PRESENCE_AVAILABLE)); 369 TEST_OK(directed_presence->set_priority(120)); 370 TEST_OK(directed_presence->set_status("*very* available")); 371 TEST_OK(roster->SendDirectedPresence(directed_presence.get(), 372 Jid("myhoney (at) honey.net"))); 373 374 EXPECT_EQ(roster_handler.StrClear(), ""); 375 EXPECT_EQ(handler.OutputActivity(), 376 "<presence to=\"myhoney (at) honey.net\">" 377 "<priority>120</priority>" 378 "<status>*very* available</status>" 379 "</presence>"); 380 EXPECT_EQ(handler.SessionActivity(), ""); 381 } 382 383 TEST_F(RosterModuleTest, TestIncomingPresence) { 384 rtc::scoped_ptr<XmppEngine> engine(XmppEngine::Create()); 385 XmppTestHandler handler(engine.get()); 386 XmppTestRosterHandler roster_handler; 387 388 rtc::scoped_ptr<XmppRosterModule> roster(XmppRosterModule::Create()); 389 roster->set_roster_handler(&roster_handler); 390 391 // Configure the roster module 392 roster->RegisterEngine(engine.get()); 393 394 // Set up callbacks 395 engine->SetOutputHandler(&handler); 396 engine->AddStanzaHandler(&handler); 397 engine->SetSessionHandler(&handler); 398 399 // Set up minimal login info 400 engine->SetUser(Jid("david@my-server")); 401 // engine->SetPassword("david"); 402 403 // Do the whole login handshake 404 RunLogin(this, engine.get(), &handler); 405 EXPECT_EQ("", handler.OutputActivity()); 406 407 // Load up with a bunch of data 408 std::string input; 409 input = "<presence from='maude (at) example.net/studio' " 410 "to='david@my-server/test'/>" 411 "<presence from='walter (at) example.net/home' " 412 "to='david@my-server/test'>" 413 "<priority>-10</priority>" 414 "<show>xa</show>" 415 "<status>Off bowling</status>" 416 "</presence>" 417 "<presence from='walter (at) example.net/alley' " 418 "to='david@my-server/test'>" 419 "<priority>20</priority>" 420 "<status>Looking for toes...</status>" 421 "</presence>" 422 "<presence from='donny (at) example.net/alley' " 423 "to='david@my-server/test'>" 424 "<priority>10</priority>" 425 "<status>Throwing rocks</status>" 426 "</presence>"; 427 TEST_OK(engine->HandleInput(input.c_str(), input.length())); 428 429 EXPECT_EQ(roster_handler.StrClear(), 430 "[IncomingPresenceChanged " 431 "presence:[Presence jid:maude (at) example.net/studio available:1 " 432 "presence_show:[default] priority:0 status:]" 433 "<cli:presence from=\"maude (at) example.net/studio\" " 434 "to=\"david@my-server/test\" " 435 "xmlns:cli=\"jabber:client\"/>]" 436 "[IncomingPresenceChanged " 437 "presence:[Presence jid:walter (at) example.net/home available:1 " 438 "presence_show:xa priority:-10 status:Off bowling]" 439 "<cli:presence from=\"walter (at) example.net/home\" " 440 "to=\"david@my-server/test\" " 441 "xmlns:cli=\"jabber:client\">" 442 "<cli:priority>-10</cli:priority>" 443 "<cli:show>xa</cli:show>" 444 "<cli:status>Off bowling</cli:status>" 445 "</cli:presence>]" 446 "[IncomingPresenceChanged " 447 "presence:[Presence jid:walter (at) example.net/alley available:1 " 448 "presence_show:[default] " 449 "priority:20 status:Looking for toes...]" 450 "<cli:presence from=\"walter (at) example.net/alley\" " 451 "to=\"david@my-server/test\" " 452 "xmlns:cli=\"jabber:client\">" 453 "<cli:priority>20</cli:priority>" 454 "<cli:status>Looking for toes...</cli:status>" 455 "</cli:presence>]" 456 "[IncomingPresenceChanged " 457 "presence:[Presence jid:donny (at) example.net/alley available:1 " 458 "presence_show:[default] priority:10 status:Throwing rocks]" 459 "<cli:presence from=\"donny (at) example.net/alley\" " 460 "to=\"david@my-server/test\" " 461 "xmlns:cli=\"jabber:client\">" 462 "<cli:priority>10</cli:priority>" 463 "<cli:status>Throwing rocks</cli:status>" 464 "</cli:presence>]"); 465 EXPECT_EQ(handler.OutputActivity(), ""); 466 handler.SessionActivity(); // Ignore the session output 467 468 // Now look at the data structure we've built 469 EXPECT_EQ(roster->GetIncomingPresenceCount(), static_cast<size_t>(4)); 470 EXPECT_EQ(roster->GetIncomingPresenceForJidCount(Jid("maude (at) example.net")), 471 static_cast<size_t>(1)); 472 EXPECT_EQ(roster->GetIncomingPresenceForJidCount(Jid("walter (at) example.net")), 473 static_cast<size_t>(2)); 474 475 const XmppPresence * presence; 476 presence = roster->GetIncomingPresenceForJid(Jid("walter (at) example.net"), 1); 477 478 std::stringstream dump; 479 WritePresence(dump, presence); 480 EXPECT_EQ(dump.str(), 481 "[Presence jid:walter (at) example.net/alley available:1 " 482 "presence_show:[default] priority:20 status:Looking for toes...]" 483 "<cli:presence from=\"walter (at) example.net/alley\" " 484 "to=\"david@my-server/test\" " 485 "xmlns:cli=\"jabber:client\">" 486 "<cli:priority>20</cli:priority>" 487 "<cli:status>Looking for toes...</cli:status>" 488 "</cli:presence>"); 489 dump.str(""); 490 491 // Maude took off... 492 input = "<presence from='maude (at) example.net/studio' " 493 "to='david@my-server/test' " 494 "type='unavailable'>" 495 "<status>Stealing my rug back</status>" 496 "<priority>-10</priority>" 497 "</presence>"; 498 TEST_OK(engine->HandleInput(input.c_str(), input.length())); 499 500 EXPECT_EQ(roster_handler.StrClear(), 501 "[IncomingPresenceChanged " 502 "presence:[Presence jid:maude (at) example.net/studio available:0 " 503 "presence_show:[default] priority:-10 " 504 "status:Stealing my rug back]" 505 "<cli:presence from=\"maude (at) example.net/studio\" " 506 "to=\"david@my-server/test\" type=\"unavailable\" " 507 "xmlns:cli=\"jabber:client\">" 508 "<cli:status>Stealing my rug back</cli:status>" 509 "<cli:priority>-10</cli:priority>" 510 "</cli:presence>]"); 511 EXPECT_EQ(handler.OutputActivity(), ""); 512 handler.SessionActivity(); // Ignore the session output 513 } 514 515 TEST_F(RosterModuleTest, TestPresenceSubscription) { 516 rtc::scoped_ptr<XmppEngine> engine(XmppEngine::Create()); 517 XmppTestHandler handler(engine.get()); 518 XmppTestRosterHandler roster_handler; 519 520 rtc::scoped_ptr<XmppRosterModule> roster(XmppRosterModule::Create()); 521 roster->set_roster_handler(&roster_handler); 522 523 // Configure the roster module 524 roster->RegisterEngine(engine.get()); 525 526 // Set up callbacks 527 engine->SetOutputHandler(&handler); 528 engine->AddStanzaHandler(&handler); 529 engine->SetSessionHandler(&handler); 530 531 // Set up minimal login info 532 engine->SetUser(Jid("david@my-server")); 533 // engine->SetPassword("david"); 534 535 // Do the whole login handshake 536 RunLogin(this, engine.get(), &handler); 537 EXPECT_EQ("", handler.OutputActivity()); 538 539 // Test incoming requests 540 std::string input; 541 input = 542 "<presence from='maude (at) example.net' type='subscribe'/>" 543 "<presence from='maude (at) example.net' type='unsubscribe'/>" 544 "<presence from='maude (at) example.net' type='subscribed'/>" 545 "<presence from='maude (at) example.net' type='unsubscribed'/>"; 546 TEST_OK(engine->HandleInput(input.c_str(), input.length())); 547 548 EXPECT_EQ(roster_handler.StrClear(), 549 "[SubscriptionRequest Jid:maude (at) example.net type:subscribe]" 550 "<cli:presence from=\"maude (at) example.net\" type=\"subscribe\" " 551 "xmlns:cli=\"jabber:client\"/>" 552 "[SubscriptionRequest Jid:maude (at) example.net type:unsubscribe]" 553 "<cli:presence from=\"maude (at) example.net\" type=\"unsubscribe\" " 554 "xmlns:cli=\"jabber:client\"/>" 555 "[SubscriptionRequest Jid:maude (at) example.net type:subscribed]" 556 "<cli:presence from=\"maude (at) example.net\" type=\"subscribed\" " 557 "xmlns:cli=\"jabber:client\"/>" 558 "[SubscriptionRequest Jid:maude (at) example.net type:unsubscribe]" 559 "<cli:presence from=\"maude (at) example.net\" type=\"unsubscribed\" " 560 "xmlns:cli=\"jabber:client\"/>"); 561 EXPECT_EQ(handler.OutputActivity(), ""); 562 handler.SessionActivity(); // Ignore the session output 563 564 TEST_OK(roster->RequestSubscription(Jid("maude (at) example.net"))); 565 TEST_OK(roster->CancelSubscription(Jid("maude (at) example.net"))); 566 TEST_OK(roster->ApproveSubscriber(Jid("maude (at) example.net"))); 567 TEST_OK(roster->CancelSubscriber(Jid("maude (at) example.net"))); 568 569 EXPECT_EQ(roster_handler.StrClear(), ""); 570 EXPECT_EQ(handler.OutputActivity(), 571 "<presence to=\"maude (at) example.net\" type=\"subscribe\"/>" 572 "<presence to=\"maude (at) example.net\" type=\"unsubscribe\"/>" 573 "<presence to=\"maude (at) example.net\" type=\"subscribed\"/>" 574 "<presence to=\"maude (at) example.net\" type=\"unsubscribed\"/>"); 575 EXPECT_EQ(handler.SessionActivity(), ""); 576 } 577 578 TEST_F(RosterModuleTest, TestRosterReceive) { 579 rtc::scoped_ptr<XmppEngine> engine(XmppEngine::Create()); 580 XmppTestHandler handler(engine.get()); 581 XmppTestRosterHandler roster_handler; 582 583 rtc::scoped_ptr<XmppRosterModule> roster(XmppRosterModule::Create()); 584 roster->set_roster_handler(&roster_handler); 585 586 // Configure the roster module 587 roster->RegisterEngine(engine.get()); 588 589 // Set up callbacks 590 engine->SetOutputHandler(&handler); 591 engine->AddStanzaHandler(&handler); 592 engine->SetSessionHandler(&handler); 593 594 // Set up minimal login info 595 engine->SetUser(Jid("david@my-server")); 596 // engine->SetPassword("david"); 597 598 // Do the whole login handshake 599 RunLogin(this, engine.get(), &handler); 600 EXPECT_EQ("", handler.OutputActivity()); 601 602 // Request a roster update 603 TEST_OK(roster->RequestRosterUpdate()); 604 605 EXPECT_EQ(roster_handler.StrClear(),""); 606 EXPECT_EQ(handler.OutputActivity(), 607 "<iq type=\"get\" id=\"2\">" 608 "<query xmlns=\"jabber:iq:roster\"/>" 609 "</iq>"); 610 EXPECT_EQ(handler.SessionActivity(), ""); 611 612 // Prime the roster with a starting set 613 std::string input = 614 "<iq to='david@myserver/test' type='result' id='2'>" 615 "<query xmlns='jabber:iq:roster'>" 616 "<item jid='maude (at) example.net' " 617 "name='Maude Lebowski' " 618 "subscription='none' " 619 "ask='subscribe'>" 620 "<group>Business Partners</group>" 621 "</item>" 622 "<item jid='walter (at) example.net' " 623 "name='Walter Sobchak' " 624 "subscription='both'>" 625 "<group>Friends</group>" 626 "<group>Bowling Team</group>" 627 "<group>Bowling League</group>" 628 "</item>" 629 "<item jid='donny (at) example.net' " 630 "name='Donny' " 631 "subscription='both'>" 632 "<group>Friends</group>" 633 "<group>Bowling Team</group>" 634 "<group>Bowling League</group>" 635 "</item>" 636 "<item jid='jeffrey (at) example.net' " 637 "name='The Big Lebowski' " 638 "subscription='to'>" 639 "<group>Business Partners</group>" 640 "</item>" 641 "<item jid='jesus (at) example.net' " 642 "name='Jesus Quintana' " 643 "subscription='from'>" 644 "<group>Bowling League</group>" 645 "</item>" 646 "</query>" 647 "</iq>"; 648 649 TEST_OK(engine->HandleInput(input.c_str(), input.length())); 650 651 EXPECT_EQ(roster_handler.StrClear(), 652 "[ContactsAdded index:0 number:5 " 653 "0:[Contact jid:maude (at) example.net name:Maude Lebowski " 654 "subscription_state:none_asked " 655 "groups:[Business Partners]]" 656 "<ros:item jid=\"maude (at) example.net\" name=\"Maude Lebowski\" " 657 "subscription=\"none\" ask=\"subscribe\" " 658 "xmlns:ros=\"jabber:iq:roster\">" 659 "<ros:group>Business Partners</ros:group>" 660 "</ros:item> " 661 "1:[Contact jid:walter (at) example.net name:Walter Sobchak " 662 "subscription_state:both " 663 "groups:[Friends, Bowling Team, Bowling League]]" 664 "<ros:item jid=\"walter (at) example.net\" name=\"Walter Sobchak\" " 665 "subscription=\"both\" " 666 "xmlns:ros=\"jabber:iq:roster\">" 667 "<ros:group>Friends</ros:group>" 668 "<ros:group>Bowling Team</ros:group>" 669 "<ros:group>Bowling League</ros:group>" 670 "</ros:item> " 671 "2:[Contact jid:donny (at) example.net name:Donny " 672 "subscription_state:both " 673 "groups:[Friends, Bowling Team, Bowling League]]" 674 "<ros:item jid=\"donny (at) example.net\" name=\"Donny\" " 675 "subscription=\"both\" " 676 "xmlns:ros=\"jabber:iq:roster\">" 677 "<ros:group>Friends</ros:group>" 678 "<ros:group>Bowling Team</ros:group>" 679 "<ros:group>Bowling League</ros:group>" 680 "</ros:item> " 681 "3:[Contact jid:jeffrey (at) example.net name:The Big Lebowski " 682 "subscription_state:to " 683 "groups:[Business Partners]]" 684 "<ros:item jid=\"jeffrey (at) example.net\" name=\"The Big Lebowski\" " 685 "subscription=\"to\" " 686 "xmlns:ros=\"jabber:iq:roster\">" 687 "<ros:group>Business Partners</ros:group>" 688 "</ros:item> " 689 "4:[Contact jid:jesus (at) example.net name:Jesus Quintana " 690 "subscription_state:from groups:[Bowling League]]" 691 "<ros:item jid=\"jesus (at) example.net\" name=\"Jesus Quintana\" " 692 "subscription=\"from\" xmlns:ros=\"jabber:iq:roster\">" 693 "<ros:group>Bowling League</ros:group>" 694 "</ros:item>]"); 695 EXPECT_EQ(handler.OutputActivity(), ""); 696 EXPECT_EQ(handler.SessionActivity(), ""); 697 698 // Request that someone be added 699 rtc::scoped_ptr<XmppRosterContact> contact(XmppRosterContact::Create()); 700 TEST_OK(contact->set_jid(Jid("brandt (at) example.net"))); 701 TEST_OK(contact->set_name("Brandt")); 702 TEST_OK(contact->AddGroup("Business Partners")); 703 TEST_OK(contact->AddGroup("Watchers")); 704 TEST_OK(contact->AddGroup("Friends")); 705 TEST_OK(contact->RemoveGroup("Friends")); // Maybe not... 706 TEST_OK(roster->RequestRosterChange(contact.get())); 707 708 EXPECT_EQ(roster_handler.StrClear(), ""); 709 EXPECT_EQ(handler.OutputActivity(), 710 "<iq type=\"set\" id=\"3\">" 711 "<query xmlns=\"jabber:iq:roster\">" 712 "<item jid=\"brandt (at) example.net\" " 713 "name=\"Brandt\">" 714 "<group>Business Partners</group>" 715 "<group>Watchers</group>" 716 "</item>" 717 "</query>" 718 "</iq>"); 719 EXPECT_EQ(handler.SessionActivity(), ""); 720 721 // Get the push from the server 722 input = 723 "<iq type='result' to='david@my-server/test' id='3'/>" 724 "<iq type='set' id='server_1'>" 725 "<query xmlns='jabber:iq:roster'>" 726 "<item jid='brandt (at) example.net' " 727 "name='Brandt' " 728 "subscription='none'>" 729 "<group>Business Partners</group>" 730 "<group>Watchers</group>" 731 "</item>" 732 "</query>" 733 "</iq>"; 734 TEST_OK(engine->HandleInput(input.c_str(), input.length())); 735 736 EXPECT_EQ(roster_handler.StrClear(), 737 "[ContactsAdded index:5 number:1 " 738 "5:[Contact jid:brandt (at) example.net name:Brandt " 739 "subscription_state:none " 740 "groups:[Business Partners, Watchers]]" 741 "<ros:item jid=\"brandt (at) example.net\" name=\"Brandt\" " 742 "subscription=\"none\" " 743 "xmlns:ros=\"jabber:iq:roster\">" 744 "<ros:group>Business Partners</ros:group>" 745 "<ros:group>Watchers</ros:group>" 746 "</ros:item>]"); 747 EXPECT_EQ(handler.OutputActivity(), 748 "<iq type=\"result\" id=\"server_1\"/>"); 749 EXPECT_EQ(handler.SessionActivity(), ""); 750 751 // Get a contact update 752 input = 753 "<iq type='set' id='server_2'>" 754 "<query xmlns='jabber:iq:roster'>" 755 "<item jid='walter (at) example.net' " 756 "name='Walter Sobchak' " 757 "subscription='both'>" 758 "<group>Friends</group>" 759 "<group>Bowling Team</group>" 760 "<group>Bowling League</group>" 761 "<group>Not wrong, just an...</group>" 762 "</item>" 763 "</query>" 764 "</iq>"; 765 766 TEST_OK(engine->HandleInput(input.c_str(), input.length())); 767 768 EXPECT_EQ(roster_handler.StrClear(), 769 "[ContactChanged " 770 "old_contact:[Contact jid:walter (at) example.net name:Walter Sobchak " 771 "subscription_state:both " 772 "groups:[Friends, Bowling Team, Bowling League]]" 773 "<ros:item jid=\"walter (at) example.net\" name=\"Walter Sobchak\" " 774 "subscription=\"both\" xmlns:ros=\"jabber:iq:roster\">" 775 "<ros:group>Friends</ros:group>" 776 "<ros:group>Bowling Team</ros:group>" 777 "<ros:group>Bowling League</ros:group>" 778 "</ros:item> " 779 "index:1 " 780 "new_contact:[Contact jid:walter (at) example.net name:Walter Sobchak " 781 "subscription_state:both " 782 "groups:[Friends, Bowling Team, Bowling League, " 783 "Not wrong, just an...]]" 784 "<ros:item jid=\"walter (at) example.net\" name=\"Walter Sobchak\" " 785 "subscription=\"both\" xmlns:ros=\"jabber:iq:roster\">" 786 "<ros:group>Friends</ros:group>" 787 "<ros:group>Bowling Team</ros:group>" 788 "<ros:group>Bowling League</ros:group>" 789 "<ros:group>Not wrong, just an...</ros:group>" 790 "</ros:item>]"); 791 EXPECT_EQ(handler.OutputActivity(), 792 "<iq type=\"result\" id=\"server_2\"/>"); 793 EXPECT_EQ(handler.SessionActivity(), ""); 794 795 // Remove a contact 796 TEST_OK(roster->RequestRosterRemove(Jid("jesus (at) example.net"))); 797 798 EXPECT_EQ(roster_handler.StrClear(), ""); 799 EXPECT_EQ(handler.OutputActivity(), 800 "<iq type=\"set\" id=\"4\">" 801 "<query xmlns=\"jabber:iq:roster\" jid=\"jesus (at) example.net\" " 802 "subscription=\"remove\"/>" 803 "</iq>"); 804 EXPECT_EQ(handler.SessionActivity(), ""); 805 806 // Response from the server 807 input = 808 "<iq type='result' to='david@my-server/test' id='4'/>" 809 "<iq type='set' id='server_3'>" 810 "<query xmlns='jabber:iq:roster'>" 811 "<item jid='jesus (at) example.net' " 812 "subscription='remove'>" 813 "</item>" 814 "</query>" 815 "</iq>"; 816 TEST_OK(engine->HandleInput(input.c_str(), input.length())); 817 818 EXPECT_EQ(roster_handler.StrClear(), 819 "[ContactRemoved " 820 "old_contact:[Contact jid:jesus (at) example.net name:Jesus Quintana " 821 "subscription_state:from groups:[Bowling League]]" 822 "<ros:item jid=\"jesus (at) example.net\" name=\"Jesus Quintana\" " 823 "subscription=\"from\" " 824 "xmlns:ros=\"jabber:iq:roster\">" 825 "<ros:group>Bowling League</ros:group>" 826 "</ros:item> index:4]"); 827 EXPECT_EQ(handler.OutputActivity(), 828 "<iq type=\"result\" id=\"server_3\"/>"); 829 EXPECT_EQ(handler.SessionActivity(), ""); 830 } 831 832 } 833