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