PageRenderTime 37ms CodeModel.GetById 2ms app.highlight 30ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/viewer_components/login/tests/lllogin_test.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 498 lines | 337 code | 86 blank | 75 comment | 6 complexity | 34db368de3ecac6e40a44550d19e82b4 MD5 | raw file
  1/**
  2 * @file   lllogin_test.cpp
  3 * @author Mark Palange
  4 * @date   2009-02-26
  5 * @brief  Tests of lllogin.cpp.
  6 * 
  7 * $LicenseInfo:firstyear=2009&license=viewerlgpl$
  8 * Second Life Viewer Source Code
  9 * Copyright (C) 2009-2010, Linden Research, Inc.
 10 * 
 11 * This library is free software; you can redistribute it and/or
 12 * modify it under the terms of the GNU Lesser General Public
 13 * License as published by the Free Software Foundation;
 14 * version 2.1 of the License only.
 15 * 
 16 * This library is distributed in the hope that it will be useful,
 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 19 * Lesser General Public License for more details.
 20 * 
 21 * You should have received a copy of the GNU Lesser General Public
 22 * License along with this library; if not, write to the Free Software
 23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 24 * 
 25 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 26 * $/LicenseInfo$
 27 */
 28
 29#if LL_WINDOWS
 30#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
 31#endif
 32
 33// Precompiled header
 34#include "linden_common.h"
 35// associated header
 36#include "../lllogin.h"
 37// STL headers
 38// std headers
 39#include <iostream>
 40// external library headers
 41// other Linden headers
 42#include "llsd.h"
 43#include "../../../test/lltut.h"
 44//#define DEBUG_ON
 45#include "../../../test/debug.h"
 46#include "llevents.h"
 47#include "stringize.h"
 48
 49#if LL_WINDOWS
 50#define skipwin(arg) skip(arg)
 51#define skipmac(arg)
 52#define skiplinux(arg)
 53#elif LL_DARWIN
 54#define skipwin(arg)
 55#define skipmac(arg) skip(arg)
 56#define skiplinux(arg)
 57#elif LL_LINUX
 58#define skipwin(arg)
 59#define skipmac(arg)
 60#define skiplinux(arg) skip(arg)
 61#endif
 62
 63/*****************************************************************************
 64*   Helper classes
 65*****************************************************************************/
 66// This is a listener to receive results from lllogin.
 67class LoginListener: public LLEventTrackable
 68{
 69	std::string mName;
 70	LLSD mLastEvent;
 71    Debug mDebug;
 72public:
 73	LoginListener(const std::string& name) : 
 74		mName(name),
 75        mDebug(stringize(*this))
 76	{}
 77
 78	bool call(const LLSD& event)
 79	{
 80		mDebug(STRINGIZE("LoginListener called!: " << event));
 81		
 82		mLastEvent = event;
 83		return false;
 84	}
 85
 86    LLBoundListener listenTo(LLEventPump& pump)
 87    {
 88        return pump.listen(mName, boost::bind(&LoginListener::call, this, _1));
 89	}
 90
 91	LLSD lastEvent() const { return mLastEvent; }
 92
 93    friend std::ostream& operator<<(std::ostream& out, const LoginListener& listener)
 94    {
 95        return out << "LoginListener(" << listener.mName << ')';
 96    }
 97};
 98
 99class LLAresListener: public LLEventTrackable
