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
WriteString(std::ostream & os,const std::string & str)32 WriteString(std::ostream& os, const std::string& str) {
33 os<<str;
34 }
35
36 static void
WriteSubscriptionState(std::ostream & os,XmppSubscriptionState state)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
WriteSubscriptionRequestType(std::ostream & os,XmppSubscriptionRequestType type)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
WritePresenceShow(std::ostream & os,XmppPresenceShow show)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
WritePresence(std::ostream & os,const XmppPresence * presence)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
WriteContact(std::ostream & os,const XmppRosterContact * contact)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:
XmppTestRosterHandler()153 XmppTestRosterHandler() {}
~XmppTestRosterHandler()154 virtual ~XmppTestRosterHandler() {}
155
SubscriptionRequest(XmppRosterModule *,const Jid & requesting_jid,XmppSubscriptionRequestType type,const XmlElement * raw_xml)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
SubscriptionError(XmppRosterModule *,const Jid & from,const XmlElement * raw_xml)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
RosterError(XmppRosterModule *,const XmlElement * raw_xml)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.
IncomingPresenceChanged(XmppRosterModule *,const XmppPresence * presence)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.
ContactChanged(XmppRosterModule * roster,const XmppRosterContact * old_contact,size_t index)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.
ContactsAdded(XmppRosterModule * roster,size_t index,size_t number)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.
ContactRemoved(XmppRosterModule *,const XmppRosterContact * removed_contact,size_t index)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
Str()223 std::string Str() {
224 return ss_.str();
225 }
226
StrClear()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:
RosterModuleTest()241 RosterModuleTest() {}
RunLogin(RosterModuleTest * obj,XmppEngine * engine,XmppTestHandler * handler)242 static void RunLogin(RosterModuleTest* obj, XmppEngine* engine,
243 XmppTestHandler* handler) {
244 // Needs to be similar to XmppEngineTest::RunLogin
245 }
246 };
247
TEST_F(RosterModuleTest,TestPresence)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
TEST_F(RosterModuleTest,TestOutgoingPresence)258 TEST_F(RosterModuleTest, TestOutgoingPresence) {
259 std::stringstream dump;
260
261 rtc::scoped_ptr<XmppEngine> engine(XmppEngine::Create());
262 XmppTestHandler handler(engine.get());
263 XmppTestRosterHandler roster_handler;
264
265 rtc::scoped_ptr<XmppRosterModule> roster(XmppRosterModule::Create());
266 roster->set_roster_handler(&roster_handler);
267
268 // Configure the roster module
269 roster->RegisterEngine(engine.get());
270
271 // Set up callbacks
272 engine->SetOutputHandler(&handler);
273 engine->AddStanzaHandler(&handler);
274 engine->SetSessionHandler(&handler);
275
276 // Set up minimal login info
277 engine->SetUser(Jid("david@my-server"));
278 // engine->SetPassword("david");
279
280 // Do the whole login handshake
281 RunLogin(this, engine.get(), &handler);
282 EXPECT_EQ("", handler.OutputActivity());
283
284 // Set some presence and broadcast it
285 TEST_OK(roster->outgoing_presence()->
286 set_available(XMPP_PRESENCE_AVAILABLE));
287 TEST_OK(roster->outgoing_presence()->set_priority(-37));
288 TEST_OK(roster->outgoing_presence()->set_presence_show(XMPP_PRESENCE_DND));
289 TEST_OK(roster->outgoing_presence()->
290 set_status("I'm off to the races!<>&"));
291 TEST_OK(roster->BroadcastPresence());
292
293 EXPECT_EQ(roster_handler.StrClear(), "");
294 EXPECT_EQ(handler.OutputActivity(),
295 "<presence>"
296 "<priority>-37</priority>"
297 "<show>dnd</show>"
298 "<status>I'm off to the races!<>&</status>"
299 "</presence>");
300 EXPECT_EQ(handler.SessionActivity(), "");
301
302 // Try some more
303 TEST_OK(roster->outgoing_presence()->
304 set_available(XMPP_PRESENCE_UNAVAILABLE));
305 TEST_OK(roster->outgoing_presence()->set_priority(0));
306 TEST_OK(roster->outgoing_presence()->set_presence_show(XMPP_PRESENCE_XA));
307 TEST_OK(roster->outgoing_presence()->set_status("Gone fishin'"));
308 TEST_OK(roster->BroadcastPresence());
309
310 EXPECT_EQ(roster_handler.StrClear(), "");
311 EXPECT_EQ(handler.OutputActivity(),
312 "<presence type=\"unavailable\">"
313 "<show>xa</show>"
314 "<status>Gone fishin'</status>"
315 "</presence>");
316 EXPECT_EQ(handler.SessionActivity(), "");
317
318 // Okay -- we are back on
319 TEST_OK(roster->outgoing_presence()->
320 set_available(XMPP_PRESENCE_AVAILABLE));
321 TEST_BADARGUMENT(roster->outgoing_presence()->set_priority(128));
322 TEST_OK(roster->outgoing_presence()->
323 set_presence_show(XMPP_PRESENCE_DEFAULT));
324 TEST_OK(roster->outgoing_presence()->set_status("Cookin' wit gas"));
325 TEST_OK(roster->BroadcastPresence());
326
327 EXPECT_EQ(roster_handler.StrClear(), "");
328 EXPECT_EQ(handler.OutputActivity(),
329 "<presence>"
330 "<status>Cookin' wit gas</status>"
331 "</presence>");
332 EXPECT_EQ(handler.SessionActivity(), "");
333
334 // Set it via XML
335 XmlElement presence_input(QN_PRESENCE);
336 presence_input.AddAttr(QN_TYPE, "unavailable");
337 presence_input.AddElement(new XmlElement(QN_PRIORITY));
338 presence_input.AddText("42", 1);
339 presence_input.AddElement(new XmlElement(QN_STATUS));
340 presence_input.AddAttr(QN_XML_LANG, "es", 1);
341 presence_input.AddText("Hola Amigos!", 1);
342 presence_input.AddElement(new XmlElement(QN_STATUS));
343 presence_input.AddText("Hey there, friend!", 1);
344 TEST_OK(roster->outgoing_presence()->set_raw_xml(&presence_input));
345 TEST_OK(roster->BroadcastPresence());
346
347 WritePresence(dump, roster->outgoing_presence());
348 EXPECT_EQ(dump.str(),
349 "[Presence jid: available:0 presence_show:[default] "
350 "priority:42 status:Hey there, friend!]"
351 "<cli:presence type=\"unavailable\" xmlns:cli=\"jabber:client\">"
352 "<cli:priority>42</cli:priority>"
353 "<cli:status xml:lang=\"es\">Hola Amigos!</cli:status>"
354 "<cli:status>Hey there, friend!</cli:status>"
355 "</cli:presence>");
356 dump.str("");
357 EXPECT_EQ(roster_handler.StrClear(), "");
358 EXPECT_EQ(handler.OutputActivity(),
359 "<presence type=\"unavailable\">"
360 "<priority>42</priority>"
361 "<status xml:lang=\"es\">Hola Amigos!</status>"
362 "<status>Hey there, friend!</status>"
363 "</presence>");
364 EXPECT_EQ(handler.SessionActivity(), "");
365
366 // Construct a directed presence
367 rtc::scoped_ptr<XmppPresence> directed_presence(XmppPresence::Create());
368 TEST_OK(directed_presence->set_available(XMPP_PRESENCE_AVAILABLE));
369 TEST_OK(directed_presence->set_priority(120));
370 TEST_OK(directed_presence->set_status("*very* available"));
371 TEST_OK(roster->SendDirectedPresence(directed_presence.get(),
372 Jid("myhoney@honey.net")));
373
374 EXPECT_EQ(roster_handler.StrClear(), "");
375 EXPECT_EQ(handler.OutputActivity(),
376 "<presence to=\"myhoney@honey.net\">"
377 "<priority>120</priority>"
378 "<status>*very* available</status>"
379 "</presence>");
380 EXPECT_EQ(handler.SessionActivity(), "");
381 }
382
TEST_F(RosterModuleTest,TestIncomingPresence)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@example.net/studio' "
410 "to='david@my-server/test'/>"
411 "<presence from='walter@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@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@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@example.net/studio available:1 "
432 "presence_show:[default] priority:0 status:]"
433 "<cli:presence from=\"maude@example.net/studio\" "
434 "to=\"david@my-server/test\" "
435 "xmlns:cli=\"jabber:client\"/>]"
436 "[IncomingPresenceChanged "
437 "presence:[Presence jid:walter@example.net/home available:1 "
438 "presence_show:xa priority:-10 status:Off bowling]"
439 "<cli:presence from=\"walter@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@example.net/alley available:1 "
448 "presence_show:[default] "
449 "priority:20 status:Looking for toes...]"
450 "<cli:presence from=\"walter@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@example.net/alley available:1 "
458 "presence_show:[default] priority:10 status:Throwing rocks]"
459 "<cli:presence from=\"donny@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@example.net")),
471 static_cast<size_t>(1));
472 EXPECT_EQ(roster->GetIncomingPresenceForJidCount(Jid("walter@example.net")),
473 static_cast<size_t>(2));
474
475 const XmppPresence * presence;
476 presence = roster->GetIncomingPresenceForJid(Jid("walter@example.net"), 1);
477
478 std::stringstream dump;
479 WritePresence(dump, presence);
480 EXPECT_EQ(dump.str(),
481 "[Presence jid:walter@example.net/alley available:1 "
482 "presence_show:[default] priority:20 status:Looking for toes...]"
483 "<cli:presence from=\"walter@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@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@example.net/studio available:0 "
503 "presence_show:[default] priority:-10 "
504 "status:Stealing my rug back]"
505 "<cli:presence from=\"maude@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
TEST_F(RosterModuleTest,TestPresenceSubscription)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@example.net' type='subscribe'/>"
543 "<presence from='maude@example.net' type='unsubscribe'/>"
544 "<presence from='maude@example.net' type='subscribed'/>"
545 "<presence from='maude@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@example.net type:subscribe]"
550 "<cli:presence from=\"maude@example.net\" type=\"subscribe\" "
551 "xmlns:cli=\"jabber:client\"/>"
552 "[SubscriptionRequest Jid:maude@example.net type:unsubscribe]"
553 "<cli:presence from=\"maude@example.net\" type=\"unsubscribe\" "
554 "xmlns:cli=\"jabber:client\"/>"
555 "[SubscriptionRequest Jid:maude@example.net type:subscribed]"
556 "<cli:presence from=\"maude@example.net\" type=\"subscribed\" "
557 "xmlns:cli=\"jabber:client\"/>"
558 "[SubscriptionRequest Jid:maude@example.net type:unsubscribe]"
559 "<cli:presence from=\"maude@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@example.net")));
565 TEST_OK(roster->CancelSubscription(Jid("maude@example.net")));
566 TEST_OK(roster->ApproveSubscriber(Jid("maude@example.net")));
567 TEST_OK(roster->CancelSubscriber(Jid("maude@example.net")));
568
569 EXPECT_EQ(roster_handler.StrClear(), "");
570 EXPECT_EQ(handler.OutputActivity(),
571 "<presence to=\"maude@example.net\" type=\"subscribe\"/>"
572 "<presence to=\"maude@example.net\" type=\"unsubscribe\"/>"
573 "<presence to=\"maude@example.net\" type=\"subscribed\"/>"
574 "<presence to=\"maude@example.net\" type=\"unsubscribed\"/>");
575 EXPECT_EQ(handler.SessionActivity(), "");
576 }
577
TEST_F(RosterModuleTest,TestRosterReceive)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@example.net' "
617 "name='Maude Lebowski' "
618 "subscription='none' "
619 "ask='subscribe'>"
620 "<group>Business Partners</group>"
621 "</item>"
622 "<item jid='walter@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@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@example.net' "
637 "name='The Big Lebowski' "
638 "subscription='to'>"
639 "<group>Business Partners</group>"
640 "</item>"
641 "<item jid='jesus@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@example.net name:Maude Lebowski "
654 "subscription_state:none_asked "
655 "groups:[Business Partners]]"
656 "<ros:item jid=\"maude@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@example.net name:Walter Sobchak "
662 "subscription_state:both "
663 "groups:[Friends, Bowling Team, Bowling League]]"
664 "<ros:item jid=\"walter@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@example.net name:Donny "
672 "subscription_state:both "
673 "groups:[Friends, Bowling Team, Bowling League]]"
674 "<ros:item jid=\"donny@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@example.net name:The Big Lebowski "
682 "subscription_state:to "
683 "groups:[Business Partners]]"
684 "<ros:item jid=\"jeffrey@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@example.net name:Jesus Quintana "
690 "subscription_state:from groups:[Bowling League]]"
691 "<ros:item jid=\"jesus@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@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@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@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@example.net name:Brandt "
739 "subscription_state:none "
740 "groups:[Business Partners, Watchers]]"
741 "<ros:item jid=\"brandt@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@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@example.net name:Walter Sobchak "
771 "subscription_state:both "
772 "groups:[Friends, Bowling Team, Bowling League]]"
773 "<ros:item jid=\"walter@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@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@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@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@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@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@example.net name:Jesus Quintana "
821 "subscription_state:from groups:[Bowling League]]"
822 "<ros:item jid=\"jesus@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