diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp
index a1d7cf9eadfcbe7b59db6d17e531d2e5e2a2c35a..35a3188507a4f0b5c28230ecc1057d91ce470328 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