diff options
Diffstat (limited to 'indra/llcommon/tests')
| -rw-r--r-- | indra/llcommon/tests/llinstancetracker_test.cpp | 14 | ||||
| -rw-r--r-- | indra/llcommon/tests/llprocess_test.cpp | 18 | ||||
| -rw-r--r-- | indra/llcommon/tests/threadsafeschedule_test.cpp | 69 | ||||
| -rw-r--r-- | indra/llcommon/tests/tuple_test.cpp | 47 | ||||
| -rw-r--r-- | indra/llcommon/tests/workqueue_test.cpp | 235 | 
5 files changed, 371 insertions, 12 deletions
| diff --git a/indra/llcommon/tests/llinstancetracker_test.cpp b/indra/llcommon/tests/llinstancetracker_test.cpp index 9b89159625..5daa29adf4 100644 --- a/indra/llcommon/tests/llinstancetracker_test.cpp +++ b/indra/llcommon/tests/llinstancetracker_test.cpp @@ -90,19 +90,19 @@ namespace tut          {              Keyed one("one");              ensure_equals(Keyed::instanceCount(), 1); -            Keyed* found = Keyed::getInstance("one"); -            ensure("couldn't find stack Keyed", found); -            ensure_equals("found wrong Keyed instance", found, &one); +            auto found = Keyed::getInstance("one"); +            ensure("couldn't find stack Keyed", bool(found)); +            ensure_equals("found wrong Keyed instance", found.get(), &one);              {                  boost::scoped_ptr<Keyed> two(new Keyed("two"));                  ensure_equals(Keyed::instanceCount(), 2); -                Keyed* found = Keyed::getInstance("two"); -                ensure("couldn't find heap Keyed", found); -                ensure_equals("found wrong Keyed instance", found, two.get()); +                auto found = Keyed::getInstance("two"); +                ensure("couldn't find heap Keyed", bool(found)); +                ensure_equals("found wrong Keyed instance", found.get(), two.get());              }              ensure_equals(Keyed::instanceCount(), 1);          } -        Keyed* found = Keyed::getInstance("one"); +        auto found = Keyed::getInstance("one");          ensure("Keyed key lives too long", ! found);          ensure_equals(Keyed::instanceCount(), 0);      } diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp index e530975e86..999d432079 100644 --- a/indra/llcommon/tests/llprocess_test.cpp +++ b/indra/llcommon/tests/llprocess_test.cpp @@ -356,14 +356,15 @@ namespace tut          // Create a script file in a temporary place.          NamedTempFile script("py", +			"from __future__ import print_function" EOL              "import sys" EOL              "import time" EOL              EOL              "time.sleep(2)" EOL -            "print('stdout after wait', file=sys.stdout)" EOL +            "print('stdout after wait',file=sys.stdout)" EOL              "sys.stdout.flush()" EOL              "time.sleep(2)" EOL -            "print('stderr after wait', file=sys.stderr)" EOL +            "print('stderr after wait',file=sys.stderr)" EOL              "sys.stderr.flush()" EOL              ); @@ -572,12 +573,12 @@ namespace tut      {          set_test_name("arguments");          PythonProcessLauncher py(get_test_name(), -                                 "from __future__ import with_statement\n" +                                 "from __future__ import with_statement, print_function\n"                                   "import sys\n"                                   // note nonstandard output-file arg!                                   "with open(sys.argv[3], 'w') as f:\n"                                   "    for arg in sys.argv[1:]:\n" -                                 "        print(arg, file=f)\n"); +                                 "        print(arg,file=f)\n");          // We expect that PythonProcessLauncher has already appended          // its own NamedTempFile to mParams.args (sys.argv[0]).          py.mParams.args.add("first arg");          // sys.argv[1] @@ -861,6 +862,7 @@ namespace tut          set_test_name("'bogus' test");          CaptureLog recorder;          PythonProcessLauncher py(get_test_name(), +                                 "from __future__ import print_function\n"                                   "print('Hello world')\n");          py.mParams.files.add(LLProcess::FileParam("bogus"));          py.mPy = LLProcess::create(py.mParams); @@ -876,6 +878,7 @@ namespace tut          // Replace this test with one or more real 'file' tests when we          // implement 'file' support          PythonProcessLauncher py(get_test_name(), +                                 "from __future__ import print_function\n"                                   "print('Hello world')\n");          py.mParams.files.add(LLProcess::FileParam());          py.mParams.files.add(LLProcess::FileParam("file")); @@ -891,6 +894,7 @@ namespace tut          // implement 'tpipe' support          CaptureLog recorder;          PythonProcessLauncher py(get_test_name(), +                                 "from __future__ import print_function\n"                                   "print('Hello world')\n");          py.mParams.files.add(LLProcess::FileParam());          py.mParams.files.add(LLProcess::FileParam("tpipe")); @@ -908,6 +912,7 @@ namespace tut          // implement 'npipe' support          CaptureLog recorder;          PythonProcessLauncher py(get_test_name(), +                                 "from __future__ import print_function\n"                                   "print('Hello world')\n");          py.mParams.files.add(LLProcess::FileParam());          py.mParams.files.add(LLProcess::FileParam()); @@ -984,7 +989,8 @@ namespace tut      {          set_test_name("get*Pipe() validation");          PythonProcessLauncher py(get_test_name(), -                                 "print('this output is expected)'\n"); +                                 "from __future__ import print_function\n" +                                 "print('this output is expected')\n");          py.mParams.files.add(LLProcess::FileParam("pipe")); // pipe for  stdin          py.mParams.files.add(LLProcess::FileParam());       // inherit stdout          py.mParams.files.add(LLProcess::FileParam("pipe")); // pipe for stderr @@ -1004,6 +1010,7 @@ namespace tut      {          set_test_name("talk to stdin/stdout");          PythonProcessLauncher py(get_test_name(), +                                 "from __future__ import print_function\n"                                   "import sys, time\n"                                   "print('ok')\n"                                   "sys.stdout.flush()\n" @@ -1122,6 +1129,7 @@ namespace tut      {          set_test_name("ReadPipe \"eof\" event");          PythonProcessLauncher py(get_test_name(), +                                 "from __future__ import print_function\n"                                   "print('Hello from Python!')\n");          py.mParams.files.add(LLProcess::FileParam()); // stdin          py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout diff --git a/indra/llcommon/tests/threadsafeschedule_test.cpp b/indra/llcommon/tests/threadsafeschedule_test.cpp new file mode 100644 index 0000000000..c421cc7b1c --- /dev/null +++ b/indra/llcommon/tests/threadsafeschedule_test.cpp @@ -0,0 +1,69 @@ +/** + * @file   threadsafeschedule_test.cpp + * @author Nat Goodspeed + * @date   2021-10-04 + * @brief  Test for threadsafeschedule. + *  + * $LicenseInfo:firstyear=2021&license=viewerlgpl$ + * Copyright (c) 2021, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "threadsafeschedule.h" +// STL headers +// std headers +#include <chrono> +// external library headers +// other Linden headers +#include "../test/lltut.h" + +using namespace std::literals::chrono_literals; // ms suffix +using namespace std::literals::string_literals; // s suffix +using Queue = LL::ThreadSafeSchedule<std::string>; + +/***************************************************************************** +*   TUT +*****************************************************************************/ +namespace tut +{ +    struct threadsafeschedule_data +    { +        Queue queue; +    }; +    typedef test_group<threadsafeschedule_data> threadsafeschedule_group; +    typedef threadsafeschedule_group::object object; +    threadsafeschedule_group threadsafeschedulegrp("threadsafeschedule"); + +    template<> template<> +    void object::test<1>() +    { +        set_test_name("push"); +        // Simply calling push() a few times might result in indeterminate +        // delivery order if the resolution of steady_clock is coarser than +        // the real time required for each push() call. Explicitly increment +        // the timestamp for each one -- but since we're passing explicit +        // timestamps, make the queue reorder them. +        queue.push(Queue::TimeTuple(Queue::Clock::now() + 200ms, "ghi")); +        // Given the various push() overloads, you have to match the type +        // exactly: conversions are ambiguous. +        queue.push("abc"s); +        queue.push(Queue::Clock::now() + 100ms, "def"); +        queue.close(); +        auto entry = queue.pop(); +        ensure_equals("failed to pop first", std::get<0>(entry), "abc"s); +        entry = queue.pop(); +        ensure_equals("failed to pop second", std::get<0>(entry), "def"s); +        ensure("queue not closed", queue.isClosed()); +        ensure("queue prematurely done", ! queue.done()); +        std::string s; +        bool popped = queue.tryPopFor(1s, s); +        ensure("failed to pop third", popped); +        ensure_equals("third is wrong", s, "ghi"s); +        popped = queue.tryPop(s); +        ensure("queue not empty", ! popped); +        ensure("queue not done", queue.done()); +    } +} // namespace tut diff --git a/indra/llcommon/tests/tuple_test.cpp b/indra/llcommon/tests/tuple_test.cpp new file mode 100644 index 0000000000..af94e2086c --- /dev/null +++ b/indra/llcommon/tests/tuple_test.cpp @@ -0,0 +1,47 @@ +/** + * @file   tuple_test.cpp + * @author Nat Goodspeed + * @date   2021-10-04 + * @brief  Test for tuple. + *  + * $LicenseInfo:firstyear=2021&license=viewerlgpl$ + * Copyright (c) 2021, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "tuple.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "../test/lltut.h" + +/***************************************************************************** +*   TUT +*****************************************************************************/ +namespace tut +{ +    struct tuple_data +    { +    }; +    typedef test_group<tuple_data> tuple_group; +    typedef tuple_group::object object; +    tuple_group tuplegrp("tuple"); + +    template<> template<> +    void object::test<1>() +    { +        set_test_name("tuple"); +        std::tuple<std::string, int> tup{ "abc", 17 }; +        std::tuple<int, std::string, int> ptup{ tuple_cons(34, tup) }; +        std::tuple<std::string, int> tup2; +        int i; +        std::tie(i, tup2) = tuple_split(ptup); +        ensure_equals("tuple_car() fail", i, 34); +        ensure_equals("tuple_cdr() (0) fail", std::get<0>(tup2), "abc"); +        ensure_equals("tuple_cdr() (1) fail", std::get<1>(tup2), 17); +    } +} // namespace tut diff --git a/indra/llcommon/tests/workqueue_test.cpp b/indra/llcommon/tests/workqueue_test.cpp new file mode 100644 index 0000000000..1d73f7aa0d --- /dev/null +++ b/indra/llcommon/tests/workqueue_test.cpp @@ -0,0 +1,235 @@ +/** + * @file   workqueue_test.cpp + * @author Nat Goodspeed + * @date   2021-10-07 + * @brief  Test for workqueue. + *  + * $LicenseInfo:firstyear=2021&license=viewerlgpl$ + * Copyright (c) 2021, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "workqueue.h" +// STL headers +// std headers +#include <chrono> +#include <deque> +// external library headers +// other Linden headers +#include "../test/lltut.h" +#include "../test/catch_and_store_what_in.h" +#include "llcond.h" +#include "llcoros.h" +#include "lleventcoro.h" +#include "llstring.h" +#include "stringize.h" + +using namespace LL; +using namespace std::literals::chrono_literals; // ms suffix +using namespace std::literals::string_literals; // s suffix + +/***************************************************************************** +*   TUT +*****************************************************************************/ +namespace tut +{ +    struct workqueue_data +    { +        WorkQueue queue{"queue"}; +    }; +    typedef test_group<workqueue_data> workqueue_group; +    typedef workqueue_group::object object; +    workqueue_group workqueuegrp("workqueue"); + +    template<> template<> +    void object::test<1>() +    { +        set_test_name("name"); +        ensure_equals("didn't capture name", queue.getKey(), "queue"); +        ensure("not findable", WorkQueue::getInstance("queue") == queue.getWeak().lock()); +        WorkQueue q2; +        ensure("has no name", LLStringUtil::startsWith(q2.getKey(), "WorkQueue")); +    } + +    template<> template<> +    void object::test<2>() +    { +        set_test_name("post"); +        bool wasRun{ false }; +        // We only get away with binding a simple bool because we're running +        // the work on the same thread. +        queue.post([&wasRun](){ wasRun = true; }); +        queue.close(); +        ensure("ran too soon", ! wasRun); +        queue.runUntilClose(); +        ensure("didn't run", wasRun); +    } + +    template<> template<> +    void object::test<3>() +    { +        set_test_name("postEvery"); +        // record of runs +        using Shared = std::deque<WorkQueue::TimePoint>; +        // This is an example of how to share data between the originator of +        // postEvery(work) and the work item itself, since usually a WorkQueue +        // is used to dispatch work to a different thread. Neither of them +        // should call any of LLCond's wait methods: you don't want to stall +        // either the worker thread or the originating thread (conventionally +        // main). Use LLCond or a subclass even if all you want to do is +        // signal the work item that it can quit; consider LLOneShotCond. +        LLCond<Shared> data; +        auto start = WorkQueue::TimePoint::clock::now(); +        auto interval = 100ms; +        queue.postEvery( +            interval, +            [&data, count = 0] +            () mutable +            { +                // record the timestamp at which this instance is running +                data.update_one( +                    [](Shared& data) +                    { +                        data.push_back(WorkQueue::TimePoint::clock::now()); +                    }); +                // by the 3rd call, return false to stop +                return (++count < 3); +            }); +        // no convenient way to close() our queue while we've got a +        // postEvery() running, so run until we have exhausted the iterations +        // or we time out waiting +        for (auto finish = start + 10*interval; +             WorkQueue::TimePoint::clock::now() < finish && +             data.get([](const Shared& data){ return data.size(); }) < 3; ) +        { +            queue.runPending(); +            std::this_thread::sleep_for(interval/10); +        } +        // Take a copy of the captured deque. +        Shared result = data.get(); +        ensure_equals("called wrong number of times", result.size(), 3); +        // postEvery() assumes you want the first call to happen right away. +        // Pretend our start time was (interval) earlier than that, to make +        // our too early/too late tests uniform for all entries. +        start -= interval; +        for (size_t i = 0; i < result.size(); ++i) +        { +            auto diff = result[i] - start; +            start += interval; +            try +            { +                ensure(STRINGIZE("call " << i << " too soon"), diff >= interval); +                ensure(STRINGIZE("call " << i << " too late"), diff < interval*1.5); +            } +            catch (const tut::failure&) +            { +                auto interval_ms = interval / 1ms; +                auto diff_ms = diff / 1ms; +                std::cerr << "interval " << interval_ms +                          << "ms; diff " << diff_ms << "ms" << std::endl; +                throw; +            } +        } +    } + +    template<> template<> +    void object::test<4>() +    { +        set_test_name("postTo"); +        WorkQueue main("main"); +        auto qptr = WorkQueue::getInstance("queue"); +        int result = 0; +        main.postTo( +            qptr, +            [](){ return 17; }, +            // Note that a postTo() *callback* can safely bind a reference to +            // a variable on the invoking thread, because the callback is run +            // on the invoking thread. (Of course the bound variable must +            // survive until the callback is called.) +            [&result](int i){ result = i; }); +        // this should post the callback to main +        qptr->runOne(); +        // this should run the callback +        main.runOne(); +        ensure_equals("failed to run int callback", result, 17); + +        std::string alpha; +        // postTo() handles arbitrary return types +        main.postTo( +            qptr, +            [](){ return "abc"s; }, +            [&alpha](const std::string& s){ alpha = s; }); +        qptr->runPending(); +        main.runPending(); +        ensure_equals("failed to run string callback", alpha, "abc"); +    } + +    template<> template<> +    void object::test<5>() +    { +        set_test_name("postTo with void return"); +        WorkQueue main("main"); +        auto qptr = WorkQueue::getInstance("queue"); +        std::string observe; +        main.postTo( +            qptr, +            // The ONLY reason we can get away with binding a reference to +            // 'observe' in our work callable is because we're directly +            // calling qptr->runOne() on this same thread. It would be a +            // mistake to do that if some other thread were servicing 'queue'. +            [&observe](){ observe = "queue"; }, +            [&observe](){ observe.append(";main"); }); +        qptr->runOne(); +        main.runOne(); +        ensure_equals("failed to run both lambdas", observe, "queue;main"); +    } + +    template<> template<> +    void object::test<6>() +    { +        set_test_name("waitForResult"); +        std::string stored; +        // Try to call waitForResult() on this thread's main coroutine. It +        // should throw because the main coroutine must service the queue. +        auto what{ catch_what<WorkQueue::Error>( +                [this, &stored](){ stored = queue.waitForResult( +                        [](){ return "should throw"; }); }) }; +        ensure("lambda should not have run", stored.empty()); +        ensure_not("waitForResult() should have thrown", what.empty()); +        ensure(STRINGIZE("should mention waitForResult: " << what), +               what.find("waitForResult") != std::string::npos); + +        // Call waitForResult() on a coroutine, with a string result. +        LLCoros::instance().launch( +            "waitForResult string", +            [this, &stored]() +            { stored = queue.waitForResult( +                    [](){ return "string result"; }); }); +        llcoro::suspend(); +        // Nothing will have happened yet because, even if the coroutine did +        // run immediately, all it did was to queue the inner lambda on +        // 'queue'. Service it. +        queue.runOne(); +        llcoro::suspend(); +        ensure_equals("bad waitForResult return", stored, "string result"); + +        // Call waitForResult() on a coroutine, with a void callable. +        stored.clear(); +        bool done = false; +        LLCoros::instance().launch( +            "waitForResult void", +            [this, &stored, &done]() +            { +                queue.waitForResult([&stored](){ stored = "ran"; }); +                done = true; +            }); +        llcoro::suspend(); +        queue.runOne(); +        llcoro::suspend(); +        ensure_equals("didn't run coroutine", stored, "ran"); +        ensure("void waitForResult() didn't return", done); +    } +} // namespace tut | 
