diff options
Diffstat (limited to 'indra/llcommon/tests')
| -rw-r--r-- | indra/llcommon/tests/lleventdispatcher_test.cpp | 1023 | 
1 files changed, 780 insertions, 243 deletions
| diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp index a1d7cf9ead..35a3188507 100644 --- a/indra/llcommon/tests/lleventdispatcher_test.cpp +++ b/indra/llcommon/tests/lleventdispatcher_test.cpp @@ -23,30 +23,6 @@  #include "stringize.h"  #include "tests/wrapllerrs.h" -// http://www.boost.org/doc/libs/1_45_0/libs/function_types/example/interpreter.hpp -// downloaded 2011-01-20 by NRG and adapted with example usage -// (C) Copyright Tobias Schwinger -// -// Use modification and distribution are subject to the boost Software License, -// Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt). - -//------------------------------------------------------------------------------ -// -// This example implements a simple batch-style dispatcher that is capable of -// calling functions previously registered with it. The parameter types of the -// functions are used to control the parsing of the input. -// -// Implementation description -// ========================== -// -// When a function is registered, an 'invoker' template is instantiated with -// the function's type. The 'invoker' fetches a value from the 'arg_source' -// for each parameter of the function into a tuple and finally invokes the the -// function with these values as arguments. The invoker's entrypoint, which -// is a function of the callable builtin that describes the function to call and -// a reference to the 'arg_source', is partially bound to the registered -// function and put into a map so it can be found by name during parsing. -  #include <map>  #include <string>  #include <stdexcept> @@ -58,6 +34,7 @@  #include <boost/lambda/lambda.hpp>  #include <iostream> +#include <iomanip>  using boost::lambda::constant;  using boost::lambda::constant_ref; @@ -75,98 +52,244 @@ static std::ostringstream cout;  /*****************************************************************************  *   Example data, functions, classes  *****************************************************************************/ -// sensing globals -static std::string gs; -static float gf; -static int gi; -static LLSD gl; - -void clear() +// We don't need a whole lot of different arbitrary-params methods, just (no | +// (const LLSD&) | arbitrary) args (function | static method | non-static +// method), where 'arbitrary' is (every LLSD datatype + (const char*)). +// But we need to register each one under different names for the different +// registration styles. Don't forget LLEventDispatcher subclass methods(const +// LLSD&). + +// However, the number of target parameter conversions we want to try exceeds +// boost::fusion::invoke()'s supported parameter-list size. Break out two +// different lists. +#define NPARAMSa bool b, int i, float f, double d, const char* cp +#define NPARAMSb const std::string& s, const LLUUID& uuid, const LLDate& date, \ +                 const LLURI& uri, const std::vector<U8>& bin +#define NARGSa   b, i, f, d, cp +#define NARGSb   s, uuid, date, uri, bin + +// For some registration methods we need methods on a subclass of +// LLEventDispatcher. To simplify things, we'll use this Dispatcher subclass +// for all our testing, including testing its own methods. +class Dispatcher: public LLEventDispatcher  { -    gs.clear(); -    gf = 0; -    gi = 0; -    gl = LLSD(); -} +public: +    Dispatcher(const std::string& name, const std::string& key): +        LLEventDispatcher(name, key) +    {} -void abc(const std::string& message) -{ -    cout << "abc('" << message << "')\n"; -    gs = message; -} +    // sensing member, mutable because we want to know when we've reached our +    // const method too +    mutable LLSD llsd; -void def(float value, std::string desc) -{ -    cout << "def(" << value << ", '" << desc << "')\n"; -    gf = value; -    gs = desc; -} +    void method1(const LLSD& obj) { llsd = obj; } +    void cmethod1(const LLSD& obj) const { llsd = obj; } +}; -void ghi(const std::string& foo, int bar) +// sensing vars, captured in a struct to make it convenient to clear them +struct Vars  { -    cout << "ghi('" << foo << "', " << bar << ")\n"; -    gs = foo; -    gi = bar; -} +    LLSD llsd; +    bool b; +    int i; +    float f; +    double d; +    const char* cp; +    std::string s; +    LLUUID uuid; +    LLDate date; +    LLURI uri; +    std::vector<U8> bin; + +    Vars(): +        // Only need to initialize the POD types, the rest should take care of +        // default-constructing themselves. +        b(false), +        i(0), +        f(0), +        d(0), +        cp(NULL) +    {} + +    // Detect any non-default values for convenient testing +    LLSD inspect() const +    { +        LLSD result; + +        if (llsd.isDefined()) +            result["llsd"] = llsd; +        if (b) +            result["b"] = b; +        if (i) +            result["i"] = i; +        if (f) +            result["f"] = f; +        if (d) +            result["d"] = d; +        if (cp) +            result["cp"] = cp; +        if (! s.empty()) +            result["s"] = s; +        if (uuid != LLUUID()) +            result["uuid"] = uuid; +        if (date != LLDate()) +            result["date"] = date; +        if (uri != LLURI()) +            result["uri"] = uri; +        if (! bin.empty()) +            result["bin"] = bin; + +        return result; +    } -void jkl(const char* message) -{ -    cout << "jkl('" << message << "')\n"; -    gs = message; -} +    /*------------- no-args (non-const, const, static) methods -------------*/ +    void method0() +    { +        cout << "method0()\n"; +        i = 17; +    } -void somefunc(const LLSD& value) -{ -    cout << "somefunc(" << value << ")\n"; -    gl = value; -} +    void cmethod0() const +    { +        cout << 'c'; +        const_cast<Vars*>(this)->method0(); +    } -class Dummy -{ -public: -    Dummy(): _id("Dummy") {} +    static void smethod0(); -    void mno(const std::string& message) +    /*------------ Callable (non-const, const, static) methods -------------*/ +    void method1(const LLSD& obj)      { -        cout << _id << "::mno('" << message << "')\n"; -        s = message; +        cout << "method1(" << obj << ")\n"; +        llsd = obj;      } -    void pqr(float value, std::string desc) +    void cmethod1(const LLSD& obj) const      { -        cout << _id << "::pqr(" << value << ", '" << desc << "')\n"; -        f = value; -        s = desc; +        cout << 'c'; +        const_cast<Vars*>(this)->method1(obj);      } -    void stu(const std::string& foo, int bar) +    static void smethod1(const LLSD& obj); + +    /*-------- Arbitrary-params (non-const, const, static) methods ---------*/ +    void methodna(NPARAMSa)      { -        cout << _id << "::stu('" << foo << "', " << bar << ")\n"; -        s = foo; -        i = bar; +        std::string vcp; +        if (cp == NULL) +            vcp = "NULL"; +        else +            vcp = std::string("'") + cp + "'"; + +        cout << "methodna(" << b +             << ", " << i +             << ", " << f +             << ", " << d +             << ", " << vcp +             << ")\n"; + +        this->b = b; +        this->i = i; +        this->f = f; +        this->d = d; +        this->cp = cp;      } -    void vwx(const char* message) +    void methodnb(NPARAMSb)      { -        cout << _id << "::vwx('" << message << "')\n"; -        s = message; +        std::ostringstream vbin; +        for (size_t ix = 0, ixend = bin.size(); ix < ixend; ++ix) +        { +            vbin << std::hex << std::setfill('0') << std::setw(2) << bin[ix]; +        } + +        cout << "methodnb(" << "'" << s << "'" +             << ", " << uuid +             << ", " << date +             << ", '" << uri << "'" +             << ", " << vbin.str() +             << ")\n"; + +        this->s = s; +        this->uuid = uuid; +        this->date = date; +        this->uri = uri; +        this->bin = bin;      } -    static void yz1(const std::string& message) +    void cmethodna(NPARAMSa) const      { -        cout << "Dummy::yz1('" << message << "')\n"; -        // can't access sensing members... -        gs = message; +        cout << 'c'; +        const_cast<Vars*>(this)->methodna(NARGSa);      } -    // sensing members -    std::string s; -    float f; -    int i; +    void cmethodnb(NPARAMSb) const +    { +        cout << 'c'; +        const_cast<Vars*>(this)->methodnb(NARGSb); +    } -private: -    std::string _id; +    static void smethodna(NPARAMSa); +    static void smethodnb(NPARAMSb);  }; +/*------- Global Vars instance for free functions and static methods -------*/ +static Vars g; + +/*------------ Static Vars method implementations reference 'g' ------------*/ +void Vars::smethod0() +{ +    cout << "smethod0() -> "; +    g.method0(); +} + +void Vars::smethod1(const LLSD& obj) +{ +    cout << "smethod1(" << obj << ") -> "; +    g.method1(obj); +} + +void Vars::smethodna(NPARAMSa) +{ +    cout << "smethodna(...) -> "; +    g.methodna(NARGSa); +} + +void Vars::smethodnb(NPARAMSb) +{ +    cout << "smethodnb(...) -> "; +    g.methodnb(NARGSb); +} + +/*--------------------------- Reset global Vars ----------------------------*/ +void clear() +{ +    g = Vars(); +} + +/*------------------- Free functions also reference 'g' --------------------*/ +void free0() +{ +    cout << "free0() -> "; +    g.method0(); +} + +void free1(const LLSD& obj) +{ +    cout << "free1(" << obj << ") -> "; +    g.method1(obj); +} + +void freena(NPARAMSa) +{ +    cout << "freena(...) -> "; +    g.methodna(NARGSa); +} + +void freenb(NPARAMSb) +{ +    cout << "freenb(...) -> "; +    g.methodnb(NARGSb); +}  /*****************************************************************************  *   TUT @@ -176,30 +299,300 @@ namespace tut      struct lleventdispatcher_data      {          WrapLL_ERRS redirect; -        LLEventDispatcher work; -        Dummy dummy; +        Dispatcher work; +        Vars v; +        std::string name, desc; +        typedef std::map<std::string, std::string> FuncMap; +        FuncMap funcs; +        // Required structure for Callables with requirements +        LLSD required; +        // Parameter names for freena(), freenb() +        LLSD paramsa, paramsb; +        // Full defaults arrays for params for freena(), freenb() +        LLSD dfta_array_full, dftb_array_full; +        // Start index of partial defaults arrays +        const LLSD::Integer partial_offset; +        // Full defaults maps for params for freena(), freenb() +        LLSD dfta_map_full, dftb_map_full; +        // Partial defaults maps for params for freena(), freenb() +        LLSD dfta_map_partial, dftb_map_partial;          lleventdispatcher_data(): -            work("test dispatcher", "op") +            work("test dispatcher", "op"), +            // map {d=double, array=[3 elements]} +            required(LLSDMap("d", LLSD::Real(0))("array", LLSDArray(LLSD())(LLSD())(LLSD()))), +            // first several params are required, last couple optional +            partial_offset(3)          {              // This object is reconstructed for every test<n> method. But              // clear global variables every time too.              ::clear(); -            work.add("abc", "abc", abc, LLSDArray("message")); -            work.add("def", "def", def); -            work.add("ghi", "ghi", ghi); -            work.add("jkl", "jkl", jkl); -            work.add("yz1", "yz1", &Dummy::yz1); -            work.add("mno", "mno", &Dummy::mno, var(dummy), -                     LLSDArray("message"), LLSDArray("default message")); -            work.add("mnoptr", "mno", &Dummy::mno, constant(&dummy)); -            work.add("pqr", "pqr", &Dummy::pqr, var(dummy), -                     LLSDArray("value")("desc")); -            work.add("stu", "stu", &Dummy::stu, var(dummy), -                     LLSDArray("foo")("bar"), LLSDArray(-1)); -            work.add("vwx", "vwx", &Dummy::vwx, var(dummy), -                     LLSDArray("message")); +            // Registration cases: +            // - (Callable | subclass const method | subclass non-const method | +            //   non-subclass method) (with | without) required +            // - (Free function | static method | non-static method), (no | arbitrary) params, +            //   array style +            // - (Free function | static method | non-static method), (no | arbitrary) params, +            //   map style, (empty | partial | full) (array | map) defaults +            // - Map-style errors: +            //   - (scalar | map) param names +            //   - defaults scalar +            //   - defaults array longer than params array +            //   - defaults map with plural unknown param names + +            // I hate to have to write things twice, because of having to keep +            // them consistent. If we had variadic functions, addf() would be +            // a variadic method, capturing the name and desc and passing them +            // plus "everything else" to work.add(). If I could return a pair +            // and use that pair as the first two args to work.add(), I'd do +            // that. But the best I can do with present C++ is to set two +            // instance variables as a side effect of addf(), and pass those +            // variables to each work.add() call. :-P + +            /*------------------------- Callables --------------------------*/ + +            // Arbitrary Callable with/out required params +            addf("free1", "free1"); +            work.add(name, desc, free1); +            addf("free1_req", "free1"); +            work.add(name, desc, free1, required); +            // Subclass non-const method with/out required params +            addf("Dmethod1", "method1"); +            work.add(name, desc, &Dispatcher::method1); +            addf("Dmethod1_req", "method1"); +            work.add(name, desc, &Dispatcher::method1, required); +            // Subclass const method with/out required params +            addf("Dcmethod1", "cmethod1"); +            work.add(name, desc, &Dispatcher::cmethod1); +            addf("Dcmethod1_req", "cmethod1"); +            work.add(name, desc, &Dispatcher::cmethod1, required); +            // Non-subclass method with/out required params +            addf("method1", "method1"); +            work.add(name, desc, boost::bind(&Vars::method1, boost::ref(v), _1)); +            addf("method1_req", "method1"); +            work.add(name, desc, boost::bind(&Vars::method1, boost::ref(v), _1), required); + +            /*--------------- Arbitrary params, array style ----------------*/ + +            // (Free function | static method) with (no | arbitrary) params, array style +            addf("free0_array", "free0"); +            work.add(name, desc, free0); +            addf("freena_array", "freena"); +            work.add(name, desc, freena); +            addf("freenb_array", "freenb"); +            work.add(name, desc, freenb); +            addf("smethod0_array", "smethod0"); +            work.add(name, desc, &Vars::smethod0); +            addf("smethodna_array", "smethodna"); +            work.add(name, desc, &Vars::smethodna); +            addf("smethodnb_array", "smethodnb"); +            work.add(name, desc, &Vars::smethodnb); +            // Non-static method with (no | arbitrary) params, array style +            addf("method0_array", "method0"); +            work.add(name, desc, &Vars::method0, boost::lambda::var(v)); +            addf("methodna_array", "methodna"); +            work.add(name, desc, &Vars::methodna, boost::lambda::var(v)); +            addf("methodnb_array", "methodnb"); +            work.add(name, desc, &Vars::methodnb, boost::lambda::var(v)); + +            /*---------------- Arbitrary params, map style -----------------*/ + +            // freena(), methodna(), cmethodna(), smethodna() all take same param list +            paramsa = LLSDArray("b")("i")("f")("d")("cp"); +            // same for freenb() et al. +            paramsb = LLSDArray("s")("uuid")("date")("uri")("bin"); +            // Full defaults arrays. +            dfta_array_full = LLSDArray(true)(17)(3.14)(123456.78)("classic"); +            // default LLSD::Binary value    +            std::vector<U8> binary; +            for (size_t ix = 0, h = 0xaa; ix < 6; ++ix, h += 0x11) +            { +                binary.push_back(h); +            } +            // We actually don't care what the LLUUID or LLDate values are, as +            // long as they're non-default. +            dftb_array_full = LLSDArray("string")(LLUUID::generateNewID())(LLDate::now()) +                                       (LLURI("http://www.ietf.org/rfc/rfc3986.txt"))(binary); +            // Partial defaults arrays. +            LLSD dfta_array_partial(llsd_copy_array(dfta_array_full.beginArray() + partial_offset, +                                                    dfta_array_full.endArray())); +            LLSD dftb_array_partial(llsd_copy_array(dftb_array_full.beginArray() + partial_offset, +                                                    dftb_array_full.endArray())); + +            // Generate full defaults maps by zipping (params, dftx_array_full). +            LLSD zipped(LLSDArray(LLSDArray(paramsa)(dfta_array_full)) +                                 (LLSDArray(paramsb)(dftb_array_full))); +//            std::cout << "zipped:\n" << zipped << '\n'; +            LLSD dft_maps_full, dft_maps_partial; +            for (LLSD::array_const_iterator ai(zipped.beginArray()), aend(zipped.endArray()); +                 ai != aend; ++ai) +            { +                LLSD dft_map_full; +                LLSD params((*ai)[0]); +                LLSD dft_array_full((*ai)[1]); +//                std::cout << "params:\n" << params << "\ndft_array_full:\n" << dft_array_full << '\n'; +                for (LLSD::Integer ix = 0, ixend = params.size(); ix < ixend; ++ix) +                { +                    dft_map_full[params[ix].asString()] = dft_array_full[ix]; +                } +//                std::cout << "dft_map_full:\n" << dft_map_full << '\n'; +                // Generate partial defaults map by zipping alternate entries from +                // (params, dft_array_full). Part of the point of using map-style +                // defaults is to allow any subset of the target function's +                // parameters to be optional, not just the rightmost. +                LLSD dft_map_partial; +                for (LLSD::Integer ix = 0, ixend = params.size(); ix < ixend; ix += 2) +                { +                    dft_map_partial[params[ix].asString()] = dft_array_full[ix]; +                } +//                std::cout << "dft_map_partial:\n" << dft_map_partial << '\n'; +                dft_maps_full.append(dft_map_full); +                dft_maps_partial.append(dft_map_partial); +            } +//            std::cout << "dft_maps_full:\n" << dft_maps_full << "\ndft_maps_partial:\n" << dft_maps_partial << '\n'; +            dfta_map_full = dft_maps_full[0]; +            dftb_map_full = dft_maps_full[1]; +            dfta_map_partial = dft_maps_partial[0]; +            dftb_map_partial = dft_maps_partial[1]; +//            std::cout << "dfta_map_full:\n" << dfta_map_full +//                      << "\ndftb_map_full:\n" << dftb_map_full +//                      << "\ndfta_map_partial:\n" << dfta_map_partial +//                      << "\ndftb_map_partial:\n" << dftb_map_partial << '\n'; + +            // (Free function | static method) with (no | arbitrary) params, +            // map style, no (empty array) defaults +            addf("free0_map", "free0"); +            work.add(name, desc, free0, LLSD::emptyArray()); +            addf("smethod0_map", "smethod0"); +            work.add(name, desc, &Vars::smethod0, LLSD::emptyArray()); +            addf("freena_map_allreq", "freena"); +            work.add(name, desc, freena, paramsa); +            addf("freenb_map_allreq", "freenb"); +            work.add(name, desc, freenb, paramsb); +            addf("smethodna_map_allreq", "smethodna"); +            work.add(name, desc, &Vars::smethodna, paramsa); +            addf("smethodnb_map_allreq", "smethodnb"); +            work.add(name, desc, &Vars::smethodnb, paramsb); +            // Non-static method with (no | arbitrary) params, map style, no +            // (empty array) defaults +            addf("method0_map", "method0"); +            work.add(name, desc, &Vars::method0, var(v), LLSD::emptyArray()); +            addf("methodna_map_allreq", "methodna"); +            work.add(name, desc, &Vars::methodna, var(v), paramsa); +            addf("methodnb_map_allreq", "methodnb"); +            work.add(name, desc, &Vars::methodnb, var(v), paramsb); + +            // Except for the "more (array | map) defaults than params" error +            // cases, tested separately below, the (partial | full)(array | +            // map) defaults cases don't apply to no-params functions/methods. +            // So eliminate free0, smethod0, method0 from the cases below. + +            // (Free function | static method) with arbitrary params, map +            // style, partial (array | map) defaults +            addf("freena_map_leftreq", "freena"); +            work.add(name, desc, freena, paramsa, dfta_array_partial); +            addf("freenb_map_leftreq", "freenb"); +            work.add(name, desc, freenb, paramsb, dftb_array_partial); +            addf("smethodna_map_leftreq", "smethodna"); +            work.add(name, desc, &Vars::smethodna, paramsa, dfta_array_partial); +            addf("smethodnb_map_leftreq", "smethodnb"); +            work.add(name, desc, &Vars::smethodnb, paramsb, dftb_array_partial); +            addf("freena_map_skipreq", "freena"); +            work.add(name, desc, freena, paramsa, dfta_map_partial); +            addf("freenb_map_skipreq", "freenb"); +            work.add(name, desc, freenb, paramsb, dftb_map_partial); +            addf("smethodna_map_skipreq", "smethodna"); +            work.add(name, desc, &Vars::smethodna, paramsa, dfta_map_partial); +            addf("smethodnb_map_skipreq", "smethodnb"); +            work.add(name, desc, &Vars::smethodnb, paramsb, dftb_map_partial); +            // Non-static method with arbitrary params, map style, partial +            // (array | map) defaults +            addf("methodna_map_leftreq", "methodna"); +            work.add(name, desc, &Vars::methodna, var(v), paramsa, dfta_array_partial); +            addf("methodnb_map_leftreq", "methodnb"); +            work.add(name, desc, &Vars::methodnb, var(v), paramsb, dftb_array_partial); +            addf("methodna_map_skipreq", "methodna"); +            work.add(name, desc, &Vars::methodna, var(v), paramsa, dfta_map_partial); +            addf("methodnb_map_skipreq", "methodnb"); +            work.add(name, desc, &Vars::methodnb, var(v), paramsb, dftb_map_partial); + +            // (Free function | static method) with arbitrary params, map +            // style, full (array | map) defaults +            addf("freena_map_adft", "freena"); +            work.add(name, desc, freena, paramsa, dfta_array_full); +            addf("freenb_map_adft", "freenb"); +            work.add(name, desc, freenb, paramsb, dftb_array_full); +            addf("smethodna_map_adft", "smethodna"); +            work.add(name, desc, &Vars::smethodna, paramsa, dfta_array_full); +            addf("smethodnb_map_adft", "smethodnb"); +            work.add(name, desc, &Vars::smethodnb, paramsb, dftb_array_full); +            addf("freena_map_mdft", "freena"); +            work.add(name, desc, freena, paramsa, dfta_map_full); +            addf("freenb_map_mdft", "freenb"); +            work.add(name, desc, freenb, paramsb, dftb_map_full); +            addf("smethodna_map_mdft", "smethodna"); +            work.add(name, desc, &Vars::smethodna, paramsa, dfta_map_full); +            addf("smethodnb_map_mdft", "smethodnb"); +            work.add(name, desc, &Vars::smethodnb, paramsb, dftb_map_full); +            // Non-static method with arbitrary params, map style, full +            // (array | map) defaults +            addf("methodna_map_adft", "methodna"); +            work.add(name, desc, &Vars::methodna, var(v), paramsa, dfta_array_full); +            addf("methodnb_map_adft", "methodnb"); +            work.add(name, desc, &Vars::methodnb, var(v), paramsb, dftb_array_full); +            addf("methodna_map_mdft", "methodna"); +            work.add(name, desc, &Vars::methodna, var(v), paramsa, dfta_map_full); +            addf("methodnb_map_mdft", "methodnb"); +            work.add(name, desc, &Vars::methodnb, var(v), paramsb, dftb_map_full); + +            // All the above are expected to succeed, and are setup for the +            // tests to follow. Registration error cases are exercised as +            // tests rather than as test setup. +        } + +        void addf(const std::string& n, const std::string& d) +        { +            // This method is to capture in our own FuncMap the name and +            // description of every registered function, for metadata query +            // testing. +            funcs[n] = d; +            // See constructor for rationale for setting these instance vars. +            this->name = n; +            this->desc = d; +        } + +        void verify_funcs() +        { +            // Copy funcs to a temp map of same type. +            FuncMap forgotten(funcs.begin(), funcs.end()); +            for (LLEventDispatcher::const_iterator edi(work.begin()), edend(work.end()); +                 edi != edend; ++edi) +            { +                FuncMap::iterator found = forgotten.find(edi->first); +                ensure(STRINGIZE("LLEventDispatcher records function '" << edi->first +                                 << "' we didn't enter"), +                       found != forgotten.end()); +                ensure_equals(STRINGIZE("LLEventDispatcher desc '" << edi->second << +                                        "' doesn't match what we entered: '" << found->second << "'"), +                              edi->second, found->second); +                // found in our map the name from LLEventDispatcher, good, erase +                // our map entry +                forgotten.erase(found); +            } +            if (! forgotten.empty()) +            { +                std::ostringstream out; +                out << "LLEventDispatcher failed to report"; +                const char* delim = ": "; +                for (FuncMap::const_iterator fmi(forgotten.begin()), fmend(forgotten.end()); +                     fmi != fmend; ++fmi) +                { +                    out << delim << fmi->first; +                    delim = ", "; +                } +                ensure(out.str(), false); +            }          }          void ensure_has(const std::string& outer, const std::string& inner) @@ -222,215 +615,359 @@ namespace tut              }              ensure_has(threw, exc_frag);          } + +        LLSD getMetadata(const std::string& name) +        { +            LLSD meta(work.getMetadata(name)); +            ensure(STRINGIZE("No metadata for " << name), meta.isDefined()); +            return meta; +        }      };      typedef test_group<lleventdispatcher_data> lleventdispatcher_group;      typedef lleventdispatcher_group::object object;      lleventdispatcher_group lleventdispatchergrp("lleventdispatcher"); +    // Call cases: +    // - (try_call | call) (explicit name | event key) (real | bogus) name +    // - Callable with args that (do | do not) match required +    // - (Free function | non-static method) array style with +    //   (scalar | map | array (too short | too long | just right)) +    //   [trap LL_WARNS for too-long case?] +    // - (Free function | non-static method) map style with +    //   (scalar | array | map (all | too many | holes (with | without) defaults)) +    // - const char* param gets ("" | NULL) + +    // Query cases: +    // - Iterate over all (with | without) remove() +    // - getDispatchKey() +    // - Callable style (with | without) required +    // - (Free function | non-static method), array style, (no | arbitrary) params +    // - (Free function | non-static method), map style, (no | arbitrary) params, +    //   (empty | full | partial (array | map)) defaults +      template<> template<>      void object::test<1>()      { -        LLSD hello("Hello test!"); -//        cout << std::string(hello) << "\n"; -        clear(); -        jkl(LLSDParam<const char*>(hello)); -        ensure_equals(gs, hello.asString()); +        set_test_name("map-style registration with non-array params"); +        // Pass "param names" as scalar or as map +        LLSD attempts(LLSDArray(17)(LLSDMap("pi", 3.14)("two", 2))); +        for (LLSD::array_const_iterator ai(attempts.beginArray()), aend(attempts.endArray()); +             ai != aend; ++ai) +        { +            std::string threw; +            try +            { +                work.add("freena_err", "freena", freena, *ai); +            } +            catch (const std::exception& e) +            { +                threw = e.what(); +            } +            ensure_has(threw, "must be an array"); +        }      }      template<> template<>      void object::test<2>()      { -        somefunc("abc"); -        ensure_equals(gl.asString(), "abc"); - -        somefunc(17); -        ensure_equals(gl.asInteger(), 17); - -        somefunc(3.14); -        // 7 bits is 128, just enough to express two decimal places. -        ensure_approximately_equals(gl.asReal(), 3.14, 7); - -        somefunc(LLSD().with(0, "abc").with(1, 17).with(2, 3.14)); -        ensure(gl.isArray()); -        ensure_equals(gl.size(), 4); // !!! bug in LLSD::with(Integer, const LLSD&) !!! -        ensure_equals(gl[1].asInteger(), 17); - -        somefunc(LLSD().with("alpha", "abc").with("random", 17).with("pi", 3.14)); -        ensure(gl.isMap()); -        ensure_equals(gl.size(), 3); -        ensure_equals(gl["random"].asInteger(), 17); - -        somefunc(LLSDArray("abc")(17)(3.14)); -        ensure(gl.isArray()); -        ensure_equals(gl.size(), 3); -        ensure_equals(gl[0].asString(), "abc"); - -        somefunc(LLSDMap("alpha", "abc")("random", 17)("pi", 3.14)); -        ensure(gl.isMap()); -        ensure_equals(gl.size(), 3); -        ensure_equals(gl["alpha"].asString(), "abc"); +        set_test_name("map-style registration with badly-formed defaults"); +        std::string threw; +        try +        { +            work.add("freena_err", "freena", freena, LLSDArray("a")("b"), 17); +        } +        catch (const std::exception& e) +        { +            threw = e.what(); +        } +        ensure_has(threw, "must be a map or an array");      }      template<> template<>      void object::test<3>()      { -        call_exc("gofish", LLSDArray(1), "not found"); +        set_test_name("map-style registration with too many array defaults"); +        std::string threw; +        try +        { +            work.add("freena_err", "freena", freena, +                     LLSDArray("a")("b"), +                     LLSDArray(17)(0.9)("gack")); +        } +        catch (const std::exception& e) +        { +            threw = e.what(); +        } +        ensure_has(threw, "shorter than");      }      template<> template<>      void object::test<4>()      { -        call_exc("abc", LLSD(), "missing required"); +        set_test_name("map-style registration with too many map defaults"); +        std::string threw; +        try +        { +            work.add("freena_err", "freena", freena, +                     LLSDArray("a")("b"), +                     LLSDMap("b", 17)("foo", 3.14)("bar", "sinister")); +        } +        catch (const std::exception& e) +        { +            threw = e.what(); +        } +        ensure_has(threw, "nonexistent params"); +        ensure_has(threw, "foo"); +        ensure_has(threw, "bar");      }      template<> template<>      void object::test<5>()      { -        work("abc", LLSDMap("message", "something")); -        ensure_equals(gs, "something"); +        set_test_name("query all"); +        verify_funcs();      }      template<> template<>      void object::test<6>()      { -        work("abc", LLSDMap("message", "something")("plus", "more")); -        ensure_equals(gs, "something"); +        set_test_name("query all with remove()"); +        ensure("remove('bogus') returned true", ! work.remove("bogus")); +        ensure("remove('real') returned false", work.remove("free1")); +        // Of course, remove that from 'funcs' too... +        funcs.erase("free1"); +        verify_funcs();      }      template<> template<>      void object::test<7>()      { -        call_exc("def", LLSDMap("value", 20)("desc", "questions"), "needs an args array"); +        set_test_name("getDispatchKey()"); +        ensure_equals(work.getDispatchKey(), "op");      }      template<> template<>      void object::test<8>()      { -        work("def", LLSDArray(20)("questions")); -        ensure_equals(gf, 20); -        ensure_equals(gs, "questions"); +        set_test_name("query Callables with/out required params"); +        LLSD names(LLSDArray("free1")("Dmethod1")("Dcmethod1")("method1")); +        for (LLSD::array_const_iterator ai(names.beginArray()), aend(names.endArray()); +             ai != aend; ++ai) +        { +            LLSD metadata(getMetadata(*ai)); +            ensure_equals("name mismatch", metadata["name"], *ai); +            ensure_equals(metadata["desc"].asString(), funcs[*ai]); +            ensure("should not have required structure", metadata["required"].isUndefined()); +            ensure("should not have optional", metadata["optional"].isUndefined()); + +            std::string name_req(ai->asString() + "_req"); +            metadata = getMetadata(name_req); +            ensure_equals(metadata["name"].asString(), name_req); +            ensure_equals(metadata["desc"].asString(), funcs[name_req]); +            ensure_equals("required mismatch", required, metadata["required"]); +            ensure("should not have optional", metadata["optional"].isUndefined()); +        }      }      template<> template<>      void object::test<9>()      { -        work("def", LLSDArray(3.14)("pies")); -        ensure_approximately_equals(gf, 3.14, 7); -        ensure_equals(gs, "pies"); +        set_test_name("query array-style functions/methods"); +        // Associate each registered name with expected arity. +        LLSD expected(LLSDArray +                      (LLSDArray +                       (0)(LLSDArray("free0_array")("smethod0_array")("method0_array"))) +                      (LLSDArray +                       (5)(LLSDArray("freena_array")("smethodna_array")("methodna_array"))) +                      (LLSDArray +                       (5)(LLSDArray("freenb_array")("smethodnb_array")("methodnb_array")))); +        for (LLSD::array_const_iterator ai(expected.beginArray()), aend(expected.endArray()); +             ai != aend; ++ai) +        { +            LLSD::Integer arity((*ai)[0].asInteger()); +            LLSD names((*ai)[1]); +            LLSD req(LLSD::emptyArray()); +            if (arity) +                req[arity - 1] = LLSD(); +            for (LLSD::array_const_iterator ni(names.beginArray()), nend(names.endArray()); +                 ni != nend; ++ni) +            { +                LLSD metadata(getMetadata(*ni)); +                ensure_equals("name mismatch", metadata["name"], *ni); +                ensure_equals(metadata["desc"].asString(), funcs[*ni]); +                ensure_equals(STRINGIZE("mismatched required for " << ni->asString()), +                              metadata["required"], req); +                ensure("should not have optional", metadata["optional"].isUndefined()); +            } +        }      }      template<> template<>      void object::test<10>()      { -        work("ghi", LLSDArray("answer")(17)); -        ensure_equals(gs, "answer"); -        ensure_equals(gi, 17); +        set_test_name("query map-style no-params functions/methods"); +        // - (Free function | non-static method), map style, no params (ergo +        //   no defaults) +        LLSD names(LLSDArray("free0_map")("smethod0_map")("method0_map")); +        for (LLSD::array_const_iterator ni(names.beginArray()), nend(names.endArray()); +             ni != nend; ++ni) +        { +            LLSD metadata(getMetadata(*ni)); +            ensure_equals("name mismatch", metadata["name"], *ni); +            ensure_equals(metadata["desc"].asString(), funcs[*ni]); +            ensure("should not have required", +                   (metadata["required"].isUndefined() || metadata["required"].size() == 0)); +            ensure("should not have optional", metadata["optional"].isUndefined()); +        }      }      template<> template<>      void object::test<11>()      { -        work("ghi", LLSDArray("answer")(3.14)); -        ensure_equals(gs, "answer"); -        ensure_equals(gi, 3); +        set_test_name("query map-style arbitrary-params functions/methods: " +                      "full array defaults vs. full map defaults"); +        // With functions registered with no defaults ("_allreq" suffixes), +        // there is of course no difference between array defaults and map +        // defaults. (We don't even bother registering with LLSD::emptyArray() +        // vs. LLSD::emptyMap().) With functions registered with all defaults, +        // there should (!) be no difference beween array defaults and map +        // defaults. Verify, so we can ignore the distinction for all other +        // tests. +        LLSD equivalences(LLSDArray +                          (LLSDArray("freena_map_adft")("freena_map_mdft")) +                          (LLSDArray("freenb_map_adft")("freenb_map_mdft")) +                          (LLSDArray("smethodna_map_adft")("smethodna_map_mdft")) +                          (LLSDArray("smethodnb_map_adft")("smethodnb_map_mdft")) +                          (LLSDArray("methodna_map_adft")("methodna_map_mdft")) +                          (LLSDArray("methodnb_map_adft")("methodnb_map_mdft"))); +        for (LLSD::array_const_iterator +                 ei(equivalences.beginArray()), eend(equivalences.endArray()); +             ei != eend; ++ei) +        { +            LLSD adft((*ei)[0]); +            LLSD mdft((*ei)[1]); +            // We can't just compare the results of the two getMetadata() +            // calls, because they contain ["name"], which are different. So +            // capture them, verify that each ["name"] is as expected, then +            // remove for comparing the rest. +            LLSD ameta(getMetadata(adft)); +            LLSD mmeta(getMetadata(mdft)); +            ensure_equals("adft name", adft, ameta["name"]); +            ensure_equals("mdft name", mdft, mmeta["name"]); +            ameta.erase("name"); +            mmeta.erase("name"); +            ensure_equals(STRINGIZE("metadata for " << adft.asString() +                                    << " vs. " << mdft.asString()), +                          ameta, mmeta); +        }      }      template<> template<>      void object::test<12>()      { -        work("jkl", LLSDArray("sample message")); -        ensure_equals(gs, "sample message"); -    } - -    template<> template<> -    void object::test<13>() -    { -        work("yz1", LLSDArray("w00t")); -        ensure_equals(gs, "w00t"); -    } - -    template<> template<> -    void object::test<14>() -    { -        std::string msg("nonstatic member function"); -        work("mno", LLSDMap("message", msg)); -        ensure_equals(dummy.s, msg); -    } +        set_test_name("query map-style arbitrary-params functions/methods"); +        // - (Free function | non-static method), map style, arbitrary params, +        //   (empty | full | partial (array | map)) defaults + +        // Generate maps containing all parameter names for cases in which all +        // params are required. Also maps containing left requirements for +        // partial defaults arrays. Also defaults maps from defaults arrays. +        LLSD allreqa, allreqb, leftreqa, leftreqb, rightdfta, rightdftb; +        for (LLSD::Integer pi(0), pend(std::min(partial_offset, paramsa.size())); +             pi < pend; ++pi) +        { +            allreqa[paramsa[pi].asString()] = LLSD(); +            leftreqa[paramsa[pi].asString()] = LLSD(); +        } +        for (LLSD::Integer pi(partial_offset), pend(paramsa.size()); pi < pend; ++pi) +        { +            allreqa[paramsa[pi].asString()] = LLSD(); +            rightdfta[paramsa[pi].asString()] = dfta_array_full[pi]; +        } +        for (LLSD::Integer pi(0), pend(std::min(partial_offset, paramsb.size())); +             pi < pend; ++pi) +        { +            allreqb[paramsb[pi].asString()] = LLSD(); +            leftreqb[paramsb[pi].asString()] = LLSD(); +        } +        for (LLSD::Integer pi(partial_offset), pend(paramsb.size()); pi < pend; ++pi) +        { +            allreqb[paramsb[pi].asString()] = LLSD(); +            rightdftb[paramsb[pi].asString()] = dftb_array_full[pi]; +        } -    template<> template<> -    void object::test<15>() -    { -        std::string msg("nonstatic member function reached by ptr"); -        work("mnoptr", LLSDArray(msg)); -        ensure_equals(dummy.s, msg); -    } +        // Generate maps containing parameter names not provided by the +        // dft[ab]_map_partial maps. +        LLSD skipreqa(allreqa), skipreqb(allreqb); +        for (LLSD::map_const_iterator mi(dfta_map_partial.beginMap()), +                                      mend(dfta_map_partial.endMap()); +             mi != mend; ++mi) +        { +            skipreqa.erase(mi->first); +        } +        for (LLSD::map_const_iterator mi(dftb_map_partial.beginMap()), +                                      mend(dftb_map_partial.endMap()); +             mi != mend; ++mi) +        { +            skipreqb.erase(mi->first); +        } -    template<> template<> -    void object::test<16>() -    { -        work("mno", LLSD()); -        ensure_equals(dummy.s, "default message"); -    } +        LLSD groups(LLSDArray       // array of groups -    template<> template<> -    void object::test<17>() -    { -        work("pqr", LLSDMap("value", 3.14)("desc", "pies")); -        ensure_approximately_equals(dummy.f, 3.14, 7); -        ensure_equals(dummy.s, "pies"); -    } +                    (LLSDArray      // group +                     (LLSDArray("freena_map_allreq")("smethodna_map_allreq")("methodna_map_allreq")) +                     (LLSDArray(allreqa)(LLSD()))) // required, optional -    template<> template<> -    void object::test<18>() -    { -        call_exc("pqr", LLSD(), "missing required"); -    } +                    (LLSDArray        // group +                     (LLSDArray("freenb_map_allreq")("smethodnb_map_allreq")("methodnb_map_allreq")) +                     (LLSDArray(allreqb)(LLSD()))) // required, optional -    template<> template<> -    void object::test<19>() -    { -        call_exc("pqr", LLSDMap("value", 3.14), "missing required"); -    } +                    (LLSDArray        // group +                     (LLSDArray("freena_map_leftreq")("smethodna_map_leftreq")("methodna_map_leftreq")) +                     (LLSDArray(leftreqa)(rightdfta))) // required, optional -    template<> template<> -    void object::test<20>() -    { -        call_exc("pqr", LLSDMap("desc", "pies"), "missing required"); -    } +                    (LLSDArray        // group +                     (LLSDArray("freenb_map_leftreq")("smethodnb_map_leftreq")("methodnb_map_leftreq")) +                     (LLSDArray(leftreqb)(rightdftb))) // required, optional -    template<> template<> -    void object::test<21>() -    { -        work("stu", LLSDMap("bar", 3.14)("foo", "pies")); -        ensure_equals(dummy.s, "pies"); -        ensure_equals(dummy.i, 3); -    } +                    (LLSDArray        // group +                     (LLSDArray("freena_map_skipreq")("smethodna_map_skipreq")("methodna_map_skipreq")) +                     (LLSDArray(skipreqa)(dfta_map_partial))) // required, optional -    template<> template<> -    void object::test<22>() -    { -        call_exc("stu", LLSD(), "missing required"); -    } +                    (LLSDArray        // group +                     (LLSDArray("freenb_map_skipreq")("smethodnb_map_skipreq")("methodnb_map_skipreq")) +                     (LLSDArray(skipreqb)(dftb_map_partial))) // required, optional -    template<> template<> -    void object::test<23>() -    { -        call_exc("stu", LLSDMap("bar", 3.14), "missing required"); -    } +                    // We only need mention the full-map-defaults ("_mdft" suffix) +                    // registrations, having established their equivalence with the +                    // full-array-defaults ("_adft" suffix) registrations in another test. +                    (LLSDArray        // group +                     (LLSDArray("freena_map_mdft")("smethodna_map_mdft")("methodna_map_mdft")) +                     (LLSDArray(LLSD::emptyMap())(dfta_map_full))) // required, optional -    template<> template<> -    void object::test<24>() -    { -        work("stu", LLSDMap("foo", "pies")); -        ensure_equals(dummy.s, "pies"); -        ensure_equals(dummy.i, -1); -    } +                    (LLSDArray        // group +                     (LLSDArray("freenb_map_mdft")("smethodnb_map_mdft")("methodnb_map_mdft")) +                     (LLSDArray(LLSD::emptyMap())(dftb_map_full)))); // required, optional -    template<> template<> -    void object::test<25>() -    { -        std::string msg("nonstatic method(const char*)"); -        work("vwx", LLSDMap("message", msg)); -        ensure_equals(dummy.s, msg); +        for (LLSD::array_const_iterator gi(groups.beginArray()), gend(groups.endArray()); +             gi != gend; ++gi) +        { +            // Internal structure of each group in 'groups': +            LLSD names((*gi)[0]); +            LLSD required((*gi)[1][0]); +            LLSD optional((*gi)[1][1]); +            std::cout << "For " << names << ",\n" << "required:\n" << required << "\noptional:\n" << optional << std::endl; + +            // Loop through 'names' +            for (LLSD::array_const_iterator ni(names.beginArray()), nend(names.endArray()); +                 ni != nend; ++ni) +            { +                LLSD metadata(getMetadata(*ni)); +                ensure_equals("name mismatch", metadata["name"], *ni); +                ensure_equals(metadata["desc"].asString(), funcs[*ni]); +                ensure_equals("required mismatch", metadata["required"], required); +                ensure_equals("optional mismatch", metadata["optional"], optional); +            } +        }      }  } // namespace tut | 
