diff options
Diffstat (limited to 'indra/llcommon/tests')
| -rw-r--r-- | indra/llcommon/tests/llcond_test.cpp | 67 | ||||
| -rw-r--r-- | indra/llcommon/tests/lleventcoro_test.cpp | 728 | ||||
| -rw-r--r-- | indra/llcommon/tests/lleventdispatcher_test.cpp | 123 | ||||
| -rw-r--r-- | indra/llcommon/tests/lleventfilter_test.cpp | 75 | ||||
| -rw-r--r-- | indra/llcommon/tests/llexception_test.cpp | 15 | ||||
| -rw-r--r-- | indra/llcommon/tests/llinstancetracker_test.cpp | 107 | ||||
| -rw-r--r-- | indra/llcommon/tests/llleap_test.cpp | 28 | ||||
| -rw-r--r-- | indra/llcommon/tests/llmainthreadtask_test.cpp | 137 | ||||
| -rw-r--r-- | indra/llcommon/tests/llprocess_test.cpp | 8 | ||||
| -rw-r--r-- | indra/llcommon/tests/llsdserialize_test.cpp | 29 | ||||
| -rw-r--r-- | indra/llcommon/tests/llsingleton_test.cpp | 14 | 
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) | 
