• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
WriteString(std::ostream & os,const std::string & str)49 WriteString(std::ostream& os, const std::string& str) {
50   os<<str;
51 }
52 
53 static void
WriteSubscriptionState(std::ostream & os,XmppSubscriptionState state)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
WriteSubscriptionRequestType(std::ostream & os,XmppSubscriptionRequestType type)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
WritePresenceShow(std::ostream & os,XmppPresenceShow show)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
WritePresence(std::ostream & os,const XmppPresence * presence)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
WriteContact(std::ostream & os,const XmppRosterContact * contact)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:
XmppTestRosterHandler()170   XmppTestRosterHandler() {}
~XmppTestRosterHandler()171   virtual ~XmppTestRosterHandler() {}
172 
SubscriptionRequest(XmppRosterModule *,const Jid & requesting_jid,XmppSubscriptionRequestType type,const XmlElement * raw_xml)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
SubscriptionError(XmppRosterModule *,const Jid & from,const XmlElement * raw_xml)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 
RosterError(XmppRosterModule *,const XmlElement * raw_xml)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.
IncomingPresenceChanged(XmppRosterModule *,const XmppPresence * presence)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.
ContactChanged(XmppRosterModule * roster,const XmppRosterContact * old_contact,size_t index)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.
ContactsAdded(XmppRosterModule * roster,size_t index,size_t number)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.
ContactRemoved(XmppRosterModule *,const XmppRosterContact * removed_contact,size_t index)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 
Str()240   std::string Str() {
241     return ss_.str();
242   }
243 
StrClear()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:
RosterModuleTest()258   RosterModuleTest() {}
RunLogin(RosterModuleTest * obj,XmppEngine * engine,XmppTestHandler * handler)259   static void RunLogin(RosterModuleTest* obj, XmppEngine* engine,
260                        XmppTestHandler* handler) {
261     // Needs to be similar to XmppEngineTest::RunLogin
262   }
263 };
264 
TEST_F(RosterModuleTest,TestPresence)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 
TEST_F(RosterModuleTest,TestOutgoingPresence)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!&lt;&gt;&amp;</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@honey.net")));
390 
391   EXPECT_EQ(roster_handler.StrClear(), "");
392   EXPECT_EQ(handler.OutputActivity(),
393     "<presence to=\"myhoney@honey.net\">"
394       "<priority>120</priority>"
395       "<status>*very* available</status>"
396     "</presence>");
397   EXPECT_EQ(handler.SessionActivity(), "");
398 }
399 
TEST_F(RosterModuleTest,TestIncomingPresence)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@example.net/studio' "
427                     "to='david@my-server/test'/>"
428           "<presence from='walter@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@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@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@example.net/studio available:1 "
449                  "presence_show:[default] priority:0 status:]"
450         "<cli:presence from=\"maude@example.net/studio\" "
451                       "to=\"david@my-server/test\" "
452                       "xmlns:cli=\"jabber:client\"/>]"
453     "[IncomingPresenceChanged "
454       "presence:[Presence jid:walter@example.net/home available:1 "
455                  "presence_show:xa priority:-10 status:Off bowling]"
456         "<cli:presence from=\"walter@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@example.net/alley available:1 "
465                  "presence_show:[default] "
466                  "priority:20 status:Looking for toes...]"
467         "<cli:presence from=\"walter@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@example.net/alley available:1 "
475                  "presence_show:[default] priority:10 status:Throwing rocks]"
476         "<cli:presence from=\"donny@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@example.net")),
488             static_cast<size_t>(1));
489   EXPECT_EQ(roster->GetIncomingPresenceForJidCount(Jid("walter@example.net")),
490             static_cast<size_t>(2));
491 
492   const XmppPresence * presence;
493   presence = roster->GetIncomingPresenceForJid(Jid("walter@example.net"), 1);
494 
495   std::stringstream dump;
496   WritePresence(dump, presence);
497   EXPECT_EQ(dump.str(),
498           "[Presence jid:walter@example.net/alley available:1 "
499             "presence_show:[default] priority:20 status:Looking for toes...]"
500               "<cli:presence from=\"walter@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@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@example.net/studio available:0 "
520                  "presence_show:[default] priority:-10 "
521                  "status:Stealing my rug back]"
522         "<cli:presence from=\"maude@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 
TEST_F(RosterModuleTest,TestPresenceSubscription)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@example.net' type='subscribe'/>"
560     "<presence from='maude@example.net' type='unsubscribe'/>"
561     "<presence from='maude@example.net' type='subscribed'/>"
562     "<presence from='maude@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@example.net type:subscribe]"
567       "<cli:presence from=\"maude@example.net\" type=\"subscribe\" "
568                     "xmlns:cli=\"jabber:client\"/>"
569     "[SubscriptionRequest Jid:maude@example.net type:unsubscribe]"
570       "<cli:presence from=\"maude@example.net\" type=\"unsubscribe\" "
571                     "xmlns:cli=\"jabber:client\"/>"
572     "[SubscriptionRequest Jid:maude@example.net type:subscribed]"
573       "<cli:presence from=\"maude@example.net\" type=\"subscribed\" "
574                     "xmlns:cli=\"jabber:client\"/>"
575     "[SubscriptionRequest Jid:maude@example.net type:unsubscribe]"
576       "<cli:presence from=\"maude@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@example.net")));
582   TEST_OK(roster->CancelSubscription(Jid("maude@example.net")));
583   TEST_OK(roster->ApproveSubscriber(Jid("maude@example.net")));
584   TEST_OK(roster->CancelSubscriber(Jid("maude@example.net")));
585 
586   EXPECT_EQ(roster_handler.StrClear(), "");
587   EXPECT_EQ(handler.OutputActivity(),
588     "<presence to=\"maude@example.net\" type=\"subscribe\"/>"
589     "<presence to=\"maude@example.net\" type=\"unsubscribe\"/>"
590     "<presence to=\"maude@example.net\" type=\"subscribed\"/>"
591     "<presence to=\"maude@example.net\" type=\"unsubscribed\"/>");
592   EXPECT_EQ(handler.SessionActivity(), "");
593 }
594 
TEST_F(RosterModuleTest,TestRosterReceive)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@example.net' "
634               "name='Maude Lebowski' "
635               "subscription='none' "
636               "ask='subscribe'>"
637           "<group>Business Partners</group>"
638         "</item>"
639         "<item jid='walter@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@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@example.net' "
654               "name='The Big Lebowski' "
655               "subscription='to'>"
656           "<group>Business Partners</group>"
657         "</item>"
658         "<item jid='jesus@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@example.net name:Maude Lebowski "
671                  "subscription_state:none_asked "
672                  "groups:[Business Partners]]"
673         "<ros:item jid=\"maude@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@example.net name:Walter Sobchak "
679                  "subscription_state:both "
680                  "groups:[Friends, Bowling Team, Bowling League]]"
681         "<ros:item jid=\"walter@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@example.net name:Donny "
689                  "subscription_state:both "
690                  "groups:[Friends, Bowling Team, Bowling League]]"
691         "<ros:item jid=\"donny@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@example.net name:The Big Lebowski "
699                  "subscription_state:to "
700                  "groups:[Business Partners]]"
701         "<ros:item jid=\"jeffrey@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@example.net name:Jesus Quintana "
707                  "subscription_state:from groups:[Bowling League]]"
708         "<ros:item jid=\"jesus@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@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@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@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@example.net name:Brandt "
756                  "subscription_state:none "
757                  "groups:[Business Partners, Watchers]]"
758         "<ros:item jid=\"brandt@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@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@example.net name:Walter Sobchak "
788                            "subscription_state:both "
789                            "groups:[Friends, Bowling Team, Bowling League]]"
790         "<ros:item jid=\"walter@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@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@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@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@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@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@example.net name:Jesus Quintana "
838                            "subscription_state:from groups:[Bowling League]]"
839         "<ros:item jid=\"jesus@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