PageRenderTime 26ms CodeModel.GetById 12ms RepoModel.GetById 1ms 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
Possible License(s): LGPL-2.1
  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. #if LL_WINDOWS
  29. #pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
  30. #endif
  31. // Precompiled header
  32. #include "linden_common.h"
  33. // associated header
  34. #include "../lllogin.h"
  35. // STL headers
  36. // std headers
  37. #include <iostream>
  38. // external library headers
  39. // other Linden headers
  40. #include "llsd.h"
  41. #include "../../../test/lltut.h"
  42. //#define DEBUG_ON
  43. #include "../../../test/debug.h"
  44. #include "llevents.h"
  45. #include "stringize.h"
  46. #if LL_WINDOWS
  47. #define skipwin(arg) skip(arg)
  48. #define skipmac(arg)
  49. #define skiplinux(arg)
  50. #elif LL_DARWIN
  51. #define skipwin(arg)
  52. #define skipmac(arg) skip(arg)
  53. #define skiplinux(arg)
  54. #elif LL_LINUX
  55. #define skipwin(arg)
  56. #define skipmac(arg)
  57. #define skiplinux(arg) skip(arg)
  58. #endif
  59. /*****************************************************************************
  60. * Helper classes
  61. *****************************************************************************/
  62. // This is a listener to receive results from lllogin.
  63. class LoginListener: public LLEventTrackable
  64. {
  65. std::string mName;
  66. LLSD mLastEvent;
  67. Debug mDebug;
  68. public:
  69. LoginListener(const std::string& name) :
  70. mName(name),
  71. mDebug(stringize(*this))
  72. {}
  73. bool call(const LLSD& event)
  74. {
  75. mDebug(STRINGIZE("LoginListener called!: " << event));
  76. mLastEvent = event;
  77. return false;
  78. }
  79. LLBoundListener listenTo(LLEventPump& pump)
  80. {
  81. return pump.listen(mName, boost::bind(&LoginListener::call, this, _1));
  82. }
  83. LLSD lastEvent() const { return mLastEvent; }
  84. friend std::ostream& operator<<(std::ostream& out, const LoginListener& listener)
  85. {
  86. return out << "LoginListener(" << listener.mName << ')';
  87. }
  88. };
  89. class LLAresListener: public LLEventTrackable
  90. {
  91. std::string mName;
  92. LLSD mEvent;
  93. bool mImmediateResponse;
  94. bool mMultipleURIResponse;
  95. Debug mDebug;
  96. public:
  97. LLAresListener(const std::string& name,
  98. bool i = false,
  99. bool m = false
  100. ) :
  101. mName(name),
  102. mImmediateResponse(i),
  103. mMultipleURIResponse(m),
  104. mDebug(stringize(*this))
  105. {}
  106. bool handle_event(const LLSD& event)
  107. {
  108. mDebug(STRINGIZE("LLAresListener called!: " << event));
  109. mEvent = event;
  110. if(mImmediateResponse)
  111. {
  112. sendReply();
  113. }
  114. return false;
  115. }
  116. void sendReply()
  117. {
  118. if(mEvent["op"].asString() == "rewriteURI")
  119. {
  120. LLSD result;
  121. if(mMultipleURIResponse)
  122. {
  123. result.append(LLSD("login.foo.com"));
  124. }
  125. result.append(mEvent["uri"]);
  126. LLEventPumps::instance().obtain(mEvent["reply"]).post(result);
  127. }
  128. }
  129. LLBoundListener listenTo(LLEventPump& pump)
  130. {
  131. return pump.listen(mName, boost::bind(&LLAresListener::handle_event, this, _1));
  132. }
  133. friend std::ostream& operator<<(std::ostream& out, const LLAresListener& listener)
  134. {
  135. return out << "LLAresListener(" << listener.mName << ')';
  136. }
  137. };
  138. class LLXMLRPCListener: public LLEventTrackable
  139. {
  140. std::string mName;
  141. LLSD mEvent;
  142. bool mImmediateResponse;
  143. LLSD mResponse;
  144. Debug mDebug;
  145. public:
  146. LLXMLRPCListener(const std::string& name,
  147. bool i = false,
  148. const LLSD& response = LLSD()
  149. ) :
  150. mName(name),
  151. mImmediateResponse(i),
  152. mResponse(response),
  153. mDebug(stringize(*this))
  154. {
  155. if(mResponse.isUndefined())
  156. {
  157. mResponse["status"] = "Complete"; // StatusComplete
  158. mResponse["errorcode"] = 0;
  159. mResponse["error"] = "dummy response";
  160. mResponse["transfer_rate"] = 0;
  161. mResponse["responses"]["login"] = true;
  162. }
  163. }
  164. void setResponse(const LLSD& r)
  165. {
  166. mResponse = r;
  167. }
  168. bool handle_event(const LLSD& event)
  169. {
  170. mDebug(STRINGIZE("LLXMLRPCListener called!: " << event));
  171. mEvent = event;
  172. if(mImmediateResponse)
  173. {
  174. sendReply();
  175. }
  176. return false;
  177. }
  178. void sendReply()
  179. {
  180. LLEventPumps::instance().obtain(mEvent["reply"]).post(mResponse);
  181. }
  182. LLBoundListener listenTo(LLEventPump& pump)
  183. {
  184. return pump.listen(mName, boost::bind(&LLXMLRPCListener::handle_event, this, _1));
  185. }
  186. friend std::ostream& operator<<(std::ostream& out, const LLXMLRPCListener& listener)
  187. {
  188. return out << "LLXMLRPCListener(" << listener.mName << ')';
  189. }
  190. };
  191. /*****************************************************************************
  192. * TUT
  193. *****************************************************************************/
  194. namespace tut
  195. {
  196. struct llviewerlogin_data
  197. {
  198. llviewerlogin_data() :
  199. pumps(LLEventPumps::instance())
  200. {}
  201. LLEventPumps& pumps;
  202. };
  203. typedef test_group<llviewerlogin_data> llviewerlogin_group;
  204. typedef llviewerlogin_group::object llviewerlogin_object;
  205. llviewerlogin_group llviewerlogingrp("LLViewerLogin");
  206. template<> template<>
  207. void llviewerlogin_object::test<1>()
  208. {
  209. DEBUG;
  210. // Testing login with immediate responses from Ares and XMLPRC
  211. // The response from both requests will come before the post request exits.
  212. // This tests an edge case of the login state handling.
  213. LLEventStream llaresPump("LLAres"); // Dummy LLAres pump.
  214. LLEventStream xmlrpcPump("LLXMLRPCTransaction"); // Dummy XMLRPC pump
  215. bool respond_immediately = true;
  216. // Have 'dummy ares' respond immediately.
  217. LLAresListener dummyLLAres("dummy_llares", respond_immediately);
  218. dummyLLAres.listenTo(llaresPump);
  219. // Have dummy XMLRPC respond immediately.
  220. LLXMLRPCListener dummyXMLRPC("dummy_xmlrpc", respond_immediately);
  221. dummyXMLRPC.listenTo(xmlrpcPump);
  222. LLLogin login;
  223. LoginListener listener("test_ear");
  224. listener.listenTo(login.getEventPump());
  225. LLSD credentials;
  226. credentials["first"] = "foo";
  227. credentials["last"] = "bar";
  228. credentials["passwd"] = "secret";
  229. login.connect("login.bar.com", credentials);
  230. ensure_equals("Online state", listener.lastEvent()["state"].asString(), "online");
  231. }
  232. template<> template<>
  233. void llviewerlogin_object::test<2>()
  234. {
  235. DEBUG;
  236. // Tests a successful login in with delayed responses.
  237. // Also includes 'failure' that cause the login module
  238. // to re-attempt connection, once from a basic failure
  239. // and once from the 'indeterminate' response.
  240. set_test_name("LLLogin multiple srv uris w/ success");
  241. // Testing normal login procedure.
  242. LLEventStream llaresPump("LLAres"); // Dummy LLAres pump.
  243. LLEventStream xmlrpcPump("LLXMLRPCTransaction"); // Dummy XMLRPC pump
  244. bool respond_immediately = false;
  245. bool multiple_addresses = true;
  246. LLAresListener dummyLLAres("dummy_llares", respond_immediately, multiple_addresses);
  247. dummyLLAres.listenTo(llaresPump);
  248. LLXMLRPCListener dummyXMLRPC("dummy_xmlrpc");
  249. dummyXMLRPC.listenTo(xmlrpcPump);
  250. LLLogin login;
  251. LoginListener listener("test_ear");
  252. listener.listenTo(login.getEventPump());
  253. LLSD credentials;
  254. credentials["first"] = "foo";
  255. credentials["last"] = "bar";
  256. credentials["passwd"] = "secret";
  257. login.connect("login.bar.com", credentials);
  258. ensure_equals("SRV state", listener.lastEvent()["change"].asString(), "srvrequest");
  259. dummyLLAres.sendReply();
  260. // Test Authenticating State prior to first response.
  261. ensure_equals("Auth state 1", listener.lastEvent()["change"].asString(), "authenticating");
  262. ensure_equals("Attempt 1", listener.lastEvent()["data"]["attempt"].asInteger(), 1);
  263. ensure_equals("URI 1", listener.lastEvent()["data"]["request"]["uri"].asString(), "login.foo.com");
  264. // First send emulated LLXMLRPCListener failure,
  265. // this should return login to the authenticating step and increase the attempt
  266. // count.
  267. LLSD data;
  268. data["status"] = "OtherError";
  269. data["errorcode"] = 0;
  270. data["error"] = "dummy response";
  271. data["transfer_rate"] = 0;
  272. dummyXMLRPC.setResponse(data);
  273. dummyXMLRPC.sendReply();
  274. ensure_equals("Fail back to authenticate 1", listener.lastEvent()["change"].asString(), "authenticating");
  275. ensure_equals("Attempt 2", listener.lastEvent()["data"]["attempt"].asInteger(), 2);
  276. ensure_equals("URI 2", listener.lastEvent()["data"]["request"]["uri"].asString(), "login.bar.com");
  277. // Now send the 'indeterminate' response.
  278. data.clear();
  279. data["status"] = "Complete"; // StatusComplete
  280. data["errorcode"] = 0;
  281. data["error"] = "dummy response";
  282. data["transfer_rate"] = 0;
  283. data["responses"]["login"] = "indeterminate";
  284. data["responses"]["next_url"] = "login.indeterminate.com";
  285. data["responses"]["next_method"] = "test_login_method";
  286. dummyXMLRPC.setResponse(data);
  287. dummyXMLRPC.sendReply();
  288. ensure_equals("Fail back to authenticate 2", listener.lastEvent()["change"].asString(), "authenticating");
  289. ensure_equals("Attempt 3", listener.lastEvent()["data"]["attempt"].asInteger(), 3);
  290. ensure_equals("URI 3", listener.lastEvent()["data"]["request"]["uri"].asString(), "login.indeterminate.com");
  291. ensure_equals("Method 3", listener.lastEvent()["data"]["request"]["method"].asString(), "test_login_method");
  292. // Finally let the auth succeed.
  293. data.clear();
  294. data["status"] = "Complete"; // StatusComplete
  295. data["errorcode"] = 0;
  296. data["error"] = "dummy response";
  297. data["transfer_rate"] = 0;
  298. data["responses"]["login"] = "true";
  299. dummyXMLRPC.setResponse(data);
  300. dummyXMLRPC.sendReply();
  301. ensure_equals("Success state", listener.lastEvent()["state"].asString(), "online");
  302. login.disconnect();
  303. ensure_equals("Disconnected state", listener.lastEvent()["state"].asString(), "offline");
  304. }
  305. template<> template<>
  306. void llviewerlogin_object::test<3>()
  307. {
  308. DEBUG;
  309. // Test completed response, that fails to login.
  310. set_test_name("LLLogin valid response, failure (eg. bad credentials)");
  311. // Testing normal login procedure.
  312. LLEventStream llaresPump("LLAres"); // Dummy LLAres pump.
  313. LLEventStream xmlrpcPump("LLXMLRPCTransaction"); // Dummy XMLRPC pump
  314. LLAresListener dummyLLAres("dummy_llares");
  315. dummyLLAres.listenTo(llaresPump);
  316. LLXMLRPCListener dummyXMLRPC("dummy_xmlrpc");
  317. dummyXMLRPC.listenTo(xmlrpcPump);
  318. LLLogin login;
  319. LoginListener listener("test_ear");
  320. listener.listenTo(login.getEventPump());
  321. LLSD credentials;
  322. credentials["first"] = "who";
  323. credentials["last"] = "what";
  324. credentials["passwd"] = "badpasswd";
  325. login.connect("login.bar.com", credentials);
  326. ensure_equals("SRV state", listener.lastEvent()["change"].asString(), "srvrequest");
  327. dummyLLAres.sendReply();
  328. ensure_equals("Auth state", listener.lastEvent()["change"].asString(), "authenticating");
  329. // Send the failed auth request reponse
  330. LLSD data;
  331. data["status"] = "Complete";
  332. data["errorcode"] = 0;
  333. data["error"] = "dummy response";
  334. data["transfer_rate"] = 0;
  335. data["responses"]["login"] = "false";
  336. dummyXMLRPC.setResponse(data);
  337. dummyXMLRPC.sendReply();
  338. ensure_equals("Failed to offline", listener.lastEvent()["state"].asString(), "offline");
  339. }
  340. template<> template<>
  341. void llviewerlogin_object::test<4>()
  342. {
  343. DEBUG;
  344. // Test incomplete response, that end the attempt.
  345. set_test_name("LLLogin valid response, failure (eg. bad credentials)");
  346. // Testing normal login procedure.
  347. LLEventStream llaresPump("LLAres"); // Dummy LLAres pump.
  348. LLEventStream xmlrpcPump("LLXMLRPCTransaction"); // Dummy XMLRPC pump
  349. LLAresListener dummyLLAres("dummy_llares");
  350. dummyLLAres.listenTo(llaresPump);
  351. LLXMLRPCListener dummyXMLRPC("dummy_xmlrpc");
  352. dummyXMLRPC.listenTo(xmlrpcPump);
  353. LLLogin login;
  354. LoginListener listener("test_ear");
  355. listener.listenTo(login.getEventPump());
  356. LLSD credentials;
  357. credentials["first"] = "these";
  358. credentials["last"] = "don't";
  359. credentials["passwd"] = "matter";
  360. login.connect("login.bar.com", credentials);
  361. ensure_equals("SRV state", listener.lastEvent()["change"].asString(), "srvrequest");
  362. dummyLLAres.sendReply();
  363. ensure_equals("Auth state", listener.lastEvent()["change"].asString(), "authenticating");
  364. // Send the failed auth request reponse
  365. LLSD data;
  366. data["status"] = "OtherError";
  367. data["errorcode"] = 0;
  368. data["error"] = "dummy response";
  369. data["transfer_rate"] = 0;
  370. dummyXMLRPC.setResponse(data);
  371. dummyXMLRPC.sendReply();
  372. ensure_equals("Failed to offline", listener.lastEvent()["state"].asString(), "offline");
  373. }
  374. template<> template<>
  375. void llviewerlogin_object::test<5>()
  376. {
  377. DEBUG;
  378. // Test SRV request timeout.
  379. set_test_name("LLLogin SRV timeout testing");
  380. // Testing normal login procedure.
  381. LLEventStream llaresPump("LLAres"); // Dummy LLAres pump.
  382. LLAresListener dummyLLAres("dummy_llares");
  383. dummyLLAres.listenTo(llaresPump);
  384. LLLogin login;
  385. LoginListener listener("test_ear");
  386. listener.listenTo(login.getEventPump());
  387. LLSD credentials;
  388. credentials["first"] = "these";
  389. credentials["last"] = "don't";
  390. credentials["passwd"] = "matter";
  391. credentials["cfg_srv_timeout"] = 0.0f;
  392. login.connect("login.bar.com", credentials);
  393. ensure_equals("SRV State", listener.lastEvent()["change"].asString(), "srvrequest");
  394. // Get the mainloop eventpump, which needs a pinging in order to drive the
  395. // SRV timeout.
  396. LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
  397. LLSD frame_event;
  398. mainloop.post(frame_event);
  399. // In this state we have NOT sent a reply from LLAresListener -- in
  400. // fact there's no such object. Nonetheless, we expect the timeout to
  401. // have stepped the login module forward to try to authenticate with
  402. // the original URI.
  403. ensure_equals("Auth state", listener.lastEvent()["change"].asString(), "authenticating");
  404. ensure_equals("Attempt", listener.lastEvent()["data"]["attempt"].asInteger(), 1);
  405. ensure_equals("URI", listener.lastEvent()["data"]["request"]["uri"].asString(), "login.bar.com");
  406. // EXT-4193: if the SRV reply isn't lost but merely late, and if it
  407. // arrives just at the moment we're expecting the XMLRPC reply, the
  408. // original code got confused and crashed. Drive that case here. We
  409. // observe that without the fix, this call DOES repro.
  410. dummyLLAres.sendReply();
  411. }
  412. }