summaryrefslogtreecommitdiff
path: root/indra/llcommon/tests
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon/tests')
-rw-r--r--indra/llcommon/tests/apply_test.cpp240
-rw-r--r--indra/llcommon/tests/lazyeventapi_test.cpp136
-rw-r--r--indra/llcommon/tests/lleventdispatcher_test.cpp569
-rw-r--r--indra/llcommon/tests/wrapllerrs.h5
4 files changed, 845 insertions, 105 deletions
diff --git a/indra/llcommon/tests/apply_test.cpp b/indra/llcommon/tests/apply_test.cpp
new file mode 100644
index 0000000000..56b497e0c8
--- /dev/null
+++ b/indra/llcommon/tests/apply_test.cpp
@@ -0,0 +1,240 @@
+/**
+ * @file apply_test.cpp
+ * @author Nat Goodspeed
+ * @date 2022-12-19
+ * @brief Test for apply.
+ *
+ * $LicenseInfo:firstyear=2022&license=viewerlgpl$
+ * Copyright (c) 2022, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "apply.h"
+// STL headers
+// std headers
+#include <iomanip>
+// external library headers
+// other Linden headers
+#include "llsd.h"
+#include "llsdutil.h"
+#include <array>
+#include <string>
+#include <vector>
+
+// for ensure_equals
+std::ostream& operator<<(std::ostream& out, const std::vector<std::string>& stringvec)
+{
+ const char* delim = "[";
+ for (const auto& str : stringvec)
+ {
+ out << delim << std::quoted(str);
+ delim = ", ";
+ }
+ return out << ']';
+}
+
+// the above must be declared BEFORE ensure_equals(std::vector<std::string>)
+#include "../test/lltut.h"
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ namespace statics
+ {
+ /*------------------------------ data ------------------------------*/
+ // Although we're using types from the LLSD namespace, we're not
+ // constructing LLSD values, but rather instances of the C++ types
+ // supported by LLSD.
+ static LLSD::Boolean b{true};
+ static LLSD::Integer i{17};
+ static LLSD::Real f{3.14};
+ static LLSD::String s{ "hello" };
+ static LLSD::UUID uu{ "baadf00d-dead-beef-baad-feedb0ef" };
+ static LLSD::Date dt{ "2022-12-19" };
+ static LLSD::URI uri{ "http://secondlife.com" };
+ static LLSD::Binary bin{ 0x01, 0x02, 0x03, 0x04, 0x05 };
+
+ static std::vector<LLSD::String> quick
+ {
+ "The", "quick", "brown", "fox", "etc."
+ };
+
+ static std::array<int, 5> fibs
+ {
+ 0, 1, 1, 2, 3
+ };
+
+ // ensure that apply() actually reaches the target method --
+ // lack of ensure_equals() failure could be due to no-op apply()
+ bool called{ false };
+ // capture calls from collect()
+ std::vector<std::string> collected;
+
+ /*------------------------- test functions -------------------------*/
+ void various(LLSD::Boolean b, LLSD::Integer i, LLSD::Real f, const LLSD::String& s,
+ const LLSD::UUID& uu, const LLSD::Date& dt,
+ const LLSD::URI& uri, const LLSD::Binary& bin)
+ {
+ called = true;
+ ensure_equals( "b mismatch", b, statics::b);
+ ensure_equals( "i mismatch", i, statics::i);
+ ensure_equals( "f mismatch", f, statics::f);
+ ensure_equals( "s mismatch", s, statics::s);
+ ensure_equals( "uu mismatch", uu, statics::uu);
+ ensure_equals( "dt mismatch", dt, statics::dt);
+ ensure_equals("uri mismatch", uri, statics::uri);
+ ensure_equals("bin mismatch", bin, statics::bin);
+ }
+
+ void strings(std::string s0, std::string s1, std::string s2, std::string s3, std::string s4)
+ {
+ called = true;
+ ensure_equals("s0 mismatch", s0, statics::quick[0]);
+ ensure_equals("s1 mismatch", s1, statics::quick[1]);
+ ensure_equals("s2 mismatch", s2, statics::quick[2]);
+ ensure_equals("s3 mismatch", s3, statics::quick[3]);
+ ensure_equals("s4 mismatch", s4, statics::quick[4]);
+ }
+
+ void ints(int i0, int i1, int i2, int i3, int i4)
+ {
+ called = true;
+ ensure_equals("i0 mismatch", i0, statics::fibs[0]);
+ ensure_equals("i1 mismatch", i1, statics::fibs[1]);
+ ensure_equals("i2 mismatch", i2, statics::fibs[2]);
+ ensure_equals("i3 mismatch", i3, statics::fibs[3]);
+ ensure_equals("i4 mismatch", i4, statics::fibs[4]);
+ }
+
+ void sdfunc(const LLSD& sd)
+ {
+ called = true;
+ ensure_equals("sd mismatch", sd.asInteger(), statics::i);
+ }
+
+ void intfunc(int i)
+ {
+ called = true;
+ ensure_equals("i mismatch", i, statics::i);
+ }
+
+ void voidfunc()
+ {
+ called = true;
+ }
+
+ // recursion tail
+ void collect()
+ {
+ called = true;
+ }
+
+ // collect(arbitrary)
+ template <typename... ARGS>
+ void collect(const std::string& first, ARGS&&... rest)
+ {
+ statics::collected.push_back(first);
+ collect(std::forward<ARGS>(rest)...);
+ }
+ } // namespace statics
+
+ struct apply_data
+ {
+ apply_data()
+ {
+ // reset called before each test
+ statics::called = false;
+ statics::collected.clear();
+ }
+ };
+ typedef test_group<apply_data> apply_group;
+ typedef apply_group::object object;
+ apply_group applygrp("apply");
+
+ template<> template<>
+ void object::test<1>()
+ {
+ set_test_name("apply(tuple)");
+ LL::apply(statics::various,
+ std::make_tuple(statics::b, statics::i, statics::f, statics::s,
+ statics::uu, statics::dt, statics::uri, statics::bin));
+ ensure("apply(tuple) failed", statics::called);
+ }
+
+ template<> template<>
+ void object::test<2>()
+ {
+ set_test_name("apply(array)");
+ LL::apply(statics::ints, statics::fibs);
+ ensure("apply(array) failed", statics::called);
+ }
+
+ template<> template<>
+ void object::test<3>()
+ {
+ set_test_name("apply(vector)");
+ LL::apply(statics::strings, statics::quick);
+ ensure("apply(vector) failed", statics::called);
+ }
+
+ // The various apply(LLSD) tests exercise only the success cases because
+ // the failure cases trigger assert() fail, which is hard to catch.
+ template<> template<>
+ void object::test<4>()
+ {
+ set_test_name("apply(LLSD())");
+ LL::apply(statics::voidfunc, LLSD());
+ ensure("apply(LLSD()) failed", statics::called);
+ }
+
+ template<> template<>
+ void object::test<5>()
+ {
+ set_test_name("apply(fn(int), LLSD scalar)");
+ LL::apply(statics::intfunc, LLSD(statics::i));
+ ensure("apply(fn(int), LLSD scalar) failed", statics::called);
+ }
+
+ template<> template<>
+ void object::test<6>()
+ {
+ set_test_name("apply(fn(LLSD), LLSD scalar)");
+ // This test verifies that LLSDParam<LLSD> doesn't send the compiler
+ // into infinite recursion when the target is itself LLSD.
+ LL::apply(statics::sdfunc, LLSD(statics::i));
+ ensure("apply(fn(LLSD), LLSD scalar) failed", statics::called);
+ }
+
+ template<> template<>
+ void object::test<7>()
+ {
+ set_test_name("apply(LLSD array)");
+ LL::apply(statics::various,
+ llsd::array(statics::b, statics::i, statics::f, statics::s,
+ statics::uu, statics::dt, statics::uri, statics::bin));
+ ensure("apply(LLSD array) failed", statics::called);
+ }
+
+ template<> template<>
+ void object::test<8>()
+ {
+ set_test_name("VAPPLY()");
+ // Make a std::array<std::string> from statics::quick. We can't call a
+ // variadic function with a data structure of dynamic length.
+ std::array<std::string, 5> strray;
+ for (size_t i = 0; i < strray.size(); ++i)
+ strray[i] = statics::quick[i];
+ // This doesn't work: the compiler doesn't know which overload of
+ // collect() to pass to LL::apply().
+ // LL::apply(statics::collect, strray);
+ // That's what VAPPLY() is for.
+ VAPPLY(statics::collect, strray);
+ ensure("VAPPLY() failed", statics::called);
+ ensure_equals("collected mismatch", statics::collected, statics::quick);
+ }
+} // namespace tut
diff --git a/indra/llcommon/tests/lazyeventapi_test.cpp b/indra/llcommon/tests/lazyeventapi_test.cpp
new file mode 100644
index 0000000000..31b2d6d17f
--- /dev/null
+++ b/indra/llcommon/tests/lazyeventapi_test.cpp
@@ -0,0 +1,136 @@
+/**
+ * @file lazyeventapi_test.cpp
+ * @author Nat Goodspeed
+ * @date 2022-06-18
+ * @brief Test for lazyeventapi.
+ *
+ * $LicenseInfo:firstyear=2022&license=viewerlgpl$
+ * Copyright (c) 2022, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "lazyeventapi.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+#include "../test/lltut.h"
+#include "llevents.h"
+#include "llsdutil.h"
+
+// observable side effect, solely for testing
+static LLSD data;
+
+// LLEventAPI listener subclass
+class MyListener: public LLEventAPI
+{
+public:
+ // need this trivial forwarding constructor
+ // (of course do any other initialization your subclass requires)
+ MyListener(const LL::LazyEventAPIParams& params):
+ LLEventAPI(params)
+ {}
+
+ // example operation, registered by LazyEventAPI subclass below
+ void set_data(const LLSD& event)
+ {
+ data = event["data"];
+ }
+};
+
+// LazyEventAPI registrar subclass
+class MyRegistrar: public LL::LazyEventAPI<MyListener>
+{
+ using super = LL::LazyEventAPI<MyListener>;
+ using super::listener;
+public:
+ // LazyEventAPI subclass initializes like a classic LLEventAPI subclass
+ // constructor, with API name and desc plus add() calls for the defined
+ // operations
+ MyRegistrar():
+ super("Test", "This is a test LLEventAPI")
+ {
+ add("set", "This is a set operation", &listener::set_data);
+ }
+};
+// Normally we'd declare a static instance of MyRegistrar -- but because we
+// want to test both with and without, defer declaration to individual test
+// methods.
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct lazyeventapi_data
+ {
+ lazyeventapi_data()
+ {
+ // before every test, reset 'data'
+ data.clear();
+ }
+ ~lazyeventapi_data()
+ {
+ // after every test, reset LLEventPumps
+ LLEventPumps::deleteSingleton();
+ }
+ };
+ typedef test_group<lazyeventapi_data> lazyeventapi_group;
+ typedef lazyeventapi_group::object object;
+ lazyeventapi_group lazyeventapigrp("lazyeventapi");
+
+ template<> template<>
+ void object::test<1>()
+ {
+ set_test_name("LazyEventAPI");
+ // this is where the magic (should) happen
+ // 'register' still a keyword until C++17
+ MyRegistrar regster;
+ LLEventPumps::instance().obtain("Test").post(llsd::map("op", "set", "data", "hey"));
+ ensure_equals("failed to set data", data.asString(), "hey");
+ }
+
+ template<> template<>
+ void object::test<2>()
+ {
+ set_test_name("No LazyEventAPI");
+ // Because the MyRegistrar declaration in test<1>() is local, because
+ // it has been destroyed, we fully expect NOT to reach a MyListener
+ // instance with this post.
+ LLEventPumps::instance().obtain("Test").post(llsd::map("op", "set", "data", "moot"));
+ ensure("accidentally set data", ! data.isDefined());
+ }
+
+ template<> template<>
+ void object::test<3>()
+ {
+ set_test_name("LazyEventAPI metadata");
+ MyRegistrar regster;
+ // Of course we have 'regster' in hand; we don't need to search for
+ // it. But this next test verifies that we can find (all) LazyEventAPI
+ // instances using LazyEventAPIBase::instance_snapshot. Normally we
+ // wouldn't search; normally we'd just look at each instance in the
+ // loop body.
+ const MyRegistrar* found = nullptr;
+ for (const auto& registrar : LL::LazyEventAPIBase::instance_snapshot())
+ if ((found = dynamic_cast<const MyRegistrar*>(&registrar)))
+ break;
+ ensure("Failed to find MyRegistrar via LLInstanceTracker", found);
+
+ ensure_equals("wrong API name", found->getName(), "Test");
+ ensure_contains("wrong API desc", found->getDesc(), "test LLEventAPI");
+ ensure_equals("wrong API field", found->getDispatchKey(), "op");
+ // Normally we'd just iterate over *found. But for test purposes,
+ // actually capture the range of NameDesc pairs in a vector.
+ std::vector<LL::LazyEventAPIBase::NameDesc> ops{ found->begin(), found->end() };
+ ensure_equals("failed to find operations", ops.size(), 1);
+ ensure_equals("wrong operation name", ops[0].first, "set");
+ ensure_contains("wrong operation desc", ops[0].second, "set operation");
+ LLSD metadata{ found->getMetadata(ops[0].first) };
+ ensure_equals("bad metadata name", metadata["name"].asString(), ops[0].first);
+ ensure_equals("bad metadata desc", metadata["desc"].asString(), ops[0].second);
+ }
+} // namespace tut
diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp
index 466f11f52a..b0c532887c 100644
--- a/indra/llcommon/tests/lleventdispatcher_test.cpp
+++ b/indra/llcommon/tests/lleventdispatcher_test.cpp
@@ -18,9 +18,12 @@
// external library headers
// other Linden headers
#include "../test/lltut.h"
+#include "lleventfilter.h"
#include "llsd.h"
#include "llsdutil.h"
+#include "llevents.h"
#include "stringize.h"
+#include "StringVec.h"
#include "tests/wrapllerrs.h"
#include "../test/catch_and_store_what_in.h"
#include "../test/debug.h"
@@ -32,8 +35,6 @@
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/range.hpp>
-#include <boost/foreach.hpp>
-#define foreach BOOST_FOREACH
#include <boost/lambda/lambda.hpp>
@@ -177,6 +178,7 @@ struct Vars
/*-------- Arbitrary-params (non-const, const, static) methods ---------*/
void methodna(NPARAMSa)
{
+ DEBUG;
// Because our const char* param cp might be NULL, and because we
// intend to capture the value in a std::string, have to distinguish
// between the NULL value and any non-NULL value. Use a convention
@@ -188,7 +190,7 @@ struct Vars
else
vcp = std::string("'") + cp + "'";
- debug()("methodna(", b,
+ this->debug()("methodna(", b,
", ", i,
", ", f,
", ", d,
@@ -205,7 +207,7 @@ struct Vars
void methodnb(NPARAMSb)
{
std::ostringstream vbin;
- foreach(U8 byte, bin)
+ for (U8 byte: bin)
{
vbin << std::hex << std::setfill('0') << std::setw(2) << unsigned(byte);
}
@@ -226,7 +228,8 @@ struct Vars
void cmethodna(NPARAMSa) const
{
- debug()('c', NONL);
+ DEBUG;
+ this->debug()('c', NONL);
const_cast<Vars*>(this)->methodna(NARGSa);
}
@@ -315,6 +318,31 @@ void freenb(NPARAMSb)
*****************************************************************************/
namespace tut
{
+ void ensure_has(const std::string& outer, const std::string& inner)
+ {
+ ensure(stringize("'", outer, "' does not contain '", inner, "'"),
+ outer.find(inner) != std::string::npos);
+ }
+
+ template <typename CALLABLE>
+ std::string call_exc(CALLABLE&& func, const std::string& exc_frag)
+ {
+ std::string what =
+ catch_what<LLEventDispatcher::DispatchError>(std::forward<CALLABLE>(func));
+ ensure_has(what, exc_frag);
+ return what;
+ }
+
+ template <typename CALLABLE>
+ void call_logerr(CALLABLE&& func, const std::string& frag)
+ {
+ CaptureLog capture;
+ // the error should be logged; we just need to stop the exception
+ // propagating
+ catch_what<LLEventDispatcher::DispatchError>(std::forward<CALLABLE>(func));
+ capture.messageWith(frag);
+ }
+
struct lleventdispatcher_data
{
Debug debug{"test"};
@@ -397,9 +425,9 @@ namespace tut
work.add(name, desc, &Dispatcher::cmethod1, required);
// Non-subclass method with/out required params
addf("method1", "method1", &v);
- work.add(name, desc, boost::bind(&Vars::method1, boost::ref(v), _1));
+ work.add(name, desc, [this](const LLSD& args){ return v.method1(args); });
addf("method1_req", "method1", &v);
- work.add(name, desc, boost::bind(&Vars::method1, boost::ref(v), _1), required);
+ work.add(name, desc, [this](const LLSD& args){ return v.method1(args); }, required);
/*--------------- Arbitrary params, array style ----------------*/
@@ -461,7 +489,7 @@ namespace tut
debug("dft_array_full:\n",
dft_array_full);
// Partial defaults arrays.
- foreach(LLSD::String a, ab)
+ for (LLSD::String a: ab)
{
LLSD::Integer partition(std::min(partial_offset, dft_array_full[a].size()));
dft_array_partial[a] =
@@ -471,7 +499,7 @@ namespace tut
debug("dft_array_partial:\n",
dft_array_partial);
- foreach(LLSD::String a, ab)
+ for(LLSD::String a: ab)
{
// Generate full defaults maps by zipping (params, dft_array_full).
dft_map_full[a] = zipmap(params[a], dft_array_full[a]);
@@ -583,6 +611,7 @@ namespace tut
void addf(const std::string& n, const std::string& d, Vars* v)
{
+ debug("addf('", n, "', '", d, "')");
// This method is to capture in our own DescMap the name and
// description of every registered function, for metadata query
// testing.
@@ -598,19 +627,14 @@ namespace tut
{
// Copy descs to a temp map of same type.
DescMap forgotten(descs.begin(), descs.end());
- // LLEventDispatcher intentionally provides only const_iterator:
- // since dereferencing that iterator generates values on the fly,
- // it's meaningless to have a modifiable iterator. But since our
- // 'work' object isn't const, by default BOOST_FOREACH() wants to
- // use non-const iterators. Persuade it to use the const_iterator.
- foreach(LLEventDispatcher::NameDesc nd, const_cast<const Dispatcher&>(work))
+ for (LLEventDispatcher::NameDesc nd: work)
{
DescMap::iterator found = forgotten.find(nd.first);
- ensure(STRINGIZE("LLEventDispatcher records function '" << nd.first
- << "' we didn't enter"),
+ ensure(stringize("LLEventDispatcher records function '", nd.first,
+ "' we didn't enter"),
found != forgotten.end());
- ensure_equals(STRINGIZE("LLEventDispatcher desc '" << nd.second <<
- "' doesn't match what we entered: '" << found->second << "'"),
+ ensure_equals(stringize("LLEventDispatcher desc '", nd.second,
+ "' doesn't match what we entered: '", found->second, "'"),
nd.second, found->second);
// found in our map the name from LLEventDispatcher, good, erase
// our map entry
@@ -621,41 +645,49 @@ namespace tut
std::ostringstream out;
out << "LLEventDispatcher failed to report";
const char* delim = ": ";
- foreach(const DescMap::value_type& fme, forgotten)
+ for (const DescMap::value_type& fme: forgotten)
{
out << delim << fme.first;
delim = ", ";
}
- ensure(out.str(), false);
+ throw failure(out.str());
}
}
Vars* varsfor(const std::string& name)
{
VarsMap::const_iterator found = funcvars.find(name);
- ensure(STRINGIZE("No Vars* for " << name), found != funcvars.end());
- ensure(STRINGIZE("NULL Vars* for " << name), found->second);
+ ensure(stringize("No Vars* for ", name), found != funcvars.end());
+ ensure(stringize("NULL Vars* for ", name), found->second);
return found->second;
}
- void ensure_has(const std::string& outer, const std::string& inner)
+ std::string call_exc(const std::string& func, const LLSD& args, const std::string& exc_frag)
{
- ensure(STRINGIZE("'" << outer << "' does not contain '" << inner << "'").c_str(),
- outer.find(inner) != std::string::npos);
+ return tut::call_exc(
+ [this, func, args]()
+ {
+ if (func.empty())
+ {
+ work(args);
+ }
+ else
+ {
+ work(func, args);
+ }
+ },
+ exc_frag);
}
- void call_exc(const std::string& func, const LLSD& args, const std::string& exc_frag)
+ void call_logerr(const std::string& func, const LLSD& args, const std::string& frag)
{
- std::string threw = catch_what<std::runtime_error>([this, &func, &args](){
- work(func, args);
- });
- ensure_has(threw, exc_frag);
+ tut::call_logerr([this, func, args](){ work(func, args); }, frag);
}
LLSD getMetadata(const std::string& name)
{
LLSD meta(work.getMetadata(name));
- ensure(STRINGIZE("No metadata for " << name), meta.isDefined());
+ ensure(stringize("No metadata for ", name), meta.isDefined());
return meta;
}
@@ -724,7 +756,7 @@ namespace tut
set_test_name("map-style registration with non-array params");
// Pass "param names" as scalar or as map
LLSD attempts(llsd::array(17, LLSDMap("pi", 3.14)("two", 2)));
- foreach(LLSD ae, inArray(attempts))
+ for (LLSD ae: inArray(attempts))
{
std::string threw = catch_what<std::exception>([this, &ae](){
work.add("freena_err", "freena", freena, ae);
@@ -799,7 +831,7 @@ namespace tut
{
set_test_name("query Callables with/out required params");
LLSD names(llsd::array("free1", "Dmethod1", "Dcmethod1", "method1"));
- foreach(LLSD nm, inArray(names))
+ for (LLSD nm: inArray(names))
{
LLSD metadata(getMetadata(nm));
ensure_equals("name mismatch", metadata["name"], nm);
@@ -828,19 +860,19 @@ namespace tut
(5, llsd::array("freena_array", "smethodna_array", "methodna_array")),
llsd::array
(5, llsd::array("freenb_array", "smethodnb_array", "methodnb_array"))));
- foreach(LLSD ae, inArray(expected))
+ for (LLSD ae: inArray(expected))
{
LLSD::Integer arity(ae[0].asInteger());
LLSD names(ae[1]);
LLSD req(LLSD::emptyArray());
if (arity)
req[arity - 1] = LLSD();
- foreach(LLSD nm, inArray(names))
+ for (LLSD nm: inArray(names))
{
LLSD metadata(getMetadata(nm));
ensure_equals("name mismatch", metadata["name"], nm);
ensure_equals(metadata["desc"].asString(), descs[nm]);
- ensure_equals(STRINGIZE("mismatched required for " << nm.asString()),
+ ensure_equals(stringize("mismatched required for ", nm.asString()),
metadata["required"], req);
ensure("should not have optional", metadata["optional"].isUndefined());
}
@@ -854,7 +886,7 @@ namespace tut
// - (Free function | non-static method), map style, no params (ergo
// no defaults)
LLSD names(llsd::array("free0_map", "smethod0_map", "method0_map"));
- foreach(LLSD nm, inArray(names))
+ for (LLSD nm: inArray(names))
{
LLSD metadata(getMetadata(nm));
ensure_equals("name mismatch", metadata["name"], nm);
@@ -884,7 +916,7 @@ namespace tut
llsd::array("smethodnb_map_adft", "smethodnb_map_mdft"),
llsd::array("methodna_map_adft", "methodna_map_mdft"),
llsd::array("methodnb_map_adft", "methodnb_map_mdft")));
- foreach(LLSD eq, inArray(equivalences))
+ for (LLSD eq: inArray(equivalences))
{
LLSD adft(eq[0]);
LLSD mdft(eq[1]);
@@ -898,8 +930,8 @@ namespace tut
ensure_equals("mdft name", mdft, mmeta["name"]);
ameta.erase("name");
mmeta.erase("name");
- ensure_equals(STRINGIZE("metadata for " << adft.asString()
- << " vs. " << mdft.asString()),
+ ensure_equals(stringize("metadata for ", adft.asString(),
+ " vs. ", mdft.asString()),
ameta, mmeta);
}
}
@@ -915,7 +947,7 @@ namespace tut
// params are required. Also maps containing left requirements for
// partial defaults arrays. Also defaults maps from defaults arrays.
LLSD allreq, leftreq, rightdft;
- foreach(LLSD::String a, ab)
+ for (LLSD::String a: ab)
{
// The map in which all params are required uses params[a] as
// keys, with all isUndefined() as values. We can accomplish that
@@ -943,9 +975,9 @@ namespace tut
// Generate maps containing parameter names not provided by the
// dft_map_partial maps.
LLSD skipreq(allreq);
- foreach(LLSD::String a, ab)
+ for (LLSD::String a: ab)
{
- foreach(const MapEntry& me, inMap(dft_map_partial[a]))
+ for (const MapEntry& me: inMap(dft_map_partial[a]))
{
skipreq[a].erase(me.first);
}
@@ -990,7 +1022,7 @@ namespace tut
(llsd::array("freenb_map_mdft", "smethodnb_map_mdft", "methodnb_map_mdft"),
llsd::array(LLSD::emptyMap(), dft_map_full["b"])))); // required, optional
- foreach(LLSD grp, inArray(groups))
+ for (LLSD grp: inArray(groups))
{
// Internal structure of each group in 'groups':
LLSD names(grp[0]);
@@ -1003,14 +1035,14 @@ namespace tut
optional);
// Loop through 'names'
- foreach(LLSD nm, inArray(names))
+ for (LLSD nm: inArray(names))
{
LLSD metadata(getMetadata(nm));
ensure_equals("name mismatch", metadata["name"], nm);
ensure_equals(nm.asString(), metadata["desc"].asString(), descs[nm]);
- ensure_equals(STRINGIZE(nm << " required mismatch"),
+ ensure_equals(stringize(nm, " required mismatch"),
metadata["required"], required);
- ensure_equals(STRINGIZE(nm << " optional mismatch"),
+ ensure_equals(stringize(nm, " optional mismatch"),
metadata["optional"], optional);
}
}
@@ -1031,13 +1063,7 @@ namespace tut
{
set_test_name("call with bad name");
call_exc("freek", LLSD(), "not found");
- // We don't have a comparable helper function for the one-arg
- // operator() method, and it's not worth building one just for this
- // case. Write it out.
- std::string threw = catch_what<std::runtime_error>([this](){
- work(LLSDMap("op", "freek"));
- });
- ensure_has(threw, "bad");
+ std::string threw = call_exc("", LLSDMap("op", "freek"), "bad");
ensure_has(threw, "op");
ensure_has(threw, "freek");
}
@@ -1079,7 +1105,7 @@ namespace tut
// LLSD value matching 'required' according to llsd_matches() rules.
LLSD matching(LLSDMap("d", 3.14)("array", llsd::array("answer", true, answer)));
// Okay, walk through 'tests'.
- foreach(const CallablesTriple& tr, tests)
+ for (const CallablesTriple& tr: tests)
{
// Should be able to pass 'answer' to Callables registered
// without 'required'.
@@ -1087,7 +1113,7 @@ namespace tut
ensure_equals("answer mismatch", tr.llsd, answer);
// Should NOT be able to pass 'answer' to Callables registered
// with 'required'.
- call_exc(tr.name_req, answer, "bad request");
+ call_logerr(tr.name_req, answer, "bad request");
// But SHOULD be able to pass 'matching' to Callables registered
// with 'required'.
work(tr.name_req, matching);
@@ -1101,17 +1127,20 @@ namespace tut
set_test_name("passing wrong args to (map | array)-style registrations");
// Pass scalar/map to array-style functions, scalar/array to map-style
- // functions. As that validation happens well before we engage the
- // argument magic, it seems pointless to repeat this with every
- // variation: (free function | non-static method), (no | arbitrary)
- // args. We should only need to engage it for one map-style
- // registration and one array-style registration.
- std::string array_exc("needs an args array");
- call_exc("free0_array", 17, array_exc);
- call_exc("free0_array", LLSDMap("pi", 3.14), array_exc);
+ // functions. It seems pointless to repeat this with every variation:
+ // (free function | non-static method), (no | arbitrary) args. We
+ // should only need to engage it for one map-style registration and
+ // one array-style registration.
+ // Now that LLEventDispatcher has been extended to treat an LLSD
+ // scalar as a single-entry array, the error we expect in this case is
+ // that apply() is trying to pass that non-empty array to a nullary
+ // function.
+ call_logerr("free0_array", 17, "LL::apply");
+ // similarly, apply() doesn't accept an LLSD Map
+ call_logerr("free0_array", LLSDMap("pi", 3.14), "unsupported");
std::string map_exc("needs a map");
- call_exc("free0_map", 17, map_exc);
+ call_logerr("free0_map", 17, map_exc);
// Passing an array to a map-style function works now! No longer an
// error case!
// call_exc("free0_map", llsd::array("a", "b"), map_exc);
@@ -1125,7 +1154,7 @@ namespace tut
("free0_array", "free0_map",
"smethod0_array", "smethod0_map",
"method0_array", "method0_map"));
- foreach(LLSD name, inArray(names))
+ for (LLSD name: inArray(names))
{
// Look up the Vars instance for this function.
Vars* vars(varsfor(name));
@@ -1150,15 +1179,21 @@ namespace tut
template<> template<>
void object::test<19>()
{
- set_test_name("call array-style functions with too-short arrays");
- // Could have two different too-short arrays, one for *na and one for
- // *nb, but since they both take 5 params...
+ set_test_name("call array-style functions with wrong-length arrays");
+ // Could have different wrong-length arrays for *na and for *nb, but
+ // since they both take 5 params...
LLSD tooshort(llsd::array("this", "array", "too", "short"));
- foreach(const LLSD& funcsab, inArray(array_funcs))
+ LLSD toolong (llsd::array("this", "array", "is", "one", "too", "long"));
+ LLSD badargs (llsd::array(tooshort, toolong));
+ for (const LLSD& toosomething: inArray(badargs))
{
- foreach(const llsd::MapEntry& e, inMap(funcsab))
+ for (const LLSD& funcsab: inArray(array_funcs))
{
- call_exc(e.second, tooshort, "requires more arguments");
+ for (const llsd::MapEntry& e: inMap(funcsab))
+ {
+ // apply() complains about wrong number of array entries
+ call_logerr(e.second, toosomething, "LL::apply");
+ }
}
}
}
@@ -1166,7 +1201,7 @@ namespace tut
template<> template<>
void object::test<20>()
{
- set_test_name("call array-style functions with (just right | too long) arrays");
+ set_test_name("call array-style functions with right-size arrays");
std::vector<U8> binary;
for (size_t h(0x01), i(0); i < 5; h+= 0x22, ++i)
{
@@ -1178,40 +1213,25 @@ namespace tut
LLDate("2011-02-03T15:07:00Z"),
LLURI("http://secondlife.com"),
binary)));
- LLSD argsplus(args);
- argsplus["a"].append("bogus");
- argsplus["b"].append("bogus");
LLSD expect;
- foreach(LLSD::String a, ab)
+ for (LLSD::String a: ab)
{
expect[a] = zipmap(params[a], args[a]);
}
// Adjust expect["a"]["cp"] for special Vars::cp treatment.
- expect["a"]["cp"] = std::string("'") + expect["a"]["cp"].asString() + "'";
+ expect["a"]["cp"] = stringize("'", expect["a"]["cp"].asString(), "'");
debug("expect: ", expect);
- // Use substantially the same logic for args and argsplus
- LLSD argsarrays(llsd::array(args, argsplus));
- // So i==0 selects 'args', i==1 selects argsplus
- for (LLSD::Integer i(0), iend(argsarrays.size()); i < iend; ++i)
+ for (const LLSD& funcsab: inArray(array_funcs))
{
- foreach(const LLSD& funcsab, inArray(array_funcs))
+ for (LLSD::String a: ab)
{
- foreach(LLSD::String a, ab)
- {
- // Reset the Vars instance before each call
- Vars* vars(varsfor(funcsab[a]));
- *vars = Vars();
- work(funcsab[a], argsarrays[i][a]);
- ensure_llsd(STRINGIZE(funcsab[a].asString() <<
- ": expect[\"" << a << "\"] mismatch"),
- vars->inspect(), expect[a], 7); // 7 bits ~= 2 decimal digits
-
- // TODO: in the i==1 or argsplus case, intercept LL_WARNS
- // output? Even without that, using argsplus verifies that
- // passing too many args isn't fatal; it works -- but
- // would be nice to notice the warning too.
- }
+ // Reset the Vars instance before each call
+ Vars* vars(varsfor(funcsab[a]));
+ *vars = Vars();
+ work(funcsab[a], args[a]);
+ ensure_llsd(stringize(funcsab[a].asString(), ": expect[\"", a, "\"] mismatch"),
+ vars->inspect(), expect[a], 7); // 7 bits ~= 2 decimal digits
}
}
}
@@ -1239,7 +1259,7 @@ namespace tut
("a", llsd::array(false, 255, 98.6, 1024.5, "pointer"))
("b", llsd::array("object", LLUUID::generateNewID(), LLDate::now(), LLURI("http://wiki.lindenlab.com/wiki"), LLSD::Binary(boost::begin(binary), boost::end(binary)))));
LLSD array_overfull(array_full);
- foreach(LLSD::String a, ab)
+ for (LLSD::String a: ab)
{
array_overfull[a].append("bogus");
}
@@ -1253,7 +1273,7 @@ namespace tut
ensure_not_equals("UUID collision",
array_full["b"][1].asUUID(), dft_array_full["b"][1].asUUID());
LLSD map_full, map_overfull;
- foreach(LLSD::String a, ab)
+ for (LLSD::String a: ab)
{
map_full[a] = zipmap(params[a], array_full[a]);
map_overfull[a] = map_full[a];
@@ -1294,21 +1314,360 @@ namespace tut
"freenb_map_mdft", "smethodnb_map_mdft", "methodnb_map_mdft")));
// Treat (full | overfull) (array | map) the same.
LLSD argssets(llsd::array(array_full, array_overfull, map_full, map_overfull));
- foreach(const LLSD& args, inArray(argssets))
+ for (const LLSD& args: inArray(argssets))
{
- foreach(LLSD::String a, ab)
+ for (LLSD::String a: ab)
{
- foreach(LLSD::String name, inArray(names[a]))
+ for (LLSD::String name: inArray(names[a]))
{
// Reset the Vars instance
Vars* vars(varsfor(name));
*vars = Vars();
work(name, args[a]);
- ensure_llsd(STRINGIZE(name << ": expect[\"" << a << "\"] mismatch"),
+ ensure_llsd(stringize(name, ": expect[\"", a, "\"] mismatch"),
vars->inspect(), expect[a], 7); // 7 bits, 2 decimal digits
// intercept LL_WARNS for the two overfull cases?
}
}
}
}
+
+ struct DispatchResult: public LLDispatchListener
+ {
+ using DR = DispatchResult;
+
+ DispatchResult(): LLDispatchListener("results", "op")
+ {
+ add("strfunc", "return string", &DR::strfunc);
+ add("voidfunc", "void function", &DR::voidfunc);
+ add("emptyfunc", "return empty LLSD", &DR::emptyfunc);
+ add("intfunc", "return Integer LLSD", &DR::intfunc);
+ add("llsdfunc", "return passed LLSD", &DR::llsdfunc);
+ add("mapfunc", "return map LLSD", &DR::mapfunc);
+ add("arrayfunc", "return array LLSD", &DR::arrayfunc);
+ }
+
+ std::string strfunc(const std::string& str) const { return "got " + str; }
+ void voidfunc() const {}
+ LLSD emptyfunc() const { return {}; }
+ int intfunc(int i) const { return -i; }
+ LLSD llsdfunc(const LLSD& event) const
+ {
+ LLSD result{ event };
+ result["with"] = "string";
+ return result;
+ }
+ LLSD mapfunc(int i, const std::string& str) const
+ {
+ return llsd::map("i", intfunc(i), "str", strfunc(str));
+ }
+ LLSD arrayfunc(int i, const std::string& str) const
+ {
+ return llsd::array(intfunc(i), strfunc(str));
+ }
+ };
+
+ template<> template<>
+ void object::test<23>()
+ {
+ set_test_name("string result");
+ DispatchResult service;
+ LLSD result{ service("strfunc", "a string") };
+ ensure_equals("strfunc() mismatch", result.asString(), "got a string");
+ }
+
+ template<> template<>
+ void object::test<24>()
+ {
+ set_test_name("void result");
+ DispatchResult service;
+ LLSD result{ service("voidfunc", LLSD()) };
+ ensure("voidfunc() returned defined", result.isUndefined());
+ }
+
+ template<> template<>
+ void object::test<25>()
+ {
+ set_test_name("Integer result");
+ DispatchResult service;
+ LLSD result{ service("intfunc", -17) };
+ ensure_equals("intfunc() mismatch", result.asInteger(), 17);
+ }
+
+ template<> template<>
+ void object::test<26>()
+ {
+ set_test_name("LLSD echo");
+ DispatchResult service;
+ LLSD result{ service("llsdfunc", llsd::map("op", "llsdfunc", "reqid", 17)) };
+ ensure_equals("llsdfunc() mismatch", result,
+ llsd::map("op", "llsdfunc", "reqid", 17, "with", "string"));
+ }
+
+ template<> template<>
+ void object::test<27>()
+ {
+ set_test_name("map LLSD result");
+ DispatchResult service;
+ LLSD result{ service("mapfunc", llsd::array(-12, "value")) };
+ ensure_equals("mapfunc() mismatch", result, llsd::map("i", 12, "str", "got value"));
+ }
+
+ template<> template<>
+ void object::test<28>()
+ {
+ set_test_name("array LLSD result");
+ DispatchResult service;
+ LLSD result{ service("arrayfunc", llsd::array(-8, "word")) };
+ ensure_equals("arrayfunc() mismatch", result, llsd::array(8, "got word"));
+ }
+
+ template<> template<>
+ void object::test<29>()
+ {
+ set_test_name("listener error, no reply");
+ DispatchResult service;
+ tut::call_exc(
+ [&service]()
+ { service.post(llsd::map("op", "nosuchfunc", "reqid", 17)); },
+ "nosuchfunc");
+ }
+
+ template<> template<>
+ void object::test<30>()
+ {
+ set_test_name("listener error with reply");
+ DispatchResult service;
+ LLCaptureListener<LLSD> result;
+ service.post(llsd::map("op", "nosuchfunc", "reqid", 17, "reply", result.getName()));
+ LLSD reply{ result.get() };
+ ensure("no reply", reply.isDefined());
+ ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17);
+ ensure_has(reply["error"].asString(), "nosuchfunc");
+ }
+
+ template<> template<>
+ void object::test<31>()
+ {
+ set_test_name("listener call to void function");
+ DispatchResult service;
+ LLCaptureListener<LLSD> result;
+ result.set("non-empty");
+ for (const auto& func: StringVec{ "voidfunc", "emptyfunc" })
+ {
+ service.post(llsd::map(
+ "op", func,
+ "reqid", 17,
+ "reply", result.getName()));
+ ensure_equals("reply from " + func, result.get().asString(), "non-empty");
+ }
+ }
+
+ template<> template<>
+ void object::test<32>()
+ {
+ set_test_name("listener call to string function");
+ DispatchResult service;
+ LLCaptureListener<LLSD> result;
+ service.post(llsd::map(
+ "op", "strfunc",
+ "args", llsd::array("a string"),
+ "reqid", 17,
+ "reply", result.getName()));
+ LLSD reply{ result.get() };
+ ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17);
+ ensure_equals("bad reply from strfunc", reply["data"].asString(), "got a string");
+ }
+
+ template<> template<>
+ void object::test<33>()
+ {
+ set_test_name("listener call to map function");
+ DispatchResult service;
+ LLCaptureListener<LLSD> result;
+ service.post(llsd::map(
+ "op", "mapfunc",
+ "args", llsd::array(-7, "value"),
+ "reqid", 17,
+ "reply", result.getName()));
+ LLSD reply{ result.get() };
+ ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17);
+ ensure_equals("bad i from mapfunc", reply["i"].asInteger(), 7);
+ ensure_equals("bad str from mapfunc", reply["str"], "got value");
+ }
+
+ template<> template<>
+ void object::test<34>()
+ {
+ set_test_name("batched map success");
+ DispatchResult service;
+ LLCaptureListener<LLSD> result;
+ service.post(llsd::map(
+ "op", llsd::map(
+ "strfunc", "some string",
+ "intfunc", 2,
+ "voidfunc", LLSD(),
+ "arrayfunc", llsd::array(-5, "other string")),
+ "reqid", 17,
+ "reply", result.getName()));
+ LLSD reply{ result.get() };
+ ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17);
+ reply.erase("reqid");
+ ensure_equals(
+ "bad map batch",
+ reply,
+ llsd::map(
+ "strfunc", "got some string",
+ "intfunc", -2,
+ "voidfunc", LLSD(),
+ "arrayfunc", llsd::array(5, "got other string")));
+ }
+
+ template<> template<>
+ void object::test<35>()
+ {
+ set_test_name("batched map error");
+ DispatchResult service;
+ LLCaptureListener<LLSD> result;
+ service.post(llsd::map(
+ "op", llsd::map(
+ "badfunc", 34, // !
+ "strfunc", "some string",
+ "intfunc", 2,
+ "missing", LLSD(), // !
+ "voidfunc", LLSD(),
+ "arrayfunc", llsd::array(-5, "other string")),
+ "reqid", 17,
+ "reply", result.getName()));
+ LLSD reply{ result.get() };
+ ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17);
+ reply.erase("reqid");
+ auto error{ reply["error"].asString() };
+ reply.erase("error");
+ ensure_has(error, "badfunc");
+ ensure_has(error, "missing");
+ ensure_equals(
+ "bad partial batch",
+ reply,
+ llsd::map(
+ "strfunc", "got some string",
+ "intfunc", -2,
+ "voidfunc", LLSD(),
+ "arrayfunc", llsd::array(5, "got other string")));
+ }
+
+ template<> template<>
+ void object::test<36>()
+ {
+ set_test_name("batched map exception");
+ DispatchResult service;
+ auto error = tut::call_exc(
+ [&service]()
+ {
+ service.post(llsd::map(
+ "op", llsd::map(
+ "badfunc", 34, // !
+ "strfunc", "some string",
+ "intfunc", 2,
+ "missing", LLSD(), // !
+ "voidfunc", LLSD(),
+ "arrayfunc", llsd::array(-5, "other string")),
+ "reqid", 17));
+ // no "reply"
+ },
+ "badfunc");
+ ensure_has(error, "missing");
+ }
+
+ template<> template<>
+ void object::test<37>()
+ {
+ set_test_name("batched array success");
+ DispatchResult service;
+ LLCaptureListener<LLSD> result;
+ service.post(llsd::map(
+ "op", llsd::array(
+ llsd::array("strfunc", "some string"),
+ llsd::array("intfunc", 2),
+ "arrayfunc",
+ "voidfunc"),
+ "args", llsd::array(
+ LLSD(),
+ LLSD(),
+ llsd::array(-5, "other string")),
+ // args array deliberately short, since the default
+ // [3] is undefined, which should work for voidfunc
+ "reqid", 17,
+ "reply", result.getName()));
+ LLSD reply{ result.get() };
+ ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17);
+ reply.erase("reqid");
+ ensure_equals(
+ "bad array batch",
+ reply,
+ llsd::map(
+ "data", llsd::array(
+ "got some string",
+ -2,
+ llsd::array(5, "got other string"),
+ LLSD())));
+ }
+
+ template<> template<>
+ void object::test<38>()
+ {
+ set_test_name("batched array error");
+ DispatchResult service;
+ LLCaptureListener<LLSD> result;
+ service.post(llsd::map(
+ "op", llsd::array(
+ llsd::array("strfunc", "some string"),
+ llsd::array("intfunc", 2, "whoops"), // bad form
+ "arrayfunc",
+ "voidfunc"),
+ "args", llsd::array(
+ LLSD(),
+ LLSD(),
+ llsd::array(-5, "other string")),
+ // args array deliberately short, since the default
+ // [3] is undefined, which should work for voidfunc
+ "reqid", 17,
+ "reply", result.getName()));
+ LLSD reply{ result.get() };
+ ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17);
+ reply.erase("reqid");
+ auto error{ reply["error"] };
+ reply.erase("error");
+ ensure_has(error, "[1]");
+ ensure_has(error, "unsupported");
+ ensure_equals("bad array batch", reply,
+ llsd::map("data", llsd::array("got some string")));
+ }
+
+ template<> template<>
+ void object::test<39>()
+ {
+ set_test_name("batched array exception");
+ DispatchResult service;
+ auto error = tut::call_exc(
+ [&service]()
+ {
+ service.post(llsd::map(
+ "op", llsd::array(
+ llsd::array("strfunc", "some string"),
+ llsd::array("intfunc", 2, "whoops"), // bad form
+ "arrayfunc",
+ "voidfunc"),
+ "args", llsd::array(
+ LLSD(),
+ LLSD(),
+ llsd::array(-5, "other string")),
+ // args array deliberately short, since the default
+ // [3] is undefined, which should work for voidfunc
+ "reqid", 17));
+ // no "reply"
+ },
+ "[1]");
+ ensure_has(error, "unsupported");
+ }
} // namespace tut
diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h
index 3779fb41bc..d657b329bb 100644
--- a/indra/llcommon/tests/wrapllerrs.h
+++ b/indra/llcommon/tests/wrapllerrs.h
@@ -226,6 +226,11 @@ public:
return boost::dynamic_pointer_cast<CaptureLogRecorder>(mRecorder)->streamto(out);
}
+ friend inline std::ostream& operator<<(std::ostream& out, const CaptureLog& self)
+ {
+ return self.streamto(out);
+ }
+
private:
LLError::FatalFunction mFatalFunction;
LLError::SettingsStoragePtr mOldSettings;