summaryrefslogtreecommitdiff
path: root/indra/llcommon/tests
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon/tests')
-rw-r--r--indra/llcommon/tests/llcond_test.cpp67
-rw-r--r--indra/llcommon/tests/lleventcoro_test.cpp728
-rw-r--r--indra/llcommon/tests/lleventdispatcher_test.cpp123
-rw-r--r--indra/llcommon/tests/lleventfilter_test.cpp75
-rw-r--r--indra/llcommon/tests/llexception_test.cpp15
-rw-r--r--indra/llcommon/tests/llinstancetracker_test.cpp107
-rw-r--r--indra/llcommon/tests/llleap_test.cpp28
-rw-r--r--indra/llcommon/tests/llmainthreadtask_test.cpp137
-rw-r--r--indra/llcommon/tests/llprocess_test.cpp8
-rw-r--r--indra/llcommon/tests/llsdserialize_test.cpp29
-rw-r--r--indra/llcommon/tests/llsingleton_test.cpp14
11 files changed, 598 insertions, 733 deletions
diff --git a/indra/llcommon/tests/llcond_test.cpp b/indra/llcommon/tests/llcond_test.cpp
new file mode 100644
index 0000000000..478149eacf
--- /dev/null
+++ b/indra/llcommon/tests/llcond_test.cpp
@@ -0,0 +1,67 @@
+/**
+ * @file llcond_test.cpp
+ * @author Nat Goodspeed
+ * @date 2019-07-18
+ * @brief Test for llcond.
+ *
+ * $LicenseInfo:firstyear=2019&license=viewerlgpl$
+ * Copyright (c) 2019, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llcond.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+#include "../test/lltut.h"
+#include "llcoros.h"
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct llcond_data
+ {
+ LLScalarCond<int> cond{0};
+ };
+ typedef test_group<llcond_data> llcond_group;
+ typedef llcond_group::object object;
+ llcond_group llcondgrp("llcond");
+
+ template<> template<>
+ void object::test<1>()
+ {
+ set_test_name("Immediate gratification");
+ cond.set_one(1);
+ ensure("wait_for_equal() failed",
+ cond.wait_for_equal(F32Milliseconds(1), 1));
+ ensure("wait_for_unequal() should have failed",
+ ! cond.wait_for_unequal(F32Milliseconds(1), 1));
+ }
+
+ template<> template<>
+ void object::test<2>()
+ {
+ set_test_name("Simple two-coroutine test");
+ LLCoros::instance().launch(
+ "test<2>",
+ [this]()
+ {
+ // Lambda immediately entered -- control comes here first.
+ ensure_equals(cond.get(), 0);
+ cond.set_all(1);
+ cond.wait_equal(2);
+ ensure_equals(cond.get(), 2);
+ cond.set_all(3);
+ });
+ // Main coroutine is resumed only when the lambda waits.
+ ensure_equals(cond.get(), 1);
+ cond.set_all(2);
+ cond.wait_equal(3);
+ }
+} // namespace tut
diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp
index fa02d2bb1a..032923a108 100644
--- a/indra/llcommon/tests/lleventcoro_test.cpp
+++ b/indra/llcommon/tests/lleventcoro_test.cpp
@@ -26,102 +26,33 @@
* $/LicenseInfo$
*/
-/*****************************************************************************/
-// test<1>() is cloned from a Boost.Coroutine example program whose copyright
-// info is reproduced here:
-/*---------------------------------------------------------------------------*/
-// Copyright (c) 2006, Giovanni P. Deretta
-//
-// This code may be used under either of the following two licences:
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE. OF SUCH DAMAGE.
-//
-// Or:
-//
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE_1_0.txt or copy at
-// http://www.boost.org/LICENSE_1_0.txt)
-/*****************************************************************************/
-
#define BOOST_RESULT_OF_USE_TR1 1
-// On some platforms, Boost.Coroutine must #define magic symbols before
-// #including platform-API headers. Naturally, that's ineffective unless the
-// Boost.Coroutine #include is the *first* #include of the platform header.
-// That means that client code must generally #include Boost.Coroutine headers
-// before anything else.
-#include <boost/dcoroutine/coroutine.hpp>
#include <boost/bind.hpp>
#include <boost/range.hpp>
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
+#include <boost/make_shared.hpp>
#include "linden_common.h"
#include <iostream>
#include <string>
+#include <typeinfo>
#include "../test/lltut.h"
+#include "../test/lltestapp.h"
#include "llsd.h"
#include "llsdutil.h"
#include "llevents.h"
-#include "tests/wrapllerrs.h"
-#include "stringize.h"
#include "llcoros.h"
+#include "lleventfilter.h"
#include "lleventcoro.h"
#include "../test/debug.h"
+#include "../test/sync.h"
using namespace llcoro;
/*****************************************************************************
-* from the banana.cpp example program borrowed for test<1>()
-*****************************************************************************/
-namespace coroutines = boost::dcoroutines;
-using coroutines::coroutine;
-
-template<typename Iter>
-bool match(Iter first, Iter last, std::string match) {
- std::string::iterator i = match.begin();
- for(; (first != last) && (i != match.end()); ++i) {
- if (*first != *i)
- return false;
- ++first;
- }
- return i == match.end();
-}
-
-template<typename BidirectionalIterator>
-BidirectionalIterator
-match_substring(BidirectionalIterator begin,
- BidirectionalIterator end,
- std::string xmatch,
- BOOST_DEDUCED_TYPENAME coroutine<BidirectionalIterator(void)>::self& self) {
-//BidirectionalIterator begin_ = begin;
- for(; begin != end; ++begin)
- if(match(begin, end, xmatch)) {
- self.yield(begin);
- }
- return end;
-}
-
-typedef coroutine<std::string::iterator(void)> match_coroutine_type;
-
-/*****************************************************************************
* Test helpers
*****************************************************************************/
/// Simulate an event API whose response is immediate: sent on receipt of the
@@ -131,8 +62,9 @@ typedef coroutine<std::string::iterator(void)> match_coroutine_type;
class ImmediateAPI
{
public:
- ImmediateAPI():
- mPump("immediate", true)
+ ImmediateAPI(Sync& sync):
+ mPump("immediate", true),
+ mSync(sync)
{
mPump.listen("API", boost::bind(&ImmediateAPI::operator(), this, _1));
}
@@ -141,20 +73,18 @@ public:
// Invoke this with an LLSD map containing:
// ["value"]: Integer value. We will reply with ["value"] + 1.
- // ["reply"]: Name of LLEventPump on which to send success response.
- // ["error"]: Name of LLEventPump on which to send error response.
- // ["fail"]: Presence of this key selects ["error"], else ["success"] as
- // the name of the pump on which to send the response.
+ // ["reply"]: Name of LLEventPump on which to send response.
bool operator()(const LLSD& event) const
{
+ mSync.bump();
LLSD::Integer value(event["value"]);
- LLSD::String replyPumpName(event.has("fail")? "error" : "reply");
- LLEventPumps::instance().obtain(event[replyPumpName]).post(value + 1);
+ LLEventPumps::instance().obtain(event["reply"]).post(value + 1);
return false;
}
private:
LLEventStream mPump;
+ Sync& mSync;
};
/*****************************************************************************
@@ -162,633 +92,247 @@ private:
*****************************************************************************/
namespace tut
{
- struct coroutine_data {};
- typedef test_group<coroutine_data> coroutine_group;
+ struct test_data
+ {
+ Sync mSync;
+ ImmediateAPI immediateAPI{mSync};
+ std::string replyName, errorName, threw, stringdata;
+ LLSD result, errordata;
+ int which;
+ LLTestApp testApp;
+
+ void explicit_wait(boost::shared_ptr<LLCoros::Promise<std::string>>& cbp);
+ void waitForEventOn1();
+ void coroPump();
+ void postAndWait1();
+ void coroPumpPost();
+ };
+ typedef test_group<test_data> coroutine_group;
typedef coroutine_group::object object;
coroutine_group coroutinegrp("coroutine");
- template<> template<>
- void object::test<1>()
- {
- set_test_name("From banana.cpp example program in Boost.Coroutine distro");
- std::string buffer = "banananana";
- std::string match = "nana";
- std::string::iterator begin = buffer.begin();
- std::string::iterator end = buffer.end();
-
-#if defined(BOOST_CORO_POSIX_IMPL)
-// std::cout << "Using Boost.Coroutine " << BOOST_CORO_POSIX_IMPL << '\n';
-#else
-// std::cout << "Using non-Posix Boost.Coroutine implementation" << std::endl;
-#endif
-
- typedef std::string::iterator signature(std::string::iterator,
- std::string::iterator,
- std::string,
- match_coroutine_type::self&);
-
- coroutine<std::string::iterator(void)> matcher
- (boost::bind(static_cast<signature*>(match_substring),
- begin,
- end,
- match,
- _1));
-
- std::string::iterator i = matcher();
-/*==========================================================================*|
- while(matcher && i != buffer.end()) {
- std::cout <<"Match at: "<< std::distance(buffer.begin(), i)<<'\n';
- i = matcher();
- }
-|*==========================================================================*/
- size_t matches[] = { 2, 4, 6 };
- for (size_t *mi(boost::begin(matches)), *mend(boost::end(matches));
- mi != mend; ++mi, i = matcher())
- {
- ensure("more", matcher);
- ensure("found", i != buffer.end());
- ensure_equals("value", std::distance(buffer.begin(), i), *mi);
- }
- ensure("done", ! matcher);
- }
-
- // use static data so we can intersperse coroutine functions with the
- // tests that engage them
- ImmediateAPI immediateAPI;
- std::string replyName, errorName, threw, stringdata;
- LLSD result, errordata;
- int which;
-
- // reinit vars at the start of each test
- void clear()
- {
- replyName.clear();
- errorName.clear();
- threw.clear();
- stringdata.clear();
- result = LLSD();
- errordata = LLSD();
- which = 0;
- }
-
- void explicit_wait(boost::shared_ptr<LLCoros::Future<std::string>::callback_t>& cbp)
+ void test_data::explicit_wait(boost::shared_ptr<LLCoros::Promise<std::string>>& cbp)
{
BEGIN
{
+ mSync.bump();
// The point of this test is to verify / illustrate suspending a
// coroutine for something other than an LLEventPump. In other
// words, this shows how to adapt to any async operation that
// provides a callback-style notification (and prove that it
// works).
- LLCoros::Future<std::string> future;
- // get the callback from that future
- LLCoros::Future<std::string>::callback_t callback(future.make_callback());
-
// Perhaps we would send a request to a remote server and arrange
- // for 'callback' to be called on response. Of course that might
- // involve an adapter object from the actual callback signature to
- // the signature of 'callback' -- in this case, void(std::string).
- // For test purposes, instead of handing 'callback' (or the
+ // for cbp->set_value() to be called on response.
+ // For test purposes, instead of handing 'callback' (or an
// adapter) off to some I/O subsystem, we'll just pass it back to
// our caller.
- cbp.reset(new LLCoros::Future<std::string>::callback_t(callback));
+ cbp = boost::make_shared<LLCoros::Promise<std::string>>();
+ LLCoros::Future<std::string> future = LLCoros::getFuture(*cbp);
- ensure("Not yet", ! future);
// calling get() on the future causes us to suspend
debug("about to suspend");
stringdata = future.get();
- ensure("Got it", bool(future));
+ mSync.bump();
+ ensure_equals("Got it", stringdata, "received");
}
END
}
template<> template<>
- void object::test<2>()
+ void object::test<1>()
{
- clear();
set_test_name("explicit_wait");
DEBUG;
// Construct the coroutine instance that will run explicit_wait.
- boost::shared_ptr<LLCoros::Future<std::string>::callback_t> respond;
- LLCoros::instance().launch("test<2>",
- boost::bind(explicit_wait, boost::ref(respond)));
+ boost::shared_ptr<LLCoros::Promise<std::string>> respond;
+ LLCoros::instance().launch("test<1>",
+ [this, &respond](){ explicit_wait(respond); });
+ mSync.bump();
// When the coroutine waits for the future, it returns here.
debug("about to respond");
- // Now we're the I/O subsystem delivering a result. This immediately
- // transfers control back to the coroutine.
- (*respond)("received");
+ // Now we're the I/O subsystem delivering a result. This should make
+ // the coroutine ready.
+ respond->set_value("received");
+ // but give it a chance to wake up
+ mSync.yield();
// ensure the coroutine ran and woke up again with the intended result
ensure_equals(stringdata, "received");
}
- void waitForEventOn1()
+ void test_data::waitForEventOn1()
{
BEGIN
{
+ mSync.bump();
result = suspendUntilEventOn("source");
+ mSync.bump();
}
END
}
template<> template<>
- void object::test<3>()
+ void object::test<2>()
{
- clear();
set_test_name("waitForEventOn1");
DEBUG;
- LLCoros::instance().launch("test<3>", waitForEventOn1);
+ LLCoros::instance().launch("test<2>", [this](){ waitForEventOn1(); });
+ mSync.bump();
debug("about to send");
LLEventPumps::instance().obtain("source").post("received");
+ // give waitForEventOn1() a chance to run
+ mSync.yield();
debug("back from send");
ensure_equals(result.asString(), "received");
}
- void waitForEventOn2()
- {
- BEGIN
- {
- LLEventWithID pair = suspendUntilEventOn("reply", "error");
- result = pair.first;
- which = pair.second;
- debug(STRINGIZE("result = " << result << ", which = " << which));
- }
- END
- }
-
- template<> template<>
- void object::test<4>()
- {
- clear();
- set_test_name("waitForEventOn2 reply");
- {
- DEBUG;
- LLCoros::instance().launch("test<4>", waitForEventOn2);
- debug("about to send");
- LLEventPumps::instance().obtain("reply").post("received");
- debug("back from send");
- }
- ensure_equals(result.asString(), "received");
- ensure_equals("which pump", which, 0);
- }
-
- template<> template<>
- void object::test<5>()
- {
- clear();
- set_test_name("waitForEventOn2 error");
- DEBUG;
- LLCoros::instance().launch("test<5>", waitForEventOn2);
- debug("about to send");
- LLEventPumps::instance().obtain("error").post("badness");
- debug("back from send");
- ensure_equals(result.asString(), "badness");
- ensure_equals("which pump", which, 1);
- }
-
- void coroPump()
+ void test_data::coroPump()
{
BEGIN
{
+ mSync.bump();
LLCoroEventPump waiter;
replyName = waiter.getName();
result = waiter.suspend();
+ mSync.bump();
}
END
}
template<> template<>
- void object::test<6>()
+ void object::test<3>()
{
- clear();
set_test_name("coroPump");
DEBUG;
- LLCoros::instance().launch("test<6>", coroPump);
+ LLCoros::instance().launch("test<3>", [this](){ coroPump(); });
+ mSync.bump();
debug("about to send");
LLEventPumps::instance().obtain(replyName).post("received");
+ // give coroPump() a chance to run
+ mSync.yield();
debug("back from send");
ensure_equals(result.asString(), "received");
}
- void coroPumps()
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- replyName = waiter.getName0();
- errorName = waiter.getName1();
- LLEventWithID pair(waiter.suspend());
- result = pair.first;
- which = pair.second;
- }
- END
- }
-
- template<> template<>
- void object::test<7>()
- {
- clear();
- set_test_name("coroPumps reply");
- DEBUG;
- LLCoros::instance().launch("test<7>", coroPumps);
- debug("about to send");
- LLEventPumps::instance().obtain(replyName).post("received");
- debug("back from send");
- ensure_equals(result.asString(), "received");
- ensure_equals("which pump", which, 0);
- }
-
- template<> template<>
- void object::test<8>()
- {
- clear();
- set_test_name("coroPumps error");
- DEBUG;
- LLCoros::instance().launch("test<8>", coroPumps);
- debug("about to send");
- LLEventPumps::instance().obtain(errorName).post("badness");
- debug("back from send");
- ensure_equals(result.asString(), "badness");
- ensure_equals("which pump", which, 1);
- }
-
- void coroPumpsNoEx()
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- replyName = waiter.getName0();
- errorName = waiter.getName1();
- result = waiter.suspendWithException();
- }
- END
- }
-
- template<> template<>
- void object::test<9>()
- {
- clear();
- set_test_name("coroPumpsNoEx");
- DEBUG;
- LLCoros::instance().launch("test<9>", coroPumpsNoEx);
- debug("about to send");
- LLEventPumps::instance().obtain(replyName).post("received");
- debug("back from send");
- ensure_equals(result.asString(), "received");
- }
-
- void coroPumpsEx()
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- replyName = waiter.getName0();
- errorName = waiter.getName1();
- try
- {
- result = waiter.suspendWithException();
- debug("no exception");
- }
- catch (const LLErrorEvent& e)
- {
- debug(STRINGIZE("exception " << e.what()));
- errordata = e.getData();
- }
- }
- END
- }
-
- template<> template<>
- void object::test<10>()
- {
- clear();
- set_test_name("coroPumpsEx");
- DEBUG;
- LLCoros::instance().launch("test<10>", coroPumpsEx);
- debug("about to send");
- LLEventPumps::instance().obtain(errorName).post("badness");
- debug("back from send");
- ensure("no result", result.isUndefined());
- ensure_equals("got error", errordata.asString(), "badness");
- }
-
- void coroPumpsNoLog()
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- replyName = waiter.getName0();
- errorName = waiter.getName1();
- result = waiter.suspendWithLog();
- }
- END
- }
-
- template<> template<>
- void object::test<11>()
- {
- clear();
- set_test_name("coroPumpsNoLog");
- DEBUG;
- LLCoros::instance().launch("test<11>", coroPumpsNoLog);
- debug("about to send");
- LLEventPumps::instance().obtain(replyName).post("received");
- debug("back from send");
- ensure_equals(result.asString(), "received");
- }
-
- void coroPumpsLog()
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- replyName = waiter.getName0();
- errorName = waiter.getName1();
- WrapLLErrs capture;
- threw = capture.catch_llerrs([&waiter, &debug](){
- result = waiter.suspendWithLog();
- debug("no exception");
- });
- }
- END
- }
-
- template<> template<>
- void object::test<12>()
- {
- clear();
- set_test_name("coroPumpsLog");
- DEBUG;
- LLCoros::instance().launch("test<12>", coroPumpsLog);
- debug("about to send");
- LLEventPumps::instance().obtain(errorName).post("badness");
- debug("back from send");
- ensure("no result", result.isUndefined());
- ensure_contains("got error", threw, "badness");
- }
-
- void postAndWait1()
+ void test_data::postAndWait1()
{
BEGIN
{
+ mSync.bump();
result = postAndSuspend(LLSDMap("value", 17), // request event
immediateAPI.getPump(), // requestPump
"reply1", // replyPump
"reply"); // request["reply"] = name
+ mSync.bump();
}
END
}
template<> template<>
- void object::test<13>()
+ void object::test<4>()
{
- clear();
set_test_name("postAndWait1");
DEBUG;
- LLCoros::instance().launch("test<13>", postAndWait1);
+ LLCoros::instance().launch("test<4>", [this](){ postAndWait1(); });
ensure_equals(result.asInteger(), 18);
}
- void postAndWait2()
- {
- BEGIN
- {
- LLEventWithID pair = ::postAndSuspend2(LLSDMap("value", 18),
- immediateAPI.getPump(),
- "reply2",
- "error2",
- "reply",
- "error");
- result = pair.first;
- which = pair.second;
- debug(STRINGIZE("result = " << result << ", which = " << which));
- }
- END
- }
-
- template<> template<>
- void object::test<14>()
- {
- clear();
- set_test_name("postAndWait2");
- DEBUG;
- LLCoros::instance().launch("test<14>", postAndWait2);
- ensure_equals(result.asInteger(), 19);
- ensure_equals(which, 0);
- }
-
- void postAndWait2_1()
- {
- BEGIN
- {
- LLEventWithID pair = ::postAndSuspend2(LLSDMap("value", 18)("fail", LLSD()),
- immediateAPI.getPump(),
- "reply2",
- "error2",
- "reply",
- "error");
- result = pair.first;
- which = pair.second;
- debug(STRINGIZE("result = " << result << ", which = " << which));
- }
- END
- }
-
- template<> template<>
- void object::test<15>()
- {
- clear();
- set_test_name("postAndWait2_1");
- DEBUG;
- LLCoros::instance().launch("test<15>", postAndWait2_1);
- ensure_equals(result.asInteger(), 19);
- ensure_equals(which, 1);
- }
-
- void coroPumpPost()
+ void test_data::coroPumpPost()
{
BEGIN
{
+ mSync.bump();
LLCoroEventPump waiter;
result = waiter.postAndSuspend(LLSDMap("value", 17),
immediateAPI.getPump(), "reply");
+ mSync.bump();
}
END
}
template<> template<>
- void object::test<16>()
+ void object::test<5>()
{
- clear();
set_test_name("coroPumpPost");
DEBUG;
- LLCoros::instance().launch("test<16>", coroPumpPost);
+ LLCoros::instance().launch("test<5>", [this](){ coroPumpPost(); });
ensure_equals(result.asInteger(), 18);
}
- void coroPumpsPost()
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- LLEventWithID pair(waiter.postAndSuspend(LLSDMap("value", 23),
- immediateAPI.getPump(), "reply", "error"));
- result = pair.first;
- which = pair.second;
- }
- END
- }
-
- template<> template<>
- void object::test<17>()
- {
- clear();
- set_test_name("coroPumpsPost reply");
- DEBUG;
- LLCoros::instance().launch("test<17>", coroPumpsPost);
- ensure_equals(result.asInteger(), 24);
- ensure_equals("which pump", which, 0);
- }
-
- void coroPumpsPost_1()
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- LLEventWithID pair(
- waiter.postAndSuspend(LLSDMap("value", 23)("fail", LLSD()),
- immediateAPI.getPump(), "reply", "error"));
- result = pair.first;
- which = pair.second;
- }
- END
- }
-
- template<> template<>
- void object::test<18>()
- {
- clear();
- set_test_name("coroPumpsPost error");
- DEBUG;
- LLCoros::instance().launch("test<18>", coroPumpsPost_1);
- ensure_equals(result.asInteger(), 24);
- ensure_equals("which pump", which, 1);
- }
-
- void coroPumpsPostNoEx()
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- result = waiter.postAndSuspendWithException(LLSDMap("value", 8),
- immediateAPI.getPump(), "reply", "error");
- }
- END
- }
-
- template<> template<>
- void object::test<19>()
- {
- clear();
- set_test_name("coroPumpsPostNoEx");
- DEBUG;
- LLCoros::instance().launch("test<19>", coroPumpsPostNoEx);
- ensure_equals(result.asInteger(), 9);
- }
-
- void coroPumpsPostEx()
- {
- BEGIN
+ template <class PUMP>
+ void test()
+ {
+ PUMP pump(typeid(PUMP).name());
+ bool running{false};
+ LLSD data{LLSD::emptyArray()};
+ // start things off by posting once before even starting the listener
+ // coro
+ LL_DEBUGS() << "test() posting first" << LL_ENDL;
+ LLSD first{LLSDMap("desc", "first")("value", 0)};
+ bool consumed = pump.post(first);
+ ensure("should not have consumed first", ! consumed);
+ // now launch the coro
+ LL_DEBUGS() << "test() launching listener coro" << LL_ENDL;
+ running = true;
+ LLCoros::instance().launch(
+ "listener",
+ [&pump, &running, &data](){
+ // important for this test that we consume posted values
+ LLCoros::instance().set_consuming(true);
+ // should immediately retrieve 'first' without waiting
+ LL_DEBUGS() << "listener coro waiting for first" << LL_ENDL;
+ data.append(llcoro::suspendUntilEventOnWithTimeout(pump, 0.1, LLSD()));
+ // Don't use ensure() from within the coro -- ensure() failure
+ // throws tut::fail, which won't propagate out to the main
+ // test driver, which will result in an odd failure.
+ // Wait for 'second' because it's not already pending.
+ LL_DEBUGS() << "listener coro waiting for second" << LL_ENDL;
+ data.append(llcoro::suspendUntilEventOnWithTimeout(pump, 0.1, LLSD()));
+ // and wait for 'third', which should involve no further waiting
+ LL_DEBUGS() << "listener coro waiting for third" << LL_ENDL;
+ data.append(llcoro::suspendUntilEventOnWithTimeout(pump, 0.1, LLSD()));
+ LL_DEBUGS() << "listener coro done" << LL_ENDL;
+ running = false;
+ });
+ // back from coro at the point where it's waiting for 'second'
+ LL_DEBUGS() << "test() posting second" << LL_ENDL;
+ LLSD second{llsd::map("desc", "second", "value", 1)};
+ consumed = pump.post(second);
+ ensure("should have consumed second", consumed);
+ // This is a key point: even though we've post()ed the value for which
+ // the coroutine is waiting, it's actually still suspended until we
+ // pause for some other reason. The coroutine will only pick up one
+ // value at a time from our 'pump'. It's important to exercise the
+ // case when we post() two values before it picks up either.
+ LL_DEBUGS() << "test() posting third" << LL_ENDL;
+ LLSD third{llsd::map("desc", "third", "value", 2)};
+ consumed = pump.post(third);
+ ensure("should NOT yet have consumed third", ! consumed);
+ // now just wait for coro to finish -- which it eventually will, given
+ // that all its suspend calls have short timeouts.
+ while (running)
{
- LLCoroEventPumps waiter;
- try
- {
- result = waiter.postAndSuspendWithException(
- LLSDMap("value", 9)("fail", LLSD()),
- immediateAPI.getPump(), "reply", "error");
- debug("no exception");
- }
- catch (const LLErrorEvent& e)
- {
- debug(STRINGIZE("exception " << e.what()));
- errordata = e.getData();
- }
+ LL_DEBUGS() << "test() waiting for coro done" << LL_ENDL;
+ llcoro::suspendUntilTimeout(0.1);
}
- END
+ // okay, verify expected results
+ ensure_equals("should have received three values", data,
+ llsd::array(first, second, third));
+ LL_DEBUGS() << "test() done" << LL_ENDL;
}
template<> template<>
- void object::test<20>()
- {
- clear();
- set_test_name("coroPumpsPostEx");
- DEBUG;
- LLCoros::instance().launch("test<20>", coroPumpsPostEx);
- ensure("no result", result.isUndefined());
- ensure_equals("got error", errordata.asInteger(), 10);
- }
-
- void coroPumpsPostNoLog()
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- result = waiter.postAndSuspendWithLog(LLSDMap("value", 30),
- immediateAPI.getPump(), "reply", "error");
- }
- END
- }
-
- template<> template<>
- void object::test<21>()
- {
- clear();
- set_test_name("coroPumpsPostNoLog");
- DEBUG;
- LLCoros::instance().launch("test<21>", coroPumpsPostNoLog);
- ensure_equals(result.asInteger(), 31);
- }
-
- void coroPumpsPostLog()
+ void object::test<6>()
{
- BEGIN
- {
- LLCoroEventPumps waiter;
- WrapLLErrs capture;
- threw = capture.catch_llerrs(
- [&waiter, &debug](){
- result = waiter.postAndSuspendWithLog(
- LLSDMap("value", 31)("fail", LLSD()),
- immediateAPI.getPump(), "reply", "error");
- debug("no exception");
- });
- }
- END
+ set_test_name("LLEventMailDrop");
+ tut::test<LLEventMailDrop>();
}
template<> template<>
- void object::test<22>()
+ void object::test<7>()
{
- clear();
- set_test_name("coroPumpsPostLog");
- DEBUG;
- LLCoros::instance().launch("test<22>", coroPumpsPostLog);
- ensure("no result", result.isUndefined());
- ensure_contains("got error", threw, "32");
+ set_test_name("LLEventLogProxyFor<LLEventMailDrop>");
+ tut::test< LLEventLogProxyFor<LLEventMailDrop> >();
}
}
-
-/*==========================================================================*|
-#include <boost/context/guarded_stack_allocator.hpp>
-
-namespace tut
-{
- template<> template<>
- void object::test<23>()
- {
- set_test_name("stacksize");
- std::cout << "default_stacksize: " << boost::context::guarded_stack_allocator::default_stacksize() << '\n';
- }
-} // namespace tut
-|*==========================================================================*/
diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp
index a181d5c941..9da1ecfd67 100644
--- a/indra/llcommon/tests/lleventdispatcher_test.cpp
+++ b/indra/llcommon/tests/lleventdispatcher_test.cpp
@@ -23,6 +23,7 @@
#include "stringize.h"
#include "tests/wrapllerrs.h"
#include "../test/catch_and_store_what_in.h"
+#include "../test/debug.h"
#include <map>
#include <string>
@@ -46,15 +47,6 @@ using boost::lambda::var;
using namespace llsd;
/*****************************************************************************
-* Output control
-*****************************************************************************/
-#ifdef DEBUG_ON
-using std::cout;
-#else
-static std::ostringstream cout;
-#endif
-
-/*****************************************************************************
* Example data, functions, classes
*****************************************************************************/
// We don't need a whole lot of different arbitrary-params methods, just (no |
@@ -155,13 +147,13 @@ struct Vars
/*------------- no-args (non-const, const, static) methods -------------*/
void method0()
{
- cout << "method0()\n";
+ debug()("method0()");
i = 17;
}
void cmethod0() const
{
- cout << 'c';
+ debug()('c', NONL);
const_cast<Vars*>(this)->method0();
}
@@ -170,13 +162,13 @@ struct Vars
/*------------ Callable (non-const, const, static) methods -------------*/
void method1(const LLSD& obj)
{
- cout << "method1(" << obj << ")\n";
+ debug()("method1(", obj, ")");
llsd = obj;
}
void cmethod1(const LLSD& obj) const
{
- cout << 'c';
+ debug()('c', NONL);
const_cast<Vars*>(this)->method1(obj);
}
@@ -196,12 +188,12 @@ struct Vars
else
vcp = std::string("'") + cp + "'";
- cout << "methodna(" << b
- << ", " << i
- << ", " << f
- << ", " << d
- << ", " << vcp
- << ")\n";
+ debug()("methodna(", b,
+ ", ", i,
+ ", ", f,
+ ", ", d,
+ ", ", vcp,
+ ")");
this->b = b;
this->i = i;
@@ -218,12 +210,12 @@ struct Vars
vbin << std::hex << std::setfill('0') << std::setw(2) << unsigned(byte);
}
- cout << "methodnb(" << "'" << s << "'"
- << ", " << uuid
- << ", " << date
- << ", '" << uri << "'"
- << ", " << vbin.str()
- << ")\n";
+ debug()("methodnb(", "'", s, "'",
+ ", ", uuid,
+ ", ", date,
+ ", '", uri, "'",
+ ", ", vbin.str(),
+ ")");
this->s = s;
this->uuid = uuid;
@@ -234,18 +226,30 @@ struct Vars
void cmethodna(NPARAMSa) const
{
- cout << 'c';
+ debug()('c', NONL);
const_cast<Vars*>(this)->methodna(NARGSa);
}
void cmethodnb(NPARAMSb) const
{
- cout << 'c';
+ debug()('c', NONL);
const_cast<Vars*>(this)->methodnb(NARGSb);
}
static void smethodna(NPARAMSa);
static void smethodnb(NPARAMSb);
+
+ static Debug& debug()
+ {
+ // Lazily initialize this Debug instance so it can notice if main()
+ // has forcibly set LOGTEST. If it were simply a static member, it
+ // would already have examined the environment variable by the time
+ // main() gets around to checking command-line switches. Since we have
+ // a global static Vars instance, the same would be true of a plain
+ // non-static member.
+ static Debug sDebug("Vars");
+ return sDebug;
+ }
};
/*------- Global Vars instance for free functions and static methods -------*/
static Vars g;
@@ -253,25 +257,25 @@ static Vars g;
/*------------ Static Vars method implementations reference 'g' ------------*/
void Vars::smethod0()
{
- cout << "smethod0() -> ";
+ debug()("smethod0() -> ", NONL);
g.method0();
}
void Vars::smethod1(const LLSD& obj)
{
- cout << "smethod1(" << obj << ") -> ";
+ debug()("smethod1(", obj, ") -> ", NONL);
g.method1(obj);
}
void Vars::smethodna(NPARAMSa)
{
- cout << "smethodna(...) -> ";
+ debug()("smethodna(...) -> ", NONL);
g.methodna(NARGSa);
}
void Vars::smethodnb(NPARAMSb)
{
- cout << "smethodnb(...) -> ";
+ debug()("smethodnb(...) -> ", NONL);
g.methodnb(NARGSb);
}
@@ -284,25 +288,25 @@ void clear()
/*------------------- Free functions also reference 'g' --------------------*/
void free0()
{
- cout << "free0() -> ";
+ g.debug()("free0() -> ", NONL);
g.method0();
}
void free1(const LLSD& obj)
{
- cout << "free1(" << obj << ") -> ";
+ g.debug()("free1(", obj, ") -> ", NONL);
g.method1(obj);
}
void freena(NPARAMSa)
{
- cout << "freena(...) -> ";
+ g.debug()("freena(...) -> ", NONL);
g.methodna(NARGSa);
}
void freenb(NPARAMSb)
{
- cout << "freenb(...) -> ";
+ g.debug()("freenb(...) -> ", NONL);
g.methodnb(NARGSb);
}
@@ -313,6 +317,7 @@ namespace tut
{
struct lleventdispatcher_data
{
+ Debug debug{"test"};
WrapLLErrs redirect;
Dispatcher work;
Vars v;
@@ -431,12 +436,17 @@ namespace tut
// Same for freenb() et al.
params = LLSDMap("a", LLSDArray("b")("i")("f")("d")("cp"))
("b", LLSDArray("s")("uuid")("date")("uri")("bin"));
- cout << "params:\n" << params << "\nparams[\"a\"]:\n" << params["a"] << "\nparams[\"b\"]:\n" << params["b"] << std::endl;
+ debug("params:\n",
+ params, "\n"
+ "params[\"a\"]:\n",
+ params["a"], "\n"
+ "params[\"b\"]:\n",
+ params["b"]);
// default LLSD::Binary value
std::vector<U8> binary;
for (size_t ix = 0, h = 0xaa; ix < 6; ++ix, h += 0x11)
{
- binary.push_back(h);
+ binary.push_back((U8)h);
}
// Full defaults arrays. We actually don't care what the LLUUID or
// LLDate values are, as long as they're different from the
@@ -448,7 +458,8 @@ namespace tut
(LLDate::now())
(LLURI("http://www.ietf.org/rfc/rfc3986.txt"))
(binary));
- cout << "dft_array_full:\n" << dft_array_full << std::endl;
+ debug("dft_array_full:\n",
+ dft_array_full);
// Partial defaults arrays.
foreach(LLSD::String a, ab)
{
@@ -457,7 +468,8 @@ namespace tut
llsd_copy_array(dft_array_full[a].beginArray() + partition,
dft_array_full[a].endArray());
}
- cout << "dft_array_partial:\n" << dft_array_partial << std::endl;
+ debug("dft_array_partial:\n",
+ dft_array_partial);
foreach(LLSD::String a, ab)
{
@@ -473,7 +485,10 @@ namespace tut
dft_map_partial[a][params[a][ix].asString()] = dft_array_full[a][ix];
}
}
- cout << "dft_map_full:\n" << dft_map_full << "\ndft_map_partial:\n" << dft_map_partial << '\n';
+ debug("dft_map_full:\n",
+ dft_map_full, "\n"
+ "dft_map_partial:\n",
+ dft_map_partial);
// (Free function | static method) with (no | arbitrary) params,
// map style, no (empty array) defaults
@@ -918,7 +933,12 @@ namespace tut
params[a].endArray()),
dft_array_partial[a]);
}
- cout << "allreq:\n" << allreq << "\nleftreq:\n" << leftreq << "\nrightdft:\n" << rightdft << std::endl;
+ debug("allreq:\n",
+ allreq, "\n"
+ "leftreq:\n",
+ leftreq, "\n"
+ "rightdft:\n",
+ rightdft);
// Generate maps containing parameter names not provided by the
// dft_map_partial maps.
@@ -930,7 +950,8 @@ namespace tut
skipreq[a].erase(me.first);
}
}
- cout << "skipreq:\n" << skipreq << std::endl;
+ debug("skipreq:\n",
+ skipreq);
LLSD groups(LLSDArray // array of groups
@@ -975,7 +996,11 @@ namespace tut
LLSD names(grp[0]);
LLSD required(grp[1][0]);
LLSD optional(grp[1][1]);
- cout << "For " << names << ",\n" << "required:\n" << required << "\noptional:\n" << optional << std::endl;
+ debug("For ", names, ",\n",
+ "required:\n",
+ required, "\n"
+ "optional:\n",
+ optional);
// Loop through 'names'
foreach(LLSD nm, inArray(names))
@@ -1145,7 +1170,7 @@ namespace tut
std::vector<U8> binary;
for (size_t h(0x01), i(0); i < 5; h+= 0x22, ++i)
{
- binary.push_back(h);
+ binary.push_back((U8)h);
}
LLSD args(LLSDMap("a", LLSDArray(true)(17)(3.14)(123.456)("char*"))
("b", LLSDArray("string")
@@ -1163,7 +1188,7 @@ namespace tut
}
// Adjust expect["a"]["cp"] for special Vars::cp treatment.
expect["a"]["cp"] = std::string("'") + expect["a"]["cp"].asString() + "'";
- cout << "expect: " << expect << '\n';
+ debug("expect: ", expect);
// Use substantially the same logic for args and argsplus
LLSD argsarrays(LLSDArray(args)(argsplus));
@@ -1218,7 +1243,8 @@ namespace tut
{
array_overfull[a].append("bogus");
}
- cout << "array_full: " << array_full << "\narray_overfull: " << array_overfull << std::endl;
+ debug("array_full: ", array_full, "\n"
+ "array_overfull: ", array_overfull);
// We rather hope that LLDate::now() will generate a timestamp
// distinct from the one it generated in the constructor, moments ago.
ensure_not_equals("Timestamps too close",
@@ -1233,7 +1259,8 @@ namespace tut
map_overfull[a] = map_full[a];
map_overfull[a]["extra"] = "ignore";
}
- cout << "map_full: " << map_full << "\nmap_overfull: " << map_overfull << std::endl;
+ debug("map_full: ", map_full, "\n"
+ "map_overfull: ", map_overfull);
LLSD expect(map_full);
// Twiddle the const char* param.
expect["a"]["cp"] = std::string("'") + expect["a"]["cp"].asString() + "'";
@@ -1248,7 +1275,7 @@ namespace tut
// so won't bother returning it. Predict that behavior to match the
// LLSD values.
expect["a"].erase("b");
- cout << "expect: " << expect << std::endl;
+ debug("expect: ", expect);
// For this test, calling functions registered with different sets of
// parameter defaults should make NO DIFFERENCE WHATSOEVER. Every call
// should pass all params.
diff --git a/indra/llcommon/tests/lleventfilter_test.cpp b/indra/llcommon/tests/lleventfilter_test.cpp
index 1875013794..fa2cb03e95 100644
--- a/indra/llcommon/tests/lleventfilter_test.cpp
+++ b/indra/llcommon/tests/lleventfilter_test.cpp
@@ -36,9 +36,12 @@
// other Linden headers
#include "../test/lltut.h"
#include "stringize.h"
+#include "llsdutil.h"
#include "listener.h"
#include "tests/wrapllerrs.h"
+#include <typeinfo>
+
/*****************************************************************************
* Test classes
*****************************************************************************/
@@ -401,6 +404,78 @@ namespace tut
throttle.post(";17");
ensure_equals("17", cat.result, "136;12;17"); // "17" delivered
}
+
+ template<class PUMP>
+ void test()
+ {
+ PUMP pump(typeid(PUMP).name());
+ LLSD data{LLSD::emptyArray()};
+ bool consumed{true};
+ // listener that appends to 'data'
+ // but that also returns the current value of 'consumed'
+ // Instantiate this separately because we're going to listen()
+ // multiple times with the same lambda: LLEventMailDrop only replays
+ // queued events on a new listen() call.
+ auto lambda =
+ [&data, &consumed](const LLSD& event)->bool
+ {
+ data.append(event);
+ return consumed;
+ };
+ {
+ LLTempBoundListener conn = pump.listen("lambda", lambda);
+ pump.post("first");
+ }
+ // first post() should certainly be received by listener
+ ensure_equals("first", data, llsd::array("first"));
+ // the question is, since consumed was true, did it queue the value?
+ data = LLSD::emptyArray();
+ {
+ // if it queued the value, it would be delivered on subsequent
+ // listen() call
+ LLTempBoundListener conn = pump.listen("lambda", lambda);
+ }
+ ensure_equals("empty1", data, LLSD::emptyArray());
+ data = LLSD::emptyArray();
+ // now let's NOT consume the posted data
+ consumed = false;
+ {
+ LLTempBoundListener conn = pump.listen("lambda", lambda);
+ pump.post("second");
+ pump.post("third");
+ }
+ // the two events still arrive
+ ensure_equals("second,third1", data, llsd::array("second", "third"));
+ data = LLSD::emptyArray();
+ {
+ // when we reconnect, these should be delivered again
+ // but this time they should be consumed
+ consumed = true;
+ LLTempBoundListener conn = pump.listen("lambda", lambda);
+ }
+ // unconsumed events were delivered again
+ ensure_equals("second,third2", data, llsd::array("second", "third"));
+ data = LLSD::emptyArray();
+ {
+ // when we reconnect this time, no more unconsumed events
+ LLTempBoundListener conn = pump.listen("lambda", lambda);
+ }
+ ensure_equals("empty2", data, LLSD::emptyArray());
+ }
+
+ template<> template<>
+ void filter_object::test<6>()
+ {
+ set_test_name("LLEventMailDrop");
+ tut::test<LLEventMailDrop>();
+ }
+
+ template<> template<>
+ void filter_object::test<7>()
+ {
+ set_test_name("LLEventLogProxyFor<LLEventMailDrop>");
+ tut::test< LLEventLogProxyFor<LLEventMailDrop> >();
+ }
} // namespace tut
/*****************************************************************************
diff --git a/indra/llcommon/tests/llexception_test.cpp b/indra/llcommon/tests/llexception_test.cpp
index 6bee1943c2..8ddf636cd1 100644
--- a/indra/llcommon/tests/llexception_test.cpp
+++ b/indra/llcommon/tests/llexception_test.cpp
@@ -305,4 +305,19 @@ namespace tut
std::cout << center("int", '=', margin) << std::endl;
catch_several(throw_int, "throw_int");
}
+
+ template<> template<>
+ void object::test<2>()
+ {
+ set_test_name("reporting exceptions");
+
+ try
+ {
+ LLTHROW(LLException("badness"));
+ }
+ catch (...)
+ {
+ LOG_UNHANDLED_EXCEPTION("llexception test<2>()");
+ }
+ }
} // namespace tut
diff --git a/indra/llcommon/tests/llinstancetracker_test.cpp b/indra/llcommon/tests/llinstancetracker_test.cpp
index d94fc0c56d..9b89159625 100644
--- a/indra/llcommon/tests/llinstancetracker_test.cpp
+++ b/indra/llcommon/tests/llinstancetracker_test.cpp
@@ -41,7 +41,6 @@
#include <boost/scoped_ptr.hpp>
// other Linden headers
#include "../test/lltut.h"
-#include "wrapllerrs.h"
struct Badness: public std::runtime_error
{
@@ -112,24 +111,22 @@ namespace tut
void object::test<2>()
{
ensure_equals(Unkeyed::instanceCount(), 0);
- Unkeyed* dangling = NULL;
+ std::weak_ptr<Unkeyed> dangling;
{
Unkeyed one;
ensure_equals(Unkeyed::instanceCount(), 1);
- Unkeyed* found = Unkeyed::getInstance(&one);
- ensure_equals(found, &one);
+ std::weak_ptr<Unkeyed> found = one.getWeak();
+ ensure(! found.expired());
{
boost::scoped_ptr<Unkeyed> two(new Unkeyed);
ensure_equals(Unkeyed::instanceCount(), 2);
- Unkeyed* found = Unkeyed::getInstance(two.get());
- ensure_equals(found, two.get());
}
ensure_equals(Unkeyed::instanceCount(), 1);
- // store an unwise pointer to a temp Unkeyed instance
- dangling = &one;
+ // store a weak pointer to a temp Unkeyed instance
+ dangling = found;
} // make that instance vanish
// check the now-invalid pointer to the destroyed instance
- ensure("getInstance(T*) failed to track destruction", ! Unkeyed::getInstance(dangling));
+ ensure("weak_ptr<Unkeyed> failed to track destruction", dangling.expired());
ensure_equals(Unkeyed::instanceCount(), 0);
}
@@ -142,7 +139,8 @@ namespace tut
// reimplement LLInstanceTracker using, say, a hash map instead of a
// std::map. We DO insist that every key appear exactly once.
typedef std::vector<std::string> StringVector;
- StringVector keys(Keyed::beginKeys(), Keyed::endKeys());
+ auto snap = Keyed::key_snapshot();
+ StringVector keys(snap.begin(), snap.end());
std::sort(keys.begin(), keys.end());
StringVector::const_iterator ki(keys.begin());
ensure_equals(*ki++, "one");
@@ -153,17 +151,15 @@ namespace tut
ensure("didn't reach end", ki == keys.end());
// Use a somewhat different approach to order independence with
- // beginInstances(): explicitly capture the instances we know in a
+ // instance_snapshot(): explicitly capture the instances we know in a
// set, and delete them as we iterate through.
typedef std::set<Keyed*> InstanceSet;
InstanceSet instances;
instances.insert(&one);
instances.insert(&two);
instances.insert(&three);
- for (Keyed::instance_iter ii(Keyed::beginInstances()), iend(Keyed::endInstances());
- ii != iend; ++ii)
+ for (auto& ref : Keyed::instance_snapshot())
{
- Keyed& ref = *ii;
ensure_equals("spurious instance", instances.erase(&ref), 1);
}
ensure_equals("unreported instance", instances.size(), 0);
@@ -180,11 +176,10 @@ namespace tut
instances.insert(&two);
instances.insert(&three);
- for (Unkeyed::instance_iter ii(Unkeyed::beginInstances()), iend(Unkeyed::endInstances()); ii != iend; ++ii)
- {
- Unkeyed& ref = *ii;
- ensure_equals("spurious instance", instances.erase(&ref), 1);
- }
+ for (auto& ref : Unkeyed::instance_snapshot())
+ {
+ ensure_equals("spurious instance", instances.erase(&ref), 1);
+ }
ensure_equals("unreported instance", instances.size(), 0);
}
@@ -192,49 +187,49 @@ namespace tut
template<> template<>
void object::test<5>()
{
- set_test_name("delete Keyed with outstanding instance_iter");
- std::string what;
- Keyed* keyed = new Keyed("delete Keyed with outstanding instance_iter");
- {
- WrapLLErrs wrapper;
- Keyed::instance_iter i(Keyed::beginInstances());
- what = wrapper.catch_llerrs([&keyed](){
- delete keyed;
- });
- }
- ensure(! what.empty());
+ std::string desc("delete Keyed with outstanding instance_snapshot");
+ set_test_name(desc);
+ Keyed* keyed = new Keyed(desc);
+ // capture a snapshot but do not yet traverse it
+ auto snapshot = Keyed::instance_snapshot();
+ // delete the one instance
+ delete keyed;
+ // traversing the snapshot should reflect the deletion
+ // avoid ensure_equals() because it requires the ability to stream the
+ // two values to std::ostream
+ ensure(snapshot.begin() == snapshot.end());
}
template<> template<>
void object::test<6>()
{
- set_test_name("delete Keyed with outstanding key_iter");
- std::string what;
- Keyed* keyed = new Keyed("delete Keyed with outstanding key_it");
- {
- WrapLLErrs wrapper;
- Keyed::key_iter i(Keyed::beginKeys());
- what = wrapper.catch_llerrs([&keyed](){
- delete keyed;
- });
- }
- ensure(! what.empty());
+ std::string desc("delete Keyed with outstanding key_snapshot");
+ set_test_name(desc);
+ Keyed* keyed = new Keyed(desc);
+ // capture a snapshot but do not yet traverse it
+ auto snapshot = Keyed::key_snapshot();
+ // delete the one instance
+ delete keyed;
+ // traversing the snapshot should reflect the deletion
+ // avoid ensure_equals() because it requires the ability to stream the
+ // two values to std::ostream
+ ensure(snapshot.begin() == snapshot.end());
}
template<> template<>
void object::test<7>()
{
- set_test_name("delete Unkeyed with outstanding instance_iter");
+ set_test_name("delete Unkeyed with outstanding instance_snapshot");
std::string what;
Unkeyed* unkeyed = new Unkeyed;
- {
- WrapLLErrs wrapper;
- Unkeyed::instance_iter i(Unkeyed::beginInstances());
- what = wrapper.catch_llerrs([&unkeyed](){
- delete unkeyed;
- });
- }
- ensure(! what.empty());
+ // capture a snapshot but do not yet traverse it
+ auto snapshot = Unkeyed::instance_snapshot();
+ // delete the one instance
+ delete unkeyed;
+ // traversing the snapshot should reflect the deletion
+ // avoid ensure_equals() because it requires the ability to stream the
+ // two values to std::ostream
+ ensure(snapshot.begin() == snapshot.end());
}
template<> template<>
@@ -246,11 +241,9 @@ namespace tut
// We can't use the iterator-range InstanceSet constructor because
// beginInstances() returns an iterator that dereferences to an
// Unkeyed&, not an Unkeyed*.
- for (Unkeyed::instance_iter uki(Unkeyed::beginInstances()),
- ukend(Unkeyed::endInstances());
- uki != ukend; ++uki)
+ for (auto& ref : Unkeyed::instance_snapshot())
{
- existing.insert(&*uki);
+ existing.insert(&ref);
}
try
{
@@ -273,11 +266,9 @@ namespace tut
// instances was also present in the original set. If that's not true,
// it's because our new Unkeyed ended up in the updated set despite
// its constructor exception.
- for (Unkeyed::instance_iter uki(Unkeyed::beginInstances()),
- ukend(Unkeyed::endInstances());
- uki != ukend; ++uki)
+ for (auto& ref : Unkeyed::instance_snapshot())
{
- ensure("failed to remove instance", existing.find(&*uki) != existing.end());
+ ensure("failed to remove instance", existing.find(&ref) != existing.end());
}
}
} // namespace tut
diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp
index bf0a74d10d..9d71e327d8 100644
--- a/indra/llcommon/tests/llleap_test.cpp
+++ b/indra/llcommon/tests/llleap_test.cpp
@@ -49,24 +49,28 @@ const size_t BUFFERED_LENGTH = 1023*1024; // try wrangling just under a megabyte
#endif
-void waitfor(const std::vector<LLLeap*>& instances, int timeout=60)
+// capture std::weak_ptrs to LLLeap instances so we can tell when they expire
+typedef std::vector<std::weak_ptr<LLLeap>> LLLeapVector;
+
+void waitfor(const LLLeapVector& instances, int timeout=60)
{
int i;
for (i = 0; i < timeout; ++i)
{
// Every iteration, test whether any of the passed LLLeap instances
// still exist (are still running).
- std::vector<LLLeap*>::const_iterator vli(instances.begin()), vlend(instances.end());
- for ( ; vli != vlend; ++vli)
+ bool found = false;
+ for (auto& ptr : instances)
{
- // getInstance() returns NULL if it's terminated/gone, non-NULL if
- // it's still running
- if (LLLeap::getInstance(*vli))
+ if (! ptr.expired())
+ {
+ found = true;
break;
+ }
}
// If we made it through all of 'instances' without finding one that's
// still running, we're done.
- if (vli == vlend)
+ if (! found)
{
/*==========================================================================*|
std::cout << instances.size() << " LLLeap instances terminated in "
@@ -86,8 +90,8 @@ void waitfor(const std::vector<LLLeap*>& instances, int timeout=60)
void waitfor(LLLeap* instance, int timeout=60)
{
- std::vector<LLLeap*> instances;
- instances.push_back(instance);
+ LLLeapVector instances;
+ instances.push_back(instance->getWeak());
waitfor(instances, timeout);
}
@@ -218,11 +222,11 @@ namespace tut
NamedTempFile script("py",
"import time\n"
"time.sleep(1)\n");
- std::vector<LLLeap*> instances;
+ LLLeapVector instances;
instances.push_back(LLLeap::create(get_test_name(),
- sv(list_of(PYTHON)(script.getName()))));
+ sv(list_of(PYTHON)(script.getName())))->getWeak());
instances.push_back(LLLeap::create(get_test_name(),
- sv(list_of(PYTHON)(script.getName()))));
+ sv(list_of(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.
diff --git a/indra/llcommon/tests/llmainthreadtask_test.cpp b/indra/llcommon/tests/llmainthreadtask_test.cpp
new file mode 100644
index 0000000000..69b11ccafb
--- /dev/null
+++ b/indra/llcommon/tests/llmainthreadtask_test.cpp
@@ -0,0 +1,137 @@
+/**
+ * @file llmainthreadtask_test.cpp
+ * @author Nat Goodspeed
+ * @date 2019-12-05
+ * @brief Test for llmainthreadtask.
+ *
+ * $LicenseInfo:firstyear=2019&license=viewerlgpl$
+ * Copyright (c) 2019, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llmainthreadtask.h"
+// STL headers
+// std headers
+#include <atomic>
+// external library headers
+// other Linden headers
+#include "../test/lltut.h"
+#include "../test/sync.h"
+#include "llthread.h" // on_main_thread()
+#include "lleventtimer.h"
+#include "lockstatic.h"
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct llmainthreadtask_data
+ {
+ // 5-second timeout
+ Sync mSync{F32Milliseconds(5000.0f)};
+
+ llmainthreadtask_data()
+ {
+ // we're not testing the result; this is just to cache the
+ // initial thread as the main thread.
+ on_main_thread();
+ }
+ };
+ typedef test_group<llmainthreadtask_data> llmainthreadtask_group;
+ typedef llmainthreadtask_group::object object;
+ llmainthreadtask_group llmainthreadtaskgrp("llmainthreadtask");
+
+ template<> template<>
+ void object::test<1>()
+ {
+ set_test_name("inline");
+ bool ran = false;
+ bool result = LLMainThreadTask::dispatch(
+ [&ran]()->bool{
+ ran = true;
+ return true;
+ });
+ ensure("didn't run lambda", ran);
+ ensure("didn't return result", result);
+ }
+
+ struct StaticData
+ {
+ std::mutex mMutex; // LockStatic looks for mMutex
+ bool ran{false};
+ };
+ typedef llthread::LockStatic<StaticData> LockStatic;
+
+ template<> template<>
+ void object::test<2>()
+ {
+ set_test_name("cross-thread");
+ skip("This test is prone to build-time hangs");
+ std::atomic_bool result(false);
+ // wrapping our thread lambda in a packaged_task will catch any
+ // exceptions it might throw and deliver them via future
+ std::packaged_task<void()> thread_work(
+ [this, &result](){
+ // unblock test<2>()'s yield_until(1)
+ mSync.set(1);
+ // dispatch work to main thread -- should block here
+ bool on_main(
+ LLMainThreadTask::dispatch(
+ []()->bool{
+ // have to lock static mutex to set static data
+ LockStatic()->ran = true;
+ // indicate whether task was run on the main thread
+ return on_main_thread();
+ }));
+ // wait for test<2>() to unblock us again
+ mSync.yield_until(3);
+ result = on_main;
+ });
+ auto thread_result = thread_work.get_future();
+ std::thread thread;
+ try
+ {
+ // run thread_work
+ thread = std::thread(std::move(thread_work));
+ // wait for thread to set(1)
+ mSync.yield_until(1);
+ // try to acquire the lock, should block because thread has it
+ LockStatic lk;
+ // wake up when dispatch() unlocks the static mutex
+ ensure("shouldn't have run yet", !lk->ran);
+ ensure("shouldn't have returned yet", !result);
+ // unlock so the task can acquire the lock
+ lk.unlock();
+ // run the task -- should unblock thread, which will immediately block
+ // on mSync
+ LLEventTimer::updateClass();
+ // 'lk', having unlocked, can no longer be used to access; relock with
+ // a new LockStatic instance
+ ensure("should now have run", LockStatic()->ran);
+ ensure("returned too early", !result);
+ // okay, let thread perform the assignment
+ mSync.set(3);
+ }
+ catch (...)
+ {
+ // A test failure exception anywhere in the try block can cause
+ // the test program to terminate without explanation when
+ // ~thread() finds that 'thread' is still joinable. We could
+ // either join() or detach() it -- but since it might be blocked
+ // waiting for something from the main thread that now can never
+ // happen, it's safer to detach it.
+ thread.detach();
+ throw;
+ }
+ // 'thread' should be all done now
+ thread.join();
+ // deliver any exception thrown by thread_work
+ thread_result.get();
+ ensure("ran changed", LockStatic()->ran);
+ ensure("didn't run on main thread", result);
+ }
+} // namespace tut
diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp
index 222d832084..f0eafa8201 100644
--- a/indra/llcommon/tests/llprocess_test.cpp
+++ b/indra/llcommon/tests/llprocess_test.cpp
@@ -493,14 +493,18 @@ namespace tut
}
// std::cout << "child done: rv = " << rv << " (" << manager.strerror(rv) << "), why = " << why << ", rc = " << rc << '\n';
aprchk_("apr_proc_wait(wi->child, &wi->rc, &wi->why, APR_NOWAIT)", wi.rv, APR_CHILD_DONE);
- ensure_equals_(wi.why, APR_PROC_EXIT);
- ensure_equals_(wi.rc, 0);
// Beyond merely executing all the above successfully, verify that we
// obtained expected output -- and that we duly got control while
// waiting, proving the non-blocking nature of these pipes.
try
{
+ // Perform these ensure_equals_() within this try/catch so that if
+ // we don't get expected results, we'll dump whatever we did get
+ // to help diagnose.
+ ensure_equals_(wi.why, APR_PROC_EXIT);
+ ensure_equals_(wi.rc, 0);
+
unsigned i = 0;
ensure("blocking I/O on child pipe (0)", history[i].tries);
ensure_equals_(history[i].which, "out");
diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp
index 6ac974e659..642c1c3879 100644
--- a/indra/llcommon/tests/llsdserialize_test.cpp
+++ b/indra/llcommon/tests/llsdserialize_test.cpp
@@ -271,10 +271,10 @@ namespace tut
LLSD w;
mParser->reset(); // reset() call is needed since test code re-uses mParser
mParser->parse(stream, w, stream.str().size());
-
+
try
{
- ensure_equals(msg.c_str(), w, v);
+ ensure_equals(msg, w, v);
}
catch (...)
{
@@ -432,6 +432,7 @@ namespace tut
const char source[] = "it must be a blue moon again";
std::vector<U8> data;
+ // note, includes terminating '\0'
copy(&source[0], &source[sizeof(source)], back_inserter(data));
v = data;
@@ -468,28 +469,36 @@ namespace tut
checkRoundTrip(msg + " many nested maps", v);
}
- typedef tut::test_group<TestLLSDSerializeData> TestLLSDSerialzeGroup;
- typedef TestLLSDSerialzeGroup::object TestLLSDSerializeObject;
- TestLLSDSerialzeGroup gTestLLSDSerializeGroup("llsd serialization");
+ typedef tut::test_group<TestLLSDSerializeData> TestLLSDSerializeGroup;
+ typedef TestLLSDSerializeGroup::object TestLLSDSerializeObject;
+ TestLLSDSerializeGroup gTestLLSDSerializeGroup("llsd serialization");
template<> template<>
void TestLLSDSerializeObject::test<1>()
{
- mFormatter = new LLSDNotationFormatter();
+ mFormatter = new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_PRETTY_BINARY);
mParser = new LLSDNotationParser();
- doRoundTripTests("notation serialization");
+ doRoundTripTests("pretty binary notation serialization");
}
-
+
template<> template<>
void TestLLSDSerializeObject::test<2>()
{
+ mFormatter = new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_NONE);
+ mParser = new LLSDNotationParser();
+ doRoundTripTests("raw binary notation serialization");
+ }
+
+ template<> template<>
+ void TestLLSDSerializeObject::test<3>()
+ {
mFormatter = new LLSDXMLFormatter();
mParser = new LLSDXMLParser();
doRoundTripTests("xml serialization");
}
-
+
template<> template<>
- void TestLLSDSerializeObject::test<3>()
+ void TestLLSDSerializeObject::test<4>()
{
mFormatter = new LLSDBinaryFormatter();
mParser = new LLSDBinaryParser();
diff --git a/indra/llcommon/tests/llsingleton_test.cpp b/indra/llcommon/tests/llsingleton_test.cpp
index 75ddff9d7d..15ffe68e67 100644
--- a/indra/llcommon/tests/llsingleton_test.cpp
+++ b/indra/llcommon/tests/llsingleton_test.cpp
@@ -143,8 +143,6 @@ namespace tut
\
(void)CLS::instance(); \
ensure_equals(sLog, #CLS "i" #CLS); \
- LLSingletonBase::cleanupAll(); \
- ensure_equals(sLog, #CLS "i" #CLS "x" #CLS); \
LLSingletonBase::deleteAll(); \
ensure_equals(sLog, #CLS "i" #CLS "x" #CLS "~" #CLS); \
} \
@@ -159,10 +157,8 @@ namespace tut
\
(void)CLS::instance(); \
ensure_equals(sLog, #CLS #OTHER "i" #OTHER "i" #CLS); \
- LLSingletonBase::cleanupAll(); \
- ensure_equals(sLog, #CLS #OTHER "i" #OTHER "i" #CLS "x" #CLS "x" #OTHER); \
LLSingletonBase::deleteAll(); \
- ensure_equals(sLog, #CLS #OTHER "i" #OTHER "i" #CLS "x" #CLS "x" #OTHER "~" #CLS "~" #OTHER); \
+ ensure_equals(sLog, #CLS #OTHER "i" #OTHER "i" #CLS "x" #CLS "~" #CLS "x" #OTHER "~" #OTHER); \
} \
\
template<> template<> \
@@ -175,10 +171,8 @@ namespace tut
\
(void)CLS::instance(); \
ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER); \
- LLSingletonBase::cleanupAll(); \
- ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER "x" #CLS "x" #OTHER); \
LLSingletonBase::deleteAll(); \
- ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER "x" #CLS "x" #OTHER "~" #CLS "~" #OTHER); \
+ ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER "x" #CLS "~" #CLS "x" #OTHER "~" #OTHER); \
} \
\
template<> template<> \
@@ -191,10 +185,8 @@ namespace tut
\
(void)CLS::instance(); \
ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER); \
- LLSingletonBase::cleanupAll(); \
- ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER "x" #CLS "x" #OTHER); \
LLSingletonBase::deleteAll(); \
- ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER "x" #CLS "x" #OTHER "~" #CLS "~" #OTHER); \
+ ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER "x" #CLS "~" #CLS "x" #OTHER "~" #OTHER); \
}
TESTS(A, B, 4, 5, 6, 7)