100{
101	std::string mName;
102	LLSD mEvent;
103	bool mImmediateResponse;
104	bool mMultipleURIResponse;
105    Debug mDebug;
106	
107public:
108	LLAresListener(const std::string& name, 
109				   bool i = false,
110				   bool m = false
111				   ) : 
112		mName(name),
113		mImmediateResponse(i),
114		mMultipleURIResponse(m),
115        mDebug(stringize(*this))
116	{}
117
118	bool handle_event(const LLSD& event)
119	{
120		mDebug(STRINGIZE("LLAresListener called!: " << event));
121		mEvent = event;
122		if(mImmediateResponse)
123		{
124			sendReply();
125		}
126		return false;
127	}
128
129	void sendReply()
130	{
131		if(mEvent["op"].asString() == "rewriteURI")
132		{
133			LLSD result;
134			if(mMultipleURIResponse)
135			{
136				result.append(LLSD("login.foo.com"));
137			}
138			result.append(mEvent["uri"]);
139			LLEventPumps::instance().obtain(mEvent["reply"]).post(result);
140		}
141	}
142
143	LLBoundListener listenTo(LLEventPump& pump)
144    {
145        return pump.listen(mName, boost::bind(&LLAresListener::handle_event, this, _1));
146	}
147
148    friend std::ostream& operator<<(std::ostream& out, const LLAresListener& listener)
149    {
150        return out << "LLAresListener(" << listener.mName << ')';
151    }
152};
153
154class LLXMLRPCListener: public LLEventTrackable
155{
156	std::string mName;
157	LLSD mEvent;
158	bool mImmediateResponse;
159	LLSD mResponse;
160    Debug mDebug;
161
162public:
163	LLXMLRPCListener(const std::string& name, 
164					 bool i = false,
165					 const LLSD& response = LLSD()
166					 ) : 
167		mName(name),
168		mImmediateResponse(i),
169		mResponse(response),
170        mDebug(stringize(*this))
171	{
172		if(mResponse.isUndefined())
173		{
174			mResponse["status"] = "Complete"; // StatusComplete
175			mResponse["errorcode"] = 0;
176			mResponse["error"] = "dummy response";
177			mResponse["transfer_rate"] = 0;
178			mResponse["responses"]["login"] = true;
179		}
180	}
181
182	void setResponse(const LLSD& r) 
183	{ 
184		mResponse = r; 
185	}
186
187	bool handle_event(const LLSD& event)
188	{
189		mDebug(STRINGIZE("LLXMLRPCListener called!: " << event));
190		mEvent = event;
191		if(mImmediateResponse)
192		{
193			sendReply();
194		}
195		return false;
196	}
197
198	void sendReply()
199	{
200		LLEventPumps::instance().obtain(mEvent["reply"]).post(mResponse);
201	}
202
203	LLBoundListener listenTo(LLEventPump& pump)
204    {
205        return pump.listen(mName, boost::bind(&LLXMLRPCListener::handle_event, this, _1));
206	}
207
208    friend std::ostream& operator<<(std::ostream& out, const LLXMLRPCListener& listener)
209    {
210        return out << "LLXMLRPCListener(" << listener.mName << ')';
211    }
212};
213
214/*****************************************************************************
215*   TUT
216*****************************************************************************/
217namespace tut
218{
219    struct llviewerlogin_data
220    {
221		llviewerlogin_data() :
222            pumps(LLEventPumps::instance())
223		{}
224		LLEventPumps& pumps;
225	};
226
227    typedef test_group<llviewerlogin_data> llviewerlogin_group;
228    typedef llviewerlogin_group::object llviewerlogin_object;
229    llviewerlogin_group llviewerlogingrp("LLViewerLogin");
230
231    template<> template<>
232    void llviewerlogin_object::test<1>()
233    {
234        DEBUG;
235		// Testing login with immediate responses from Ares and XMLPRC
236		// The response from both requests will come before the post request exits.
237		// This tests an edge case of the login state handling.
238		LLEventStream llaresPump("LLAres"); // Dummy LLAres pump.
239		LLEventStream xmlrpcPump("LLXMLRPCTransaction"); // Dummy XMLRPC pump
240
241		bool respond_immediately = true;
242		// Have 'dummy ares' respond immediately. 
243		LLAresListener dummyLLAres("dummy_llares", respond_immediately);
244		dummyLLAres.listenTo(llaresPump);
245
246		// Have dummy XMLRPC respond immediately.
247		LLXMLRPCListener dummyXMLRPC("dummy_xmlrpc", respond_immediately);
248		dummyXMLRPC.listenTo(xmlrpcPump);
249
250		LLLogin login;
251
252		LoginListener listener("test_ear");
253		listener.listenTo(login.getEventPump());
254
255		LLSD credentials;
256		credentials["first"] = "foo";
257		credentials["last"] = "bar";
258		credentials["passwd"] = "secret";
259
260		login.connect("login.bar.com", credentials);
261
262		ensure_equals("Online state", listener.lastEvent()["state"].asString(), "online");
263	}
264
265    template<> template<>
266    void llviewerlogin_object::test<2>()
267    {
268        DEBUG;
269		// Tests a successful login in with delayed responses. 
270		// Also includes 'failure' that cause the login module
271		// to re-attempt connection, once from a basic failure
272		// and once from the 'indeterminate' response.
273
274		set_test_name("LLLogin multiple srv uris w/ success");
275
276		// Testing normal login procedure.
277		LLEventStream llaresPump("LLAres"); // Dummy LLAres pump.
278		LLEventStream xmlrpcPump("LLXMLRPCTransaction"); // Dummy XMLRPC pump
279
280		bool respond_immediately = false;
281		bool multiple_addresses = true;
282		LLAresListener dummyLLAres("dummy_llares", respond_immediately, multiple_addresses);
283		dummyLLAres.listenTo(llaresPump);
284
285		LLXMLRPCListener dummyXMLRPC("dummy_xmlrpc");
286		dummyXMLRPC.listenTo(xmlrpcPump);
287
288		LLLogin login;
289
290		LoginListener listener("test_ear");
291		listener.listenTo(login.getEventPump());
292
293		LLSD credentials;
294		credentials["first"] = "foo";
295		credentials["last"] = "bar";
296		credentials["passwd"] = "secret";
297
298		login.connect("login.bar.com", credentials);
299
300		ensure_equals("SRV state", listener.lastEvent()["change"].asString(), "srvrequest"); 
301
302		dummyLLAres.sendReply();
303
304		// Test Authenticating State prior to first response.
305		ensure_equals("Auth state 1", listener.lastEvent()["change"].asString(), "authenticating"); 
306		ensure_equals("Attempt 1", listener.lastEvent()["data"]["attempt"].asInteger(), 1); 
307		ensure_equals("URI 1", listener.lastEvent()["data"]["request"]["uri"].asString(), "login.foo.com"); 
308
309		// First send emulated LLXMLRPCListener failure,
310		// this should return login to the authenticating step and increase the attempt 
311		// count.
312		LLSD data;
313		data["status"] = "OtherError"; 
314		data["errorcode"] = 0;
315		data["error"] = "dummy response";
316		data["transfer_rate"] = 0;
317		dummyXMLRPC.setResponse(data);
318		dummyXMLRPC.sendReply();
319
320		ensure_equals("Fail back to authenticate 1", listener.lastEvent()["change"].asString(), "authenticating"); 
321		ensure_equals("Attempt 2", listener.lastEvent()["data"]["attempt"].asInteger(), 2); 
322		ensure_equals("URI 2", listener.lastEvent()["data"]["request"]["uri"].asString(), "login.bar.com"); 
323
324		// Now send the 'indeterminate' response.
325		data.clear();
326		data["status"] = "Complete"; // StatusComplete
327		data["errorcode"] = 0;
328		data["error"] = "dummy response";
329		data["transfer_rate"] = 0;
330		data["responses"]["login"] = "indeterminate";
331		data["responses"]["next_url"] = "login.indeterminate.com";			
332		data["responses"]["next_method"] = "test_login_method"; 			
333		dummyXMLRPC.setResponse(data);
334		dummyXMLRPC.sendReply();
335
336		ensure_equals("Fail back to authenticate 2", listener.lastEvent()["change"].asString(), "authenticating"); 
337		ensure_equals("Attempt 3", listener.lastEvent()["data"]["attempt"].asInteger(), 3); 
338		ensure_equals("URI 3", listener.lastEvent()["data"]["request"]["uri"].asString(), "login.indeterminate.com"); 
339		ensure_equals("Method 3", listener.lastEvent()["data"]["request"]["method"].asString(), "test_login_method"); 
340
341		// Finally let the auth succeed.
342		data.clear();
343		data["status"] = "Complete"; // StatusComplete
344		data["errorcode"] = 0;
345		data["error"] = "dummy response";
346		data["transfer_rate"] = 0;
347		data["responses"]["login"] = "true";
348		dummyXMLRPC.setResponse(data);
349		dummyXMLRPC.sendReply();
350
351		ensure_equals("Success state", listener.lastEvent()["state"].asString(), "online");
352
353		login.disconnect();
354
355		ensure_equals("Disconnected state", listener.lastEvent()["state"].asString(), "offline");
356	}
357
358    template<> template<>
359    void llviewerlogin_object::test<3>()
360    {
361        DEBUG;
362		// Test completed response, that fails to login.
363		set_test_name("LLLogin valid response, failure (eg. bad credentials)");
364
365		// Testing normal login procedure.
366		LLEventStream llaresPump("LLAres"); // Dummy LLAres pump.
367		LLEventStream xmlrpcPump("LLXMLRPCTransaction"); // Dummy XMLRPC pump
368
369		LLAresListener dummyLLAres("dummy_llares");
370		dummyLLAres.listenTo(llaresPump);
371
372		LLXMLRPCListener dummyXMLRPC("dummy_xmlrpc");
373		dummyXMLRPC.listenTo(xmlrpcPump);
374
375		LLLogin login;
376		LoginListener listener("test_ear");
377		listener.listenTo(login.getEventPump());
378
379		LLSD credentials;
380		credentials["first"] = "who";
381		credentials["last"] = "what";
382		credentials["passwd"] = "badpasswd";
383
384		login.connect("login.bar.com", credentials);
385
386		ensure_equals("SRV state", listener.lastEvent()["change"].asString(), "srvrequest"); 
387
388		dummyLLAres.sendReply();
389
390		ensure_equals("Auth state", listener.lastEvent()["change"].asString(), "authenticating"); 
391
392		// Send the failed auth request reponse
393		LLSD data;
394		data["status"] = "Complete";
395		data["errorcode"] = 0;
396		data["error"] = "dummy response";
397		data["transfer_rate"] = 0;
398		data["responses"]["login"] = "false";
399		dummyXMLRPC.setResponse(data);
400		dummyXMLRPC.sendReply();
401
402		ensure_equals("Failed to offline", listener.lastEvent()["state"].asString(), "offline");
403	}
404
405    template<> template<>
406    void llviewerlogin_object::test<4>()
407    {
408        DEBUG;
409		// Test incomplete response, that end the attempt.
410		set_test_name("LLLogin valid response, failure (eg. bad credentials)");
411
412		// Testing normal login procedure.
413		LLEventStream llaresPump("LLAres"); // Dummy LLAres pump.
414		LLEventStream xmlrpcPump("LLXMLRPCTransaction"); // Dummy XMLRPC pump
415
416		LLAresListener dummyLLAres("dummy_llares");
417		dummyLLAres.listenTo(llaresPump);
418
419		LLXMLRPCListener dummyXMLRPC("dummy_xmlrpc");
420		dummyXMLRPC.listenTo(xmlrpcPump);
421
422		LLLogin login;
423		LoginListener listener("test_ear");
424		listener.listenTo(login.getEventPump());
425
426		LLSD credentials;
427		credentials["first"] = "these";
428		credentials["last"] = "don't";
429		credentials["passwd"] = "matter";
430
431		login.connect("login.bar.com", credentials);
432
433		ensure_equals("SRV state", listener.lastEvent()["change"].asString(), "srvrequest"); 
434
435		dummyLLAres.sendReply();
436
437		ensure_equals("Auth state", listener.lastEvent()["change"].asString(), "authenticating"); 
438
439		// Send the failed auth request reponse
440		LLSD data;
441		data["status"] = "OtherError";
442		data["errorcode"] = 0;
443		data["error"] = "dummy response";
444		data["transfer_rate"] = 0;
445		dummyXMLRPC.setResponse(data);
446		dummyXMLRPC.sendReply();
447
448		ensure_equals("Failed to offline", listener.lastEvent()["state"].asString(), "offline");
449	}
450
451	template<> template<>
452    void llviewerlogin_object::test<5>()
453    {
454        DEBUG;
455		// Test SRV request timeout.
456		set_test_name("LLLogin SRV timeout testing");
457
458		// Testing normal login procedure.
459		LLEventStream llaresPump("LLAres"); // Dummy LLAres pump.
460
461		LLAresListener dummyLLAres("dummy_llares");
462		dummyLLAres.listenTo(llaresPump);
463
464		LLLogin login;
465		LoginListener listener("test_ear");
466		listener.listenTo(login.getEventPump());
467
468		LLSD credentials;
469		credentials["first"] = "these";
470		credentials["last"] = "don't";
471		credentials["passwd"] = "matter";
472		credentials["cfg_srv_timeout"] = 0.0f;
473
474		login.connect("login.bar.com", credentials);
475
476		ensure_equals("SRV State", listener.lastEvent()["change"].asString(), "srvrequest"); 
477
478		// Get the mainloop eventpump, which needs a pinging in order to drive the 
479		// SRV timeout.
480		LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
481		LLSD frame_event;
482		mainloop.post(frame_event);
483
484		// In this state we have NOT sent a reply from LLAresListener -- in
485		// fact there's no such object. Nonetheless, we expect the timeout to
486		// have stepped the login module forward to try to authenticate with
487		// the original URI.
488		ensure_equals("Auth state", listener.lastEvent()["change"].asString(), "authenticating"); 
489		ensure_equals("Attempt", listener.lastEvent()["data"]["attempt"].asInteger(), 1); 
490		ensure_equals("URI", listener.lastEvent()["data"]["request"]["uri"].asString(), "login.bar.com");
491
492		// EXT-4193: if the SRV reply isn't lost but merely late, and if it
493		// arrives just at the moment we're expecting the XMLRPC reply, the
494		// original code got confused and crashed. Drive that case here. We
495		// observe that without the fix, this call DOES repro.
496		dummyLLAres.sendReply();
497	}
498}