summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/llcommon/tests/lleventdispatcher_test.cpp1023
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