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/llleap_test.cpp259
-rw-r--r--indra/llcommon/tests/llprocess_test.cpp74
-rw-r--r--indra/llcommon/tests/llrand_test.cpp53
-rw-r--r--indra/llcommon/tests/llsdserialize_test.cpp133
-rw-r--r--indra/llcommon/tests/workqueue_test.cpp6
-rw-r--r--indra/llcommon/tests/wrapllerrs.h5
9 files changed, 1132 insertions, 343 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/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp
index 3ae48a2532..7197dedfbf 100644
--- a/indra/llcommon/tests/llleap_test.cpp
+++ b/indra/llcommon/tests/llleap_test.cpp
@@ -17,8 +17,6 @@
// std headers
#include <functional>
// external library headers
-#include <boost/assign/list_of.hpp>
-#include <boost/phoenix/core/argument.hpp>
// other Linden headers
#include "../test/lltut.h"
#include "../test/namedtempfile.h"
@@ -30,10 +28,6 @@
#include "stringize.h"
#include "StringVec.h"
-using boost::assign::list_of;
-
-StringVec sv(const StringVec& listof) { return listof; }
-
#if defined(LL_WINDOWS)
#define sleep(secs) _sleep((secs) * 1000)
@@ -104,17 +98,12 @@ namespace tut
llleap_data():
reader(".py",
// This logic is adapted from vita.viewerclient.receiveEvent()
- boost::phoenix::placeholders::arg1 <<
+ [](std::ostream& out){ out <<
"import re\n"
"import os\n"
"import sys\n"
"\n"
- "try:\n"
- // new freestanding llsd package
- " import llsd\n"
- "except ImportError:\n"
- // older llbase.llsd module
- " from llbase import llsd\n"
+ "import llsd\n"
"\n"
"class ProtocolError(Exception):\n"
" def __init__(self, msg, data):\n"
@@ -193,7 +182,7 @@ namespace tut
"def request(pump, data):\n"
" # we expect 'data' is a dict\n"
" data['reply'] = _reply\n"
- " send(pump, data)\n"),
+ " send(pump, data)\n";}),
// Get the actual pathname of the NamedExtTempFile and trim off
// the ".py" extension. (We could cache reader.getName() in a
// separate member variable, but I happen to know getName() just
@@ -218,14 +207,14 @@ namespace tut
void object::test<1>()
{
set_test_name("multiple LLLeap instances");
- NamedTempFile script("py",
- "import time\n"
- "time.sleep(1)\n");
+ NamedExtTempFile script("py",
+ "import time\n"
+ "time.sleep(1)\n");
LLLeapVector instances;
instances.push_back(LLLeap::create(get_test_name(),
- sv(list_of(PYTHON)(script.getName())))->getWeak());
+ StringVec{PYTHON, script.getName()})->getWeak());
instances.push_back(LLLeap::create(get_test_name(),
- sv(list_of(PYTHON)(script.getName())))->getWeak());
+ StringVec{PYTHON, script.getName()})->getWeak());
// In this case we're simply establishing that two LLLeap instances
// can coexist without throwing exceptions or bombing in any other
// way. Wait for them to terminate.
@@ -236,10 +225,10 @@ namespace tut
void object::test<2>()
{
set_test_name("stderr to log");
- NamedTempFile script("py",
- "import sys\n"
- "sys.stderr.write('''Hello from Python!\n"
- "note partial line''')\n");
+ NamedExtTempFile script("py",
+ "import sys\n"
+ "sys.stderr.write('''Hello from Python!\n"
+ "note partial line''')\n");
StringVec vcommand{ PYTHON, script.getName() };
CaptureLog log(LLError::LEVEL_INFO);
waitfor(LLLeap::create(get_test_name(), vcommand));
@@ -251,11 +240,11 @@ namespace tut
void object::test<3>()
{
set_test_name("bad stdout protocol");
- NamedTempFile script("py",
- "print('Hello from Python!')\n");
+ NamedExtTempFile script("py",
+ "print('Hello from Python!')\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
- sv(list_of(PYTHON)(script.getName()))));
+ StringVec{PYTHON, script.getName()}));
ensure_contains("error log line",
log.messageWith("invalid protocol"), "Hello from Python!");
}
@@ -264,13 +253,13 @@ namespace tut
void object::test<4>()
{
set_test_name("leftover stdout");
- NamedTempFile script("py",
- "import sys\n"
- // note lack of newline
- "sys.stdout.write('Hello from Python!')\n");
+ NamedExtTempFile script("py",
+ "import sys\n"
+ // note lack of newline
+ "sys.stdout.write('Hello from Python!')\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
- sv(list_of(PYTHON)(script.getName()))));
+ StringVec{PYTHON, script.getName()}));
ensure_contains("error log line",
log.messageWith("Discarding"), "Hello from Python!");
}
@@ -279,12 +268,12 @@ namespace tut
void object::test<5>()
{
set_test_name("bad stdout len prefix");
- NamedTempFile script("py",
- "import sys\n"
- "sys.stdout.write('5a2:something')\n");
+ NamedExtTempFile script("py",
+ "import sys\n"
+ "sys.stdout.write('5a2:something')\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
- sv(list_of(PYTHON)(script.getName()))));
+ StringVec{PYTHON, script.getName()}));
ensure_contains("error log line",
log.messageWith("invalid protocol"), "5a2:");
}
@@ -386,17 +375,18 @@ namespace tut
set_test_name("round trip");
AckAPI api;
Result result;
- NamedTempFile script("py",
- boost::phoenix::placeholders::arg1 <<
- "from " << reader_module << " import *\n"
- // make a request on our little API
- "request(pump='" << api.getName() << "', data={})\n"
- // wait for its response
- "resp = get()\n"
- "result = '' if resp == dict(pump=replypump(), data='ack')\\\n"
- " else 'bad: ' + str(resp)\n"
- "send(pump='" << result.getName() << "', data=result)\n");
- waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName()))));
+ NamedExtTempFile script("py",
+ [&](std::ostream& out){ out <<
+ "from " << reader_module << " import *\n"
+ // make a request on our little API
+ "request(pump='" << api.getName() << "', data={})\n"
+ // wait for its response
+ "resp = get()\n"
+ "result = '' if resp == dict(pump=replypump(), data='ack')\\\n"
+ " else 'bad: ' + str(resp)\n"
+ "send(pump='" << result.getName() << "', data=result)\n";});
+ waitfor(LLLeap::create(get_test_name(),
+ StringVec{PYTHON, script.getName()}));
result.ensure();
}
@@ -424,38 +414,38 @@ namespace tut
// iterations etc. in OS pipes and the LLLeap/LLProcess implementation.
ReqIDAPI api;
Result result;
- NamedTempFile script("py",
- boost::phoenix::placeholders::arg1 <<
- "import sys\n"
- "from " << reader_module << " import *\n"
- // Note that since reader imports llsd, this
- // 'import *' gets us llsd too.
- "sample = llsd.format_notation(dict(pump='" <<
- api.getName() << "', data=dict(reqid=999999, reply=replypump())))\n"
- // The whole packet has length prefix too: "len:data"
- "samplen = len(str(len(sample))) + 1 + len(sample)\n"
- // guess how many messages it will take to
- // accumulate BUFFERED_LENGTH
- "count = int(" << BUFFERED_LENGTH << "/samplen)\n"
- "print('Sending %s requests' % count, file=sys.stderr)\n"
- "for i in range(count):\n"
- " request('" << api.getName() << "', dict(reqid=i))\n"
- // The assumption in this specific test that
- // replies will arrive in the same order as
- // requests is ONLY valid because the API we're
- // invoking sends replies instantly. If the API
- // had to wait for some external event before
- // sending its reply, replies could arrive in
- // arbitrary order, and we'd have to tick them
- // off from a set.
- "result = ''\n"
- "for i in range(count):\n"
- " resp = get()\n"
- " if resp['data']['reqid'] != i:\n"
- " result = 'expected reqid=%s in %s' % (i, resp)\n"
- " break\n"
- "send(pump='" << result.getName() << "', data=result)\n");
- waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName()))),
+ NamedExtTempFile script("py",
+ [&](std::ostream& out){ out <<
+ "import sys\n"
+ "from " << reader_module << " import *\n"
+ // Note that since reader imports llsd, this
+ // 'import *' gets us llsd too.
+ "sample = llsd.format_notation(dict(pump='" <<
+ api.getName() << "', data=dict(reqid=999999, reply=replypump())))\n"
+ // The whole packet has length prefix too: "len:data"
+ "samplen = len(str(len(sample))) + 1 + len(sample)\n"
+ // guess how many messages it will take to
+ // accumulate BUFFERED_LENGTH
+ "count = int(" << BUFFERED_LENGTH << "/samplen)\n"
+ "print('Sending %s requests' % count, file=sys.stderr)\n"
+ "for i in range(count):\n"
+ " request('" << api.getName() << "', dict(reqid=i))\n"
+ // The assumption in this specific test that
+ // replies will arrive in the same order as
+ // requests is ONLY valid because the API we're
+ // invoking sends replies instantly. If the API
+ // had to wait for some external event before
+ // sending its reply, replies could arrive in
+ // arbitrary order, and we'd have to tick them
+ // off from a set.
+ "result = ''\n"
+ "for i in range(count):\n"
+ " resp = get()\n"
+ " if resp['data']['reqid'] != i:\n"
+ " result = 'expected reqid=%s in %s' % (i, resp)\n"
+ " break\n"
+ "send(pump='" << result.getName() << "', data=result)\n";});
+ waitfor(LLLeap::create(get_test_name(), StringVec{PYTHON, script.getName()}),
300); // needs more realtime than most tests
result.ensure();
}
@@ -467,65 +457,62 @@ namespace tut
{
ReqIDAPI api;
Result result;
- NamedTempFile script("py",
- boost::phoenix::placeholders::arg1 <<
- "import sys\n"
- "from " << reader_module << " import *\n"
- // Generate a very large string value.
- "desired = int(sys.argv[1])\n"
- // 7 chars per item: 6 digits, 1 comma
- "count = int((desired - 50)/7)\n"
- "large = ''.join('%06d,' % i for i in range(count))\n"
- // Pass 'large' as reqid because we know the API
- // will echo reqid, and we want to receive it back.
- "request('" << api.getName() << "', dict(reqid=large))\n"
- "try:\n"
- " resp = get()\n"
- "except ParseError as e:\n"
- " # try to find where e.data diverges from expectation\n"
- // Normally we'd expect a 'pump' key in there,
- // too, with value replypump(). But Python
- // serializes keys in a different order than C++,
- // so incoming data start with 'data'.
- // Truthfully, though, if we get as far as 'pump'
- // before we find a difference, something's very
- // strange.
- " expect = llsd.format_notation(dict(data=dict(reqid=large)))\n"
- " chunk = 40\n"
- " for offset in range(0, max(len(e.data), len(expect)), chunk):\n"
- " if e.data[offset:offset+chunk] != \\\n"
- " expect[offset:offset+chunk]:\n"
- " print('Offset %06d: expect %r,\\n'\\\n"
- " ' get %r' %\\\n"
- " (offset,\n"
- " expect[offset:offset+chunk],\n"
- " e.data[offset:offset+chunk]),\n"
- " file=sys.stderr)\n"
- " break\n"
- " else:\n"
- " print('incoming data matches expect?!', file=sys.stderr)\n"
- " send('" << result.getName() << "', '%s: %s' % (e.__class__.__name__, e))\n"
- " sys.exit(1)\n"
- "\n"
- "echoed = resp['data']['reqid']\n"
- "if echoed == large:\n"
- " send('" << result.getName() << "', '')\n"
- " sys.exit(0)\n"
- // Here we know echoed did NOT match; try to find where
- "for i in range(count):\n"
- " start = 7*i\n"
- " end = 7*(i+1)\n"
- " if end > len(echoed)\\\n"
- " or echoed[start:end] != large[start:end]:\n"
- " send('" << result.getName() << "',\n"
- " 'at offset %s, expected %r but got %r' %\n"
- " (start, large[start:end], echoed[start:end]))\n"
- "sys.exit(1)\n");
+ NamedExtTempFile script("py",
+ [&](std::ostream& out){ out <<
+ "import sys\n"
+ "from " << reader_module << " import *\n"
+ // Generate a very large string value.
+ "desired = int(sys.argv[1])\n"
+ // 7 chars per item: 6 digits, 1 comma
+ "count = int((desired - 50)/7)\n"
+ "large = ''.join('%06d,' % i for i in range(count))\n"
+ // Pass 'large' as reqid because we know the API
+ // will echo reqid, and we want to receive it back.
+ "request('" << api.getName() << "', dict(reqid=large))\n"
+ "try:\n"
+ " resp = get()\n"
+ "except ParseError as e:\n"
+ " # try to find where e.data diverges from expectation\n"
+ // Normally we'd expect a 'pump' key in there,
+ // too, with value replypump(). But Python
+ // serializes keys in a different order than C++,
+ // so incoming data start with 'data'.
+ // Truthfully, though, if we get as far as 'pump'
+ // before we find a difference, something's very
+ // strange.
+ " expect = llsd.format_notation(dict(data=dict(reqid=large)))\n"
+ " chunk = 40\n"
+ " for offset in range(0, max(len(e.data), len(expect)), chunk):\n"
+ " if e.data[offset:offset+chunk] != \\\n"
+ " expect[offset:offset+chunk]:\n"
+ " print('Offset %06d: expect %r,\\n'\\\n"
+ " ' get %r' %\\\n"
+ " (offset,\n"
+ " expect[offset:offset+chunk],\n"
+ " e.data[offset:offset+chunk]),\n"
+ " file=sys.stderr)\n"
+ " break\n"
+ " else:\n"
+ " print('incoming data matches expect?!', file=sys.stderr)\n"
+ " send('" << result.getName() << "', '%s: %s' % (e.__class__.__name__, e))\n"
+ " sys.exit(1)\n"
+ "\n"
+ "echoed = resp['data']['reqid']\n"
+ "if echoed == large:\n"
+ " send('" << result.getName() << "', '')\n"
+ " sys.exit(0)\n"
+ // Here we know echoed did NOT match; try to find where
+ "for i in range(count):\n"
+ " start = 7*i\n"
+ " end = 7*(i+1)\n"
+ " if end > len(echoed)\\\n"
+ " or echoed[start:end] != large[start:end]:\n"
+ " send('" << result.getName() << "',\n"
+ " 'at offset %s, expected %r but got %r' %\n"
+ " (start, large[start:end], echoed[start:end]))\n"
+ "sys.exit(1)\n";});
waitfor(LLLeap::create(test_name,
- sv(list_of
- (PYTHON)
- (script.getName())
- (stringize(size)))),
+ StringVec{PYTHON, script.getName(), stringize(size)}),
180); // try a longer timeout
result.ensure();
}
diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp
index 81449b4a42..b6b297b8d7 100644
--- a/indra/llcommon/tests/llprocess_test.cpp
+++ b/indra/llcommon/tests/llprocess_test.cpp
@@ -151,8 +151,38 @@ struct PythonProcessLauncher
/// Launch Python script; verify that it launched
void launch()
{
- mPy = LLProcess::create(mParams);
- tut::ensure(STRINGIZE("Couldn't launch " << mDesc << " script"), bool(mPy));
+ try
+ {
+ mPy = LLProcess::create(mParams);
+ tut::ensure(STRINGIZE("Couldn't launch " << mDesc << " script"), bool(mPy));
+ }
+ catch (const tut::failure&)
+ {
+ // On Windows, if APR_LOG is set, our version of APR's
+ // apr_create_proc() logs to the specified file. If this test
+ // failed, try to report that log.
+ const char* APR_LOG = getenv("APR_LOG");
+ if (APR_LOG && *APR_LOG)
+ {
+ std::ifstream inf(APR_LOG);
+ if (! inf.is_open())
+ {
+ LL_WARNS() << "Couldn't open '" << APR_LOG << "'" << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS() << "==============================" << LL_ENDL;
+ LL_WARNS() << "From '" << APR_LOG << "':" << LL_ENDL;
+ std::string line;
+ while (std::getline(inf, line))
+ {
+ LL_WARNS() << line << LL_ENDL;
+ }
+ LL_WARNS() << "==============================" << LL_ENDL;
+ }
+ }
+ throw;
+ }
}
/// Run Python script and wait for it to complete.
@@ -191,7 +221,7 @@ struct PythonProcessLauncher
LLProcess::Params mParams;
LLProcessPtr mPy;
std::string mDesc;
- NamedTempFile mScript;
+ NamedExtTempFile mScript;
};
/// convenience function for PythonProcessLauncher::run()
@@ -214,30 +244,26 @@ static std::string python_out(const std::string& desc, const CONTENT& script)
class NamedTempDir: public boost::noncopyable
{
public:
- // Use python() function to create a temp directory: I've found
- // nothing in either Boost.Filesystem or APR quite like Python's
- // tempfile.mkdtemp().
- // Special extra bonus: on Mac, mkdtemp() reports a pathname
- // starting with /var/folders/something, whereas that's really a
- // symlink to /private/var/folders/something. Have to use
- // realpath() to compare properly.
NamedTempDir():
- mPath(python_out("mkdtemp()",
- "from __future__ import with_statement\n"
- "import os.path, sys, tempfile\n"
- "with open(sys.argv[1], 'w') as f:\n"
- " f.write(os.path.normcase(os.path.normpath(os.path.realpath(tempfile.mkdtemp()))))\n"))
- {}
+ mPath(NamedTempFile::temp_path()),
+ mCreated(boost::filesystem::create_directories(mPath))
+ {
+ mPath = boost::filesystem::canonical(mPath);
+ }
~NamedTempDir()
{
- aprchk(apr_dir_remove(mPath.c_str(), gAPRPoolp));
+ if (mCreated)
+ {
+ boost::filesystem::remove_all(mPath);
+ }
}
- std::string getName() const { return mPath; }
+ std::string getName() const { return mPath.string(); }
private:
- std::string mPath;
+ boost::filesystem::path mPath;
+ bool mCreated;
};
/*****************************************************************************
@@ -355,7 +381,7 @@ namespace tut
set_test_name("raw APR nonblocking I/O");
// Create a script file in a temporary place.
- NamedTempFile script("py",
+ NamedExtTempFile script("py",
"from __future__ import print_function" EOL
"import sys" EOL
"import time" EOL
@@ -565,7 +591,13 @@ namespace tut
" f.write(os.path.normcase(os.path.normpath(os.getcwd())))\n");
// Before running, call setWorkingDirectory()
py.mParams.cwd = tempdir.getName();
- ensure_equals("os.getcwd()", py.run_read(), tempdir.getName());
+ std::string expected{ tempdir.getName() };
+#if LL_WINDOWS
+ // SIGH, don't get tripped up by "C:" != "c:" --
+ // but on the Mac, using tolower() fails because "/users" != "/Users"!
+ expected = utf8str_tolower(expected);
+#endif
+ ensure_equals("os.getcwd()", py.run_read(), expected);
}
template<> template<>
diff --git a/indra/llcommon/tests/llrand_test.cpp b/indra/llcommon/tests/llrand_test.cpp
index 383e6f9e0a..ac5a33d0ba 100644
--- a/indra/llcommon/tests/llrand_test.cpp
+++ b/indra/llcommon/tests/llrand_test.cpp
@@ -29,7 +29,23 @@
#include "../test/lltut.h"
#include "../llrand.h"
+#include "stringize.h"
+// In llrand.h, every function is documented to return less than the high end
+// -- specifically, because you can pass a negative extent, they're documented
+// never to return a value equal to the extent.
+// So that we don't need two different versions of ensure_in_range(), when
+// testing extent < 0, negate the return value and the extent before passing
+// into ensure_in_range().
+template <typename NUMBER>
+void ensure_in_range(const std::string_view& name,
+ NUMBER value, NUMBER low, NUMBER high)
+{
+ auto failmsg{ stringize(name, " >= ", low, " (", value, ')') };
+ tut::ensure(failmsg, (value >= low));
+ failmsg = stringize(name, " < ", high, " (", value, ')');
+ tut::ensure(failmsg, (value < high));
+}
namespace tut
{
@@ -44,84 +60,65 @@ namespace tut
template<> template<>
void random_object_t::test<1>()
{
- F32 number = 0.0f;
for(S32 ii = 0; ii < 100000; ++ii)
{
- number = ll_frand();
- ensure("frand >= 0", (number >= 0.0f));
- ensure("frand < 1", (number < 1.0f));
+ ensure_in_range("frand", ll_frand(), 0.0f, 1.0f);
}
}
template<> template<>
void random_object_t::test<2>()
{
- F64 number = 0.0f;
for(S32 ii = 0; ii < 100000; ++ii)
{
- number = ll_drand();
- ensure("drand >= 0", (number >= 0.0));
- ensure("drand < 1", (number < 1.0));
+ ensure_in_range("drand", ll_drand(), 0.0, 1.0);
}
}
template<> template<>
void random_object_t::test<3>()
{
- F32 number = 0.0f;
for(S32 ii = 0; ii < 100000; ++ii)
{
- number = ll_frand(2.0f) - 1.0f;
- ensure("frand >= 0", (number >= -1.0f));
- ensure("frand < 1", (number <= 1.0f));
+ ensure_in_range("frand(2.0f)", ll_frand(2.0f) - 1.0f, -1.0f, 1.0f);
}
}
template<> template<>
void random_object_t::test<4>()
{
- F32 number = 0.0f;
for(S32 ii = 0; ii < 100000; ++ii)
{
- number = ll_frand(-7.0);
- ensure("drand <= 0", (number <= 0.0));
- ensure("drand > -7", (number > -7.0));
+ // Negate the result so we don't have to allow a templated low-end
+ // comparison as well.
+ ensure_in_range("-frand(-7.0)", -ll_frand(-7.0), 0.0f, 7.0f);
}
}
template<> template<>
void random_object_t::test<5>()
{
- F64 number = 0.0f;
for(S32 ii = 0; ii < 100000; ++ii)
{
- number = ll_drand(-2.0);
- ensure("drand <= 0", (number <= 0.0));
- ensure("drand > -2", (number > -2.0));
+ ensure_in_range("-drand(-2.0)", -ll_drand(-2.0), 0.0, 2.0);
}
}
template<> template<>
void random_object_t::test<6>()
{
- S32 number = 0;
for(S32 ii = 0; ii < 100000; ++ii)
{
- number = ll_rand(100);
- ensure("rand >= 0", (number >= 0));
- ensure("rand < 100", (number < 100));
+ ensure_in_range("rand(100)", ll_rand(100), 0, 100);
}
}
template<> template<>
void random_object_t::test<7>()
{
- S32 number = 0;
for(S32 ii = 0; ii < 100000; ++ii)
{
- number = ll_rand(-127);
- ensure("rand <= 0", (number <= 0));
- ensure("rand > -127", (number > -127));
+ ensure_in_range("-rand(-127)", -ll_rand(-127), 0, 127);
}
}
}
diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp
index acb2953b5b..ac40125f75 100644
--- a/indra/llcommon/tests/llsdserialize_test.cpp
+++ b/indra/llcommon/tests/llsdserialize_test.cpp
@@ -45,11 +45,6 @@ typedef U32 uint32_t;
#endif
#include "boost/range.hpp"
-#include "boost/foreach.hpp"
-#include "boost/bind.hpp"
-#include "boost/phoenix/bind/bind_function.hpp"
-#include "boost/phoenix/core/argument.hpp"
-using namespace boost::phoenix;
#include "llsd.h"
#include "llsdserialize.h"
@@ -57,9 +52,11 @@ using namespace boost::phoenix;
#include "llformat.h"
#include "llmemorystream.h"
+#include "../test/hexdump.h"
#include "../test/lltut.h"
#include "../test/namedtempfile.h"
#include "stringize.h"
+#include "StringVec.h"
#include <functional>
typedef std::function<void(const LLSD& data, std::ostream& str)> FormatterFunction;
@@ -1796,16 +1793,12 @@ namespace tut
// helper for TestPythonCompatible
static std::string import_llsd("import os.path\n"
"import sys\n"
- "try:\n"
- // new freestanding llsd package
- " import llsd\n"
- "except ImportError:\n"
- // older llbase.llsd module
- " from llbase import llsd\n");
+ "import llsd\n");
// helper for TestPythonCompatible
- template <typename CONTENT>
- void python(const std::string& desc, const CONTENT& script, int expect=0)
+ template <typename CONTENT, typename... ARGS>
+ void python_expect(const std::string& desc, const CONTENT& script, int expect=0,
+ ARGS&&... args)
{
auto PYTHON(LLStringUtil::getenv("PYTHON"));
ensure("Set $PYTHON to the Python interpreter", !PYTHON.empty());
@@ -1816,7 +1809,8 @@ namespace tut
std::string q("\"");
std::string qPYTHON(q + PYTHON + q);
std::string qscript(q + scriptfile.getName() + q);
- int rc = _spawnl(_P_WAIT, PYTHON.c_str(), qPYTHON.c_str(), qscript.c_str(), NULL);
+ int rc = _spawnl(_P_WAIT, PYTHON.c_str(), qPYTHON.c_str(), qscript.c_str(),
+ std::forward<ARGS>(args)..., NULL);
if (rc == -1)
{
char buffer[256];
@@ -1832,6 +1826,10 @@ namespace tut
LLProcess::Params params;
params.executable = PYTHON;
params.args.add(scriptfile.getName());
+ for (const std::string& arg : StringVec{ std::forward<ARGS>(args)... })
+ {
+ params.args.add(arg);
+ }
LLProcessPtr py(LLProcess::create(params));
ensure(STRINGIZE("Couldn't launch " << desc << " script"), bool(py));
// Implementing timeout would mean messing with alarm() and
@@ -1866,6 +1864,14 @@ namespace tut
#endif
}
+ // helper for TestPythonCompatible
+ template <typename CONTENT, typename... ARGS>
+ void python(const std::string& desc, const CONTENT& script, ARGS&&... args)
+ {
+ // plain python() expects rc 0
+ python_expect(desc, script, 0, std::forward<ARGS>(args)...);
+ }
+
struct TestPythonCompatible
{
TestPythonCompatible() {}
@@ -1880,10 +1886,10 @@ namespace tut
void TestPythonCompatibleObject::test<1>()
{
set_test_name("verify python()");
- python("hello",
- "import sys\n"
- "sys.exit(17)\n",
- 17); // expect nonzero rc
+ python_expect("hello",
+ "import sys\n"
+ "sys.exit(17)\n",
+ 17); // expect nonzero rc
}
template<> template<>
@@ -1899,7 +1905,7 @@ namespace tut
static void writeLLSDArray(const FormatterFunction& serialize,
std::ostream& out, const LLSD& array)
{
- for (const LLSD& item : llsd::inArray(array))
+ for (const LLSD& item: llsd::inArray(array))
{
// It's important to delimit the entries in this file somehow
// because, although Python's llsd.parse() can accept a file
@@ -1914,7 +1920,14 @@ namespace tut
auto buffstr{ buffer.str() };
int bufflen{ static_cast<int>(buffstr.length()) };
out.write(reinterpret_cast<const char*>(&bufflen), sizeof(bufflen));
+ LL_DEBUGS() << "Wrote length: "
+ << hexdump(reinterpret_cast<const char*>(&bufflen),
+ sizeof(bufflen))
+ << LL_ENDL;
out.write(buffstr.c_str(), buffstr.length());
+ LL_DEBUGS() << "Wrote data: "
+ << hexmix(buffstr.c_str(), buffstr.length())
+ << LL_ENDL;
}
}
@@ -1943,10 +1956,10 @@ namespace tut
" else:\n"
" raise AssertionError('Too many data items')\n";
- // Create an llsdXXXXXX file containing 'data' serialized to
- // notation.
+ // Create an llsdXXXXXX file containing 'data' serialized per
+ // FormatterFunction.
NamedTempFile file("llsd",
- // NamedTempFile's boost::function constructor
+ // NamedTempFile's function constructor
// takes a callable. To this callable it passes the
// std::ostream with which it's writing the
// NamedTempFile.
@@ -1954,34 +1967,50 @@ namespace tut
(std::ostream& out)
{ writeLLSDArray(serialize, out, cdata); });
- python("read C++ " + desc,
- placeholders::arg1 <<
- import_llsd <<
- "from functools import partial\n"
- "import io\n"
- "import struct\n"
- "lenformat = struct.Struct('i')\n"
- "def parse_each(inf):\n"
- " for rawlen in iter(partial(inf.read, lenformat.size), b''):\n"
- " len = lenformat.unpack(rawlen)[0]\n"
- // Since llsd.parse() has no max_bytes argument, instead of
- // passing the input stream directly to parse(), read the item
- // into a distinct bytes object and parse that.
- " data = inf.read(len)\n"
- " try:\n"
- " frombytes = llsd.parse(data)\n"
- " except llsd.LLSDParseError as err:\n"
- " print(f'*** {err}')\n"
- " print(f'Bad content:\\n{data!r}')\n"
- " raise\n"
- // Also try parsing from a distinct stream.
- " stream = io.BytesIO(data)\n"
- " fromstream = llsd.parse(stream)\n"
- " assert frombytes == fromstream\n"
- " yield frombytes\n"
- << pydata <<
- // Don't forget raw-string syntax for Windows pathnames.
- "verify(parse_each(open(r'" << file.getName() << "', 'rb')))\n");
+ // 'debug' starts empty because it's intended as an output file
+ NamedTempFile debug("debug", "");
+
+ try
+ {
+ python("read C++ " + desc,
+ [&](std::ostream& out){ out <<
+ import_llsd <<
+ "from functools import partial\n"
+ "import io\n"
+ "import struct\n"
+ "lenformat = struct.Struct('i')\n"
+ "def parse_each(inf):\n"
+ " for rawlen in iter(partial(inf.read, lenformat.size), b''):\n"
+ " print('Read length:', ''.join(('%02x' % b) for b in rawlen),\n"
+ " file=debug)\n"
+ " len = lenformat.unpack(rawlen)[0]\n"
+ // Since llsd.parse() has no max_bytes argument, instead of
+ // passing the input stream directly to parse(), read the item
+ // into a distinct bytes object and parse that.
+ " data = inf.read(len)\n"
+ " print('Read data: ', repr(data), file=debug)\n"
+ " try:\n"
+ " frombytes = llsd.parse(data)\n"
+ " except llsd.LLSDParseError as err:\n"
+ " print(f'*** {err}')\n"
+ " print(f'Bad content:\\n{data!r}')\n"
+ " raise\n"
+ // Also try parsing from a distinct stream.
+ " stream = io.BytesIO(data)\n"
+ " fromstream = llsd.parse(stream)\n"
+ " assert frombytes == fromstream\n"
+ " yield frombytes\n"
+ << pydata <<
+ // Don't forget raw-string syntax for Windows pathnames.
+ "debug = open(r'" << debug.getName() << "', 'w')\n"
+ "verify(parse_each(open(r'" << file.getName() << "', 'rb')))\n";});
+ }
+ catch (const failure&)
+ {
+ LL_DEBUGS() << "Script debug output:" << LL_ENDL;
+ debug.peep_log();
+ throw;
+ }
}
template<> template<>
@@ -2068,7 +2097,7 @@ namespace tut
NamedTempFile file("llsd", "");
python("Python " + pyformatter,
- placeholders::arg1 <<
+ [&](std::ostream& out){ out <<
import_llsd <<
"import struct\n"
"lenformat = struct.Struct('i')\n"
@@ -2086,7 +2115,7 @@ namespace tut
" for item in DATA:\n"
" serialized = llsd." << pyformatter << "(item)\n"
" f.write(lenformat.pack(len(serialized)))\n"
- " f.write(serialized)\n");
+ " f.write(serialized)\n";});
std::ifstream inf(file.getName().c_str());
LLSD item;
diff --git a/indra/llcommon/tests/workqueue_test.cpp b/indra/llcommon/tests/workqueue_test.cpp
index 41aa858084..df16f4a46e 100644
--- a/indra/llcommon/tests/workqueue_test.cpp
+++ b/indra/llcommon/tests/workqueue_test.cpp
@@ -83,7 +83,11 @@ namespace tut
// signal the work item that it can quit; consider LLOneShotCond.
LLCond<Shared> data;
auto start = WorkSchedule::TimePoint::clock::now();
- auto interval = 100ms;
+ // 2s seems like a long time to wait, since it directly impacts the
+ // duration of this test program. Unfortunately GitHub's Mac runners
+ // are pretty wimpy, and we're getting spurious "too late" errors just
+ // because the thread doesn't wake up as soon as we want.
+ auto interval = 2s;
queue.postEvery(
interval,
[&data, count = 0]
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;