Home | History | Annotate | Download | only in xmpp
      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!&lt;&gt;&amp;</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