PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/indra/llcommon/tests/lleventdispatcher_test.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1324 lines | 929 code | 115 blank | 280 comment | 29 complexity | 5f3181bdc613ec3c63a3cd31ab1c85ab MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file lleventdispatcher_test.cpp
  3. * @author Nat Goodspeed
  4. * @date 2011-01-20
  5. * @brief Test for lleventdispatcher.
  6. *
  7. * $LicenseInfo:firstyear=2011&license=viewerlgpl$
  8. * Copyright (c) 2011, Linden Research, Inc.
  9. * $/LicenseInfo$
  10. */
  11. // Precompiled header
  12. #include "linden_common.h"
  13. // associated header
  14. #include "lleventdispatcher.h"
  15. // STL headers
  16. // std headers
  17. // external library headers
  18. // other Linden headers
  19. #include "../test/lltut.h"
  20. #include "llsd.h"
  21. #include "llsdutil.h"
  22. #include "stringize.h"
  23. #include "tests/wrapllerrs.h"
  24. #include <map>
  25. #include <string>
  26. #include <stdexcept>
  27. #include <boost/bind.hpp>
  28. #include <boost/function.hpp>
  29. #include <boost/range.hpp>
  30. #include <boost/foreach.hpp>
  31. #define foreach BOOST_FOREACH
  32. #include <boost/lambda/lambda.hpp>
  33. #include <iostream>
  34. #include <iomanip>
  35. using boost::lambda::constant;
  36. using boost::lambda::constant_ref;
  37. using boost::lambda::var;
  38. using namespace llsd;
  39. /*****************************************************************************
  40. * Output control
  41. *****************************************************************************/
  42. #ifdef DEBUG_ON
  43. using std::cout;
  44. #else
  45. static std::ostringstream cout;
  46. #endif
  47. /*****************************************************************************
  48. * Example data, functions, classes
  49. *****************************************************************************/
  50. // We don't need a whole lot of different arbitrary-params methods, just (no |
  51. // (const LLSD&) | arbitrary) args (function | static method | non-static
  52. // method), where 'arbitrary' is (every LLSD datatype + (const char*)).
  53. // But we need to register each one under different names for the different
  54. // registration styles. Don't forget LLEventDispatcher subclass methods(const
  55. // LLSD&).
  56. // However, the number of target parameter conversions we want to try exceeds
  57. // boost::fusion::invoke()'s supported parameter-list size. Break out two
  58. // different lists.
  59. #define NPARAMSa bool b, int i, float f, double d, const char* cp
  60. #define NPARAMSb const std::string& s, const LLUUID& uuid, const LLDate& date, \
  61. const LLURI& uri, const std::vector<U8>& bin
  62. #define NARGSa b, i, f, d, cp
  63. #define NARGSb s, uuid, date, uri, bin
  64. // For some registration methods we need methods on a subclass of
  65. // LLEventDispatcher. To simplify things, we'll use this Dispatcher subclass
  66. // for all our testing, including testing its own methods.
  67. class Dispatcher: public LLEventDispatcher
  68. {
  69. public:
  70. Dispatcher(const std::string& name, const std::string& key):
  71. LLEventDispatcher(name, key)
  72. {}
  73. // sensing member, mutable because we want to know when we've reached our
  74. // const method too
  75. mutable LLSD llsd;
  76. void method1(const LLSD& obj) { llsd = obj; }
  77. void cmethod1(const LLSD& obj) const { llsd = obj; }
  78. };
  79. // sensing vars, captured in a struct to make it convenient to clear them
  80. struct Vars
  81. {
  82. LLSD llsd;
  83. bool b;
  84. int i;
  85. float f;
  86. double d;
  87. // Capture param passed as char*. But merely storing a char* received from
  88. // our caller, possibly the .c_str() from a concatenation expression,
  89. // would be Bad: the pointer will be invalidated long before we can query
  90. // it. We could allocate a new chunk of memory, copy the string data and
  91. // point to that instead -- but hey, guess what, we already have a class
  92. // that does that!
  93. std::string cp;
  94. std::string s;
  95. LLUUID uuid;
  96. LLDate date;
  97. LLURI uri;
  98. std::vector<U8> bin;
  99. Vars():
  100. // Only need to initialize the POD types, the rest should take care of
  101. // default-constructing themselves.
  102. b(false),
  103. i(0),
  104. f(0),
  105. d(0)
  106. {}
  107. // Detect any non-default values for convenient testing
  108. LLSD inspect() const
  109. {
  110. LLSD result;
  111. if (llsd.isDefined())
  112. result["llsd"] = llsd;
  113. if (b)
  114. result["b"] = b;
  115. if (i)
  116. result["i"] = i;
  117. if (f)
  118. result["f"] = f;
  119. if (d)
  120. result["d"] = d;
  121. if (! cp.empty())
  122. result["cp"] = cp;
  123. if (! s.empty())
  124. result["s"] = s;
  125. if (uuid != LLUUID())
  126. result["uuid"] = uuid;
  127. if (date != LLDate())
  128. result["date"] = date;
  129. if (uri != LLURI())
  130. result["uri"] = uri;
  131. if (! bin.empty())
  132. result["bin"] = bin;
  133. return result;
  134. }
  135. /*------------- no-args (non-const, const, static) methods -------------*/
  136. void method0()
  137. {
  138. cout << "method0()\n";
  139. i = 17;
  140. }
  141. void cmethod0() const
  142. {
  143. cout << 'c';
  144. const_cast<Vars*>(this)->method0();
  145. }
  146. static void smethod0();
  147. /*------------ Callable (non-const, const, static) methods -------------*/
  148. void method1(const LLSD& obj)
  149. {
  150. cout << "method1(" << obj << ")\n";
  151. llsd = obj;
  152. }
  153. void cmethod1(const LLSD& obj) const
  154. {
  155. cout << 'c';
  156. const_cast<Vars*>(this)->method1(obj);
  157. }
  158. static void smethod1(const LLSD& obj);
  159. /*-------- Arbitrary-params (non-const, const, static) methods ---------*/
  160. void methodna(NPARAMSa)
  161. {
  162. // Because our const char* param cp might be NULL, and because we
  163. // intend to capture the value in a std::string, have to distinguish
  164. // between the NULL value and any non-NULL value. Use a convention
  165. // easy for a human reader: enclose any non-NULL value in single
  166. // quotes, reserving the unquoted string "NULL" to represent a NULL ptr.
  167. std::string vcp;
  168. if (cp == NULL)
  169. vcp = "NULL";
  170. else
  171. vcp = std::string("'") + cp + "'";
  172. cout << "methodna(" << b
  173. << ", " << i
  174. << ", " << f
  175. << ", " << d
  176. << ", " << vcp
  177. << ")\n";
  178. this->b = b;
  179. this->i = i;
  180. this->f = f;
  181. this->d = d;
  182. this->cp = vcp;
  183. }
  184. void methodnb(NPARAMSb)
  185. {
  186. std::ostringstream vbin;
  187. foreach(U8 byte, bin)
  188. {
  189. vbin << std::hex << std::setfill('0') << std::setw(2) << unsigned(byte);
  190. }
  191. cout << "methodnb(" << "'" << s << "'"
  192. << ", " << uuid
  193. << ", " << date
  194. << ", '" << uri << "'"
  195. << ", " << vbin.str()
  196. << ")\n";
  197. this->s = s;
  198. this->uuid = uuid;
  199. this->date = date;
  200. this->uri = uri;
  201. this->bin = bin;
  202. }
  203. void cmethodna(NPARAMSa) const
  204. {
  205. cout << 'c';
  206. const_cast<Vars*>(this)->methodna(NARGSa);
  207. }
  208. void cmethodnb(NPARAMSb) const
  209. {
  210. cout << 'c';
  211. const_cast<Vars*>(this)->methodnb(NARGSb);
  212. }
  213. static void smethodna(NPARAMSa);
  214. static void smethodnb(NPARAMSb);
  215. };
  216. /*------- Global Vars instance for free functions and static methods -------*/
  217. static Vars g;
  218. /*------------ Static Vars method implementations reference 'g' ------------*/
  219. void Vars::smethod0()
  220. {
  221. cout << "smethod0() -> ";
  222. g.method0();
  223. }
  224. void Vars::smethod1(const LLSD& obj)
  225. {
  226. cout << "smethod1(" << obj << ") -> ";
  227. g.method1(obj);
  228. }
  229. void Vars::smethodna(NPARAMSa)
  230. {
  231. cout << "smethodna(...) -> ";
  232. g.methodna(NARGSa);
  233. }
  234. void Vars::smethodnb(NPARAMSb)
  235. {
  236. cout << "smethodnb(...) -> ";
  237. g.methodnb(NARGSb);
  238. }
  239. /*--------------------------- Reset global Vars ----------------------------*/
  240. void clear()
  241. {
  242. g = Vars();
  243. }
  244. /*------------------- Free functions also reference 'g' --------------------*/
  245. void free0()
  246. {
  247. cout << "free0() -> ";
  248. g.method0();
  249. }
  250. void free1(const LLSD& obj)
  251. {
  252. cout << "free1(" << obj << ") -> ";
  253. g.method1(obj);
  254. }
  255. void freena(NPARAMSa)
  256. {
  257. cout << "freena(...) -> ";
  258. g.methodna(NARGSa);
  259. }
  260. void freenb(NPARAMSb)
  261. {
  262. cout << "freenb(...) -> ";
  263. g.methodnb(NARGSb);
  264. }
  265. /*****************************************************************************
  266. * TUT
  267. *****************************************************************************/
  268. namespace tut
  269. {
  270. struct lleventdispatcher_data
  271. {
  272. WrapLL_ERRS redirect;
  273. Dispatcher work;
  274. Vars v;
  275. std::string name, desc;
  276. // Capture our own copy of all registered functions' descriptions
  277. typedef std::map<std::string, std::string> DescMap;
  278. DescMap descs;
  279. // Capture the Vars instance on which we expect each function to operate
  280. typedef std::map<std::string, Vars*> VarsMap;
  281. VarsMap funcvars;
  282. // Required structure for Callables with requirements
  283. LLSD required;
  284. // Parameter names for freena(), freenb()
  285. LLSD params;
  286. // Full, partial defaults arrays for params for freena(), freenb()
  287. LLSD dft_array_full, dft_array_partial;
  288. // Start index of partial defaults arrays
  289. const LLSD::Integer partial_offset;
  290. // Full, partial defaults maps for params for freena(), freenb()
  291. LLSD dft_map_full, dft_map_partial;
  292. // Most of the above are indexed by "a" or "b". Useful to have an
  293. // array containing those strings for iterating.
  294. std::vector<LLSD::String> ab;
  295. lleventdispatcher_data():
  296. work("test dispatcher", "op"),
  297. // map {d=double, array=[3 elements]}
  298. required(LLSDMap("d", LLSD::Real(0))("array", LLSDArray(LLSD())(LLSD())(LLSD()))),
  299. // first several params are required, last couple optional
  300. partial_offset(3)
  301. {
  302. // This object is reconstructed for every test<n> method. But
  303. // clear global variables every time too.
  304. ::clear();
  305. const char* abs[] = { "a", "b" };
  306. ab.assign(boost::begin(abs), boost::end(abs));
  307. // Registration cases:
  308. // - (Callable | subclass const method | subclass non-const method |
  309. // non-subclass method) (with | without) required
  310. // - (Free function | static method | non-static method), (no | arbitrary) params,
  311. // array style
  312. // - (Free function | static method | non-static method), (no | arbitrary) params,
  313. // map style, (empty | partial | full) (array | map) defaults
  314. // - Map-style errors:
  315. // - (scalar | map) param names
  316. // - defaults scalar
  317. // - defaults array longer than params array
  318. // - defaults map with plural unknown param names
  319. // I hate to have to write things twice, because of having to keep
  320. // them consistent. If we had variadic functions, addf() would be
  321. // a variadic method, capturing the name and desc and passing them
  322. // plus "everything else" to work.add(). If I could return a pair
  323. // and use that pair as the first two args to work.add(), I'd do
  324. // that. But the best I can do with present C++ is to set two
  325. // instance variables as a side effect of addf(), and pass those
  326. // variables to each work.add() call. :-P
  327. /*------------------------- Callables --------------------------*/
  328. // Arbitrary Callable with/out required params
  329. addf("free1", "free1", &g);
  330. work.add(name, desc, free1);
  331. addf("free1_req", "free1", &g);
  332. work.add(name, desc, free1, required);
  333. // Subclass non-const method with/out required params
  334. addf("Dmethod1", "method1", NULL);
  335. work.add(name, desc, &Dispatcher::method1);
  336. addf("Dmethod1_req", "method1", NULL);
  337. work.add(name, desc, &Dispatcher::method1, required);
  338. // Subclass const method with/out required params
  339. addf("Dcmethod1", "cmethod1", NULL);
  340. work.add(name, desc, &Dispatcher::cmethod1);
  341. addf("Dcmethod1_req", "cmethod1", NULL);
  342. work.add(name, desc, &Dispatcher::cmethod1, required);
  343. // Non-subclass method with/out required params
  344. addf("method1", "method1", &v);
  345. work.add(name, desc, boost::bind(&Vars::method1, boost::ref(v), _1));
  346. addf("method1_req", "method1", &v);
  347. work.add(name, desc, boost::bind(&Vars::method1, boost::ref(v), _1), required);
  348. /*--------------- Arbitrary params, array style ----------------*/
  349. // (Free function | static method) with (no | arbitrary) params, array style
  350. addf("free0_array", "free0", &g);
  351. work.add(name, desc, free0);
  352. addf("freena_array", "freena", &g);
  353. work.add(name, desc, freena);
  354. addf("freenb_array", "freenb", &g);
  355. work.add(name, desc, freenb);
  356. addf("smethod0_array", "smethod0", &g);
  357. work.add(name, desc, &Vars::smethod0);
  358. addf("smethodna_array", "smethodna", &g);
  359. work.add(name, desc, &Vars::smethodna);
  360. addf("smethodnb_array", "smethodnb", &g);
  361. work.add(name, desc, &Vars::smethodnb);
  362. // Non-static method with (no | arbitrary) params, array style
  363. addf("method0_array", "method0", &v);
  364. work.add(name, desc, &Vars::method0, boost::lambda::var(v));
  365. addf("methodna_array", "methodna", &v);
  366. work.add(name, desc, &Vars::methodna, boost::lambda::var(v));
  367. addf("methodnb_array", "methodnb", &v);
  368. work.add(name, desc, &Vars::methodnb, boost::lambda::var(v));
  369. /*---------------- Arbitrary params, map style -----------------*/
  370. // We lay out each params list as an array, also each array of
  371. // default values we'll register. We'll zip these into
  372. // (param=value) maps. Why not define them as maps and just
  373. // extract the keys and values to arrays? Because that wouldn't
  374. // give us the right params-list order.
  375. // freena(), methodna(), cmethodna(), smethodna() all take same param list.
  376. // Same for freenb() et al.
  377. params = LLSDMap("a", LLSDArray("b")("i")("f")("d")("cp"))
  378. ("b", LLSDArray("s")("uuid")("date")("uri")("bin"));
  379. cout << "params:\n" << params << "\nparams[\"a\"]:\n" << params["a"] << "\nparams[\"b\"]:\n" << params["b"] << std::endl;
  380. // default LLSD::Binary value
  381. std::vector<U8> binary;
  382. for (size_t ix = 0, h = 0xaa; ix < 6; ++ix, h += 0x11)
  383. {
  384. binary.push_back(h);
  385. }
  386. // Full defaults arrays. We actually don't care what the LLUUID or
  387. // LLDate values are, as long as they're different from the
  388. // LLUUID() and LLDate() default values so inspect() will report
  389. // them.
  390. dft_array_full = LLSDMap("a", LLSDArray(true)(17)(3.14)(123456.78)("classic"))
  391. ("b", LLSDArray("string")
  392. (LLUUID::generateNewID())
  393. (LLDate::now())
  394. (LLURI("http://www.ietf.org/rfc/rfc3986.txt"))
  395. (binary));
  396. cout << "dft_array_full:\n" << dft_array_full << std::endl;
  397. // Partial defaults arrays.
  398. foreach(LLSD::String a, ab)
  399. {
  400. LLSD::Integer partition(std::min(partial_offset, dft_array_full[a].size()));
  401. dft_array_partial[a] =
  402. llsd_copy_array(dft_array_full[a].beginArray() + partition,
  403. dft_array_full[a].endArray());
  404. }
  405. cout << "dft_array_partial:\n" << dft_array_partial << std::endl;
  406. foreach(LLSD::String a, ab)
  407. {
  408. // Generate full defaults maps by zipping (params, dft_array_full).
  409. dft_map_full[a] = zipmap(params[a], dft_array_full[a]);
  410. // Generate partial defaults map by zipping alternate entries from
  411. // (params, dft_array_full). Part of the point of using map-style
  412. // defaults is to allow any subset of the target function's
  413. // parameters to be optional, not just the rightmost.
  414. for (LLSD::Integer ix = 0, ixend = params[a].size(); ix < ixend; ix += 2)
  415. {
  416. dft_map_partial[a][params[a][ix].asString()] = dft_array_full[a][ix];
  417. }
  418. }
  419. cout << "dft_map_full:\n" << dft_map_full << "\ndft_map_partial:\n" << dft_map_partial << '\n';
  420. // (Free function | static method) with (no | arbitrary) params,
  421. // map style, no (empty array) defaults
  422. addf("free0_map", "free0", &g);
  423. work.add(name, desc, free0, LLSD::emptyArray());
  424. addf("smethod0_map", "smethod0", &g);
  425. work.add(name, desc, &Vars::smethod0, LLSD::emptyArray());
  426. addf("freena_map_allreq", "freena", &g);
  427. work.add(name, desc, freena, params["a"]);
  428. addf("freenb_map_allreq", "freenb", &g);
  429. work.add(name, desc, freenb, params["b"]);
  430. addf("smethodna_map_allreq", "smethodna", &g);
  431. work.add(name, desc, &Vars::smethodna, params["a"]);
  432. addf("smethodnb_map_allreq", "smethodnb", &g);
  433. work.add(name, desc, &Vars::smethodnb, params["b"]);
  434. // Non-static method with (no | arbitrary) params, map style, no
  435. // (empty array) defaults
  436. addf("method0_map", "method0", &v);
  437. work.add(name, desc, &Vars::method0, var(v), LLSD::emptyArray());
  438. addf("methodna_map_allreq", "methodna", &v);
  439. work.add(name, desc, &Vars::methodna, var(v), params["a"]);
  440. addf("methodnb_map_allreq", "methodnb", &v);
  441. work.add(name, desc, &Vars::methodnb, var(v), params["b"]);
  442. // Except for the "more (array | map) defaults than params" error
  443. // cases, tested separately below, the (partial | full)(array |
  444. // map) defaults cases don't apply to no-params functions/methods.
  445. // So eliminate free0, smethod0, method0 from the cases below.
  446. // (Free function | static method) with arbitrary params, map
  447. // style, partial (array | map) defaults
  448. addf("freena_map_leftreq", "freena", &g);
  449. work.add(name, desc, freena, params["a"], dft_array_partial["a"]);
  450. addf("freenb_map_leftreq", "freenb", &g);
  451. work.add(name, desc, freenb, params["b"], dft_array_partial["b"]);
  452. addf("smethodna_map_leftreq", "smethodna", &g);
  453. work.add(name, desc, &Vars::smethodna, params["a"], dft_array_partial["a"]);
  454. addf("smethodnb_map_leftreq", "smethodnb", &g);
  455. work.add(name, desc, &Vars::smethodnb, params["b"], dft_array_partial["b"]);
  456. addf("freena_map_skipreq", "freena", &g);
  457. work.add(name, desc, freena, params["a"], dft_map_partial["a"]);
  458. addf("freenb_map_skipreq", "freenb", &g);
  459. work.add(name, desc, freenb, params["b"], dft_map_partial["b"]);
  460. addf("smethodna_map_skipreq", "smethodna", &g);
  461. work.add(name, desc, &Vars::smethodna, params["a"], dft_map_partial["a"]);
  462. addf("smethodnb_map_skipreq", "smethodnb", &g);
  463. work.add(name, desc, &Vars::smethodnb, params["b"], dft_map_partial["b"]);
  464. // Non-static method with arbitrary params, map style, partial
  465. // (array | map) defaults
  466. addf("methodna_map_leftreq", "methodna", &v);
  467. work.add(name, desc, &Vars::methodna, var(v), params["a"], dft_array_partial["a"]);
  468. addf("methodnb_map_leftreq", "methodnb", &v);
  469. work.add(name, desc, &Vars::methodnb, var(v), params["b"], dft_array_partial["b"]);
  470. addf("methodna_map_skipreq", "methodna", &v);
  471. work.add(name, desc, &Vars::methodna, var(v), params["a"], dft_map_partial["a"]);
  472. addf("methodnb_map_skipreq", "methodnb", &v);
  473. work.add(name, desc, &Vars::methodnb, var(v), params["b"], dft_map_partial["b"]);
  474. // (Free function | static method) with arbitrary params, map
  475. // style, full (array | map) defaults
  476. addf("freena_map_adft", "freena", &g);
  477. work.add(name, desc, freena, params["a"], dft_array_full["a"]);
  478. addf("freenb_map_adft", "freenb", &g);
  479. work.add(name, desc, freenb, params["b"], dft_array_full["b"]);
  480. addf("smethodna_map_adft", "smethodna", &g);
  481. work.add(name, desc, &Vars::smethodna, params["a"], dft_array_full["a"]);
  482. addf("smethodnb_map_adft", "smethodnb", &g);
  483. work.add(name, desc, &Vars::smethodnb, params["b"], dft_array_full["b"]);
  484. addf("freena_map_mdft", "freena", &g);
  485. work.add(name, desc, freena, params["a"], dft_map_full["a"]);
  486. addf("freenb_map_mdft", "freenb", &g);
  487. work.add(name, desc, freenb, params["b"], dft_map_full["b"]);
  488. addf("smethodna_map_mdft", "smethodna", &g);
  489. work.add(name, desc, &Vars::smethodna, params["a"], dft_map_full["a"]);
  490. addf("smethodnb_map_mdft", "smethodnb", &g);
  491. work.add(name, desc, &Vars::smethodnb, params["b"], dft_map_full["b"]);
  492. // Non-static method with arbitrary params, map style, full
  493. // (array | map) defaults
  494. addf("methodna_map_adft", "methodna", &v);
  495. work.add(name, desc, &Vars::methodna, var(v), params["a"], dft_array_full["a"]);
  496. addf("methodnb_map_adft", "methodnb", &v);
  497. work.add(name, desc, &Vars::methodnb, var(v), params["b"], dft_array_full["b"]);
  498. addf("methodna_map_mdft", "methodna", &v);
  499. work.add(name, desc, &Vars::methodna, var(v), params["a"], dft_map_full["a"]);
  500. addf("methodnb_map_mdft", "methodnb", &v);
  501. work.add(name, desc, &Vars::methodnb, var(v), params["b"], dft_map_full["b"]);
  502. // All the above are expected to succeed, and are setup for the
  503. // tests to follow. Registration error cases are exercised as
  504. // tests rather than as test setup.
  505. }
  506. void addf(const std::string& n, const std::string& d, Vars* v)
  507. {
  508. // This method is to capture in our own DescMap the name and
  509. // description of every registered function, for metadata query
  510. // testing.
  511. descs[n] = d;
  512. // Also capture the Vars instance on which each function should operate.
  513. funcvars[n] = v;
  514. // See constructor for rationale for setting these instance vars.
  515. this->name = n;
  516. this->desc = d;
  517. }
  518. void verify_descs()
  519. {
  520. // Copy descs to a temp map of same type.
  521. DescMap forgotten(descs.begin(), descs.end());
  522. // LLEventDispatcher intentionally provides only const_iterator:
  523. // since dereferencing that iterator generates values on the fly,
  524. // it's meaningless to have a modifiable iterator. But since our
  525. // 'work' object isn't const, by default BOOST_FOREACH() wants to
  526. // use non-const iterators. Persuade it to use the const_iterator.
  527. foreach(LLEventDispatcher::NameDesc nd, const_cast<const Dispatcher&>(work))
  528. {
  529. DescMap::iterator found = forgotten.find(nd.first);
  530. ensure(STRINGIZE("LLEventDispatcher records function '" << nd.first
  531. << "' we didn't enter"),
  532. found != forgotten.end());
  533. ensure_equals(STRINGIZE("LLEventDispatcher desc '" << nd.second <<
  534. "' doesn't match what we entered: '" << found->second << "'"),
  535. nd.second, found->second);
  536. // found in our map the name from LLEventDispatcher, good, erase
  537. // our map entry
  538. forgotten.erase(found);
  539. }
  540. if (! forgotten.empty())
  541. {
  542. std::ostringstream out;
  543. out << "LLEventDispatcher failed to report";
  544. const char* delim = ": ";
  545. foreach(const DescMap::value_type& fme, forgotten)
  546. {
  547. out << delim << fme.first;
  548. delim = ", ";
  549. }
  550. ensure(out.str(), false);
  551. }
  552. }
  553. Vars* varsfor(const std::string& name)
  554. {
  555. VarsMap::const_iterator found = funcvars.find(name);
  556. ensure(STRINGIZE("No Vars* for " << name), found != funcvars.end());
  557. ensure(STRINGIZE("NULL Vars* for " << name), found->second);
  558. return found->second;
  559. }
  560. void ensure_has(const std::string& outer, const std::string& inner)
  561. {
  562. ensure(STRINGIZE("'" << outer << "' does not contain '" << inner << "'").c_str(),
  563. outer.find(inner) != std::string::npos);
  564. }
  565. void call_exc(const std::string& func, const LLSD& args, const std::string& exc_frag)
  566. {
  567. std::string threw;
  568. try
  569. {
  570. work(func, args);
  571. }
  572. catch (const std::runtime_error& e)
  573. {
  574. cout << "*** " << e.what() << '\n';
  575. threw = e.what();
  576. }
  577. ensure_has(threw, exc_frag);
  578. }
  579. LLSD getMetadata(const std::string& name)
  580. {
  581. LLSD meta(work.getMetadata(name));
  582. ensure(STRINGIZE("No metadata for " << name), meta.isDefined());
  583. return meta;
  584. }
  585. // From two related LLSD arrays, e.g. a param-names array and a values
  586. // array, zip them together into an LLSD map.
  587. LLSD zipmap(const LLSD& keys, const LLSD& values)
  588. {
  589. LLSD map;
  590. for (LLSD::Integer i = 0, iend = keys.size(); i < iend; ++i)
  591. {
  592. // Have to select asString() since you can index an LLSD
  593. // object with either String or Integer.
  594. map[keys[i].asString()] = values[i];
  595. }
  596. return map;
  597. }
  598. // If I call this ensure_equals(), it blocks visibility of all other
  599. // ensure_equals() overloads. Normally I could say 'using
  600. // baseclass::ensure_equals;' and fix that, but I don't know what the
  601. // base class is!
  602. void ensure_llsd(const std::string& msg, const LLSD& actual, const LLSD& expected, U32 bits)
  603. {
  604. std::ostringstream out;
  605. if (! msg.empty())
  606. {
  607. out << msg << ": ";
  608. }
  609. out << "expected " << expected << ", actual " << actual;
  610. ensure(out.str(), llsd_equals(actual, expected, bits));
  611. }
  612. void ensure_llsd(const LLSD& actual, const LLSD& expected, U32 bits)
  613. {
  614. ensure_llsd("", actual, expected, bits);
  615. }
  616. };
  617. typedef test_group<lleventdispatcher_data> lleventdispatcher_group;
  618. typedef lleventdispatcher_group::object object;
  619. lleventdispatcher_group lleventdispatchergrp("lleventdispatcher");
  620. // Call cases:
  621. // - (try_call | call) (explicit name | event key) (real | bogus) name
  622. // - Callable with args that (do | do not) match required
  623. // - (Free function | non-static method), no args, (array | map) style
  624. // - (Free function | non-static method), arbitrary args,
  625. // (array style with (scalar | map) | map style with scalar)
  626. // - (Free function | non-static method), arbitrary args, array style with
  627. // array (too short | too long | just right)
  628. // [trap LL_WARNS for too-long case?]
  629. // - (Free function | non-static method), arbitrary args, map style with
  630. // (array | map) (all | too many | holes (with | without) defaults)
  631. // - const char* param gets ("" | NULL)
  632. // Query cases:
  633. // - Iterate over all (with | without) remove()
  634. // - getDispatchKey()
  635. // - Callable style (with | without) required
  636. // - (Free function | non-static method), array style, (no | arbitrary) params
  637. // - (Free function | non-static method), map style, (no | arbitrary) params,
  638. // (empty | full | partial (array | map)) defaults
  639. template<> template<>
  640. void object::test<1>()
  641. {
  642. set_test_name("map-style registration with non-array params");
  643. // Pass "param names" as scalar or as map
  644. LLSD attempts(LLSDArray(17)(LLSDMap("pi", 3.14)("two", 2)));
  645. foreach(LLSD ae, inArray(attempts))
  646. {
  647. std::string threw;
  648. try
  649. {
  650. work.add("freena_err", "freena", freena, ae);
  651. }
  652. catch (const std::exception& e)
  653. {
  654. threw = e.what();
  655. }
  656. ensure_has(threw, "must be an array");
  657. }
  658. }
  659. template<> template<>
  660. void object::test<2>()
  661. {
  662. set_test_name("map-style registration with badly-formed defaults");
  663. std::string threw;
  664. try
  665. {
  666. work.add("freena_err", "freena", freena, LLSDArray("a")("b"), 17);
  667. }
  668. catch (const std::exception& e)
  669. {
  670. threw = e.what();
  671. }
  672. ensure_has(threw, "must be a map or an array");
  673. }
  674. template<> template<>
  675. void object::test<3>()
  676. {
  677. set_test_name("map-style registration with too many array defaults");
  678. std::string threw;
  679. try
  680. {
  681. work.add("freena_err", "freena", freena,
  682. LLSDArray("a")("b"),
  683. LLSDArray(17)(0.9)("gack"));
  684. }
  685. catch (const std::exception& e)
  686. {
  687. threw = e.what();
  688. }
  689. ensure_has(threw, "shorter than");
  690. }
  691. template<> template<>
  692. void object::test<4>()
  693. {
  694. set_test_name("map-style registration with too many map defaults");
  695. std::string threw;
  696. try
  697. {
  698. work.add("freena_err", "freena", freena,
  699. LLSDArray("a")("b"),
  700. LLSDMap("b", 17)("foo", 3.14)("bar", "sinister"));
  701. }
  702. catch (const std::exception& e)
  703. {
  704. threw = e.what();
  705. }
  706. ensure_has(threw, "nonexistent params");
  707. ensure_has(threw, "foo");
  708. ensure_has(threw, "bar");
  709. }
  710. template<> template<>
  711. void object::test<5>()
  712. {
  713. set_test_name("query all");
  714. verify_descs();
  715. }
  716. template<> template<>
  717. void object::test<6>()
  718. {
  719. set_test_name("query all with remove()");
  720. ensure("remove('bogus') returned true", ! work.remove("bogus"));
  721. ensure("remove('real') returned false", work.remove("free1"));
  722. // Of course, remove that from 'descs' too...
  723. descs.erase("free1");
  724. verify_descs();
  725. }
  726. template<> template<>
  727. void object::test<7>()
  728. {
  729. set_test_name("getDispatchKey()");
  730. ensure_equals(work.getDispatchKey(), "op");
  731. }
  732. template<> template<>
  733. void object::test<8>()
  734. {
  735. set_test_name("query Callables with/out required params");
  736. LLSD names(LLSDArray("free1")("Dmethod1")("Dcmethod1")("method1"));
  737. foreach(LLSD nm, inArray(names))
  738. {
  739. LLSD metadata(getMetadata(nm));
  740. ensure_equals("name mismatch", metadata["name"], nm);
  741. ensure_equals(metadata["desc"].asString(), descs[nm]);
  742. ensure("should not have required structure", metadata["required"].isUndefined());
  743. ensure("should not have optional", metadata["optional"].isUndefined());
  744. std::string name_req(nm.asString() + "_req");
  745. metadata = getMetadata(name_req);
  746. ensure_equals(metadata["name"].asString(), name_req);
  747. ensure_equals(metadata["desc"].asString(), descs[name_req]);
  748. ensure_equals("required mismatch", required, metadata["required"]);
  749. ensure("should not have optional", metadata["optional"].isUndefined());
  750. }
  751. }
  752. template<> template<>
  753. void object::test<9>()
  754. {
  755. set_test_name("query array-style functions/methods");
  756. // Associate each registered name with expected arity.
  757. LLSD expected(LLSDArray
  758. (LLSDArray
  759. (0)(LLSDArray("free0_array")("smethod0_array")("method0_array")))
  760. (LLSDArray
  761. (5)(LLSDArray("freena_array")("smethodna_array")("methodna_array")))
  762. (LLSDArray
  763. (5)(LLSDArray("freenb_array")("smethodnb_array")("methodnb_array"))));
  764. foreach(LLSD ae, inArray(expected))
  765. {
  766. LLSD::Integer arity(ae[0].asInteger());
  767. LLSD names(ae[1]);
  768. LLSD req(LLSD::emptyArray());
  769. if (arity)
  770. req[arity - 1] = LLSD();
  771. foreach(LLSD nm, inArray(names))
  772. {
  773. LLSD metadata(getMetadata(nm));
  774. ensure_equals("name mismatch", metadata["name"], nm);
  775. ensure_equals(metadata["desc"].asString(), descs[nm]);
  776. ensure_equals(STRINGIZE("mismatched required for " << nm.asString()),
  777. metadata["required"], req);
  778. ensure("should not have optional", metadata["optional"].isUndefined());
  779. }
  780. }
  781. }
  782. template<> template<>
  783. void object::test<10>()
  784. {
  785. set_test_name("query map-style no-params functions/methods");
  786. // - (Free function | non-static method), map style, no params (ergo
  787. // no defaults)
  788. LLSD names(LLSDArray("free0_map")("smethod0_map")("method0_map"));
  789. foreach(LLSD nm, inArray(names))
  790. {
  791. LLSD metadata(getMetadata(nm));
  792. ensure_equals("name mismatch", metadata["name"], nm);
  793. ensure_equals(metadata["desc"].asString(), descs[nm]);
  794. ensure("should not have required",
  795. (metadata["required"].isUndefined() || metadata["required"].size() == 0));
  796. ensure("should not have optional", metadata["optional"].isUndefined());
  797. }
  798. }
  799. template<> template<>
  800. void object::test<11>()
  801. {
  802. set_test_name("query map-style arbitrary-params functions/methods: "
  803. "full array defaults vs. full map defaults");
  804. // With functions registered with no defaults ("_allreq" suffixes),
  805. // there is of course no difference between array defaults and map
  806. // defaults. (We don't even bother registering with LLSD::emptyArray()
  807. // vs. LLSD::emptyMap().) With functions registered with all defaults,
  808. // there should (!) be no difference beween array defaults and map
  809. // defaults. Verify, so we can ignore the distinction for all other
  810. // tests.
  811. LLSD equivalences(LLSDArray
  812. (LLSDArray("freena_map_adft")("freena_map_mdft"))
  813. (LLSDArray("freenb_map_adft")("freenb_map_mdft"))
  814. (LLSDArray("smethodna_map_adft")("smethodna_map_mdft"))
  815. (LLSDArray("smethodnb_map_adft")("smethodnb_map_mdft"))
  816. (LLSDArray("methodna_map_adft")("methodna_map_mdft"))
  817. (LLSDArray("methodnb_map_adft")("methodnb_map_mdft")));
  818. foreach(LLSD eq, inArray(equivalences))
  819. {
  820. LLSD adft(eq[0]);
  821. LLSD mdft(eq[1]);
  822. // We can't just compare the results of the two getMetadata()
  823. // calls, because they contain ["name"], which are different. So
  824. // capture them, verify that each ["name"] is as expected, then
  825. // remove for comparing the rest.
  826. LLSD ameta(getMetadata(adft));
  827. LLSD mmeta(getMetadata(mdft));
  828. ensure_equals("adft name", adft, ameta["name"]);
  829. ensure_equals("mdft name", mdft, mmeta["name"]);
  830. ameta.erase("name");
  831. mmeta.erase("name");
  832. ensure_equals(STRINGIZE("metadata for " << adft.asString()
  833. << " vs. " << mdft.asString()),
  834. ameta, mmeta);
  835. }
  836. }
  837. template<> template<>
  838. void object::test<12>()
  839. {
  840. set_test_name("query map-style arbitrary-params functions/methods");
  841. // - (Free function | non-static method), map style, arbitrary params,
  842. // (empty | full | partial (array | map)) defaults
  843. // Generate maps containing all parameter names for cases in which all
  844. // params are required. Also maps containing left requirements for
  845. // partial defaults arrays. Also defaults maps from defaults arrays.
  846. LLSD allreq, leftreq, rightdft;
  847. foreach(LLSD::String a, ab)
  848. {
  849. // The map in which all params are required uses params[a] as
  850. // keys, with all isUndefined() as values. We can accomplish that
  851. // by passing zipmap() an empty values array.
  852. allreq[a] = zipmap(params[a], LLSD::emptyArray());
  853. // Same for leftreq, save that we use the subset of the params not
  854. // supplied by dft_array_partial[a].
  855. LLSD::Integer partition(params[a].size() - dft_array_partial[a].size());
  856. leftreq[a] = zipmap(llsd_copy_array(params[a].beginArray(),
  857. params[a].beginArray() + partition),
  858. LLSD::emptyArray());
  859. // Generate map pairing dft_array_partial[a] values with their
  860. // param names.
  861. rightdft[a] = zipmap(llsd_copy_array(params[a].beginArray() + partition,
  862. params[a].endArray()),
  863. dft_array_partial[a]);
  864. }
  865. cout << "allreq:\n" << allreq << "\nleftreq:\n" << leftreq << "\nrightdft:\n" << rightdft << std::endl;
  866. // Generate maps containing parameter names not provided by the
  867. // dft_map_partial maps.
  868. LLSD skipreq(allreq);
  869. foreach(LLSD::String a, ab)
  870. {
  871. foreach(const MapEntry& me, inMap(dft_map_partial[a]))
  872. {
  873. skipreq[a].erase(me.first);
  874. }
  875. }
  876. cout << "skipreq:\n" << skipreq << std::endl;
  877. LLSD groups(LLSDArray // array of groups
  878. (LLSDArray // group
  879. (LLSDArray("freena_map_allreq")("smethodna_map_allreq")("methodna_map_allreq"))
  880. (LLSDArray(allreq["a"])(LLSD()))) // required, optional
  881. (LLSDArray // group
  882. (LLSDArray("freenb_map_allreq")("smethodnb_map_allreq")("methodnb_map_allreq"))
  883. (LLSDArray(allreq["b"])(LLSD()))) // required, optional
  884. (LLSDArray // group
  885. (LLSDArray("freena_map_leftreq")("smethodna_map_leftreq")("methodna_map_leftreq"))
  886. (LLSDArray(leftreq["a"])(rightdft["a"]))) // required, optional
  887. (LLSDArray // group
  888. (LLSDArray("freenb_map_leftreq")("smethodnb_map_leftreq")("methodnb_map_leftreq"))
  889. (LLSDArray(leftreq["b"])(rightdft["b"]))) // required, optional
  890. (LLSDArray // group
  891. (LLSDArray("freena_map_skipreq")("smethodna_map_skipreq")("methodna_map_skipreq"))
  892. (LLSDArray(skipreq["a"])(dft_map_partial["a"]))) // required, optional
  893. (LLSDArray // group
  894. (LLSDArray("freenb_map_skipreq")("smethodnb_map_skipreq")("methodnb_map_skipreq"))
  895. (LLSDArray(skipreq["b"])(dft_map_partial["b"]))) // required, optional
  896. // We only need mention the full-map-defaults ("_mdft" suffix)
  897. // registrations, having established their equivalence with the
  898. // full-array-defaults ("_adft" suffix) registrations in another test.
  899. (LLSDArray // group
  900. (LLSDArray("freena_map_mdft")("smethodna_map_mdft")("methodna_map_mdft"))
  901. (LLSDArray(LLSD::emptyMap())(dft_map_full["a"]))) // required, optional
  902. (LLSDArray // group
  903. (LLSDArray("freenb_map_mdft")("smethodnb_map_mdft")("methodnb_map_mdft"))
  904. (LLSDArray(LLSD::emptyMap())(dft_map_full["b"])))); // required, optional
  905. foreach(LLSD grp, inArray(groups))
  906. {
  907. // Internal structure of each group in 'groups':
  908. LLSD names(grp[0]);
  909. LLSD required(grp[1][0]);
  910. LLSD optional(grp[1][1]);
  911. cout << "For " << names << ",\n" << "required:\n" << required << "\noptional:\n" << optional << std::endl;
  912. // Loop through 'names'
  913. foreach(LLSD nm, inArray(names))
  914. {
  915. LLSD metadata(getMetadata(nm));
  916. ensure_equals("name mismatch", metadata["name"], nm);
  917. ensure_equals(nm.asString(), metadata["desc"].asString(), descs[nm]);
  918. ensure_equals(STRINGIZE(nm << " required mismatch"),
  919. metadata["required"], required);
  920. ensure_equals(STRINGIZE(nm << " optional mismatch"),
  921. metadata["optional"], optional);
  922. }
  923. }
  924. }
  925. template<> template<>
  926. void object::test<13>()
  927. {
  928. set_test_name("try_call()");
  929. ensure("try_call(bogus name, LLSD()) returned true", ! work.try_call("freek", LLSD()));
  930. ensure("try_call(bogus name) returned true", ! work.try_call(LLSDMap("op", "freek")));
  931. ensure("try_call(real name, LLSD()) returned false", work.try_call("free0_array", LLSD()));
  932. ensure("try_call(real name) returned false", work.try_call(LLSDMap("op", "free0_map")));
  933. }
  934. template<> template<>
  935. void object::test<14>()
  936. {
  937. set_test_name("call with bad name");
  938. call_exc("freek", LLSD(), "not found");
  939. // We don't have a comparable helper function for the one-arg
  940. // operator() method, and it's not worth building one just for this
  941. // case. Write it out.
  942. std::string threw;
  943. try
  944. {
  945. work(LLSDMap("op", "freek"));
  946. }
  947. catch (const std::runtime_error& e)
  948. {
  949. cout << "*** " << e.what() << "\n";
  950. threw = e.what();
  951. }
  952. ensure_has(threw, "bad");
  953. ensure_has(threw, "op");
  954. ensure_has(threw, "freek");
  955. }
  956. template<> template<>
  957. void object::test<15>()
  958. {
  959. set_test_name("call with event key");
  960. // We don't need a separate test for operator()(string, LLSD) with
  961. // valid name, because all the rest of the tests exercise that case.
  962. // The one we don't exercise elsewhere is operator()(LLSD) with valid
  963. // name, so here it is.
  964. work(LLSDMap("op", "free0_map"));
  965. ensure_equals(g.i, 17);
  966. }
  967. // Cannot be defined inside function body... remind me again why we use C++... :-P
  968. struct CallablesTriple
  969. {
  970. std::string name, name_req;
  971. LLSD& llsd;
  972. };
  973. template<> template<>
  974. void object::test<16>()
  975. {
  976. set_test_name("call Callables");
  977. CallablesTriple tests[] =
  978. {
  979. { "free1", "free1_req", g.llsd },
  980. { "Dmethod1", "Dmethod1_req", work.llsd },
  981. { "Dcmethod1", "Dcmethod1_req", work.llsd },
  982. { "method1", "method1_req", v.llsd }
  983. };
  984. // Arbitrary LLSD value that we should be able to pass to Callables
  985. // without 'required', but should not be able to pass to Callables
  986. // with 'required'.
  987. LLSD answer(42);
  988. // LLSD value matching 'required' according to llsd_matches() rules.
  989. LLSD matching(LLSDMap("d", 3.14)("array", LLSDArray("answer")(true)(answer)));
  990. // Okay, walk through 'tests'.
  991. foreach(const CallablesTriple& tr, tests)
  992. {
  993. // Should be able to pass 'answer' to Callables registered
  994. // without 'required'.
  995. work(tr.name, answer);
  996. ensure_equals("answer mismatch", tr.llsd, answer);
  997. // Should NOT be able to pass 'answer' to Callables registered
  998. // with 'required'.
  999. call_exc(tr.name_req, answer, "bad request");
  1000. // But SHOULD be able to pass 'matching' to Callables registered
  1001. // with 'required'.
  1002. work(tr.name_req, matching);
  1003. ensure_equals("matching mismatch", tr.llsd, matching);
  1004. }
  1005. }
  1006. template<> template<>
  1007. void object::test<17>()
  1008. {
  1009. set_test_name("passing wrong args to (map | array)-style registrations");
  1010. // Pass scalar/map to array-style functions, scalar/array to map-style
  1011. // functions. As that validation happens well before we engage the
  1012. // argument magic, it seems pointless to repeat this with every
  1013. // variation: (free function | non-static method), (no | arbitrary)
  1014. // args. We should only need to engage it for one map-style
  1015. // registration and one array-style registration.
  1016. std::string array_exc("needs an args array");
  1017. call_exc("free0_array", 17, array_exc);
  1018. call_exc("free0_array", LLSDMap("pi", 3.14), array_exc);
  1019. std::string map_exc("needs a map");
  1020. call_exc("free0_map", 17, map_exc);
  1021. // Passing an array to a map-style function works now! No longer an
  1022. // error case!
  1023. // call_exc("free0_map", LLSDArray("a")("b"), map_exc);
  1024. }
  1025. template<> template<>
  1026. void object::test<18>()
  1027. {
  1028. set_test_name("call no-args functions");
  1029. LLSD names(LLSDArray
  1030. ("free0_array")("free0_map")
  1031. ("smethod0_array")("smethod0_map")
  1032. ("method0_array")("method0_map"));
  1033. foreach(LLSD name, inArray(names))
  1034. {
  1035. // Look up the Vars instance for this function.
  1036. Vars* vars(varsfor(name));
  1037. // Both the global and stack Vars instances are automatically
  1038. // cleared at the start of each test<n> method. But since we're
  1039. // calling these things several different times in the same
  1040. // test<n> method, manually reset the Vars between each.
  1041. *vars = Vars();
  1042. ensure_equals(vars->i, 0);
  1043. // call function with empty array (or LLSD(), should be equivalent)
  1044. work(name, LLSD());
  1045. ensure_equals(vars->i, 17);
  1046. }
  1047. }
  1048. // Break out this data because we use it in a couple different tests.
  1049. LLSD array_funcs(LLSDArray
  1050. (LLSDMap("a", "freena_array") ("b", "freenb_array"))
  1051. (LLSDMap("a", "smethodna_array")("b", "smethodnb_array"))
  1052. (LLSDMap("a", "methodna_array") ("b", "methodnb_array")));
  1053. template<> template<>
  1054. void object::test<19>()
  1055. {
  1056. set_test_name("call array-style functions with too-short arrays");
  1057. // Could have two different too-short arrays, one for *na and one for
  1058. // *nb, but since they both take 5 params...
  1059. LLSD tooshort(LLSDArray("this")("array")("too")("short"));
  1060. foreach(const LLSD& funcsab, inArray(array_funcs))
  1061. {
  1062. foreach(const llsd::MapEntry& e, inMap(funcsab))
  1063. {
  1064. call_exc(e.second, tooshort, "requires more arguments");
  1065. }
  1066. }
  1067. }
  1068. template<> template<>
  1069. void object::test<20>()
  1070. {
  1071. set_test_name("call array-style functions with (just right | too long) arrays");
  1072. std::vector<U8> binary;
  1073. for (size_t h(0x01), i(0); i < 5; h+= 0x22, ++i)
  1074. {
  1075. binary.push_back(h);
  1076. }
  1077. LLSD args(LLSDMap("a", LLSDArray(true)(17)(3.14)(123.456)("char*"))
  1078. ("b", LLSDArray("string")
  1079. (LLUUID("01234567-89ab-cdef-0123-456789abcdef"))
  1080. (LLDate("2011-02-03T15:07:00Z"))
  1081. (LLURI("http://secondlife.com"))
  1082. (binary)));
  1083. LLSD argsplus(args);
  1084. argsplus["a"].append("bogus");
  1085. argsplus["b"].append("bogus");
  1086. LLSD expect;
  1087. foreach(LLSD::String a, ab)
  1088. {
  1089. expect[a] = zipmap(params[a], args[a]);
  1090. }
  1091. // Adjust expect["a"]["cp"] for special Vars::cp treatment.
  1092. expect["a"]["cp"] = std::string("'") + expect["a"]["cp"].asString() + "'";
  1093. cout << "expect: " << expect << '\n';
  1094. // Use substantially the same logic for args and argsplus
  1095. LLSD argsarrays(LLSDArray(args)(argsplus));
  1096. // So i==0 selects 'args', i==1 selects argsplus
  1097. for (LLSD::Integer i(0), iend(argsarrays.size()); i < iend; ++i)
  1098. {
  1099. foreach(const LLSD& funcsab, inArray(array_funcs))
  1100. {
  1101. foreach(LLSD::String a, ab)
  1102. {
  1103. // Reset the Vars instance before each call
  1104. Vars* vars(varsfor(funcsab[a]));
  1105. *vars = Vars();
  1106. work(funcsab[a], argsarrays[i][a]);
  1107. ensure_llsd(STRINGIZE(funcsab[a].asString() <<
  1108. ": expect[\"" << a << "\"] mismatch"),
  1109. vars->inspect(), expect[a], 7); // 7 bits ~= 2 decimal digits
  1110. // TODO: in the i==1 or argsplus case, intercept LL_WARNS
  1111. // output? Even without that, using argsplus verifies that
  1112. // passing too many args isn't fatal; it works -- but
  1113. // would be nice to notice the warning too.
  1114. }
  1115. }
  1116. }
  1117. }
  1118. template<> template<>
  1119. void object::test<21>()
  1120. {
  1121. set_test_name("verify that passing LLSD() to const char* sends NULL");
  1122. ensure_equals("Vars::cp init", v.cp, "");
  1123. work("methodna_map_mdft", LLSDMap("cp", LLSD()));
  1124. ensure_equals("passing LLSD()", v.cp, "NULL");
  1125. work("methodna_map_mdft", LLSDMap("cp", ""));
  1126. ensure_equals("passing \"\"", v.cp, "''");
  1127. work("methodna_map_mdft", LLSDMap("cp", "non-NULL"));
  1128. ensure_equals("passing \"non-NULL\"", v.cp, "'non-NULL'");
  1129. }
  1130. template<> template<>
  1131. void object::test<22>()
  1132. {
  1133. set_test_name("call map-style functions with (full | oversized) (arrays | maps)");
  1134. const char binary[] = "\x99\x88\x77\x66\x55";
  1135. LLSD array_full(LLSDMap
  1136. ("a", LLSDArray(false)(255)(98.6)(1024.5)("pointer"))
  1137. ("b", LLSDArray("object")(LLUUID::generateNewID())(LLDate::now())(LLURI("http://wiki.lindenlab.com/wiki"))(LLSD::Binary(boost::begin(binary), boost::end(binary)))));
  1138. LLSD array_overfull(array_full);
  1139. foreach(LLSD::String a, ab)
  1140. {
  1141. array_overfull[a].append("bogus");
  1142. }
  1143. cout << "array_full: " << array_full << "\narray_overfull: " << array_overfull << std::endl;
  1144. // We rather hope that LLDate::now() will generate a timestamp
  1145. // distinct from the one it generated in the constructor, moments ago.
  1146. ensure_not_equals("Timestamps too close",
  1147. array_full["b"][2].asDate(), dft_array_full["b"][2].asDate());
  1148. // We /insist/ that LLUUID::generateNewID() do so.
  1149. ensure_not_equals("UUID collision",
  1150. array_full["b"][1].asUUID(), dft_array_full["b"][1].asUUID());
  1151. LLSD map_full, map_overfull;
  1152. foreach(LLSD::String a, ab)
  1153. {
  1154. map_full[a] = zipmap(params[a], array_full[a]);
  1155. map_overfull[a] = map_full[a];
  1156. map_overfull[a]["extra"] = "ignore";
  1157. }
  1158. cout << "map_full: " << map_full << "\nmap_overfull: " << map_overfull << std::endl;
  1159. LLSD expect(map_full);
  1160. // Twiddle the const char* param.
  1161. expect["a"]["cp"] = std::string("'") + expect["a"]["cp"].asString() + "'";
  1162. // Another adjustment. For each data type, we're trying to distinguish
  1163. // three values: the Vars member's initial value (member wasn't
  1164. // stored; control never reached the set function), the registered
  1165. // default param value from dft_array_full, and the array_full value
  1166. // in this test. But bool can only distinguish two values. In this
  1167. // case, we want to differentiate the local array_full value from the
  1168. // dft_array_full value, so we use 'false'. However, that means
  1169. // Vars::inspect() doesn't differentiate it from the initial value,
  1170. // so won't bother returning it. Predict that behavior to match the
  1171. // LLSD values.
  1172. expect["a"].erase("b");
  1173. cout << "expect: " << expect << std::endl;
  1174. // For this test, calling functions registered with different sets of
  1175. // parameter defaults should make NO DIFFERENCE WHATSOEVER. Every call
  1176. // should pass all params.
  1177. LLSD names(LLSDMap
  1178. ("a", LLSDArray
  1179. ("freena_map_allreq") ("smethodna_map_allreq") ("methodna_map_allreq")
  1180. ("freena_map_leftreq")("smethodna_map_leftreq")("methodna_map_leftreq")
  1181. ("freena_map_skipreq")("smethodna_map_skipreq")("methodna_map_skipreq")
  1182. ("freena_map_adft") ("smethodna_map_adft") ("methodna_map_adft")
  1183. ("freena_map_mdft") ("smethodna_map_mdft") ("methodna_map_mdft"))
  1184. ("b", LLSDArray
  1185. ("freenb_map_allreq") ("smethodnb_map_allreq") ("methodnb_map_allreq")
  1186. ("freenb_map_leftreq")("smethodnb_map_leftreq")("methodnb_map_leftreq")
  1187. ("freenb_map_skipreq")("smethodnb_map_skipreq")("methodnb_map_skipreq")
  1188. ("freenb_map_adft") ("smethodnb_map_adft") ("methodnb_map_adft")
  1189. ("freenb_map_mdft") ("smethodnb_map_mdft") ("methodnb_map_mdft")));
  1190. // Treat (full | overfull) (array | map) the same.
  1191. LLSD argssets(LLSDArray(array_full)(array_overfull)(map_full)(map_overfull));
  1192. foreach(const LLSD& args, inArray(argssets))
  1193. {
  1194. foreach(LLSD::String a, ab)
  1195. {
  1196. foreach(LLSD::String name, inArray(names[a]))
  1197. {
  1198. // Reset the Vars instance
  1199. Vars* vars(varsfor(name));
  1200. *vars = Vars();
  1201. work(name, args[a]);
  1202. ensure_llsd(STRINGIZE(name << ": expect[\"" << a << "\"] mismatch"),
  1203. vars->inspect(), expect[a], 7); // 7 bits, 2 decimal digits
  1204. // intercept LL_WARNS for the two overfull cases?
  1205. }
  1206. }
  1207. }
  1208. }
  1209. } // namespace tut