PageRenderTime 154ms CodeModel.GetById 27ms app.highlight 115ms RepoModel.GetById 1ms 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

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file