Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / libjingle / source / talk / xmpp / xmppengine_unittest.cc
1 // Copyright 2004 Google Inc. All Rights Reserved
2 // Author: David Bau
3
4 #include <iostream>
5 #include <sstream>
6 #include <string>
7 #include "webrtc/libjingle/xmllite/xmlelement.h"
8 #include "webrtc/libjingle/xmpp/constants.h"
9 #include "webrtc/libjingle/xmpp/plainsaslhandler.h"
10 #include "webrtc/libjingle/xmpp/saslplainmechanism.h"
11 #include "webrtc/libjingle/xmpp/util_unittest.h"
12 #include "webrtc/libjingle/xmpp/xmppengine.h"
13 #include "webrtc/base/common.h"
14 #include "webrtc/base/gunit.h"
15
16 using buzz::Jid;
17 using buzz::QName;
18 using buzz::XmlElement;
19 using buzz::XmppEngine;
20 using buzz::XmppIqCookie;
21 using buzz::XmppIqHandler;
22 using buzz::XmppTestHandler;
23 using buzz::QN_ID;
24 using buzz::QN_IQ;
25 using buzz::QN_TYPE;
26 using buzz::QN_ROSTER_QUERY;
27 using buzz::XMPP_RETURN_OK;
28 using buzz::XMPP_RETURN_BADARGUMENT;
29
30 // XmppEngineTestIqHandler
31 //    This class grabs the response to an IQ stanza and stores it in a string.
32 class XmppEngineTestIqHandler : public XmppIqHandler {
33  public:
34   virtual void IqResponse(XmppIqCookie, const XmlElement * stanza) {
35     ss_ << stanza->Str();
36   }
37
38   std::string IqResponseActivity() {
39     std::string result = ss_.str();
40     ss_.str("");
41     return result;
42   }
43
44  private:
45   std::stringstream ss_;
46 };
47
48 class XmppEngineTest : public testing::Test {
49  public:
50   XmppEngine* engine() { return engine_.get(); }
51   XmppTestHandler* handler() { return handler_.get(); }
52   virtual void SetUp() {
53     engine_.reset(XmppEngine::Create());
54     handler_.reset(new XmppTestHandler(engine_.get()));
55
56     Jid jid("david@my-server");
57     rtc::InsecureCryptStringImpl pass;
58     pass.password() = "david";
59     engine_->SetSessionHandler(handler_.get());
60     engine_->SetOutputHandler(handler_.get());
61     engine_->AddStanzaHandler(handler_.get());
62     engine_->SetUser(jid);
63     engine_->SetSaslHandler(
64         new buzz::PlainSaslHandler(jid, rtc::CryptString(pass), true));
65   }
66   virtual void TearDown() {
67     handler_.reset();
68     engine_.reset();
69   }
70   void RunLogin();
71
72  private:
73   rtc::scoped_ptr<XmppEngine> engine_;
74   rtc::scoped_ptr<XmppTestHandler> handler_;
75 };
76
77 void XmppEngineTest::RunLogin() {
78   // Connect
79   EXPECT_EQ(XmppEngine::STATE_START, engine()->GetState());
80   engine()->Connect();
81   EXPECT_EQ(XmppEngine::STATE_OPENING, engine()->GetState());
82
83   EXPECT_EQ("[OPENING]", handler_->SessionActivity());
84
85   EXPECT_EQ("<stream:stream to=\"my-server\" xml:lang=\"*\" version=\"1.0\" "
86            "xmlns:stream=\"http://etherx.jabber.org/streams\" "
87            "xmlns=\"jabber:client\">\r\n", handler_->OutputActivity());
88
89   std::string input =
90     "<stream:stream id=\"a5f2d8c9\" version=\"1.0\" "
91     "xmlns:stream=\"http://etherx.jabber.org/streams\" "
92     "xmlns=\"jabber:client\">";
93   engine()->HandleInput(input.c_str(), input.length());
94
95   input =
96     "<stream:features>"
97       "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'>"
98         "<required/>"
99       "</starttls>"
100       "<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
101         "<mechanism>DIGEST-MD5</mechanism>"
102         "<mechanism>PLAIN</mechanism>"
103       "</mechanisms>"
104     "</stream:features>";
105   engine()->HandleInput(input.c_str(), input.length());
106   EXPECT_EQ("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>",
107       handler_->OutputActivity());
108
109   EXPECT_EQ("", handler_->SessionActivity());
110   EXPECT_EQ("", handler_->StanzaActivity());
111
112   input = "<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>";
113   engine()->HandleInput(input.c_str(), input.length());
114   EXPECT_EQ("[START-TLS my-server]"
115            "<stream:stream to=\"my-server\" xml:lang=\"*\" "
116            "version=\"1.0\" xmlns:stream=\"http://etherx.jabber.org/streams\" "
117            "xmlns=\"jabber:client\">\r\n", handler_->OutputActivity());
118
119   EXPECT_EQ("", handler_->SessionActivity());
120   EXPECT_EQ("", handler_->StanzaActivity());
121
122   input = "<stream:stream id=\"01234567\" version=\"1.0\" "
123           "xmlns:stream=\"http://etherx.jabber.org/streams\" "
124           "xmlns=\"jabber:client\">";
125   engine()->HandleInput(input.c_str(), input.length());
126
127   input =
128     "<stream:features>"
129       "<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
130         "<mechanism>DIGEST-MD5</mechanism>"
131         "<mechanism>PLAIN</mechanism>"
132       "</mechanisms>"
133     "</stream:features>";
134   engine()->HandleInput(input.c_str(), input.length());
135   EXPECT_EQ("<auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" "
136       "mechanism=\"PLAIN\" "
137       "auth:allow-non-google-login=\"true\" "
138       "auth:client-uses-full-bind-result=\"true\" "
139       "xmlns:auth=\"http://www.google.com/talk/protocol/auth\""
140       ">AGRhdmlkAGRhdmlk</auth>",
141       handler_->OutputActivity());
142
143   EXPECT_EQ("", handler_->SessionActivity());
144   EXPECT_EQ("", handler_->StanzaActivity());
145
146   input = "<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>";
147   engine()->HandleInput(input.c_str(), input.length());
148   EXPECT_EQ("<stream:stream to=\"my-server\" xml:lang=\"*\" version=\"1.0\" "
149       "xmlns:stream=\"http://etherx.jabber.org/streams\" "
150       "xmlns=\"jabber:client\">\r\n", handler_->OutputActivity());
151
152   EXPECT_EQ("", handler_->SessionActivity());
153   EXPECT_EQ("", handler_->StanzaActivity());
154
155   input = "<stream:stream id=\"01234567\" version=\"1.0\" "
156       "xmlns:stream=\"http://etherx.jabber.org/streams\" "
157       "xmlns=\"jabber:client\">";
158   engine()->HandleInput(input.c_str(), input.length());
159
160   input = "<stream:features>"
161           "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>"
162           "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
163           "</stream:features>";
164   engine()->HandleInput(input.c_str(), input.length());
165   EXPECT_EQ("<iq type=\"set\" id=\"0\">"
166            "<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/></iq>",
167            handler_->OutputActivity());
168
169   EXPECT_EQ("", handler_->SessionActivity());
170   EXPECT_EQ("", handler_->StanzaActivity());
171
172   input = "<iq type='result' id='0'>"
173           "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><jid>"
174           "david@my-server/test</jid></bind></iq>";
175   engine()->HandleInput(input.c_str(), input.length());
176
177   EXPECT_EQ("<iq type=\"set\" id=\"1\">"
178            "<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/></iq>",
179            handler_->OutputActivity());
180
181   EXPECT_EQ("", handler_->SessionActivity());
182   EXPECT_EQ("", handler_->StanzaActivity());
183
184   input = "<iq type='result' id='1'/>";
185   engine()->HandleInput(input.c_str(), input.length());
186
187   EXPECT_EQ("[OPEN]", handler_->SessionActivity());
188   EXPECT_EQ("", handler_->StanzaActivity());
189   EXPECT_EQ(Jid("david@my-server/test"), engine()->FullJid());
190 }
191
192 // TestSuccessfulLogin()
193 //    This function simply tests to see if a login works.  This includes
194 //    encryption and authentication
195 TEST_F(XmppEngineTest, TestSuccessfulLoginAndDisconnect) {
196   RunLogin();
197   engine()->Disconnect();
198   EXPECT_EQ("</stream:stream>[CLOSED]", handler()->OutputActivity());
199   EXPECT_EQ("[CLOSED]", handler()->SessionActivity());
200   EXPECT_EQ("", handler()->StanzaActivity());
201 }
202
203 TEST_F(XmppEngineTest, TestSuccessfulLoginAndConnectionClosed) {
204   RunLogin();
205   engine()->ConnectionClosed(0);
206   EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
207   EXPECT_EQ("[CLOSED][ERROR-CONNECTION-CLOSED]", handler()->SessionActivity());
208   EXPECT_EQ("", handler()->StanzaActivity());
209 }
210
211
212 // TestNotXmpp()
213 //    This tests the error case when connecting to a non XMPP service
214 TEST_F(XmppEngineTest, TestNotXmpp) {
215   // Connect
216   engine()->Connect();
217   EXPECT_EQ("<stream:stream to=\"my-server\" xml:lang=\"*\" version=\"1.0\" "
218           "xmlns:stream=\"http://etherx.jabber.org/streams\" "
219           "xmlns=\"jabber:client\">\r\n", handler()->OutputActivity());
220
221   // Send garbage response (courtesy of apache)
222   std::string input = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">";
223   engine()->HandleInput(input.c_str(), input.length());
224
225   EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
226   EXPECT_EQ("[OPENING][CLOSED][ERROR-XML]", handler()->SessionActivity());
227   EXPECT_EQ("", handler()->StanzaActivity());
228 }
229
230 // TestPassthrough()
231 //    This tests that arbitrary stanzas can be passed to the server through
232 //    the engine.
233 TEST_F(XmppEngineTest, TestPassthrough) {
234   // Queue up an app stanza
235   XmlElement application_stanza(QName("test", "app-stanza"));
236   application_stanza.AddText("this-is-a-test");
237   engine()->SendStanza(&application_stanza);
238
239   // Do the whole login handshake
240   RunLogin();
241
242   EXPECT_EQ("<test:app-stanza xmlns:test=\"test\">this-is-a-test"
243           "</test:app-stanza>", handler()->OutputActivity());
244
245   // do another stanza
246   XmlElement roster_get(QN_IQ);
247   roster_get.AddAttr(QN_TYPE, "get");
248   roster_get.AddAttr(QN_ID, engine()->NextId());
249   roster_get.AddElement(new XmlElement(QN_ROSTER_QUERY, true));
250   engine()->SendStanza(&roster_get);
251   EXPECT_EQ("<iq type=\"get\" id=\"2\"><query xmlns=\"jabber:iq:roster\"/>"
252           "</iq>", handler()->OutputActivity());
253
254   // now say the server ends the stream
255   engine()->HandleInput("</stream:stream>", 16);
256   EXPECT_EQ("[CLOSED][ERROR-DOCUMENT-CLOSED]", handler()->SessionActivity());
257   EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
258   EXPECT_EQ("", handler()->StanzaActivity());
259 }
260
261 // TestIqCallback()
262 //    This tests the routing of Iq stanzas and responses.
263 TEST_F(XmppEngineTest, TestIqCallback) {
264   XmppEngineTestIqHandler iq_response;
265   XmppIqCookie cookie;
266
267   // Do the whole login handshake
268   RunLogin();
269
270   // Build an iq request
271   XmlElement roster_get(QN_IQ);
272   roster_get.AddAttr(QN_TYPE, "get");
273   roster_get.AddAttr(QN_ID, engine()->NextId());
274   roster_get.AddElement(new XmlElement(QN_ROSTER_QUERY, true));
275   engine()->SendIq(&roster_get, &iq_response, &cookie);
276   EXPECT_EQ("<iq type=\"get\" id=\"2\"><query xmlns=\"jabber:iq:roster\"/>"
277           "</iq>", handler()->OutputActivity());
278   EXPECT_EQ("", handler()->SessionActivity());
279   EXPECT_EQ("", handler()->StanzaActivity());
280   EXPECT_EQ("", iq_response.IqResponseActivity());
281
282   // now say the server responds to the iq
283   std::string input = "<iq type='result' id='2'>"
284                       "<query xmlns='jabber:iq:roster'><item>foo</item>"
285                       "</query></iq>";
286   engine()->HandleInput(input.c_str(), input.length());
287   EXPECT_EQ("", handler()->OutputActivity());
288   EXPECT_EQ("", handler()->SessionActivity());
289   EXPECT_EQ("", handler()->StanzaActivity());
290   EXPECT_EQ("<cli:iq type=\"result\" id=\"2\" xmlns:cli=\"jabber:client\">"
291           "<query xmlns=\"jabber:iq:roster\"><item>foo</item></query>"
292           "</cli:iq>", iq_response.IqResponseActivity());
293
294   EXPECT_EQ(XMPP_RETURN_BADARGUMENT, engine()->RemoveIqHandler(cookie, NULL));
295
296   // Do it again with another id to test cancel
297   roster_get.SetAttr(QN_ID, engine()->NextId());
298   engine()->SendIq(&roster_get, &iq_response, &cookie);
299   EXPECT_EQ("<iq type=\"get\" id=\"3\"><query xmlns=\"jabber:iq:roster\"/>"
300           "</iq>", handler()->OutputActivity());
301   EXPECT_EQ("", handler()->SessionActivity());
302   EXPECT_EQ("", handler()->StanzaActivity());
303   EXPECT_EQ("", iq_response.IqResponseActivity());
304
305   // cancel the handler this time
306   EXPECT_EQ(XMPP_RETURN_OK, engine()->RemoveIqHandler(cookie, NULL));
307
308   // now say the server responds to the iq: the iq handler should not get it.
309   input = "<iq type='result' id='3'><query xmlns='jabber:iq:roster'><item>bar"
310           "</item></query></iq>";
311   engine()->HandleInput(input.c_str(), input.length());
312   EXPECT_EQ("<cli:iq type=\"result\" id=\"3\" xmlns:cli=\"jabber:client\">"
313           "<query xmlns=\"jabber:iq:roster\"><item>bar</item></query>"
314           "</cli:iq>", handler()->StanzaActivity());
315   EXPECT_EQ("", iq_response.IqResponseActivity());
316   EXPECT_EQ("", handler()->OutputActivity());
317   EXPECT_EQ("", handler()->SessionActivity());
318 }