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