From ec162c8dabc8cd72e0c3b21da488d2a261fd9fb4 Mon Sep 17 00:00:00 2001 From: Rye Date: Mon, 12 Oct 2026 17:24:48 -0400 Subject: [PATCH 01/10] llsdserialize's Python group is a test file of its own llsdserialize_test.cpp held one group that runs Python's llsd package as an independent implementation, and so made the whole file a Python test. That group moves, with the helpers only it used, to llsdserialize_python_test.cpp, the Python test now, and the rest runs without an interpreter. The includes each half no longer needs go. The two files give exactly the results the one gave: 84 tests, 72 in llsdserialize_test and the 12 of the Python group. In stage 3 the Python file joins llleap and llprocess in llcommon_python_tests, and the rest llcommon_tests. Co-Authored-By: Claude Opus 5.5 --- indra/llcommon/CMakeLists.txt | 9 +- .../tests/llsdserialize_python_test.cpp | 464 ++++++++++++++++++ indra/llcommon/tests/llsdserialize_test.cpp | 417 ---------------- 3 files changed, 469 insertions(+), 421 deletions(-) create mode 100644 indra/llcommon/tests/llsdserialize_python_test.cpp diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index d9724dbb60..69564a3be3 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -337,6 +337,7 @@ if(AL_BUILD_TESTS) llrand llsd llsdjson + llsdserialize llsdutil llsingleton llstreamtools @@ -357,10 +358,10 @@ if(AL_BUILD_TESTS) endforeach() # These drive a Python peer: LEAP's protocol is to an external script, - # llprocess spawns one, and llsdserialize round-trips through the llsd - # package as an independent implementation. A peer that imports a script a - # test wrote to the temp folder would leave its bytecode there. - foreach(test llleap llprocess llsdserialize) + # llprocess spawns one, and llsdserialize_python round-trips through the + # llsd package as an independent implementation. A peer that imports a + # script a test wrote to the temp folder would leave its bytecode there. + foreach(test llleap llprocess llsdserialize_python) al_add_test( ${test} PROJECT llcommon diff --git a/indra/llcommon/tests/llsdserialize_python_test.cpp b/indra/llcommon/tests/llsdserialize_python_test.cpp new file mode 100644 index 0000000000..ece3cc7b30 --- /dev/null +++ b/indra/llcommon/tests/llsdserialize_python_test.cpp @@ -0,0 +1,464 @@ +/** + * @file llsdserialize_python_test.cpp + * @date 2006-04 + * @brief LLSDSerialize round trips through Python's llsd package + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +// llsdserialize_test.cpp's group that needs a Python interpreter, apart so +// the rest can run without one. + +#include "linden_common.h" + +#if LL_WINDOWS +#include +#include +#else +#include +#include +#include "llprocess.h" +#endif + +#include "llsd.h" +#include "llsdserialize.h" +#include "llsdutil.h" +#include "llmemorystream.h" +#include "llstring.h" + +#include "hexdump.h" +#include "../test/lltut.h" +#include "../test/namedtempfile.h" +#include "stringize.h" +#include "StringVec.h" +#include + +typedef std::function FormatterFunction; +typedef std::function ParserFunction; + +namespace tut +{ + // helper for TestPythonCompatible + static std::string import_llsd("import os.path\n" + "import sys\n" + "import llsd\n"); + + // helper for TestPythonCompatible + template + static 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()); + + NamedTempFile scriptfile("py", script); + +#if LL_WINDOWS + std::string q("\""); + std::string qPYTHON(q + PYTHON + q); + std::string qscript(q + scriptfile.getPath().string() + q); + int rc = (int)_spawnl(_P_WAIT, PYTHON.c_str(), qPYTHON.c_str(), qscript.c_str(), + std::forward(args)..., NULL); + if (rc == -1) + { + char buffer[256]; + strerror_s(buffer, errno); // C++ can infer the buffer size! :-O + ensure(STRINGIZE("Couldn't run Python " << desc << "script: " << buffer), false); + } + else + { + ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc), rc, expect); + } + +#else // LL_DARWIN, LL_LINUX + LLProcess::Params params; + params.executable = PYTHON; + params.args.add(scriptfile.getPath().string()); + for (const std::string& arg : StringVec{ std::forward(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 + // catching SIGALRM... later maybe... + int status(0); + if (waitpid(py->getProcessID(), &status, 0) == -1) + { + int waitpid_errno(errno); + ensure_equals(STRINGIZE("Couldn't retrieve rc from " << desc << " script: " + "waitpid() errno " << waitpid_errno), + waitpid_errno, ECHILD); + } + else + { + if (WIFEXITED(status)) + { + int rc(WEXITSTATUS(status)); + ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc), + rc, expect); + } + else if (WIFSIGNALED(status)) + { + ensure(STRINGIZE(desc << " script terminated by signal " << WTERMSIG(status)), + false); + } + else + { + ensure(STRINGIZE(desc << " script produced impossible status " << status), + false); + } + } +#endif + } + + // helper for TestPythonCompatible + template + static void python(const std::string& desc, const CONTENT& script, ARGS&&... args) + { + // plain python() expects rc 0 + python_expect(desc, script, 0, std::forward(args)...); + } + + struct TestPythonCompatible + { + TestPythonCompatible() {} + ~TestPythonCompatible() {} + }; + + typedef tut::test_group TestPythonCompatibleGroup; + typedef TestPythonCompatibleGroup::object TestPythonCompatibleObject; + TestPythonCompatibleGroup pycompat("LLSD serialize Python compatibility"); + + template<> template<> + void TestPythonCompatibleObject::test<1>() + { + set_test_name("verify python()"); + python_expect("hello", + "import sys\n" + "sys.exit(17)\n", + 17); // expect nonzero rc + } + + template<> template<> + void TestPythonCompatibleObject::test<2>() + { + set_test_name("verify NamedTempFile"); + python("platform", + "import sys\n" + "print('Running on', sys.platform)\n"); + } + + // helper for test<3> - test<7> + static void writeLLSDArray(const FormatterFunction& serialize, + std::ostream& out, const LLSD& 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 + // stream, the XML parser expects EOF after a single outer element + // -- it doesn't just stop. So we must extract a sequence of bytes + // strings from the file. But since one of the serialization + // formats we want to test is binary, we can't pick any single + // byte value as a delimiter! Use a binary integer length prefix + // instead. + std::ostringstream buffer; + serialize(item, buffer); + auto buffstr{ buffer.str() }; + int bufflen{ static_cast(buffstr.length()) }; + out.write(reinterpret_cast(&bufflen), sizeof(bufflen)); + LL_DEBUGS() << "Wrote length: " + << LL::hexdump(reinterpret_cast(&bufflen), + sizeof(bufflen)) + << LL_ENDL; + out.write(buffstr.c_str(), buffstr.length()); + LL_DEBUGS() << "Wrote data: " + << LL::hexmix(buffstr.c_str(), buffstr.length()) + << LL_ENDL; + } + } + + // helper for test<3> - test<7> + static void toPythonUsing(const std::string& desc, + const FormatterFunction& serialize) + { + LLSD cdata(llsd::array(17, 3.14, + "This string\n" + "has several\n" + "lines.")); + + const char pydata[] = + "def verify(iterable):\n" + " it = iter(iterable)\n" + " assert next(it) == 17\n" + " assert abs(next(it) - 3.14) < 0.01\n" + " assert next(it) == '''\\\n" + "This string\n" + "has several\n" + "lines.'''\n" + " try:\n" + " next(it)\n" + " except StopIteration:\n" + " pass\n" + " else:\n" + " raise AssertionError('Too many data items')\n"; + + // Create an llsdXXXXXX file containing 'data' serialized per + // FormatterFunction. + NamedTempFile file("llsd", + // NamedTempFile's function constructor + // takes a callable. To this callable it passes the + // std::ostream with which it's writing the + // NamedTempFile. + [serialize, cdata] + (std::ostream& out) + { writeLLSDArray(serialize, out, cdata); }); + + // '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.getPath().string() << "', 'w')\n" + "verify(parse_each(open(r'" << file.getPath().string() << "', 'rb')))\n";}); + } + catch (const failure&) + { + LL_DEBUGS() << "Script debug output:" << LL_ENDL; + debug.peep_log(); + throw; + } + } + + template<> template<> + void TestPythonCompatibleObject::test<3>() + { + set_test_name("to Python using LLSDSerialize::serialize(LLSD_XML)"); + toPythonUsing("LLSD_XML", + [](const LLSD& sd, std::ostream& out) + { LLSDSerialize::serialize(sd, out, LLSDSerialize::LLSD_XML); }); + } + + template<> template<> + void TestPythonCompatibleObject::test<4>() + { + set_test_name("to Python using LLSDSerialize::serialize(LLSD_NOTATION)"); + toPythonUsing("LLSD_NOTATION", + [](const LLSD& sd, std::ostream& out) + { LLSDSerialize::serialize(sd, out, LLSDSerialize::LLSD_NOTATION); }); + } + + template<> template<> + void TestPythonCompatibleObject::test<5>() + { + set_test_name("to Python using LLSDSerialize::serialize(LLSD_BINARY)"); + toPythonUsing("LLSD_BINARY", + [](const LLSD& sd, std::ostream& out) + { LLSDSerialize::serialize(sd, out, LLSDSerialize::LLSD_BINARY); }); + } + + template<> template<> + void TestPythonCompatibleObject::test<6>() + { + set_test_name("to Python using LLSDSerialize::toXML()"); + toPythonUsing("toXML()", LLSDSerialize::toXML); + } + + template<> template<> + void TestPythonCompatibleObject::test<7>() + { + set_test_name("to Python using LLSDSerialize::toNotation()"); + toPythonUsing("toNotation()", LLSDSerialize::toNotation); + } + +/*==========================================================================*| + template<> template<> + void TestPythonCompatibleObject::test<8>() + { + set_test_name("to Python using LLSDSerialize::toBinary()"); + // We don't expect this to work because, without a header, + // llsd.parse() will assume notation rather than binary. + toPythonUsing("toBinary()", LLSDSerialize::toBinary); + } +|*==========================================================================*/ + + // helper for test<8> - test<12> + static bool itemFromStream(std::istream& istr, LLSD& item, const ParserFunction& parse) + { + // reset the output value for debugging clarity + item.clear(); + // We use an int length prefix as a foolproof delimiter even for + // binary serialized streams. + int length{ 0 }; + istr.read(reinterpret_cast(&length), sizeof(length)); +// return parse(istr, item, length); + // Sadly, as of 2022-12-01 it seems we can't really trust our LLSD + // parsers to honor max_bytes: this test works better when we read + // each item into its own distinct LLMemoryStream, instead of passing + // the original istr with a max_bytes constraint. + std::vector buffer(length); + istr.read(reinterpret_cast(buffer.data()), length); + LLMemoryStream stream(buffer.data(), length); + return parse(stream, item, length); + } + + // helper for test<8> - test<12> + static void fromPythonUsing(const std::string& pyformatter, + const ParserFunction& parse= + [](std::istream& istr, LLSD& data, llssize max_bytes) + { return LLSDSerialize::deserialize(data, istr, max_bytes); }) + { + // Create an empty data file. This is just a placeholder for our + // script to write into. Create it to establish a unique name that + // we know. + NamedTempFile file("llsd", ""); + + python("Python " + pyformatter, + [&](std::ostream& out){ out << + import_llsd << + "import struct\n" + "lenformat = struct.Struct('i')\n" + "DATA = [\n" + " 17,\n" + " 3.14,\n" + " '''\\\n" + "This string\n" + "has several\n" + "lines.''',\n" + "]\n" + // Don't forget raw-string syntax for Windows pathnames. + // N.B. Using 'print' implicitly adds newlines. + "with open(r'" << ll_convert(file.getPath().u8string()).c_str() << "', 'wb') as f:\n" + " for item in DATA:\n" + " serialized = llsd." << pyformatter << "(item)\n" + " f.write(lenformat.pack(len(serialized)))\n" + " f.write(serialized)\n";}); + + llifstream inf(file.getPath()); + LLSD item; + try + { + ensure("Failed to read LLSD::Integer from Python", + itemFromStream(inf, item, parse)); + ensure_equals(item.asInteger(), 17); + ensure("Failed to read LLSD::Real from Python", + itemFromStream(inf, item, parse)); + ensure_approximately_equals("Bad LLSD::Real value from Python", + item.asReal(), 3.14, 7); // 7 bits ~= 0.01 + ensure("Failed to read LLSD::String from Python", + itemFromStream(inf, item, parse)); + ensure_equals(item.asString(), + "This string\n" + "has several\n" + "lines."); + } + catch (const tut::failure& err) + { + std::cout << "for " << err.what() << ", item = " << item << std::endl; + throw; + } + } + + template<> template<> + void TestPythonCompatibleObject::test<8>() + { + set_test_name("from Python XML using LLSDSerialize::deserialize()"); + fromPythonUsing("format_xml"); + } + + template<> template<> + void TestPythonCompatibleObject::test<9>() + { + set_test_name("from Python notation using LLSDSerialize::deserialize()"); + fromPythonUsing("format_notation"); + } + + template<> template<> + void TestPythonCompatibleObject::test<10>() + { + set_test_name("from Python binary using LLSDSerialize::deserialize()"); + fromPythonUsing("format_binary"); + } + + template<> template<> + void TestPythonCompatibleObject::test<11>() + { + set_test_name("from Python XML using fromXML()"); + // fromXML()'s optional 3rd param isn't max_bytes, it's emit_errors + fromPythonUsing("format_xml", + [](std::istream& istr, LLSD& data, llssize) + { return LLSDSerialize::fromXML(data, istr) > 0; }); + } + + template<> template<> + void TestPythonCompatibleObject::test<12>() + { + set_test_name("from Python notation using fromNotation()"); + fromPythonUsing("format_notation", + [](std::istream& istr, LLSD& data, llssize max_bytes) + { return LLSDSerialize::fromNotation(data, istr, max_bytes) > 0; }); + } + +/*==========================================================================*| + template<> template<> + void TestPythonCompatibleObject::test<13>() + { + set_test_name("from Python binary using fromBinary()"); + // We don't expect this to work because format_binary() emits a + // header, but fromBinary() won't recognize a header. + fromPythonUsing("format_binary", + [](std::istream& istr, LLSD& data, llssize max_bytes) + { return LLSDSerialize::fromBinary(data, istr, max_bytes) > 0; }); + } +|*==========================================================================*/ +} diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp index 189c18420c..840674f7d2 100644 --- a/indra/llcommon/tests/llsdserialize_test.cpp +++ b/indra/llcommon/tests/llsdserialize_test.cpp @@ -31,17 +31,8 @@ #if LL_WINDOWS #include typedef U32 uint32_t; -#include -#include #else -#include #include -#include -#include -#include -#include -#include "llprocess.h" -#include "llstring.h" #endif #include "boost/range.hpp" @@ -54,11 +45,8 @@ typedef U32 uint32_t; #include "llformat.h" #include "llmemorystream.h" -#include "hexdump.h" #include "../test/lltut.h" -#include "../test/namedtempfile.h" #include "stringize.h" -#include "StringVec.h" #include "wrapllerrs.h" #include @@ -2251,409 +2239,4 @@ namespace tut ensureBinaryAndNotation("map", test); ensureBinaryAndXML("map", test); } - - // helper for TestPythonCompatible - static std::string import_llsd("import os.path\n" - "import sys\n" - "import llsd\n"); - - // helper for TestPythonCompatible - template - static 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()); - - NamedTempFile scriptfile("py", script); - -#if LL_WINDOWS - std::string q("\""); - std::string qPYTHON(q + PYTHON + q); - std::string qscript(q + scriptfile.getPath().string() + q); - int rc = (int)_spawnl(_P_WAIT, PYTHON.c_str(), qPYTHON.c_str(), qscript.c_str(), - std::forward(args)..., NULL); - if (rc == -1) - { - char buffer[256]; - strerror_s(buffer, errno); // C++ can infer the buffer size! :-O - ensure(STRINGIZE("Couldn't run Python " << desc << "script: " << buffer), false); - } - else - { - ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc), rc, expect); - } - -#else // LL_DARWIN, LL_LINUX - LLProcess::Params params; - params.executable = PYTHON; - params.args.add(scriptfile.getPath().string()); - for (const std::string& arg : StringVec{ std::forward(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 - // catching SIGALRM... later maybe... - int status(0); - if (waitpid(py->getProcessID(), &status, 0) == -1) - { - int waitpid_errno(errno); - ensure_equals(STRINGIZE("Couldn't retrieve rc from " << desc << " script: " - "waitpid() errno " << waitpid_errno), - waitpid_errno, ECHILD); - } - else - { - if (WIFEXITED(status)) - { - int rc(WEXITSTATUS(status)); - ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc), - rc, expect); - } - else if (WIFSIGNALED(status)) - { - ensure(STRINGIZE(desc << " script terminated by signal " << WTERMSIG(status)), - false); - } - else - { - ensure(STRINGIZE(desc << " script produced impossible status " << status), - false); - } - } -#endif - } - - // helper for TestPythonCompatible - template - static void python(const std::string& desc, const CONTENT& script, ARGS&&... args) - { - // plain python() expects rc 0 - python_expect(desc, script, 0, std::forward(args)...); - } - - struct TestPythonCompatible - { - TestPythonCompatible() {} - ~TestPythonCompatible() {} - }; - - typedef tut::test_group TestPythonCompatibleGroup; - typedef TestPythonCompatibleGroup::object TestPythonCompatibleObject; - TestPythonCompatibleGroup pycompat("LLSD serialize Python compatibility"); - - template<> template<> - void TestPythonCompatibleObject::test<1>() - { - set_test_name("verify python()"); - python_expect("hello", - "import sys\n" - "sys.exit(17)\n", - 17); // expect nonzero rc - } - - template<> template<> - void TestPythonCompatibleObject::test<2>() - { - set_test_name("verify NamedTempFile"); - python("platform", - "import sys\n" - "print('Running on', sys.platform)\n"); - } - - // helper for test<3> - test<7> - static void writeLLSDArray(const FormatterFunction& serialize, - std::ostream& out, const LLSD& 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 - // stream, the XML parser expects EOF after a single outer element - // -- it doesn't just stop. So we must extract a sequence of bytes - // strings from the file. But since one of the serialization - // formats we want to test is binary, we can't pick any single - // byte value as a delimiter! Use a binary integer length prefix - // instead. - std::ostringstream buffer; - serialize(item, buffer); - auto buffstr{ buffer.str() }; - int bufflen{ static_cast(buffstr.length()) }; - out.write(reinterpret_cast(&bufflen), sizeof(bufflen)); - LL_DEBUGS() << "Wrote length: " - << LL::hexdump(reinterpret_cast(&bufflen), - sizeof(bufflen)) - << LL_ENDL; - out.write(buffstr.c_str(), buffstr.length()); - LL_DEBUGS() << "Wrote data: " - << LL::hexmix(buffstr.c_str(), buffstr.length()) - << LL_ENDL; - } - } - - // helper for test<3> - test<7> - static void toPythonUsing(const std::string& desc, - const FormatterFunction& serialize) - { - LLSD cdata(llsd::array(17, 3.14, - "This string\n" - "has several\n" - "lines.")); - - const char pydata[] = - "def verify(iterable):\n" - " it = iter(iterable)\n" - " assert next(it) == 17\n" - " assert abs(next(it) - 3.14) < 0.01\n" - " assert next(it) == '''\\\n" - "This string\n" - "has several\n" - "lines.'''\n" - " try:\n" - " next(it)\n" - " except StopIteration:\n" - " pass\n" - " else:\n" - " raise AssertionError('Too many data items')\n"; - - // Create an llsdXXXXXX file containing 'data' serialized per - // FormatterFunction. - NamedTempFile file("llsd", - // NamedTempFile's function constructor - // takes a callable. To this callable it passes the - // std::ostream with which it's writing the - // NamedTempFile. - [serialize, cdata] - (std::ostream& out) - { writeLLSDArray(serialize, out, cdata); }); - - // '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.getPath().string() << "', 'w')\n" - "verify(parse_each(open(r'" << file.getPath().string() << "', 'rb')))\n";}); - } - catch (const failure&) - { - LL_DEBUGS() << "Script debug output:" << LL_ENDL; - debug.peep_log(); - throw; - } - } - - template<> template<> - void TestPythonCompatibleObject::test<3>() - { - set_test_name("to Python using LLSDSerialize::serialize(LLSD_XML)"); - toPythonUsing("LLSD_XML", - [](const LLSD& sd, std::ostream& out) - { LLSDSerialize::serialize(sd, out, LLSDSerialize::LLSD_XML); }); - } - - template<> template<> - void TestPythonCompatibleObject::test<4>() - { - set_test_name("to Python using LLSDSerialize::serialize(LLSD_NOTATION)"); - toPythonUsing("LLSD_NOTATION", - [](const LLSD& sd, std::ostream& out) - { LLSDSerialize::serialize(sd, out, LLSDSerialize::LLSD_NOTATION); }); - } - - template<> template<> - void TestPythonCompatibleObject::test<5>() - { - set_test_name("to Python using LLSDSerialize::serialize(LLSD_BINARY)"); - toPythonUsing("LLSD_BINARY", - [](const LLSD& sd, std::ostream& out) - { LLSDSerialize::serialize(sd, out, LLSDSerialize::LLSD_BINARY); }); - } - - template<> template<> - void TestPythonCompatibleObject::test<6>() - { - set_test_name("to Python using LLSDSerialize::toXML()"); - toPythonUsing("toXML()", LLSDSerialize::toXML); - } - - template<> template<> - void TestPythonCompatibleObject::test<7>() - { - set_test_name("to Python using LLSDSerialize::toNotation()"); - toPythonUsing("toNotation()", LLSDSerialize::toNotation); - } - -/*==========================================================================*| - template<> template<> - void TestPythonCompatibleObject::test<8>() - { - set_test_name("to Python using LLSDSerialize::toBinary()"); - // We don't expect this to work because, without a header, - // llsd.parse() will assume notation rather than binary. - toPythonUsing("toBinary()", LLSDSerialize::toBinary); - } -|*==========================================================================*/ - - // helper for test<8> - test<12> - static bool itemFromStream(std::istream& istr, LLSD& item, const ParserFunction& parse) - { - // reset the output value for debugging clarity - item.clear(); - // We use an int length prefix as a foolproof delimiter even for - // binary serialized streams. - int length{ 0 }; - istr.read(reinterpret_cast(&length), sizeof(length)); -// return parse(istr, item, length); - // Sadly, as of 2022-12-01 it seems we can't really trust our LLSD - // parsers to honor max_bytes: this test works better when we read - // each item into its own distinct LLMemoryStream, instead of passing - // the original istr with a max_bytes constraint. - std::vector buffer(length); - istr.read(reinterpret_cast(buffer.data()), length); - LLMemoryStream stream(buffer.data(), length); - return parse(stream, item, length); - } - - // helper for test<8> - test<12> - static void fromPythonUsing(const std::string& pyformatter, - const ParserFunction& parse= - [](std::istream& istr, LLSD& data, llssize max_bytes) - { return LLSDSerialize::deserialize(data, istr, max_bytes); }) - { - // Create an empty data file. This is just a placeholder for our - // script to write into. Create it to establish a unique name that - // we know. - NamedTempFile file("llsd", ""); - - python("Python " + pyformatter, - [&](std::ostream& out){ out << - import_llsd << - "import struct\n" - "lenformat = struct.Struct('i')\n" - "DATA = [\n" - " 17,\n" - " 3.14,\n" - " '''\\\n" - "This string\n" - "has several\n" - "lines.''',\n" - "]\n" - // Don't forget raw-string syntax for Windows pathnames. - // N.B. Using 'print' implicitly adds newlines. - "with open(r'" << ll_convert(file.getPath().u8string()).c_str() << "', 'wb') as f:\n" - " for item in DATA:\n" - " serialized = llsd." << pyformatter << "(item)\n" - " f.write(lenformat.pack(len(serialized)))\n" - " f.write(serialized)\n";}); - - llifstream inf(file.getPath()); - LLSD item; - try - { - ensure("Failed to read LLSD::Integer from Python", - itemFromStream(inf, item, parse)); - ensure_equals(item.asInteger(), 17); - ensure("Failed to read LLSD::Real from Python", - itemFromStream(inf, item, parse)); - ensure_approximately_equals("Bad LLSD::Real value from Python", - item.asReal(), 3.14, 7); // 7 bits ~= 0.01 - ensure("Failed to read LLSD::String from Python", - itemFromStream(inf, item, parse)); - ensure_equals(item.asString(), - "This string\n" - "has several\n" - "lines."); - } - catch (const tut::failure& err) - { - std::cout << "for " << err.what() << ", item = " << item << std::endl; - throw; - } - } - - template<> template<> - void TestPythonCompatibleObject::test<8>() - { - set_test_name("from Python XML using LLSDSerialize::deserialize()"); - fromPythonUsing("format_xml"); - } - - template<> template<> - void TestPythonCompatibleObject::test<9>() - { - set_test_name("from Python notation using LLSDSerialize::deserialize()"); - fromPythonUsing("format_notation"); - } - - template<> template<> - void TestPythonCompatibleObject::test<10>() - { - set_test_name("from Python binary using LLSDSerialize::deserialize()"); - fromPythonUsing("format_binary"); - } - - template<> template<> - void TestPythonCompatibleObject::test<11>() - { - set_test_name("from Python XML using fromXML()"); - // fromXML()'s optional 3rd param isn't max_bytes, it's emit_errors - fromPythonUsing("format_xml", - [](std::istream& istr, LLSD& data, llssize) - { return LLSDSerialize::fromXML(data, istr) > 0; }); - } - - template<> template<> - void TestPythonCompatibleObject::test<12>() - { - set_test_name("from Python notation using fromNotation()"); - fromPythonUsing("format_notation", - [](std::istream& istr, LLSD& data, llssize max_bytes) - { return LLSDSerialize::fromNotation(data, istr, max_bytes) > 0; }); - } - -/*==========================================================================*| - template<> template<> - void TestPythonCompatibleObject::test<13>() - { - set_test_name("from Python binary using fromBinary()"); - // We don't expect this to work because format_binary() emits a - // header, but fromBinary() won't recognize a header. - fromPythonUsing("format_binary", - [](std::istream& istr, LLSD& data, llssize max_bytes) - { return LLSDSerialize::fromBinary(data, istr, max_bytes) > 0; }); - } -|*==========================================================================*/ } From b879f7fb3f3c8991301db16af8a73cf5c19defaa Mon Sep 17 00:00:00 2001 From: Rye Date: Mon, 12 Oct 2026 17:29:33 -0400 Subject: [PATCH 02/10] llsingleton's dependency-order tests are written out, over one helper A TESTS(CLS, OTHER, N0..N3) macro made eight of llsingleton_test's fifteen tests, four for each order of A and B, each a copy of the same steps with the class names pasted into its expected log. A test a macro makes is one tut2catch.py cannot convert or map. The steps are now one helper, check_order, given the dependency flags and the two halves of the log it expects, and the eight tests are written out, each naming its own case. The eight give the same results under the same names, and still fail when an expected log is wrong. Co-Authored-By: Claude Opus 5.5 --- indra/llcommon/tests/llsingleton_test.cpp | 127 ++++++++++++---------- 1 file changed, 70 insertions(+), 57 deletions(-) diff --git a/indra/llcommon/tests/llsingleton_test.cpp b/indra/llcommon/tests/llsingleton_test.cpp index 51f2b92f12..08c3c1cb27 100644 --- a/indra/llcommon/tests/llsingleton_test.cpp +++ b/indra/llcommon/tests/llsingleton_test.cpp @@ -137,65 +137,78 @@ namespace tut ensure(LLSingletonTest::instanceExists()); } -#define TESTS(CLS, OTHER, N0, N1, N2, N3) \ - template<> template<> \ - void singleton_object_t::test() \ - { \ - set_test_name("just " #CLS); \ - CLS::sDepFlag = CLS::DEP_NONE; \ - OTHER::sDepFlag = OTHER::DEP_NONE; \ - sLog.clear(); \ - \ - (void)CLS::instance(); \ - ensure_equals(sLog, #CLS "i" #CLS); \ - LLSingletonBase::deleteAll(); \ - ensure_equals(sLog, #CLS "i" #CLS "x" #CLS "~" #CLS); \ - } \ - \ - template<> template<> \ - void singleton_object_t::test() \ - { \ - set_test_name(#CLS " ctor depends " #OTHER); \ - CLS::sDepFlag = CLS::DEP_CTOR; \ - OTHER::sDepFlag = OTHER::DEP_NONE; \ - sLog.clear(); \ - \ - (void)CLS::instance(); \ - ensure_equals(sLog, #CLS #OTHER "i" #OTHER "i" #CLS); \ - LLSingletonBase::deleteAll(); \ - ensure_equals(sLog, #CLS #OTHER "i" #OTHER "i" #CLS "x" #CLS "~" #CLS "x" #OTHER "~" #OTHER); \ - } \ - \ - template<> template<> \ - void singleton_object_t::test() \ - { \ - set_test_name(#CLS " init depends " #OTHER); \ - CLS::sDepFlag = CLS::DEP_INIT; \ - OTHER::sDepFlag = OTHER::DEP_NONE; \ - sLog.clear(); \ - \ - (void)CLS::instance(); \ - ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER); \ - LLSingletonBase::deleteAll(); \ - ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER "x" #CLS "~" #CLS "x" #OTHER "~" #OTHER); \ - } \ - \ - template<> template<> \ - void singleton_object_t::test() \ - { \ - set_test_name(#CLS " circular init"); \ - CLS::sDepFlag = CLS::DEP_INIT; \ - OTHER::sDepFlag = OTHER::DEP_CTOR; \ - sLog.clear(); \ - \ - (void)CLS::instance(); \ - ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER); \ - LLSingletonBase::deleteAll(); \ - ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER "x" #CLS "~" #CLS "x" #OTHER "~" #OTHER); \ + // Makes CLS, CLS and OTHER depending on each other as the flags say, and + // checks the order they were made in, then deletes every singleton and + // checks the order they were cleaned up and destroyed in. + template + static void check_order(typename CLS::dep_flag cls_dep, typename OTHER::dep_flag other_dep, + const std::string& made, const std::string& deleted) + { + CLS::sDepFlag = cls_dep; + OTHER::sDepFlag = other_dep; + sLog.clear(); + + (void)CLS::instance(); + ensure_equals(sLog, made); + LLSingletonBase::deleteAll(); + ensure_equals(sLog, made + deleted); + } + + template<> template<> + void singleton_object_t::test<4>() + { + set_test_name("just A"); + check_order(A::DEP_NONE, B::DEP_NONE, "AiA", "xA~A"); + } + + template<> template<> + void singleton_object_t::test<5>() + { + set_test_name("A ctor depends B"); + check_order(A::DEP_CTOR, B::DEP_NONE, "ABiBiA", "xA~AxB~B"); + } + + template<> template<> + void singleton_object_t::test<6>() + { + set_test_name("A init depends B"); + check_order(A::DEP_INIT, B::DEP_NONE, "AiABiB", "xA~AxB~B"); + } + + template<> template<> + void singleton_object_t::test<7>() + { + set_test_name("A circular init"); + check_order(A::DEP_INIT, B::DEP_CTOR, "AiABiB", "xA~AxB~B"); + } + + template<> template<> + void singleton_object_t::test<8>() + { + set_test_name("just B"); + check_order(B::DEP_NONE, A::DEP_NONE, "BiB", "xB~B"); } - TESTS(A, B, 4, 5, 6, 7) - TESTS(B, A, 8, 9, 10, 11) + template<> template<> + void singleton_object_t::test<9>() + { + set_test_name("B ctor depends A"); + check_order(B::DEP_CTOR, A::DEP_NONE, "BAiAiB", "xB~BxA~A"); + } + + template<> template<> + void singleton_object_t::test<10>() + { + set_test_name("B init depends A"); + check_order(B::DEP_INIT, A::DEP_NONE, "BiBAiA", "xB~BxA~A"); + } + + template<> template<> + void singleton_object_t::test<11>() + { + set_test_name("B circular init"); + check_order(B::DEP_INIT, A::DEP_CTOR, "BiBAiA", "xB~BxA~A"); + } #define PARAMSINGLETON(cls) \ class cls: public LLParamSingleton \ From 9a876de8664c03f27978f310696e3e2af00c2364 Mon Sep 17 00:00:00 2001 From: Rye Date: Mon, 12 Oct 2026 17:49:02 -0400 Subject: [PATCH 03/10] LLURI compares with operator==, not the operator!= TUT needed LLURI declared operator!= alone, "required for tut", whose ensure_equals compared with !=. Catch2's CHECK(a == b) needs ==, and under C++20 a declared == gives != too, so == replaces it: two URIs are equal when their strings are, as before. llsd_test's helpers compare an LLURI as they compare every other type, and need it. Co-Authored-By: Claude Opus 5.5 --- indra/llcommon/lluri.cpp | 4 ++-- indra/llcommon/lluri.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/indra/llcommon/lluri.cpp b/indra/llcommon/lluri.cpp index df82276cd2..acbabdcede 100644 --- a/indra/llcommon/lluri.cpp +++ b/indra/llcommon/lluri.cpp @@ -752,7 +752,7 @@ std::string LLURI::mapToQueryString(const LLSD& queryMap) return query_string; } -bool operator!=(const LLURI& first, const LLURI& second) +bool operator==(const LLURI& first, const LLURI& second) { - return (first.asString() != second.asString()); + return first.asString() == second.asString(); } diff --git a/indra/llcommon/lluri.h b/indra/llcommon/lluri.h index 37ee0a0ac7..c6e881e2fd 100644 --- a/indra/llcommon/lluri.h +++ b/indra/llcommon/lluri.h @@ -187,7 +187,7 @@ class LL_COMMON_API LLURI std::string mEscapedQuery; }; -// this operator required for tut -LL_COMMON_API bool operator!=(const LLURI& first, const LLURI& second); +// Two URIs are equal when their strings are. +LL_COMMON_API bool operator==(const LLURI& first, const LLURI& second); #endif // LL_LLURI_H From dbec72cf34fa3474653002514070923ba80bc8bf Mon Sep 17 00:00:00 2001 From: Rye Date: Mon, 12 Oct 2026 17:49:40 -0400 Subject: [PATCH 04/10] A test binary runs a helper process alone, given --al-helper= llprocess's Windows tests launch the test executable itself as a helper that owns child processes and then exits, to show the children die with it. Under TUT the relaunched executable ran its tests until the first of llprocess's fixtures saw the helper's environment and exited. In a Catch2 binary of several files that would run the other files' tests in the child first. A test file now registers such a helper by name, AL_TEST_HELPER(name) { ...; return code; }, as it registers a test case, and launches its binary with Helper::argument(name), --al-helper=. The main finds that argument before Catch2 sees any, sets the process up as for a test run (APR, LLError, LL_ERRS throwing, LLTrace's recorder), runs that helper alone and exits with its code. An unknown name exits 2; two helpers of one name abort the binary as it starts. test_support_tests gains a helper and two entries: one runs it and expects its exit code, its report that APR is up and no test run, and one names a helper that is not there. Each fails when the main skips the setup or leaves the argument to Catch2. Co-Authored-By: Claude Opus 5.5 --- indra/test/CMakeLists.txt | 17 +++++ indra/test/altesthelper.cpp | 84 +++++++++++++++++++++++++ indra/test/altesthelper.h | 73 +++++++++++++++++++++ indra/test/altestmain.cpp | 38 ++++++++--- indra/test/tests/altestsupport_test.cpp | 11 ++++ 5 files changed, 213 insertions(+), 10 deletions(-) create mode 100644 indra/test/altesthelper.cpp create mode 100644 indra/test/altesthelper.h diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt index ae25f9a371..97b375a221 100644 --- a/indra/test/CMakeLists.txt +++ b/indra/test/CMakeLists.txt @@ -36,6 +36,7 @@ target_sources( al_test_support PRIVATE altestfatal.cpp + altesthelper.cpp altestlogreplay.cpp altestmain.cpp altestmatchers.cpp @@ -51,6 +52,7 @@ target_sources( FILES altestfailure.h altestfatal.h + altesthelper.h altestlogreplay.h altestmatchers.h altestname.h @@ -169,6 +171,21 @@ al_expect_from_test_support( OUTPUT "left the environment changed.*AL_TEST_SUPPORT_SKIP_LEAK=1" ) al_expect_from_test_support(nothing_matched_fails ARGS "no such test case" EXIT 2) +# A test can launch its own binary as a helper process: the main sets the +# process up and runs that helper alone, and no test case. +al_expect_from_test_support( + helper_runs_alone + ARGS "--al-helper=altestsupport_exit" + EXIT 37 + OUTPUT "the helper ran, with APR up" + REJECT "test cases" +) +al_expect_from_test_support( + helper_unknown + ARGS "--al-helper=nonesuch" + EXIT 2 + OUTPUT "No test helper is named nonesuch" +) al_expect_from_test_support( isa_gate_skips ARGS "--isa-level|9" diff --git a/indra/test/altesthelper.cpp b/indra/test/altesthelper.cpp new file mode 100644 index 0000000000..da8bb36ab2 --- /dev/null +++ b/indra/test/altesthelper.cpp @@ -0,0 +1,84 @@ +/** + * @file altesthelper.cpp + * @brief A process a test launches from its own binary, run by name and + * without the binary's test cases. + * + * $LicenseInfo:firstyear=2026&license=viewerlgpl$ + * Alchemy Viewer Source Code + * Copyright (C) 2026, Rye + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * $/LicenseInfo$ + */ + +#include "altesthelper.h" + +#include +#include +#include +#include + +namespace ll_test +{ + namespace + { + constexpr char PREFIX[] = "--al-helper="; + + // Made on first use, so a helper registered from any static + // initializer finds it ready. + std::map& helpers() + { + static std::map sHelpers; + return sHelpers; + } + } + + Helper::Helper(const char* name, Main main) + { + if (!helpers().emplace(name, main).second) + { + std::cerr << "Two test helpers are named " << name << std::endl; + std::abort(); + } + } + + std::string Helper::argument(const std::string& name) + { + return PREFIX + name; + } + + const char* Helper::named(int argc, char* argv[]) + { + for (int i = 1; i < argc; ++i) + { + if (std::strncmp(argv[i], PREFIX, sizeof(PREFIX) - 1) == 0) + { + return argv[i] + sizeof(PREFIX) - 1; + } + } + return nullptr; + } + + int Helper::run(const std::string& name) + { + auto found = helpers().find(name); + if (found == helpers().end()) + { + std::cerr << "No test helper is named " << name << std::endl; + return 2; + } + return found->second(); + } +} diff --git a/indra/test/altesthelper.h b/indra/test/altesthelper.h new file mode 100644 index 0000000000..29f32f5e18 --- /dev/null +++ b/indra/test/altesthelper.h @@ -0,0 +1,73 @@ +/** + * @file altesthelper.h + * @brief A process a test launches from its own binary, run by name and + * without the binary's test cases. + * + * $LicenseInfo:firstyear=2026&license=viewerlgpl$ + * Alchemy Viewer Source Code + * Copyright (C) 2026, Rye + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * $/LicenseInfo$ + */ + +#ifndef AL_ALTESTHELPER_H +#define AL_ALTESTHELPER_H + +#include + +namespace ll_test +{ + // A test that needs a child process of its own making -- one that owns + // other processes, say, and then exits -- can launch its own binary with + // Helper::argument(name). The main then runs only the helper of that + // name, as LL code expects to find a process set up, and exits with what + // it returns: no test case runs, so a binary whose files share a process + // does not run its whole suite again in the child. The argument is the + // main's alone; Catch2 never sees it. + // + // A helper is registered as a test case is, by AL_TEST_HELPER(name) in + // the test file that launches it. Names are unique within a binary. + class Helper + { + public: + using Main = int (*)(); + + // AL_TEST_HELPER's: a second helper of one name aborts the process, + // as it starts. + Helper(const char* name, Main main); + + // What to pass the binary to run the helper of that name. + static std::string argument(const std::string& name); + + // The main's: the helper argv names, or null when it names none. + static const char* named(int argc, char* argv[]); + + // The main's: runs the helper of that name and returns its exit code, + // or reports that there is none and returns 2. + static int run(const std::string& name); + }; +} + +// Defines the helper `name`, a function returning the helper process's exit +// code, and registers it: +// AL_TEST_HELPER(llprocess_autokill) { ...; return 0; } +#define AL_TEST_HELPER(name) \ + static int al_test_helper_##name(); \ + static const ll_test::Helper al_test_helper_registration_##name(#name, \ + &al_test_helper_##name); \ + static int al_test_helper_##name() + +#endif // AL_ALTESTHELPER_H diff --git a/indra/test/altestmain.cpp b/indra/test/altestmain.cpp index a6358e06d3..7869441d52 100644 --- a/indra/test/altestmain.cpp +++ b/indra/test/altestmain.cpp @@ -27,6 +27,7 @@ #include "linden_common.h" #include "altestfatal.h" +#include "altesthelper.h" #include "altestlogreplay.h" #include "alteststateguard.h" #include "altestteardown.h" @@ -92,6 +93,21 @@ namespace LLError::logToFile(llformat("%s.%d.log", executable, LLApp::getPid())); } } + + // What a test run and a helper both find: APR, LLError as LL code + // expects it, LL_ERRS throwing, and LLTrace's master recorder. + void setUpProcess(const char* executable, bool log_to_file) + { + ll_init_apr(); + setUpLogging(executable, log_to_file); + ll_test::installFatalFunction(); + ll_test::installTerminateHandler(); + + // LLTrace needs a master recorder before any thread records. Never + // freed, as the TUT runner never freed it: static destructors that + // record would find it gone. + LLTrace::set_master_thread_recorder(new LLTrace::ThreadRecorder()); + } } int main(int argc, char* argv[]) @@ -101,6 +117,17 @@ int main(int argc, char* argv[]) LL_PROFILER_FRAME_END; LL_PROFILER_SET_THREAD_NAME("App"); + // A helper a test launched from this binary runs alone, and Catch2 never + // sees its argument. + if (const char* helper = ll_test::Helper::named(argc, argv)) + { + setUpProcess(argv[0], false); + const int result = ll_test::Helper::run(helper); + ll_test::Teardown::runAll(); + ll_cleanup_apr(); + return result; + } + Catch::Session session; // Long names only: Catch2 owns every short one. @@ -139,16 +166,7 @@ int main(int argc, char* argv[]) return 125; } - ll_init_apr(); - setUpLogging(argv[0], log_to_file); - ll_test::installFatalFunction(); - ll_test::installTerminateHandler(); - - // LLTrace needs a master recorder before any thread records. Never freed, - // as the TUT runner never freed it: static destructors that record would - // find it gone. - LLTrace::set_master_thread_recorder(new LLTrace::ThreadRecorder()); - + setUpProcess(argv[0], log_to_file); ll_test::StateGuard::addCommonChecks(); int result = session.run(); diff --git a/indra/test/tests/altestsupport_test.cpp b/indra/test/tests/altestsupport_test.cpp index 4626568892..b9177be416 100644 --- a/indra/test/tests/altestsupport_test.cpp +++ b/indra/test/tests/altestsupport_test.cpp @@ -28,11 +28,13 @@ #include "linden_common.h" #include "altestfatal.h" +#include "altesthelper.h" #include "altestmatchers.h" #include "altestname.h" #include "alteststateguard.h" #include "altestteardown.h" +#include "llapr.h" #include "llerror.h" #include "llsd.h" @@ -43,6 +45,7 @@ #include #include +#include #include #include #include @@ -84,6 +87,14 @@ namespace CATCH_REGISTER_LISTENER(LibraryChecks) +// The helper the helper_* entries run, which says whether it found the +// process set up as a test would, and exits with a code no test run gives. +AL_TEST_HELPER(altestsupport_exit) +{ + std::cout << "the helper ran, " << (gAPRPoolp ? "with" : "without") << " APR up" << std::endl; + return 37; +} + TEST_CASE("WithinBits holds to the bits asked for, and no further", "[altestsupport]") { // 2^-20 apart: equal to 16 bits of the fraction, not to 24. (Not "near": From c6ac6debe371ddbca2ced540127fa54a02d7ed68 Mon Sep 17 00:00:00 2001 From: Rye Date: Mon, 12 Oct 2026 18:43:05 -0400 Subject: [PATCH 05/10] tut_parity.py reads every Catch2 report, and takes a reviewed list of sites that never run Two gaps llcommon's conversion found: - Catch2 writes a value it prints into its XML report, \xNN for a control character or a byte that is not UTF-8, but lets vertical tab, form feed and a UTF-8-encoded surrogate through, which XML forbids too. commonmisc's binary LLSD checks print all three, and its report could not be read. The report is read through a filter that writes those as Catch2 writes the rest, and that reads on when a surrogate's bytes straddle two reads. - Every assertion in a passing case's body must have run, but a few legitimately never do on a host: in a loop that should not turn, or a branch of an if constexpr the build discards (alsimd's AVX checks on arm64). check --unrun names them in a JSON, {file: {line: why}}, each reviewed, and they are listed with the reason rather than counted a mismatch. Every other site that never ran still is. Co-Authored-By: Claude Opus 5.5 --- scripts/testing/tut_parity.py | 87 +++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 24 deletions(-) diff --git a/scripts/testing/tut_parity.py b/scripts/testing/tut_parity.py index c2139ecfd0..9342074861 100644 --- a/scripts/testing/tut_parity.py +++ b/scripts/testing/tut_parity.py @@ -70,7 +70,10 @@ have stopped early, so neither is held to it; nor is one the build leaves out, which the file's compile command in the build's compile_commands.json, run as clang++ -E, says. Where there is no such - command, one under an #if that never ran is listed. + command, one under an #if that never ran is listed. So is one --unrun + names, reviewed as never running in a passing case on this host: in a + loop that should not turn, or a branch of an if constexpr the build + discards; A test the mapping says TUT never ran (past its group's limit) or this host does not compile is listed, not counted against parity, as is an assertion @@ -276,6 +279,31 @@ def record(args): # -------------------------------------------------------------------------- # check # -------------------------------------------------------------------------- +class LegalXml: + """A Catch2 XML report as a file to parse. Catch2 writes a value it + prints as XML, \\xNN for a control character or a byte that is not + UTF-8, but lets through vertical tab, form feed and a UTF-8 surrogate, + which XML forbids too: a check on bytes that hold one, as commonmisc's + binary LLSD does, would leave its report unreadable. Those are read as + Catch2 writes the rest.""" + + FORBIDDEN = re.compile(rb"[\x0b\x0c]|\xed[\xa0-\xbf][\x80-\xbf]") + + def __init__(self, raw): + self.raw = raw + + def read(self, size=-1): + data = self.raw.read(size) + # A surrogate's three bytes may straddle two reads; 0xED leads one + # and is never a later byte of any. + while b"\xed" in data[-2:]: + more = self.raw.read(2) + if not more: + break + data += more + return self.FORBIDDEN.sub(lambda m: "".join(f"\\x{b:02X}" for b in m.group()).encode(), data) + + def catch2_results(test, timeout): """The cases a Catch2 entry ran, as XML with its passing assertions too (-s), in the order it ran them: each {name, tags, line, result: ok | skip @@ -289,28 +317,29 @@ def catch2_results(test, timeout): cases = [] case = None try: - for event, element in ElementTree.iterparse(report, events=("start", "end")): - if event == "start": - if element.tag == "TestCase": - case = {"name": element.get("name"), "tags": element.get("tags", ""), - "line": int(element.get("line", "0")), "result": "fail", "ran": set()} - continue - if case is None: - continue - if element.tag in RAN_ELEMENTS: - case["ran"].add((element.get("filename", ""), int(element.get("line", "0")))) - element.clear() - elif element.tag == "OverallResult": - if element.get("success") != "true": - case["result"] = "fail" - elif int(element.get("skips", "0")) > 0: - case["result"] = "skip" - else: - case["result"] = "ok" - elif element.tag == "TestCase": - cases.append(case) - case = None - element.clear() + with open(report, "rb") as raw: + for event, element in ElementTree.iterparse(LegalXml(raw), events=("start", "end")): + if event == "start": + if element.tag == "TestCase": + case = {"name": element.get("name"), "tags": element.get("tags", ""), + "line": int(element.get("line", "0")), "result": "fail", "ran": set()} + continue + if case is None: + continue + if element.tag in RAN_ELEMENTS: + case["ran"].add((element.get("filename", ""), int(element.get("line", "0")))) + element.clear() + elif element.tag == "OverallResult": + if element.get("success") != "true": + case["result"] = "fail" + elif int(element.get("skips", "0")) > 0: + case["result"] = "skip" + else: + case["result"] = "ok" + elif element.tag == "TestCase": + cases.append(case) + case = None + element.clear() except (ElementTree.ParseError, FileNotFoundError): return code, None, text return code, cases, text @@ -623,7 +652,11 @@ def compiled(first, last, conditional): continue here = compiled(first, last, bool(blocks - body["held"])) if here: - problems.append(f"{label}: {case['name']!r} passed without running the {macro} on line {first}") + why = args.expect_unrun.get(path, {}).get(str(first)) + if why: + notes.append(f"{case['name']!r}: the {macro} on line {first} never ran, as reviewed: {why}") + else: + problems.append(f"{label}: {case['name']!r} passed without running the {macro} on line {first}") elif here is None: notes.append(f"{case['name']!r}: the {macro} on line {first} never ran, and is under an #if no " f"compile command here says this build compiles") @@ -716,6 +749,10 @@ def compiled(first, last, conditional): def check(args): mapping, file_records = read_mapping(args.mapping) + args.expect_unrun = {} + if args.unrun: + with open(args.unrun) as f: + args.expect_unrun = json.load(f) with open(args.tut) as f: tut = json.load(f) # By stem and tier, the tier read from each record's CTest name: results @@ -837,6 +874,8 @@ def main(): chk.add_argument("--recounted", action="store_true", help="the files were edited by hand since conversion: list each case's assertion count " "rather than hold it to the mapping's (names and results still must match)") + chk.add_argument("--unrun", help="JSON of assertion sites reviewed as never running in a passing case on " + "this host, {file: {line: why}}: each is listed rather than a mismatch") chk.add_argument("files", nargs="*", help="only these converted files (default: all the mapping has)") args = parser.parse_args() return record(args) if args.step == "record" else check(args) From a7fad41211748ae39ab63d36c31d39037e1d6f5b Mon Sep 17 00:00:00 2001 From: Rye Date: Mon, 12 Oct 2026 18:43:24 -0400 Subject: [PATCH 06/10] llcommon's tests are Catch2 binaries, converted by tut2catch.py TM20, the third wave of stage 3: llcommon's 58 TUT files, 733 tests, become four Catch2 binaries, each file still run in a process of its own: - llcommon_tests, 53 files, alsimd at the tree's tier among them, and lleventcoro, which shares a process now that it keeps one app for it; - llcommon_python_tests: llleap, llprocess and llsdserialize_python, which drive a Python peer; - llsingleton_test and llapp_test alone: one deletes every singleton, the other makes and ends an LLApp, and LLCommon with it, in each case. alsimd at each other x86-64 tier joins the kernels in llmath's simd_tests. wrapllerrs.h, whose users are all here, fails a search of the captured log with FAIL rather than a tut::failure. The files are tut2catch.py's output, with the names the idiom pass that follows chose, and these hand resolutions of what it marked TM-CHECK, applied by a script and each reviewed: - ensure_equals() in code this Mac does not compile (lldate's parser boundary block, Windows-only code in llprocess, llstring_utf, stringize and llsdserialize_python), and in llsd's helpers, which compare an LLURI too: UNSCOPED_INFO and REQUIRE(a == b); - llinstancetracker's two set_test_name(desc) calls go, the names given through --names; - llleap's and llsdserialize_python's catches of tut::failure catch ll_test::Failure, which carries no message, and test_or_split's `throw e` is a rethrow; - lldependencies' display, which only TUT's failure messages reached, is [[maybe_unused]]; - lleventcoro's one app, and llapp's fixture as it ends each app, tell the state guard what an LLApp changes on purpose, as lllogin's does. tut_parity.py, against the TUT executables' recorded results (732 tests: 729 ok, 3 known skips; one more is Windows-only), holds for all 58 files under --recounted, and plain for the 47 the script did not touch, with nine assertion sites reviewed as never running in a passing case here: alsimd's AVX checks under if constexpr, and three in loops that should not turn. All 62 entries pass. Co-Authored-By: Claude Opus 5.5 --- indra/llcommon/CMakeLists.txt | 155 +- indra/llcommon/tests/alfilewrite_test.cpp | 185 +- indra/llcommon/tests/alrecoverystore_test.cpp | 874 ++++-- indra/llcommon/tests/alregex_test.cpp | 441 ++-- indra/llcommon/tests/alsavehistory_test.cpp | 200 +- indra/llcommon/tests/alserialworker_test.cpp | 70 +- indra/llcommon/tests/alsimd_test.cpp | 233 +- indra/llcommon/tests/alwatchedfile_test.cpp | 223 +- indra/llcommon/tests/apply_test.cpp | 128 +- indra/llcommon/tests/bitpack_test.cpp | 51 +- .../llcommon/tests/classic_callback_test.cpp | 38 +- indra/llcommon/tests/commonmisc_test.cpp | 528 ++-- indra/llcommon/tests/lazyeventapi_test.cpp | 55 +- indra/llcommon/tests/llapp_test.cpp | 36 +- indra/llcommon/tests/llbase64_test.cpp | 32 +- indra/llcommon/tests/llcond_test.cpp | 31 +- indra/llcommon/tests/lldate_test.cpp | 186 +- indra/llcommon/tests/lldeadmantimer_test.cpp | 347 +-- indra/llcommon/tests/lldependencies_test.cpp | 94 +- .../llcommon/tests/lldoubledispatch_test.cpp | 54 +- indra/llcommon/tests/llerror_test.cpp | 174 +- indra/llcommon/tests/lleventcoro_test.cpp | 75 +- .../llcommon/tests/lleventdispatcher_test.cpp | 406 ++- indra/llcommon/tests/lleventfilter_test.cpp | 85 +- indra/llcommon/tests/llevents_test.cpp | 236 +- indra/llcommon/tests/llfile_test.cpp | 495 ++-- indra/llcommon/tests/llframetimer_test.cpp | 95 +- indra/llcommon/tests/llheteromap_test.cpp | 24 +- indra/llcommon/tests/llhttpdate_test.cpp | 39 +- indra/llcommon/tests/llinitparam_test.cpp | 454 ++-- .../llcommon/tests/llinstancetracker_test.cpp | 107 +- indra/llcommon/tests/llleap_test.cpp | 117 +- .../llcommon/tests/llmainthreadtask_test.cpp | 46 +- indra/llcommon/tests/llmutex_test.cpp | 113 +- indra/llcommon/tests/llpounceable_test.cpp | 139 +- indra/llcommon/tests/llprocess_test.cpp | 785 +++--- indra/llcommon/tests/llprocessor_test.cpp | 29 +- indra/llcommon/tests/llprocinfo_test.cpp | 33 +- indra/llcommon/tests/llptrto_test.cpp | 18 +- indra/llcommon/tests/llrand_test.cpp | 253 +- indra/llcommon/tests/llsd_test.cpp | 312 ++- indra/llcommon/tests/llsdjson_test.cpp | 272 +- .../tests/llsdserialize_python_test.cpp | 112 +- indra/llcommon/tests/llsdserialize_test.cpp | 1989 +++++++------- indra/llcommon/tests/llsdutil_test.cpp | 160 +- indra/llcommon/tests/llsingleton_test.cpp | 134 +- indra/llcommon/tests/llstreamtools_test.cpp | 400 +-- indra/llcommon/tests/llstring_test.cpp | 1136 +++++--- indra/llcommon/tests/llstring_utf_test.cpp | 2342 ++++++++++------- indra/llcommon/tests/lltimer_test.cpp | 67 +- indra/llcommon/tests/lltrace_test.cpp | 74 +- indra/llcommon/tests/lltreeiterators_test.cpp | 124 +- indra/llcommon/tests/llunits_test.cpp | 295 ++- indra/llcommon/tests/lluri_test.cpp | 274 +- indra/llcommon/tests/lluuid_test.cpp | 330 +-- indra/llcommon/tests/stringize_test.cpp | 52 +- .../tests/threadsafeschedule_test.cpp | 37 +- indra/llcommon/tests/tuple_test.cpp | 26 +- indra/llcommon/tests/workqueue_test.cpp | 88 +- indra/llcommon/tests/wrapllerrs.h | 6 +- indra/llmath/CMakeLists.txt | 11 +- 61 files changed, 8812 insertions(+), 7113 deletions(-) diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 69564a3be3..186b5d6b43 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -295,90 +295,91 @@ target_link_libraries( ) if(AL_BUILD_TESTS) - # No unit tests: the harness itself links llcommon, so a test of one of its - # files could not stand alone. Every test here links the library. - foreach( - test - alfilewrite - alrecoverystore - alregex - alsavehistory - alserialworker - alwatchedfile - apply - bitpack - classic_callback - commonmisc - lazyeventapi - llapp - llbase64 - llcond - lldate - lldeadmantimer - lldependencies - lldoubledispatch - llerror - lleventcoro - lleventdispatcher - lleventfilter - llevents - llfile - llframetimer - llhttpdate - llheteromap - llinitparam - llinstancetracker - llmainthreadtask - llmutex - llpounceable - llprocessor - llprocinfo - llptrto - llrand - llsd - llsdjson - llsdserialize - llsdutil - llsingleton - llstreamtools - llstring - llstring_utf - lltimer - lltrace - lltreeiterators - llunits - lluri - lluuid - stringize - threadsafeschedule - tuple - workqueue + # One Catch2 binary for llcommon's tests, each test file run in a process + # of its own. None of them may delete every singleton: the files will + # share a process. + al_add_test_binary( + llcommon_tests + PROJECT llcommon + SOURCES + tests/alfilewrite_test.cpp + tests/alrecoverystore_test.cpp + tests/alregex_test.cpp + tests/alsavehistory_test.cpp + tests/alserialworker_test.cpp + tests/alsimd_test.cpp + tests/alwatchedfile_test.cpp + tests/apply_test.cpp + tests/bitpack_test.cpp + tests/classic_callback_test.cpp + tests/commonmisc_test.cpp + tests/lazyeventapi_test.cpp + tests/llbase64_test.cpp + tests/llcond_test.cpp + tests/lldate_test.cpp + tests/lldeadmantimer_test.cpp + tests/lldependencies_test.cpp + tests/lldoubledispatch_test.cpp + tests/llerror_test.cpp + tests/lleventcoro_test.cpp + tests/lleventdispatcher_test.cpp + tests/lleventfilter_test.cpp + tests/llevents_test.cpp + tests/llfile_test.cpp + tests/llframetimer_test.cpp + tests/llheteromap_test.cpp + tests/llhttpdate_test.cpp + tests/llinitparam_test.cpp + tests/llinstancetracker_test.cpp + tests/llmainthreadtask_test.cpp + tests/llmutex_test.cpp + tests/llpounceable_test.cpp + tests/llprocessor_test.cpp + tests/llprocinfo_test.cpp + tests/llptrto_test.cpp + tests/llrand_test.cpp + tests/llsd_test.cpp + tests/llsdjson_test.cpp + tests/llsdserialize_test.cpp + tests/llsdutil_test.cpp + tests/llstreamtools_test.cpp + tests/llstring_test.cpp + tests/llstring_utf_test.cpp + tests/lltimer_test.cpp + tests/lltrace_test.cpp + tests/lltreeiterators_test.cpp + tests/llunits_test.cpp + tests/lluri_test.cpp + tests/lluuid_test.cpp + tests/stringize_test.cpp + tests/threadsafeschedule_test.cpp + tests/tuple_test.cpp + tests/workqueue_test.cpp + LIBRARIES llcommon ) - al_add_test(${test} PROJECT llcommon LIBRARIES llcommon) - endforeach() # These drive a Python peer: LEAP's protocol is to an external script, # llprocess spawns one, and llsdserialize_python round-trips through the # llsd package as an independent implementation. A peer that imports a # script a test wrote to the temp folder would leave its bytecode there. - foreach(test llleap llprocess llsdserialize_python) - al_add_test( - ${test} - PROJECT llcommon - LIBRARIES llcommon - PYTHON - ENVIRONMENT PYTHONDONTWRITEBYTECODE=1 - ) - endforeach() + al_add_test_binary( + llcommon_python_tests + PROJECT llcommon + SOURCES tests/llleap_test.cpp tests/llprocess_test.cpp tests/llsdserialize_python_test.cpp + LIBRARIES llcommon + PYTHON + ENVIRONMENT PYTHONDONTWRITEBYTECODE=1 + ) - # The SIMD ops layer, at the tree's tier and, where the tiers differ, at - # each of them; a variant the host cannot run skips. - al_add_test(alsimd PROJECT llcommon LIBRARIES llcommon) - if(BUILD_TARGET_IS_X86_64 AND NOT DARWIN) - foreach(tier baseline v2 v3 v4) - al_add_test(alsimd PROJECT llcommon LIBRARIES llcommon ISA_TIER ${tier}) - endforeach() - endif() + # Alone: llsingleton's tests delete every singleton, and llapp's make and + # destroy the process's LLApp, by design. + al_add_test_binary( + llsingleton_test + PROJECT llcommon + SOURCES tests/llsingleton_test.cpp + LIBRARIES llcommon + ) + al_add_test_binary(llapp_test PROJECT llcommon SOURCES tests/llapp_test.cpp LIBRARIES llcommon) # The ops layer's assembly, one function per op, compiled with the tree's # flags and read by AlSimdAsmCheck.cmake for a table lookup, a call or a diff --git a/indra/llcommon/tests/alfilewrite_test.cpp b/indra/llcommon/tests/alfilewrite_test.cpp index 88c9aa61ea..7731391a3d 100644 --- a/indra/llcommon/tests/alfilewrite_test.cpp +++ b/indra/llcommon/tests/alfilewrite_test.cpp @@ -30,7 +30,7 @@ #include "../llfile.h" #include "../lluuid.h" -#include "../test/lltut.h" +#include #include #include @@ -41,7 +41,7 @@ #include #endif -namespace tut +namespace { namespace fs = std::filesystem; @@ -94,99 +94,106 @@ namespace tut } }; - typedef test_group alfilewrite_group; - typedef alfilewrite_group::object alfilewrite_object; - alfilewrite_group alfilewrite_instance("alfilewrite"); - - template<> template<> - void alfilewrite_object::test<1>() + TEST_CASE_METHOD(alfilewrite_data, "alfilewrite: whole writes a new file or over an old and leaves nothing beside it", "[alfilewrite]") { - set_test_name("a file is written whole, a new one or over an old, and nothing is left beside it"); const std::string file = in("script.lsl"); - ensure("a new file", ALFileWrite::whole(file, "default {}\n")); - ensure_equals("its text", read(file), std::string("default {}\n")); - ensure("over it", ALFileWrite::whole(file, "x")); - ensure_equals("the new text, all of the old gone", read(file), std::string("x")); - ensure("empty is a text too", ALFileWrite::whole(file, "")); - ensure_equals("and the file is empty", read(file), std::string()); + UNSCOPED_INFO("a new file"); + CHECK(ALFileWrite::whole(file, "default {}\n")); + UNSCOPED_INFO("its text"); + CHECK(read(file) == std::string("default {}\n")); + UNSCOPED_INFO("over it"); + CHECK(ALFileWrite::whole(file, "x")); + UNSCOPED_INFO("the new text, all of the old gone"); + CHECK(read(file) == std::string("x")); + UNSCOPED_INFO("empty is a text too"); + CHECK(ALFileWrite::whole(file, "")); + UNSCOPED_INFO("and the file is empty"); + CHECK(read(file) == std::string()); std::error_code ec; - ensure("nothing left beside it", !fs::exists(fsyspath(ALFileWrite::besideOf(file)), ec)); + UNSCOPED_INFO("nothing left beside it"); + CHECK_FALSE(fs::exists(fsyspath(ALFileWrite::besideOf(file)), ec)); } - template<> template<> - void alfilewrite_object::test<2>() + TEST_CASE_METHOD(alfilewrite_data, "alfilewrite: a file that could not be written in place is left as it was", "[alfilewrite]") { - set_test_name("a file that could not be written in place is left as it was"); if (root()) { - skip("root writes what it likes"); + SKIP("root writes what it likes"); } const std::string file = in("readonly.lsl"); put(file, "what was there"); std::error_code ec; fs::permissions(fsyspath(file), fs::perms::owner_read, fs::perm_options::replace, ec); - ensure("refused", !ALFileWrite::whole(file, "new text")); - ensure_equals("the old text, whole", read(file), std::string("what was there")); - ensure("nothing left beside it", !fs::exists(fsyspath(ALFileWrite::besideOf(file)), ec)); + UNSCOPED_INFO("refused"); + CHECK_FALSE(ALFileWrite::whole(file, "new text")); + UNSCOPED_INFO("the old text, whole"); + CHECK(read(file) == std::string("what was there")); + UNSCOPED_INFO("nothing left beside it"); + CHECK_FALSE(fs::exists(fsyspath(ALFileWrite::besideOf(file)), ec)); } - template<> template<> - void alfilewrite_object::test<3>() + TEST_CASE_METHOD(alfilewrite_data, "alfilewrite: a file with no room beside it is written in place and the folder stays", "[alfilewrite]") { - set_test_name("where nothing can be put beside a file, it is written in place, and what is there by that name stays"); const std::string file = in("keep.lsl"); put(file, "what was there"); // Where the text would go beside it is a folder, which no file can // be written as. std::error_code ec; fs::create_directories(fsyspath(ALFileWrite::besideOf(file)), ec); - ensure("written", ALFileWrite::whole(file, "new text")); - ensure_equals("in place", read(file), std::string("new text")); - ensure("the folder by that name left", fs::is_directory(fsyspath(ALFileWrite::besideOf(file)), ec)); + UNSCOPED_INFO("written"); + CHECK(ALFileWrite::whole(file, "new text")); + UNSCOPED_INFO("in place"); + CHECK(read(file) == std::string("new text")); + UNSCOPED_INFO("the folder by that name left"); + CHECK(fs::is_directory(fsyspath(ALFileWrite::besideOf(file)), ec)); } - template<> template<> - void alfilewrite_object::test<4>() + TEST_CASE_METHOD(alfilewrite_data, "alfilewrite: a file keeps its permissions and a link is written through", "[alfilewrite]") { - set_test_name("a file keeps its permissions, and a link is written through and stays a link"); #if LL_WINDOWS - skip("permissions and links are the Posix file systems' to test"); + SKIP("permissions and links are the Posix file systems' to test"); #else const std::string file = in("mine.lsl"); put(file, "old"); std::error_code ec; fs::permissions(fsyspath(file), fs::perms::owner_read | fs::perms::owner_write, fs::perm_options::replace, ec); - ensure("written", ALFileWrite::whole(file, "new")); - ensure_equals("the new text", read(file), std::string("new")); - ensure("its permissions as they were", fs::status(fsyspath(file)).permissions() == (fs::perms::owner_read | fs::perms::owner_write)); + UNSCOPED_INFO("written"); + CHECK(ALFileWrite::whole(file, "new")); + UNSCOPED_INFO("the new text"); + CHECK(read(file) == std::string("new")); + UNSCOPED_INFO("its permissions as they were"); + CHECK(fs::status(fsyspath(file)).permissions() == (fs::perms::owner_read | fs::perms::owner_write)); const std::string link = in("link.lsl"); fs::create_symlink(fsyspath(file), fsyspath(link), ec); if (ec) { - skip("no links here"); + SKIP("no links here"); } - ensure("through the link", ALFileWrite::whole(link, "through")); - ensure("still a link", fs::is_symlink(fsyspath(link))); - ensure_equals("the file it names written", read(file), std::string("through")); + UNSCOPED_INFO("through the link"); + CHECK(ALFileWrite::whole(link, "through")); + UNSCOPED_INFO("still a link"); + CHECK(fs::is_symlink(fsyspath(link))); + UNSCOPED_INFO("the file it names written"); + CHECK(read(file) == std::string("through")); const std::string dangling = in("dangling.lsl"); fs::create_symlink(fsyspath(in("nowhere.lsl")), fsyspath(dangling), ec); - ensure("a link to nothing has nowhere to write", !ALFileWrite::whole(dangling, "lost")); - ensure("and is left a link", fs::is_symlink(fsyspath(dangling))); + UNSCOPED_INFO("a link to nothing has nowhere to write"); + CHECK_FALSE(ALFileWrite::whole(dangling, "lost")); + UNSCOPED_INFO("and is left a link"); + CHECK(fs::is_symlink(fsyspath(dangling))); #endif } - template<> template<> - void alfilewrite_object::test<5>() + TEST_CASE_METHOD(alfilewrite_data, "alfilewrite: a file in a folder only its files may be written in is written in place", "[alfilewrite]") { - set_test_name("a file in a folder only its files may be written in is written in place"); #if LL_WINDOWS - skip("a folder's permissions are the Posix file systems' to test"); + SKIP("a folder's permissions are the Posix file systems' to test"); #else if (root()) { - skip("root writes what it likes"); + SKIP("root writes what it likes"); } const std::string sub = in("closed"); std::error_code ec; @@ -196,59 +203,79 @@ namespace tut fs::permissions(fsyspath(sub), fs::perms::owner_read | fs::perms::owner_exec, fs::perm_options::replace, ec); const bool written = ALFileWrite::whole(file, "new"); fs::permissions(fsyspath(sub), fs::perms::owner_all, fs::perm_options::replace, ec); - ensure("written", written); - ensure_equals("in place", read(file), std::string("new")); + UNSCOPED_INFO("written"); + CHECK(written); + UNSCOPED_INFO("in place"); + CHECK(read(file) == std::string("new")); #endif } - template<> template<> - void alfilewrite_object::test<6>() + TEST_CASE_METHOD(alfilewrite_data, "alfilewrite: a durable write is whole or nothing and never in place", "[alfilewrite]") { - set_test_name("a durable write is whole or nothing: where nothing can be put beside the file, it is not written in place"); const std::string file = in("journal.llsd"); - ensure("a new one", ALFileWrite::whole(file, "one", /*durable*/ true)); - ensure("over it", ALFileWrite::whole(file, "two", /*durable*/ true)); - ensure_equals("the new text", read(file), std::string("two")); + UNSCOPED_INFO("a new one"); + CHECK(ALFileWrite::whole(file, "one", /*durable*/ true)); + UNSCOPED_INFO("over it"); + CHECK(ALFileWrite::whole(file, "two", /*durable*/ true)); + UNSCOPED_INFO("the new text"); + CHECK(read(file) == std::string("two")); std::error_code ec; - ensure("nothing left beside it", !fs::exists(fsyspath(ALFileWrite::besideOf(file)), ec)); + UNSCOPED_INFO("nothing left beside it"); + REQUIRE_FALSE(fs::exists(fsyspath(ALFileWrite::besideOf(file)), ec)); fs::create_directories(fsyspath(ALFileWrite::besideOf(file)), ec); - ensure("refused", !ALFileWrite::whole(file, "three", /*durable*/ true)); - ensure_equals("the old text, whole", read(file), std::string("two")); + UNSCOPED_INFO("refused"); + CHECK_FALSE(ALFileWrite::whole(file, "three", /*durable*/ true)); + UNSCOPED_INFO("the old text, whole"); + CHECK(read(file) == std::string("two")); } - template<> template<> - void alfilewrite_object::test<7>() + TEST_CASE_METHOD(alfilewrite_data, "alfilewrite: ALFileRead reads an ordinary file up to a limit and no folder or pipe", "[alfilewrite]") { - set_test_name("the one reader: an ordinary file whole, up to a limit; not a folder, and not a pipe, which it does not wait on"); const std::string file = in("include.lsl"); put(file, "integer x;\n"); std::string text = "left over"; - ensure("read", ALFileRead::whole(file, text, 64)); - ensure_equals("whole", text, std::string("integer x;\n")); - ensure("at the limit", ALFileRead::whole(file, text, 11) && text.size() == 11); - ensure("past it", !ALFileRead::whole(file, text, 10) && text.empty()); - ensure("a folder is not read", !ALFileRead::whole(folder, text, 64)); - ensure("nothing is not read", !ALFileRead::whole(in("gone.lsl"), text, 64)); + UNSCOPED_INFO("read"); + CHECK(ALFileRead::whole(file, text, 64)); + UNSCOPED_INFO("whole"); + CHECK(text == std::string("integer x;\n")); + { + INFO("at the limit"); + CHECK(ALFileRead::whole(file, text, 11)); + REQUIRE(text.size() == 11); + } + { + INFO("past it"); + CHECK_FALSE(ALFileRead::whole(file, text, 10)); + REQUIRE(text.empty()); + } + UNSCOPED_INFO("a folder is not read"); + CHECK_FALSE(ALFileRead::whole(folder, text, 64)); + UNSCOPED_INFO("nothing is not read"); + CHECK_FALSE(ALFileRead::whole(in("gone.lsl"), text, 64)); #if !LL_WINDOWS const std::string pipe = in("pipe"); if (mkfifo(pipe.c_str(), 0600) == 0) { - ensure("a pipe is not read", !ALFileRead::whole(pipe, text, 64)); + UNSCOPED_INFO("a pipe is not read"); + CHECK_FALSE(ALFileRead::whole(pipe, text, 64)); } #endif } - template<> template<> - void alfilewrite_object::test<8>() + TEST_CASE_METHOD(alfilewrite_data, "alfilewrite: temp writes in place for the user alone and never through a link", "[alfilewrite]") { - set_test_name("a temp file is written in place, the user's alone, and never through a link"); const std::string file = in("sl_script_x_id.lsl"); - ensure("written", ALFileWrite::temp(file, "default {}")); - ensure_equals("its text", read(file), std::string("default {}")); - ensure("again, shorter", ALFileWrite::temp(file, "x")); - ensure_equals("all of the old gone", read(file), std::string("x")); + UNSCOPED_INFO("written"); + CHECK(ALFileWrite::temp(file, "default {}")); + UNSCOPED_INFO("its text"); + CHECK(read(file) == std::string("default {}")); + UNSCOPED_INFO("again, shorter"); + CHECK(ALFileWrite::temp(file, "x")); + UNSCOPED_INFO("all of the old gone"); + CHECK(read(file) == std::string("x")); #if !LL_WINDOWS - ensure("the user's alone", fs::status(fsyspath(file)).permissions() == (fs::perms::owner_read | fs::perms::owner_write)); + UNSCOPED_INFO("the user's alone"); + CHECK(fs::status(fsyspath(file)).permissions() == (fs::perms::owner_read | fs::perms::owner_write)); const std::string target = in("elsewhere.txt"); put(target, "somebody's"); const std::string link = in("sl_script_y_id.lsl"); @@ -256,8 +283,10 @@ namespace tut fs::create_symlink(fsyspath(target), fsyspath(link), ec); if (!ec) { - ensure("not through a link", !ALFileWrite::temp(link, "mine")); - ensure_equals("what it names untouched", read(target), std::string("somebody's")); + UNSCOPED_INFO("not through a link"); + CHECK_FALSE(ALFileWrite::temp(link, "mine")); + UNSCOPED_INFO("what it names untouched"); + CHECK(read(target) == std::string("somebody's")); } #endif } diff --git a/indra/llcommon/tests/alrecoverystore_test.cpp b/indra/llcommon/tests/alrecoverystore_test.cpp index 015d06ee43..256e61dd88 100644 --- a/indra/llcommon/tests/alrecoverystore_test.cpp +++ b/indra/llcommon/tests/alrecoverystore_test.cpp @@ -31,14 +31,14 @@ #include "llsdserialize.h" #include "lltimer.h" -#include "../test/lltut.h" +#include #include #include #include #include -namespace tut +namespace { struct alrecoverystore_data { @@ -79,7 +79,8 @@ namespace tut { for (ALRecoveryEntry& one : entries) { - ensure("read whole: " + one.key, store.load(one)); + UNSCOPED_INFO("read whole: " + one.key); + REQUIRE(store.load(one)); } return entries; } @@ -87,7 +88,8 @@ namespace tut { if (one) { - ensure("read whole: " + one->key, store.load(*one)); + UNSCOPED_INFO("read whole: " + one->key); + REQUIRE(store.load(*one)); } return one; } @@ -101,28 +103,24 @@ namespace tut return one; } }; - typedef test_group alrecoverystore_group; - typedef alrecoverystore_group::object alrecoverystore_object; - tut::alrecoverystore_group alrecoverystore_instance("alrecoverystore"); - template<> template<> - void alrecoverystore_object::test<1>() + TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: keyOf tells an object's item from the inventory's and a file's", "[alrecoverystore]") { - set_test_name("a key says whose text it is: an object's item, the inventory's, or a file's"); const LLUUID object = LLUUID::generateNewID(); const LLUUID item = LLUUID::generateNewID(); const std::string task = ALRecoveryStore::keyOf(object, item, std::string()); const std::string inventory = ALRecoveryStore::keyOf(LLUUID::null, item, std::string()); const std::string file = ALRecoveryStore::keyOf(LLUUID::null, LLUUID::null, "/scripts/lib.lsl"); - ensure("an object's item and the inventory's differ", task != inventory); - ensure("a file is its path", file == "disk:/scripts/lib.lsl"); - ensure_equals("and a key is the same asked twice", ALRecoveryStore::keyOf(object, item, std::string()), task); + UNSCOPED_INFO("an object's item and the inventory's differ"); + CHECK(task != inventory); + UNSCOPED_INFO("a file is its path"); + CHECK(file == "disk:/scripts/lib.lsl"); + UNSCOPED_INFO("and a key is the same asked twice"); + CHECK(ALRecoveryStore::keyOf(object, item, std::string()) == task); } - template<> template<> - void alrecoverystore_object::test<2>() + TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: an entry reads back as it was written", "[alrecoverystore]") { - set_test_name("an entry reads back as it was written"); ALRecoveryEntry was = entry("task:a:b", "default\n{\n state_entry() { llSay(0, \"<&>\"); }\n}\n"); was.session = "one"; was.state = ALRecoveryEntry::State::Kept; @@ -137,237 +135,361 @@ namespace tut was.compileTarget = "luau"; was.baseAsset = LLUUID::generateNewID(); ALRecoveryEntry back; - ensure("it reads", ALRecoveryEntry::fromLLSD(was.asLLSD(), back)); - ensure_equals("the text, whatever is in it", back.text, was.text); - ensure("kept", back.state == ALRecoveryEntry::State::Kept); - ensure_equals("when", back.when.secondsSinceEpoch(), was.when.secondsSinceEpoch()); - ensure("the object and the item", back.object == was.object && back.item == was.item); - ensure("where it was", back.objectName == "Door" && back.region == "Ahern"); - ensure("what it is", back.lua && !back.notecard && back.wrapped && back.compileTarget == "luau" && back.baseAsset == was.baseAsset); - ensure("no history said, none read", !back.history.isDefined() && back.caretLine == -1); + UNSCOPED_INFO("it reads"); + REQUIRE(ALRecoveryEntry::fromLLSD(was.asLLSD(), back)); + UNSCOPED_INFO("the text, whatever is in it"); + CHECK(back.text == was.text); + UNSCOPED_INFO("kept"); + CHECK(back.state == ALRecoveryEntry::State::Kept); + UNSCOPED_INFO("when"); + CHECK(back.when.secondsSinceEpoch() == was.when.secondsSinceEpoch()); + { + INFO("the object and the item"); + CHECK(back.object == was.object); + CHECK(back.item == was.item); + } + { + INFO("where it was"); + CHECK(back.objectName == "Door"); + CHECK(back.region == "Ahern"); + } + { + INFO("what it is"); + CHECK(back.lua); + CHECK_FALSE(back.notecard); + CHECK(back.wrapped); + CHECK(back.compileTarget == "luau"); + CHECK(back.baseAsset == was.baseAsset); + } + { + INFO("no history said, none read"); + REQUIRE_FALSE(back.history.isDefined()); + CHECK(back.caretLine == -1); + } LLSD history; history["undo"] = LLSD::emptyArray().with(0, LLSD().with("label", "rename")); history["saved"] = 0; was.history = history; was.caretLine = 4; was.caretColumn = 7; - ensure("it reads again", ALRecoveryEntry::fromLLSD(was.asLLSD(), back)); - ensure("the history as it was written", back.history["undo"][0]["label"].asString() == "rename" && back.history["saved"].asInteger() == 0); - ensure("and where the caret stood", back.caretLine == 4 && back.caretColumn == 7); - ensure("something that is no entry is not read as one", !ALRecoveryEntry::fromLLSD(LLSD("text"), back)); - } - - template<> template<> - void alrecoverystore_object::test<7>() - { - set_test_name("a notecard keeps its items with its text, in order, through the disk and back"); - ALRecoveryStore store(folder, "mine"); - ALRecoveryEntry card = entry("item:card", std::string("Look: ") + "\xF4\x80\x80\x80" + " and " + "\xF4\x80\x80\x81"); - card.notecard = true; - LLSD first; - first["item_id"] = LLUUID::generateNewID(); - first["name"] = "Sunset"; - LLSD second; - second["item_id"] = LLUUID::generateNewID(); - second["name"] = "Home"; - card.embedded = LLSD::emptyArray().with(0, first).with(1, second); - ensure("written", store.write(card)); - const std::vector all = whole(store, store.list()); - ensure_equals("one", all.size(), size_t(1)); - ensure("a notecard", all.front().notecard); - ensure_equals("its text, the placeholders in it byte for byte", all.front().text, card.text); - ensure_equals("both items", all.front().embedded.size(), 2); - ensure("in the order the text stands them", - all.front().embedded[0]["name"].asString() == "Sunset" && all.front().embedded[1]["name"].asString() == "Home"); - ensure("each whole", all.front().embedded[1]["item_id"].asUUID() == second["item_id"].asUUID()); - // Discarded, it keeps them too. - ensure("discarded", store.discard(all.front())); - const std::vector thrown = whole(store, store.list()); - ensure("the items go with it", thrown.size() == 1 && thrown.front().embedded.size() == 2); - // A script has none, and says so as an empty list. - ALRecoveryEntry script; - ensure("read", ALRecoveryEntry::fromLLSD(entry("item:s", "x").asLLSD(), script)); - ensure("no items", script.embedded.isArray() && script.embedded.size() == 0); + UNSCOPED_INFO("it reads again"); + CHECK(ALRecoveryEntry::fromLLSD(was.asLLSD(), back)); + { + INFO("the history as it was written"); + CHECK(back.history["undo"][0]["label"].asString() == "rename"); + CHECK(back.history["saved"].asInteger() == 0); + } + { + INFO("and where the caret stood"); + CHECK(back.caretLine == 4); + CHECK(back.caretColumn == 7); + } + UNSCOPED_INFO("something that is no entry is not read as one"); + CHECK_FALSE(ALRecoveryEntry::fromLLSD(LLSD("text"), back)); } - template<> template<> - void alrecoverystore_object::test<3>() + TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: a session's entry is written whole then over by its next and forgotten", "[alrecoverystore]") { - set_test_name("a session's entry is written whole, written over by its next, and forgotten"); ALRecoveryStore store(folder, "session-a"); - ensure("written", store.write(entry("item:x", "first"))); - ensure("written again", store.write(entry("item:x", "second"))); + UNSCOPED_INFO("written"); + CHECK(store.write(entry("item:x", "first"))); + UNSCOPED_INFO("written again"); + CHECK(store.write(entry("item:x", "second"))); const std::vector all = whole(store, store.list()); - ensure_equals("one entry for one key", all.size(), size_t(1)); - ensure_equals("the last text", all.front().text, std::string("second")); - ensure_equals("this session's", all.front().session, std::string("session-a")); - ensure("unsaved", all.front().state == ALRecoveryEntry::State::Unsaved); + UNSCOPED_INFO("one entry for one key"); + REQUIRE(all.size() == size_t(1)); + UNSCOPED_INFO("the last text"); + CHECK(all.front().text == std::string("second")); + UNSCOPED_INFO("this session's"); + CHECK(all.front().session == std::string("session-a")); + UNSCOPED_INFO("unsaved"); + CHECK(all.front().state == ALRecoveryEntry::State::Unsaved); for (const std::string& name : files()) { - ensure("nothing half written left beside it: " + name, name.find(".tmp") == std::string::npos); + UNSCOPED_INFO("nothing half written left beside it: " + name); + REQUIRE(name.find(".tmp") == std::string::npos); } - ensure("this session's own is nothing to offer", !store.hasOffers()); - ensure("nor left by another", store.left().empty()); + UNSCOPED_INFO("this session's own is nothing to offer"); + CHECK_FALSE(store.hasOffers()); + UNSCOPED_INFO("nor left by another"); + REQUIRE(store.left().empty()); store.forget("item:x"); - ensure("forgotten", store.list().empty()); + UNSCOPED_INFO("forgotten"); + REQUIRE(store.list().empty()); } - template<> template<> - void alrecoverystore_object::test<4>() + TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: typing in a script another session left keeps what it left", "[alrecoverystore]") { - set_test_name("one session's entries are apart from another's: typing in a script another session left does not write over what it left"); ALRecoveryStore before(folder, "before"); ALRecoveryStore now(folder, "now"); - ensure("the crashed session's", before.write(entry("task:o:i", "left behind"))); - ensure("this session's", now.write(entry("task:o:i", "typed since"))); - ensure_equals("both kept", now.list().size(), size_t(2)); + UNSCOPED_INFO("the crashed session's"); + CHECK(before.write(entry("task:o:i", "left behind"))); + UNSCOPED_INFO("this session's"); + CHECK(now.write(entry("task:o:i", "typed since"))); + UNSCOPED_INFO("both kept"); + REQUIRE(now.list().size() == size_t(2)); const std::vector left = whole(now, now.left()); - ensure_equals("one left by another session", left.size(), size_t(1)); - ensure_equals("its text", left.front().text, std::string("left behind")); + UNSCOPED_INFO("one left by another session"); + REQUIRE(left.size() == size_t(1)); + UNSCOPED_INFO("its text"); + CHECK(left.front().text == std::string("left behind")); const std::optional found = whole(now, now.leftFor("task:o:i")); - ensure("found by its key", found.has_value() && found->text == "left behind"); - ensure("nothing for another key", !now.leftFor("task:o:j").has_value()); - ensure("something to offer", now.hasOffers()); + { + INFO("found by its key"); + REQUIRE(found.has_value()); + CHECK(found->text == "left behind"); + } + UNSCOPED_INFO("nothing for another key"); + REQUIRE_FALSE(now.leftFor("task:o:j").has_value()); + UNSCOPED_INFO("something to offer"); + CHECK(now.hasOffers()); now.forget("task:o:i"); - ensure("this session forgetting its own leaves the other's", now.leftFor("task:o:i").has_value()); + UNSCOPED_INFO("this session forgetting its own leaves the other's"); + REQUIRE(now.leftFor("task:o:i").has_value()); now.remove(*found); - ensure("taken up, it is gone", !now.leftFor("task:o:i").has_value() && !now.hasOffers()); + { + INFO("taken up, it is gone"); + REQUIRE_FALSE(now.leftFor("task:o:i").has_value()); + CHECK_FALSE(now.hasOffers()); + } } - template<> template<> - void alrecoverystore_object::test<5>() + TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: a discarded entry is pruned once old but an unsaved one never is", "[alrecoverystore]") { - set_test_name("a discarded entry is kept apart a while, then let go of; an unsaved one never is"); ALRecoveryStore store(folder, "mine"); ALRecoveryStore other(folder, "theirs"); - ensure("written", store.write(entry("item:gone", "thrown away"))); - ensure("and another session's", other.write(entry("item:kept", "never thrown away"))); + UNSCOPED_INFO("written"); + CHECK(store.write(entry("item:gone", "thrown away"))); + UNSCOPED_INFO("and another session's"); + CHECK(other.write(entry("item:kept", "never thrown away"))); { const std::vector written = store.list(); const auto mine = std::find_if(written.begin(), written.end(), [](const ALRecoveryEntry& one) { return one.key == "item:gone"; }); - ensure("discarded", mine != written.end() && store.discard(*mine)); + { + INFO("discarded"); + REQUIRE(mine != written.end()); + CHECK(store.discard(*mine)); + } } std::vector all = whole(store, store.list()); - ensure_equals("both still listed", all.size(), size_t(2)); + UNSCOPED_INFO("both still listed"); + REQUIRE(all.size() == size_t(2)); const auto discarded = std::find_if(all.begin(), all.end(), [](const ALRecoveryEntry& one) { return one.key == "item:gone"; }); - ensure("the discarded one", discarded != all.end() && discarded->state == ALRecoveryEntry::State::Discarded); - ensure_equals("with its text", discarded->text, std::string("thrown away")); - ensure_equals("in the folder beside", files("/discarded").size(), size_t(1)); - ensure("not among what was left unsaved", store.leftFor("item:gone") == std::nullopt); - ensure("but offered", store.hasOffers()); + { + INFO("the discarded one"); + REQUIRE(discarded != all.end()); + CHECK(discarded->state == ALRecoveryEntry::State::Discarded); + } + UNSCOPED_INFO("with its text"); + CHECK(discarded->text == std::string("thrown away")); + UNSCOPED_INFO("in the folder beside"); + REQUIRE(files("/discarded").size() == size_t(1)); + UNSCOPED_INFO("not among what was left unsaved"); + CHECK(store.leftFor("item:gone") == std::nullopt); + UNSCOPED_INFO("but offered"); + CHECK(store.hasOffers()); store.prune(60.0, LLDate(LLDate::now().secondsSinceEpoch() + 30.0)); - ensure_equals("young, it stays", files("/discarded").size(), size_t(1)); + UNSCOPED_INFO("young, it stays"); + REQUIRE(files("/discarded").size() == size_t(1)); store.prune(60.0, LLDate(LLDate::now().secondsSinceEpoch() + 120.0)); - ensure("old, it goes", files("/discarded").empty()); - ensure("and the unsaved one does not", store.leftFor("item:kept").has_value()); + UNSCOPED_INFO("old, it goes"); + REQUIRE(files("/discarded").empty()); + UNSCOPED_INFO("and the unsaved one does not"); + REQUIRE(store.leftFor("item:kept").has_value()); // Another session's, discarded from the list of what was left. const std::optional theirs = store.leftFor("item:kept"); - ensure("discarded too", store.discard(*theirs)); - ensure("gone from what was left", !store.leftFor("item:kept").has_value()); - ensure_equals("into the folder beside", files("/discarded").size(), size_t(1)); + UNSCOPED_INFO("discarded too"); + CHECK(store.discard(*theirs)); + UNSCOPED_INFO("gone from what was left"); + REQUIRE_FALSE(store.leftFor("item:kept").has_value()); + UNSCOPED_INFO("into the folder beside"); + REQUIRE(files("/discarded").size() == size_t(1)); } - template<> template<> - void alrecoverystore_object::test<6>() + TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: a file that cannot be read is passed over and left in place", "[alrecoverystore]") { - set_test_name("a file that cannot be read is passed over and left where it is, for a person to look at"); ALRecoveryStore store(folder, "mine"); - ensure("written", store.write(entry("item:ok", "fine"))); + UNSCOPED_INFO("written"); + CHECK(store.write(entry("item:ok", "fine"))); { LLFILE* broken = LLFile::fopen(folder + "/0000.someone.llsd", LLFILE_MODE("wb")); - ensure("made", broken != nullptr); + UNSCOPED_INFO("made"); + REQUIRE(broken != nullptr); fputs("texthalf", broken); fclose(broken); } const std::vector all = store.list(); - ensure_equals("the one that reads", all.size(), size_t(1)); - ensure_equals("is the good one", all.front().key, std::string("item:ok")); - ensure("the other is still there", LLFile::isfile(folder + "/0000.someone.llsd")); + UNSCOPED_INFO("the one that reads"); + REQUIRE(all.size() == size_t(1)); + UNSCOPED_INFO("is the good one"); + CHECK(all.front().key == std::string("item:ok")); + UNSCOPED_INFO("the other is still there"); + CHECK(LLFile::isfile(folder + "/0000.someone.llsd")); + } + + TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: a notecard keeps its items in order with its text through the disk", "[alrecoverystore]") + { + ALRecoveryStore store(folder, "mine"); + ALRecoveryEntry card = entry("item:card", std::string("Look: ") + "\xF4\x80\x80\x80" + " and " + "\xF4\x80\x80\x81"); + card.notecard = true; + LLSD first; + first["item_id"] = LLUUID::generateNewID(); + first["name"] = "Sunset"; + LLSD second; + second["item_id"] = LLUUID::generateNewID(); + second["name"] = "Home"; + card.embedded = LLSD::emptyArray().with(0, first).with(1, second); + UNSCOPED_INFO("written"); + CHECK(store.write(card)); + const std::vector all = whole(store, store.list()); + UNSCOPED_INFO("one"); + REQUIRE(all.size() == size_t(1)); + UNSCOPED_INFO("a notecard"); + CHECK(all.front().notecard); + UNSCOPED_INFO("its text, the placeholders in it byte for byte"); + CHECK(all.front().text == card.text); + UNSCOPED_INFO("both items"); + REQUIRE(all.front().embedded.size() == 2); + { + INFO("in the order the text stands them"); + CHECK(all.front().embedded[0]["name"].asString() == "Sunset"); + CHECK(all.front().embedded[1]["name"].asString() == "Home"); + } + UNSCOPED_INFO("each whole"); + CHECK(all.front().embedded[1]["item_id"].asUUID() == second["item_id"].asUUID()); + // Discarded, it keeps them too. + UNSCOPED_INFO("discarded"); + CHECK(store.discard(all.front())); + const std::vector thrown = whole(store, store.list()); + { + INFO("the items go with it"); + REQUIRE(thrown.size() == 1); + REQUIRE(thrown.front().embedded.size() == 2); + } + // A script has none, and says so as an empty list. + ALRecoveryEntry script; + UNSCOPED_INFO("read"); + REQUIRE(ALRecoveryEntry::fromLLSD(entry("item:s", "x").asLLSD(), script)); + { + INFO("no items"); + REQUIRE(script.embedded.isArray()); + REQUIRE(script.embedded.size() == 0); + } } - template<> template<> - void alrecoverystore_object::test<8>() + TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: setAside leaves its source; prune sweeps halves; an XML entry reads", "[alrecoverystore]") { - set_test_name("a text set aside leaves what it came from; half-written files and old discards go at a prune; an XML entry still reads"); ALRecoveryStore store(folder, "mine"); ALRecoveryStore other(folder, "theirs"); - ensure("another session's", other.write(entry("item:a", "left"))); - ensure("set aside", store.setAside(entry("item:a", "thrown"))); - ensure_equals("among the discarded", files("/discarded").size(), size_t(1)); + UNSCOPED_INFO("another session's"); + CHECK(other.write(entry("item:a", "left"))); + UNSCOPED_INFO("set aside"); + CHECK(store.setAside(entry("item:a", "thrown"))); + UNSCOPED_INFO("among the discarded"); + REQUIRE(files("/discarded").size() == size_t(1)); const std::optional left = whole(store, store.leftFor("item:a")); - ensure("what it came from still left", left.has_value() && left->text == "left"); - ensure_equals("and only that is left", store.left().size(), size_t(1)); + { + INFO("what it came from still left"); + REQUIRE(left.has_value()); + CHECK(left->text == "left"); + } + UNSCOPED_INFO("and only that is left"); + REQUIRE(store.left().size() == size_t(1)); const std::vector all = whole(store, store.list()); const auto thrown = std::find_if(all.begin(), all.end(), [](const ALRecoveryEntry& one) { return one.text == "thrown"; }); - ensure("set aside by this session", thrown != all.end() && thrown->session == "mine" && thrown->state == ALRecoveryEntry::State::Discarded); + { + INFO("set aside by this session"); + REQUIRE(thrown != all.end()); + REQUIRE(thrown->session == "mine"); + CHECK(thrown->state == ALRecoveryEntry::State::Discarded); + } // Written half by a session that went, and by this one. for (const char* name : { "/0001.theirs.llsd.tmp", "/0002.mine.llsd.tmp" }) { LLFILE* half = LLFile::fopen(folder + name, LLFILE_MODE("wb")); - ensure("made", half != nullptr); + UNSCOPED_INFO("made"); + REQUIRE(half != nullptr); fputs("\n{'key':'ite", half); fclose(half); } store.prune(60.0); - ensure("the gone session's half gone", !LLFile::isfile(folder + "/0001.theirs.llsd.tmp")); - ensure("this session's own left", LLFile::isfile(folder + "/0002.mine.llsd.tmp")); - ensure_equals("the young discard stays", files("/discarded").size(), size_t(1)); + UNSCOPED_INFO("the gone session's half gone"); + CHECK_FALSE(LLFile::isfile(folder + "/0001.theirs.llsd.tmp")); + UNSCOPED_INFO("this session's own left"); + CHECK(LLFile::isfile(folder + "/0002.mine.llsd.tmp")); + UNSCOPED_INFO("the young discard stays"); + REQUIRE(files("/discarded").size() == size_t(1)); // An entry as the XML it was once written as. { ALRecoveryEntry was = entry("item:old", "from before"); was.session = "older"; LLFILE* xml = LLFile::fopen(folder + "/0003.older.llsd", LLFILE_MODE("wb")); - ensure("made", xml != nullptr); + UNSCOPED_INFO("made"); + REQUIRE(xml != nullptr); std::ostringstream text; LLSDSerialize::toPrettyXML(was.asLLSD(), text); fputs(text.str().c_str(), xml); fclose(xml); } const std::vector now = whole(store, store.left()); - ensure("read", std::any_of(now.begin(), now.end(), [](const ALRecoveryEntry& one) { return one.text == "from before"; })); + UNSCOPED_INFO("read"); + CHECK(std::any_of(now.begin(), now.end(), [](const ALRecoveryEntry& one) { return one.text == "from before"; })); } - template<> template<> - void alrecoverystore_object::test<9>() + TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: letGo of a changed tab sets its text aside and drops what it took up", "[alrecoverystore]") { - set_test_name("a changed tab let go of has its text set aside, its own entry forgotten, and the entry it took up let go of"); ALRecoveryStore store(folder, "mine"); ALRecoveryStore other(folder, "theirs"); - ensure("left by another session", other.write(entry("item:x", "left before"))); + UNSCOPED_INFO("left by another session"); + CHECK(other.write(entry("item:x", "left before"))); const std::optional took = store.leftFor("item:x"); - ensure("found", took.has_value()); - ensure("kept by this one", store.write(entry("item:x", "typed since"))); + UNSCOPED_INFO("found"); + REQUIRE(took.has_value()); + UNSCOPED_INFO("kept by this one"); + CHECK(store.write(entry("item:x", "typed since"))); ALRecoveryStore::Parting parting; parting.key = "item:x"; parting.unsaved = entry("item:x", "typed since, then closed"); parting.tookUp = took; parting.settled = true; - ensure("let go of", store.letGo(parting)); + UNSCOPED_INFO("let go of"); + CHECK(store.letGo(parting)); const std::vector all = whole(store, store.list()); - ensure_equals("one entry", all.size(), size_t(1)); - ensure("set aside, as it stood", all.front().state == ALRecoveryEntry::State::Discarded && all.front().text == "typed since, then closed"); - ensure("by this session", all.front().session == "mine"); - ensure("what it took up gone", !store.leftFor("item:x").has_value() && store.left().empty()); + UNSCOPED_INFO("one entry"); + REQUIRE(all.size() == size_t(1)); + { + INFO("set aside, as it stood"); + CHECK(all.front().state == ALRecoveryEntry::State::Discarded); + CHECK(all.front().text == "typed since, then closed"); + } + UNSCOPED_INFO("by this session"); + CHECK(all.front().session == "mine"); + { + INFO("what it took up gone"); + REQUIRE_FALSE(store.leftFor("item:x").has_value()); + REQUIRE(store.left().empty()); + } } - template<> template<> - void alrecoverystore_object::test<10>() + TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: where the text cannot be set aside letGo lets go of nothing", "[alrecoverystore]") { - set_test_name("where the text cannot be set aside, nothing is let go of: its own entry and the one it took up both stay"); ALRecoveryStore store(folder, "mine"); ALRecoveryStore other(folder, "theirs"); - ensure("left by another session", other.write(entry("item:x", "left before"))); + UNSCOPED_INFO("left by another session"); + CHECK(other.write(entry("item:x", "left before"))); const std::optional took = store.leftFor("item:x"); - ensure("kept by this one", store.write(entry("item:x", "typed since"))); + UNSCOPED_INFO("kept by this one"); + CHECK(store.write(entry("item:x", "typed since"))); // A file where the folder of the discarded would be: nothing can be // written there. { LLFILE* blocking = LLFile::fopen(folder + "/discarded", LLFILE_MODE("wb")); - ensure("made", blocking != nullptr); + UNSCOPED_INFO("made"); + REQUIRE(blocking != nullptr); fclose(blocking); } @@ -376,103 +498,134 @@ namespace tut parting.unsaved = entry("item:x", "typed since, then closed"); parting.tookUp = took; parting.settled = true; - ensure("said to have failed", !store.letGo(parting)); + UNSCOPED_INFO("said to have failed"); + CHECK_FALSE(store.letGo(parting)); const std::vector all = whole(store, store.list()); const auto own = std::find_if(all.begin(), all.end(), [](const ALRecoveryEntry& one) { return one.session == "mine"; }); - ensure("its own entry still there", own != all.end() && own->text == "typed since"); - ensure("and the one it took up", store.leftFor("item:x").has_value()); - ensure("a set-aside that fails is said", !store.setAside(entry("item:y", "anything"))); + { + INFO("its own entry still there"); + REQUIRE(own != all.end()); + CHECK(own->text == "typed since"); + } + UNSCOPED_INFO("and the one it took up"); + REQUIRE(store.leftFor("item:x").has_value()); + UNSCOPED_INFO("a set-aside that fails is said"); + CHECK_FALSE(store.setAside(entry("item:y", "anything"))); } - template<> template<> - void alrecoverystore_object::test<11>() + TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: a clean tab drops what it took up only where a save could reach it", "[alrecoverystore]") { - set_test_name("a clean tab forgets its own entry, and lets go of what it took up only where a save could reach its text"); ALRecoveryStore store(folder, "mine"); ALRecoveryStore other(folder, "theirs"); - ensure("left by another session", other.write(entry("item:x", "left before"))); + UNSCOPED_INFO("left by another session"); + CHECK(other.write(entry("item:x", "left before"))); // Holding the kept text where nothing can save it -- the script may // no longer be changed, or never loaded: left to be offered again. - ensure("kept by this one", store.write(entry("item:x", "typed since"))); + UNSCOPED_INFO("kept by this one"); + CHECK(store.write(entry("item:x", "typed since"))); ALRecoveryStore::Parting stuck; stuck.key = "item:x"; stuck.tookUp = store.leftFor("item:x"); stuck.settled = false; - ensure("let go of", store.letGo(stuck)); + UNSCOPED_INFO("let go of"); + CHECK(store.letGo(stuck)); const std::vector after = store.list(); - ensure("its own forgotten", std::none_of(after.begin(), after.end(), [](const ALRecoveryEntry& one) { return one.session == "mine"; })); - ensure("what it took up still offered", store.leftFor("item:x").has_value()); - ensure("nothing set aside", files("/discarded").empty()); + UNSCOPED_INFO("its own forgotten"); + CHECK(std::none_of(after.begin(), after.end(), [](const ALRecoveryEntry& one) { return one.session == "mine"; })); + UNSCOPED_INFO("what it took up still offered"); + REQUIRE(store.leftFor("item:x").has_value()); + UNSCOPED_INFO("nothing set aside"); + REQUIRE(files("/discarded").empty()); // Loaded and clean: the text it took up is what was saved. ALRecoveryStore::Parting clean; clean.key = "item:x"; clean.tookUp = store.leftFor("item:x"); clean.settled = true; - ensure("let go of", store.letGo(clean)); - ensure("what it took up gone", !store.leftFor("item:x").has_value()); - ensure("and nothing set aside", files("/discarded").empty()); + UNSCOPED_INFO("let go of"); + CHECK(store.letGo(clean)); + UNSCOPED_INFO("what it took up gone"); + REQUIRE_FALSE(store.leftFor("item:x").has_value()); + UNSCOPED_INFO("and nothing set aside"); + REQUIRE(files("/discarded").empty()); } - template<> template<> - void alrecoverystore_object::test<12>() + TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: a carrying tab sets aside text with no file and leaves an entry on disk", "[alrecoverystore]") { - set_test_name("a tab let go of before what it carried was put in: a text with no file of its own set aside, an entry on disk left as it was"); ALRecoveryStore store(folder, "mine"); ALRecoveryStore other(folder, "theirs"); // Moved from another window, or loading its item under what it held: // the text it carried is the only copy of what was typed, and this // session's entry for it was written by the tab it came from. - ensure("kept by the tab it came from", store.write(entry("item:moved", "typed in the other window"))); + UNSCOPED_INFO("kept by the tab it came from"); + CHECK(store.write(entry("item:moved", "typed in the other window"))); ALRecoveryStore::Parting moved; moved.key = "item:moved"; moved.tookUp = entry("item:moved", "typed in the other window"); moved.carrying = true; - ensure("let go of", store.letGo(moved)); + UNSCOPED_INFO("let go of"); + CHECK(store.letGo(moved)); const std::vector all = whole(store, store.list()); - ensure_equals("one entry", all.size(), size_t(1)); - ensure("the carried text set aside", all.front().state == ALRecoveryEntry::State::Discarded && all.front().text == "typed in the other window"); + UNSCOPED_INFO("one entry"); + REQUIRE(all.size() == size_t(1)); + { + INFO("the carried text set aside"); + CHECK(all.front().state == ALRecoveryEntry::State::Discarded); + CHECK(all.front().text == "typed in the other window"); + } // Opened to take up another session's entry, and closed before it // loaded: that entry is still what it was, to be offered again. - ensure("left by another session", other.write(entry("item:left", "left before"))); + UNSCOPED_INFO("left by another session"); + CHECK(other.write(entry("item:left", "left before"))); ALRecoveryStore::Parting waiting; waiting.key = "item:left"; waiting.tookUp = store.leftFor("item:left"); waiting.carrying = true; - ensure("let go of", store.letGo(waiting)); - ensure("the entry left", store.leftFor("item:left").has_value()); - ensure_equals("and nothing more set aside", files("/discarded").size(), size_t(1)); + UNSCOPED_INFO("let go of"); + CHECK(store.letGo(waiting)); + UNSCOPED_INFO("the entry left"); + REQUIRE(store.leftFor("item:left").has_value()); + UNSCOPED_INFO("and nothing more set aside"); + REQUIRE(files("/discarded").size() == size_t(1)); // A new item opened with a text of its own: nothing to keep. - ensure("kept", store.write(entry("item:new", "template"))); + UNSCOPED_INFO("kept"); + CHECK(store.write(entry("item:new", "template"))); ALRecoveryStore::Parting fresh; fresh.key = "item:new"; fresh.carrying = true; - ensure("let go of", store.letGo(fresh)); + UNSCOPED_INFO("let go of"); + CHECK(store.letGo(fresh)); const std::vector after = store.list(); - ensure("forgotten", std::none_of(after.begin(), after.end(), [](const ALRecoveryEntry& one) { return one.key == "item:new"; })); + UNSCOPED_INFO("forgotten"); + CHECK(std::none_of(after.begin(), after.end(), [](const ALRecoveryEntry& one) { return one.key == "item:new"; })); } - template<> template<> - void alrecoverystore_object::test<13>() + TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: a failed load is retried with delays tripling and a few times only", "[alrecoverystore]") { - set_test_name("a failed load is tried again a moment later, three times as long after each failure since, and a few times only"); - ensure_equals("after the first", ALRecoveryRetry::delayAfter(1), ALRecoveryRetry::FIRST); - ensure_equals("after the second", ALRecoveryRetry::delayAfter(2), ALRecoveryRetry::FIRST * 3.0); - ensure_equals("after the third", ALRecoveryRetry::delayAfter(3), ALRecoveryRetry::FIRST * 9.0); - ensure_equals("none yet is as the first", ALRecoveryRetry::delayAfter(0), ALRecoveryRetry::FIRST); - ensure("tried while there have been fewer failures than the tries", ALRecoveryRetry::mayTry(0) && ALRecoveryRetry::mayTry(ALRecoveryRetry::TRIES - 1)); - ensure("and then waits to be asked", !ALRecoveryRetry::mayTry(ALRecoveryRetry::TRIES)); + UNSCOPED_INFO("after the first"); + CHECK(ALRecoveryRetry::delayAfter(1) == ALRecoveryRetry::FIRST); + UNSCOPED_INFO("after the second"); + CHECK(ALRecoveryRetry::delayAfter(2) == ALRecoveryRetry::FIRST * 3.0); + UNSCOPED_INFO("after the third"); + CHECK(ALRecoveryRetry::delayAfter(3) == ALRecoveryRetry::FIRST * 9.0); + UNSCOPED_INFO("none yet is as the first"); + CHECK(ALRecoveryRetry::delayAfter(0) == ALRecoveryRetry::FIRST); + { + INFO("tried while there have been fewer failures than the tries"); + CHECK(ALRecoveryRetry::mayTry(0)); + CHECK(ALRecoveryRetry::mayTry(ALRecoveryRetry::TRIES - 1)); + } + UNSCOPED_INFO("and then waits to be asked"); + CHECK_FALSE(ALRecoveryRetry::mayTry(ALRecoveryRetry::TRIES)); } - template<> template<> - void alrecoverystore_object::test<14>() + TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: written as notation; setAside under the session named; newest left first", "[alrecoverystore]") { - set_test_name("written as notation, whatever the text and history hold; set aside by the session named, a moment apart kept apart; the newest left first"); ALRecoveryStore store(folder, "mine"); ALRecoveryEntry awkward = entry("item:a", "tab\there, 'quoted' \"twice\", back\\slash,\r\nlines, \xC3\xA9, \xF4\x80\x80\x80"); LLSD history; @@ -481,78 +634,110 @@ namespace tut history["redo"] = LLSD::emptyArray(); history["saved"] = -1; awkward.history = history; - ensure("written", store.write(awkward)); + UNSCOPED_INFO("written"); + CHECK(store.write(awkward)); { const std::vector names = files(); - ensure_equals("one file", names.size(), size_t(1)); + UNSCOPED_INFO("one file"); + REQUIRE(names.size() == size_t(1)); std::error_code ec; const std::string written = LLFile::getContents(fsyspath(folder + "/" + names.front()), ec); // A line of what a listing reads, then a line of the rest, each // LLSD notation. const size_t line = written.find('\n'); - ensure("as notation, a line of what a listing reads", written.rfind("{", 0) == 0 && line != std::string::npos); - ensure("which is not the text", written.substr(0, line).find("'text'") == std::string::npos); - ensure("then the rest", written.compare(line + 1, 8, "{'caret'") == 0 || written.compare(line + 1, 10, "{'history'") == 0 || - written.compare(line + 1, 7, "{'text'") == 0); + { + INFO("as notation, a line of what a listing reads"); + CHECK(written.rfind("{", 0) == 0); + REQUIRE(line != std::string::npos); + } + UNSCOPED_INFO("which is not the text"); + REQUIRE(written.substr(0, line).find("'text'") == std::string::npos); + UNSCOPED_INFO("then the rest"); + CHECK((written.compare(line + 1, 8, "{'caret'") == 0 || written.compare(line + 1, 10, "{'history'") == 0 || + written.compare(line + 1, 7, "{'text'") == 0)); } const std::vector back = whole(store, store.list()); - ensure("the text as it was", back.size() == 1 && back.front().text == awkward.text); - ensure("the history as it was", back.front().history["undo"][0]["edits"][0][5].asString() == awkward.text && - back.front().history["undo"][0]["label"].asString() == "it's"); + { + INFO("the text as it was"); + REQUIRE(back.size() == 1); + CHECK(back.front().text == awkward.text); + } + { + INFO("the history as it was"); + CHECK(back.front().history["undo"][0]["edits"][0][5].asString() == awkward.text); + CHECK(back.front().history["undo"][0]["label"].asString() == "it's"); + } // Set aside for another session, and two a moment apart. ALRecoveryEntry theirs = entry("item:b", "theirs"); theirs.session = "theirs"; - ensure("set aside", store.setAside(theirs)); + UNSCOPED_INFO("set aside"); + CHECK(store.setAside(theirs)); // Apart by more than the centisecond a date is written to. ms_sleep(20); - ensure("and another", store.setAside(entry("item:b", "mine, later"))); - ensure_equals("both kept", files("/discarded").size(), size_t(2)); + UNSCOPED_INFO("and another"); + CHECK(store.setAside(entry("item:b", "mine, later"))); + UNSCOPED_INFO("both kept"); + REQUIRE(files("/discarded").size() == size_t(2)); const std::vector all = whole(store, store.list()); - ensure("the session it was set aside for", std::any_of(all.begin(), all.end(), [](const ALRecoveryEntry& one) { return one.text == "theirs" && one.session == "theirs"; })); - ensure("this one where none was named", std::any_of(all.begin(), all.end(), [](const ALRecoveryEntry& one) { return one.text == "mine, later" && one.session == "mine"; })); + UNSCOPED_INFO("the session it was set aside for"); + CHECK(std::any_of(all.begin(), all.end(), [](const ALRecoveryEntry& one) { return one.text == "theirs" && one.session == "theirs"; })); + UNSCOPED_INFO("this one where none was named"); + CHECK(std::any_of(all.begin(), all.end(), [](const ALRecoveryEntry& one) { return one.text == "mine, later" && one.session == "mine"; })); // Left by other sessions: the newest first. ALRecoveryStore older(folder, "older"); ALRecoveryStore newer(folder, "newer"); - ensure("older", older.write(entry("item:c", "older"))); + UNSCOPED_INFO("older"); + CHECK(older.write(entry("item:c", "older"))); // Apart by more than the centisecond a date is written to. ms_sleep(20); - ensure("newer", newer.write(entry("item:d", "newer"))); + UNSCOPED_INFO("newer"); + CHECK(newer.write(entry("item:d", "newer"))); const std::vector left = whole(store, store.left()); - ensure("newest first", left.size() == 2 && left.front().text == "newer" && left.back().text == "older"); + { + INFO("newest first"); + REQUIRE(left.size() == 2); + CHECK(left.front().text == "newer"); + CHECK(left.back().text == "older"); + } } - template<> template<> - void alrecoverystore_object::test<15>() + TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: a discard whose name says no time is pruned by what it says inside", "[alrecoverystore]") { - set_test_name("a discarded file whose name does not say when is pruned by what it says inside"); ALRecoveryStore store(folder, "mine"); - ensure("set aside", store.setAside(entry("item:a", "old"))); + UNSCOPED_INFO("set aside"); + CHECK(store.setAside(entry("item:a", "old"))); // Renamed to say nothing of when, and then to say it badly. const std::vector names = files("/discarded"); - ensure_equals("one", names.size(), size_t(1)); + UNSCOPED_INFO("one"); + REQUIRE(names.size() == size_t(1)); const std::string stamped = folder + "/discarded/" + names.front(); const std::string bare = folder + "/discarded/0000.mine.llsd"; - ensure("renamed", LLFile::rename(stamped, bare) == 0); + UNSCOPED_INFO("renamed"); + CHECK(LLFile::rename(stamped, bare) == 0); store.prune(60.0, LLDate(LLDate::now().secondsSinceEpoch() + 30.0)); - ensure("young by what it says, it stays", LLFile::isfile(bare)); + UNSCOPED_INFO("young by what it says, it stays"); + CHECK(LLFile::isfile(bare)); store.prune(60.0, LLDate(LLDate::now().secondsSinceEpoch() + 120.0)); - ensure("old by what it says, it goes", !LLFile::isfile(bare)); + UNSCOPED_INFO("old by what it says, it goes"); + CHECK_FALSE(LLFile::isfile(bare)); - ensure("set aside", store.setAside(entry("item:b", "old"))); + UNSCOPED_INFO("set aside"); + CHECK(store.setAside(entry("item:b", "old"))); const std::string garbled = folder + "/discarded/0001.mine.soon.llsd"; - ensure("renamed", LLFile::rename(folder + "/discarded/" + files("/discarded").front(), garbled) == 0); + UNSCOPED_INFO("renamed"); + CHECK(LLFile::rename(folder + "/discarded/" + files("/discarded").front(), garbled) == 0); store.prune(60.0, LLDate(LLDate::now().secondsSinceEpoch() + 30.0)); - ensure("a stamp that is no number read past", LLFile::isfile(garbled)); + UNSCOPED_INFO("a stamp that is no number read past"); + CHECK(LLFile::isfile(garbled)); store.prune(60.0, LLDate(LLDate::now().secondsSinceEpoch() + 120.0)); - ensure("and pruned by what it says", !LLFile::isfile(garbled)); + UNSCOPED_INFO("and pruned by what it says"); + CHECK_FALSE(LLFile::isfile(garbled)); } - template<> template<> - void alrecoverystore_object::test<16>() + TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: writeSoon writes on the store's thread and the newest for a key wins", "[alrecoverystore]") { - set_test_name("what typing writes goes on the store's thread, the newest for a key winning, and whatever else is asked waits for it"); { ALRecoveryStore store(folder, "session-a"); for (int i = 0; i < 20; ++i) @@ -562,15 +747,19 @@ namespace tut store.writeSoon(entry("item:y", "other")); store.flush(); const std::vector all = whole(store, store.list()); - ensure_equals("one entry a key", all.size(), size_t(2)); + UNSCOPED_INFO("one entry a key"); + REQUIRE(all.size() == size_t(2)); for (const ALRecoveryEntry& one : all) { - ensure("the newest of x: " + one.text, one.key != "item:x" || one.text == "typed 19"); - ensure("this session's", one.session == "session-a"); + UNSCOPED_INFO("the newest of x: " + one.text); + REQUIRE((one.key != "item:x" || one.text == "typed 19")); + UNSCOPED_INFO("this session's"); + REQUIRE(one.session == "session-a"); } for (const std::string& name : files()) { - ensure("nothing half written: " + name, name.find(".tmp") == std::string::npos); + UNSCOPED_INFO("nothing half written: " + name); + REQUIRE(name.find(".tmp") == std::string::npos); } // Forgotten right after it was asked to be written: the write // goes first, and the file does not come back after. @@ -578,8 +767,13 @@ namespace tut store.forget("item:x"); store.flush(); const std::vector after = store.list(); - ensure("x gone: " + std::to_string(after.size()), after.size() == 1 && after.front().key == "item:y"); - ensure("nothing failed", store.takeFailures().empty()); + { + INFO("x gone: " + std::to_string(after.size())); + REQUIRE(after.size() == 1); + CHECK(after.front().key == "item:y"); + } + UNSCOPED_INFO("nothing failed"); + REQUIRE(store.takeFailures().empty()); // Left waiting as the store goes: written all the same. store.writeSoon(entry("item:z", "as it went")); } @@ -589,7 +783,8 @@ namespace tut { z |= one.key == "item:z" && one.text == "as it went"; } - ensure("written as the store went", z); + UNSCOPED_INFO("written as the store went"); + CHECK(z); // Where the folder cannot be written, said by key. const std::string blocked = folder + "/blocked"; { @@ -600,139 +795,242 @@ namespace tut nowhere.writeSoon(entry("item:w", "lost")); nowhere.flush(); const std::vector failed = nowhere.takeFailures(); - ensure("failed, by key", failed.size() == 1 && failed.front() == "item:w"); - ensure("and said once", nowhere.takeFailures().empty()); + { + INFO("failed, by key"); + REQUIRE(failed.size() == 1); + CHECK(failed.front() == "item:w"); + } + UNSCOPED_INFO("and said once"); + REQUIRE(nowhere.takeFailures().empty()); // Each asking of its own key leaves another's. nowhere.writeSoon(entry("item:u", "lost")); nowhere.writeSoon(entry("item:v", "lost")); nowhere.flush(); - ensure("one key's own", nowhere.takeFailure("item:u") && !nowhere.takeFailure("item:u")); - ensure("the other's left for it", nowhere.takeFailures() == std::vector({ "item:v" })); + { + INFO("one key's own"); + CHECK(nowhere.takeFailure("item:u")); + CHECK_FALSE(nowhere.takeFailure("item:u")); + } + UNSCOPED_INFO("the other's left for it"); + CHECK(nowhere.takeFailures() == std::vector({ "item:v" })); } - template<> template<> - void alrecoverystore_object::test<17>() + TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: whenSaid gives the day and time an entry was written", "[alrecoverystore]") { - set_test_name("when an entry was written, said as a day and a time"); ALRecoveryEntry one; one.when = LLDate(1758772800.0); const std::string said = one.whenSaid(); // The day and the hour are the local zone's; the shape is not. - ensure_equals("YYYY-MM-DD HH:MM: " + said, said.size(), size_t(16)); - ensure("dashes and a colon: " + said, said[4] == '-' && said[7] == '-' && said[10] == ' ' && said[13] == ':'); - ensure("the year: " + said, said.starts_with("2025-09-2")); + UNSCOPED_INFO("YYYY-MM-DD HH:MM: " + said); + REQUIRE(said.size() == size_t(16)); + { + INFO("dashes and a colon: " + said); + CHECK(said[4] == '-'); + CHECK(said[7] == '-'); + CHECK(said[10] == ' '); + CHECK(said[13] == ':'); + } + UNSCOPED_INFO("the year: " + said); + CHECK(said.starts_with("2025-09-2")); } - template<> template<> - void alrecoverystore_object::test<18>() + TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: reclaim sets this session's own entry aside keeping when it was written", "[alrecoverystore]") { - set_test_name("this session's own entry, reclaimed where no tab holds it, is set aside keeping when it was written, and out of the way of forget"); ALRecoveryStore store(folder, "now"); ALRecoveryEntry went = entry("task:a:b", "typed, then its window went"); store.write(went); const LLDate written = store.list().front().when; - ensure("not left by another session", !store.leftFor("task:a:b")); + UNSCOPED_INFO("not left by another session"); + REQUIRE_FALSE(store.leftFor("task:a:b")); const std::optional back = store.reclaim("task:a:b"); - ensure("reclaimed", back.has_value()); - ensure_equals("its text", back->text, went.text); - ensure("set aside", back->state == ALRecoveryEntry::State::Discarded); - ensure_equals("when it was written, not when it was set aside", back->when.secondsSinceEpoch(), written.secondsSinceEpoch()); - ensure("its own file gone, and it among the discarded", files().empty() && files("/discarded").size() == 1); - ensure("where it is now", back->path.find("discarded") != std::string::npos); + UNSCOPED_INFO("reclaimed"); + REQUIRE(back.has_value()); + UNSCOPED_INFO("its text"); + CHECK(back->text == went.text); + UNSCOPED_INFO("set aside"); + CHECK(back->state == ALRecoveryEntry::State::Discarded); + UNSCOPED_INFO("when it was written, not when it was set aside"); + CHECK(back->when.secondsSinceEpoch() == written.secondsSinceEpoch()); + { + INFO("its own file gone, and it among the discarded"); + REQUIRE(files().empty()); + REQUIRE(files("/discarded").size() == 1); + } + UNSCOPED_INFO("where it is now"); + REQUIRE(back->path.find("discarded") != std::string::npos); store.forget("task:a:b"); - ensure("a tab opened clean forgets nothing of it", files("/discarded").size() == 1); + UNSCOPED_INFO("a tab opened clean forgets nothing of it"); + REQUIRE(files("/discarded").size() == 1); const std::vector listed = whole(store, store.list()); - ensure("offered under Recover Unsaved Changes", listed.size() == 1 && listed.front().text == went.text); - ensure("reclaimed once", !store.reclaim("task:a:b")); + { + INFO("offered under Recover Unsaved Changes"); + REQUIRE(listed.size() == 1); + CHECK(listed.front().text == went.text); + } + UNSCOPED_INFO("reclaimed once"); + REQUIRE_FALSE(store.reclaim("task:a:b")); ALRecoveryStore other(folder, "earlier"); other.write(entry("task:c:d", "another session's")); - ensure("another session's is not this one's to reclaim", !store.reclaim("task:c:d") && store.leftFor("task:c:d")); + { + INFO("another session's is not this one's to reclaim"); + REQUIRE_FALSE(store.reclaim("task:c:d")); + REQUIRE(store.leftFor("task:c:d")); + } store.remove(*back); - ensure("taken up, gone", files("/discarded").empty()); + UNSCOPED_INFO("taken up, gone"); + REQUIRE(files("/discarded").empty()); } - template<> template<> - void alrecoverystore_object::test<19>() + TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: a listing reads only what it shows and load reads the rest", "[alrecoverystore]") { - set_test_name("a listing reads what it shows and no more; the rest is read as it is taken up; a history written as notation reads back as one"); ALRecoveryStore store(folder, "mine"); ALRecoveryStore other(folder, "theirs"); ALRecoveryEntry big = entry("item:big", std::string(200000, 'x')); big.caretLine = 3; big.caretColumn = 4; big.historyWritten = "{'version':i2,'undo':[{'label':'it\\'s','edits':[]}],'redo':[],'saved':i-1}"; - ensure("written", other.write(big)); + UNSCOPED_INFO("written"); + CHECK(other.write(big)); const std::vector listed = store.left(); - ensure_equals("listed", listed.size(), size_t(1)); - ensure("with what it shows", listed.front().name == "My Script" && listed.front().key == "item:big" && listed.front().session == "theirs"); - ensure("and not its text", !listed.front().whole && listed.front().text.empty() && listed.front().caretLine < 0); + UNSCOPED_INFO("listed"); + REQUIRE(listed.size() == size_t(1)); + { + INFO("with what it shows"); + CHECK(listed.front().name == "My Script"); + CHECK(listed.front().key == "item:big"); + CHECK(listed.front().session == "theirs"); + } + { + INFO("and not its text"); + CHECK_FALSE(listed.front().whole); + REQUIRE(listed.front().text.empty()); + CHECK(listed.front().caretLine < 0); + } ALRecoveryEntry taken = listed.front(); - ensure("read whole", store.load(taken)); - ensure("its text", taken.whole && taken.text == big.text); - ensure("its caret", taken.caretLine == 3 && taken.caretColumn == 4); - ensure("its history, written as notation, read back", taken.history["undo"][0]["label"].asString() == "it's" && taken.history["saved"].asInteger() == -1); - ensure("whole already, read again as it is", store.load(taken) && taken.text == big.text); + UNSCOPED_INFO("read whole"); + CHECK(store.load(taken)); + { + INFO("its text"); + CHECK(taken.whole); + CHECK(taken.text == big.text); + } + { + INFO("its caret"); + CHECK(taken.caretLine == 3); + CHECK(taken.caretColumn == 4); + } + { + INFO("its history, written as notation, read back"); + CHECK(taken.history["undo"][0]["label"].asString() == "it's"); + CHECK(taken.history["saved"].asInteger() == -1); + } + { + INFO("whole already, read again as it is"); + CHECK(store.load(taken)); + CHECK(taken.text == big.text); + } store.remove(taken); ALRecoveryEntry gone = listed.front(); - ensure("its file gone, not read", !store.load(gone)); + UNSCOPED_INFO("its file gone, not read"); + CHECK_FALSE(store.load(gone)); // Set aside from a listing, it is read whole first: nothing of its // text is lost among the discarded. - ensure("written again", other.write(big)); + UNSCOPED_INFO("written again"); + CHECK(other.write(big)); const std::optional again = store.leftFor("item:big"); - ensure("found, as a listing reads it", again && !again->whole); - ensure("discarded", store.discard(*again)); + { + INFO("found, as a listing reads it"); + REQUIRE(again); + CHECK_FALSE(again->whole); + } + UNSCOPED_INFO("discarded"); + CHECK(store.discard(*again)); const std::vector thrown = whole(store, store.list()); - ensure("with its text", thrown.size() == 1 && thrown.front().text == big.text && thrown.front().history["undo"].size() == 1); + { + INFO("with its text"); + REQUIRE(thrown.size() == 1); + CHECK(thrown.front().text == big.text); + REQUIRE(thrown.front().history["undo"].size() == 1); + } } - template<> template<> - void alrecoverystore_object::test<20>() + TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: offers expire; discards are capped; offers are counted once a moment", "[alrecoverystore]") { - set_test_name("what was offered and left goes a while after; the discarded are held to so many and so much; offers are counted once a moment"); ALRecoveryStore store(folder, "mine"); ALRecoveryStore other(folder, "theirs"); - ensure("nothing to offer", !store.hasOffers()); - ensure("left by another session", other.write(entry("item:a", "left"))); - ensure("counted a moment ago: not yet seen", !store.hasOffers()); + UNSCOPED_INFO("nothing to offer"); + CHECK_FALSE(store.hasOffers()); + UNSCOPED_INFO("left by another session"); + CHECK(other.write(entry("item:a", "left"))); + UNSCOPED_INFO("counted a moment ago: not yet seen"); + CHECK_FALSE(store.hasOffers()); store.forget("item:none"); - ensure("seen once this store changes anything", store.hasOffers()); + UNSCOPED_INFO("seen once this store changes anything"); + CHECK(store.hasOffers()); // Offered: marked when, still offered, and gone a while after. const LLDate offered = LLDate::now(); store.markOffered(store.left(), offered); const std::vector names = files(); - ensure("renamed to say when", names.size() == 1 && names.front().find(std::to_string(static_cast(offered.secondsSinceEpoch() * 1000.0))) != std::string::npos); - ensure("still offered", store.hasOffers() && store.left().size() == 1 && store.leftFor("item:a").has_value()); - ensure("and read", whole(store, store.leftFor("item:a"))->text == "left"); + { + INFO("renamed to say when"); + REQUIRE(names.size() == 1); + REQUIRE(names.front().find(std::to_string(static_cast(offered.secondsSinceEpoch() * 1000.0))) != std::string::npos); + } + { + INFO("still offered"); + CHECK(store.hasOffers()); + REQUIRE(store.left().size() == 1); + REQUIRE(store.leftFor("item:a").has_value()); + } + UNSCOPED_INFO("and read"); + CHECK(whole(store, store.leftFor("item:a"))->text == "left"); store.markOffered(store.left(), LLDate(offered.secondsSinceEpoch() + 100.0)); - ensure("marked once", files().front() == names.front()); + UNSCOPED_INFO("marked once"); + CHECK(files().front() == names.front()); store.prune(60.0, LLDate(offered.secondsSinceEpoch() + 30.0)); - ensure("young, it stays", files().size() == 1); + UNSCOPED_INFO("young, it stays"); + REQUIRE(files().size() == 1); store.prune(60.0, LLDate(offered.secondsSinceEpoch() + 120.0)); - ensure("left alone a while, it goes", files().empty() && !store.hasOffers()); - ensure("this session's own is never offered", store.write(entry("item:mine", "mine"))); + { + INFO("left alone a while, it goes"); + REQUIRE(files().empty()); + CHECK_FALSE(store.hasOffers()); + } + UNSCOPED_INFO("this session's own is never offered"); + CHECK(store.write(entry("item:mine", "mine"))); store.markOffered(store.list()); - ensure("nor marked", files().size() == 1 && files().front().find(".mine.llsd") != std::string::npos); + { + INFO("nor marked"); + REQUIRE(files().size() == 1); + REQUIRE(files().front().find(".mine.llsd") != std::string::npos); + } // The discarded held to three, then to what two of them weigh. store.limitDiscarded(3, 1024 * 1024); for (int i = 0; i < 5; ++i) { - ensure("set aside", store.setAside(entry("item:d" + std::to_string(i), "discarded " + std::to_string(i)))); + UNSCOPED_INFO("set aside"); + REQUIRE(store.setAside(entry("item:d" + std::to_string(i), "discarded " + std::to_string(i)))); // Apart by more than the millisecond their names say. ms_sleep(5); } std::vector kept = whole(store, store.list()); - ensure_equals("three kept", files("/discarded").size(), size_t(3)); - ensure("the newest", std::any_of(kept.begin(), kept.end(), [](const ALRecoveryEntry& one) { return one.text == "discarded 4"; }) && - std::none_of(kept.begin(), kept.end(), [](const ALRecoveryEntry& one) { return one.text == "discarded 1"; })); + UNSCOPED_INFO("three kept"); + REQUIRE(files("/discarded").size() == size_t(3)); + { + INFO("the newest"); + CHECK(std::any_of(kept.begin(), kept.end(), [](const ALRecoveryEntry& one) { return one.text == "discarded 4"; })); + CHECK(std::none_of(kept.begin(), kept.end(), [](const ALRecoveryEntry& one) { return one.text == "discarded 1"; })); + } std::error_code ec; const uintmax_t one = std::filesystem::file_size(fsyspath(folder + "/discarded/" + files("/discarded").front()), ec); store.limitDiscarded(100, static_cast(one * 2 + one / 2)); store.prune(60.0); - ensure_equals("two, by what they weigh", files("/discarded").size(), size_t(2)); + UNSCOPED_INFO("two, by what they weigh"); + REQUIRE(files("/discarded").size() == size_t(2)); } } diff --git a/indra/llcommon/tests/alregex_test.cpp b/indra/llcommon/tests/alregex_test.cpp index 6cb46241a5..7e5ce01b21 100644 --- a/indra/llcommon/tests/alregex_test.cpp +++ b/indra/llcommon/tests/alregex_test.cpp @@ -27,126 +27,202 @@ #include "../alregex.h" -#include "../test/lltut.h" +#include #include #include #include -namespace tut +namespace { - struct alregex_data + TEST_CASE("alregex: a pattern compiles or says why not and a failed one matches nothing", "[alregex]") { - }; - - typedef test_group alregex_group; - typedef alregex_group::object alregex_object; - alregex_group alregex_instance("alregex"); - - template<> template<> - void alregex_object::test<1>() - { - set_test_name("a pattern compiles or says why not, and one that did not, or none, matches nothing"); const ALRegex good("a(b)c"); - ensure("compiles", good.ok()); - ensure("no error", good.error().empty()); - ensure_equals("the pattern as given", good.pattern(), std::string("a(b)c")); - ensure_equals("its groups", good.groups(), 1); + UNSCOPED_INFO("compiles"); + CHECK(good.ok()); + UNSCOPED_INFO("no error"); + REQUIRE(good.error().empty()); + UNSCOPED_INFO("the pattern as given"); + CHECK(good.pattern() == std::string("a(b)c")); + UNSCOPED_INFO("its groups"); + CHECK(good.groups() == 1); const ALRegex ahead("x(?=y)"); - ensure("RE2 has no lookahead", !ahead.ok()); - ensure("and says so", !ahead.error().empty()); - ensure_equals("the pattern is still the one given", ahead.pattern(), std::string("x(?=y)")); - ensure("it matches nothing", !ahead.search("xy") && !ahead.match("x")); - ensure_equals("and has no groups", ahead.groups(), 0); + UNSCOPED_INFO("RE2 has no lookahead"); + CHECK_FALSE(ahead.ok()); + UNSCOPED_INFO("and says so"); + REQUIRE_FALSE(ahead.error().empty()); + UNSCOPED_INFO("the pattern is still the one given"); + CHECK(ahead.pattern() == std::string("x(?=y)")); + { + INFO("it matches nothing"); + CHECK_FALSE(ahead.search("xy")); + CHECK_FALSE(ahead.match("x")); + } + UNSCOPED_INFO("and has no groups"); + CHECK(ahead.groups() == 0); const ALRegex none; - ensure("none is not ok", !none.ok()); - ensure("and says why", !none.error().empty()); - ensure("and matches nothing, not even nothing", !none.match("") && !none.search("")); + UNSCOPED_INFO("none is not ok"); + CHECK_FALSE(none.ok()); + UNSCOPED_INFO("and says why"); + REQUIRE_FALSE(none.error().empty()); + { + INFO("and matches nothing, not even nothing"); + CHECK_FALSE(none.match("")); + CHECK_FALSE(none.search("")); + } std::string text = "abc"; - ensure_equals("and replaces nothing", none.replaceAll(text, "x"), size_t(0)); - ensure_equals("text as it was", text, std::string("abc")); + UNSCOPED_INFO("and replaces nothing"); + CHECK(none.replaceAll(text, "x") == size_t(0)); + UNSCOPED_INFO("text as it was"); + CHECK(text == std::string("abc")); const ALRegex copy = good; - ensure("a copy matches as the original", copy.ok() && copy.match("abc")); + { + INFO("a copy matches as the original"); + CHECK(copy.ok()); + CHECK(copy.match("abc")); + } } - template<> template<> - void alregex_object::test<2>() + TEST_CASE("alregex: dot takes line breaks and ^ $ match per line unless told; case as asked", "[alregex]") { - set_test_name("as Boost.Regex's Perl syntax: . takes a line break and ^ $ are at every line's ends, unless told otherwise; case as asked"); - ensure("dot takes a break", ALRegex("a.b").match("a\nb")); - ensure("not when told", !ALRegex("a.b", ALRegex::NO_DOT_NL).match("a\nb")); - ensure("$ before a break", ALRegex("x$").search("x\ny")); - ensure("^ after one", ALRegex("^y").search("x\ny")); - ensure("not when told", !ALRegex("x$", ALRegex::NO_MULTILINE).search("x\ny") && !ALRegex("^y", ALRegex::NO_MULTILINE).search("x\ny")); - ensure("the text's own ends either way", ALRegex("^x$", ALRegex::NO_MULTILINE).match("x")); - ensure("case matters", !ALRegex("abc").match("ABC")); - ensure("unless it does not", ALRegex("abc", ALRegex::ICASE).match("aBc")); - ensure("a class too", ALRegex("[a-c]+", ALRegex::ICASE).match("CAB")); + UNSCOPED_INFO("dot takes a break"); + CHECK(ALRegex("a.b").match("a\nb")); + UNSCOPED_INFO("not when told"); + CHECK_FALSE(ALRegex("a.b", ALRegex::NO_DOT_NL).match("a\nb")); + UNSCOPED_INFO("$ before a break"); + CHECK(ALRegex("x$").search("x\ny")); + UNSCOPED_INFO("^ after one"); + CHECK(ALRegex("^y").search("x\ny")); + { + INFO("not when told"); + CHECK_FALSE(ALRegex("x$", ALRegex::NO_MULTILINE).search("x\ny")); + CHECK_FALSE(ALRegex("^y", ALRegex::NO_MULTILINE).search("x\ny")); + } + UNSCOPED_INFO("the text's own ends either way"); + CHECK(ALRegex("^x$", ALRegex::NO_MULTILINE).match("x")); + UNSCOPED_INFO("case matters"); + CHECK_FALSE(ALRegex("abc").match("ABC")); + UNSCOPED_INFO("unless it does not"); + CHECK(ALRegex("abc", ALRegex::ICASE).match("aBc")); + UNSCOPED_INFO("a class too"); + CHECK(ALRegex("[a-c]+", ALRegex::ICASE).match("CAB")); } - template<> template<> - void alregex_object::test<3>() + TEST_CASE("alregex: text is bytes as it was to Boost.Regex unless told it is UTF-8", "[alregex]") { - set_test_name("the text is bytes, as it was to Boost.Regex; told it is UTF-8, a byte that is no part of a character stops . and a negated class"); const std::string bad("a\xE9z"); - ensure("any byte is a character", ALRegex("a.z").match(bad)); - ensure("to a negated class too", ALRegex("a[^x]z").match(bad)); - ensure("and \\S", ALRegex("\\S+").match(bad)); - ensure("not as UTF-8", !ALRegex("a.z", ALRegex::UTF8).match(bad)); + UNSCOPED_INFO("any byte is a character"); + CHECK(ALRegex("a.z").match(bad)); + UNSCOPED_INFO("to a negated class too"); + CHECK(ALRegex("a[^x]z").match(bad)); + UNSCOPED_INFO("and \\S"); + CHECK(ALRegex("\\S+").match(bad)); + UNSCOPED_INFO("not as UTF-8"); + CHECK_FALSE(ALRegex("a.z", ALRegex::UTF8).match(bad)); const std::string good("a\xC3\xA9z"); - ensure("where a character is two bytes, the dot is one of them", !ALRegex("a.z").match(good) && ALRegex("a..z").match(good)); - ensure("as UTF-8, the dot is the character", ALRegex("a.z", ALRegex::UTF8).match(good)); - ensure("a name with an accent finds itself", ALRegex("\\bJos\xC3\xA9 Smith\\b", ALRegex::ICASE).search("hi Jos\xC3\xA9 SMITH!")); + { + INFO("where a character is two bytes, the dot is one of them"); + CHECK_FALSE(ALRegex("a.z").match(good)); + CHECK(ALRegex("a..z").match(good)); + } + UNSCOPED_INFO("as UTF-8, the dot is the character"); + CHECK(ALRegex("a.z", ALRegex::UTF8).match(good)); + UNSCOPED_INFO("a name with an accent finds itself"); + CHECK(ALRegex("\\bJos\xC3\xA9 Smith\\b", ALRegex::ICASE).search("hi Jos\xC3\xA9 SMITH!")); } - template<> template<> - void alregex_object::test<4>() + TEST_CASE("alregex: match takes the whole text and search finds from a place or only there", "[alregex]") { - set_test_name("match is the whole text; search the first match from a place, where what comes before is still read, or only there"); const ALRegex word("\\bfoo\\b"); - ensure("the whole", word.match("foo")); - ensure("not a part", !word.match("foo bar")); - ensure("search finds a part", word.search("a foo b")); + UNSCOPED_INFO("the whole"); + CHECK(word.match("foo")); + UNSCOPED_INFO("not a part"); + CHECK_FALSE(word.match("foo bar")); + UNSCOPED_INFO("search finds a part"); + CHECK(word.search("a foo b")); ALRegexMatch found; - ensure("from a place", word.search("foo foo", &found, 1)); - ensure_equals("the second", found.begin(), size_t(4)); - ensure("what comes before a place is read for \\b", !word.search("xfoo", &found, 1)); - ensure("where it is not a word", word.search(" foo", &found, 1) && found.begin() == 1); - ensure("anchored there, only there", !word.search("a foo", &found, 1, true) && word.search("a foo", &found, 2, true)); - ensure("^ after a break, from a place", ALRegex("^b").search("a\nb", nullptr, 2, true)); - ensure("but not mid-line", !ALRegex("^b").search("ab", nullptr, 1, true)); - ensure("past the end is nothing", !word.search("foo", &found, 4)); - ensure("an empty text", ALRegex("x*").match("") && ALRegex("x*").match(std::string_view())); + UNSCOPED_INFO("from a place"); + REQUIRE(word.search("foo foo", &found, 1)); + UNSCOPED_INFO("the second"); + CHECK(found.begin() == size_t(4)); + UNSCOPED_INFO("what comes before a place is read for \\b"); + CHECK_FALSE(word.search("xfoo", &found, 1)); + { + INFO("where it is not a word"); + CHECK(word.search(" foo", &found, 1)); + CHECK(found.begin() == 1); + } + { + INFO("anchored there, only there"); + CHECK_FALSE(word.search("a foo", &found, 1, true)); + CHECK(word.search("a foo", &found, 2, true)); + } + UNSCOPED_INFO("^ after a break, from a place"); + CHECK(ALRegex("^b").search("a\nb", nullptr, 2, true)); + UNSCOPED_INFO("but not mid-line"); + CHECK_FALSE(ALRegex("^b").search("ab", nullptr, 1, true)); + UNSCOPED_INFO("past the end is nothing"); + CHECK_FALSE(word.search("foo", &found, 4)); + { + INFO("an empty text"); + CHECK(ALRegex("x*").match("")); + CHECK(ALRegex("x*").match(std::string_view())); + } } - template<> template<> - void alregex_object::test<5>() + TEST_CASE("alregex: groups say where each is and whether it took part however many", "[alregex]") { - set_test_name("groups: where each is, whether it took part, and as many as a pattern has, or as asked for"); const ALRegex date("([0-9]{1,2})/([0-9]{1,2})/([0-9]{4})( ([AP]M))?"); ALRegexMatch found; - ensure("matches", date.search("on 12/25/2026 then", &found)); - ensure_equals("four groups and the whole", found.size(), size_t(6)); - ensure_equals("the whole", found.str(), std::string("12/25/2026")); - ensure_equals("where", found.begin(), size_t(3)); - ensure_equals("to", found.end(), size_t(13)); - ensure_equals("the month", found.str(1), std::string("12")); - ensure_equals("the year's place", found.begin(3), size_t(9)); - ensure_equals("its length", found.length(3), size_t(4)); - ensure("an optional group that was not there", !found.matched(4) && !found.matched(5)); - ensure_equals("is no place", found.begin(4), ALRegexMatch::npos); - ensure("and nothing", found.str(4).empty() && found.length(4) == 0); - ensure("past the groups is nothing", !found.matched(9)); + UNSCOPED_INFO("matches"); + REQUIRE(date.search("on 12/25/2026 then", &found)); + UNSCOPED_INFO("four groups and the whole"); + REQUIRE(found.size() == size_t(6)); + UNSCOPED_INFO("the whole"); + CHECK(found.str() == std::string("12/25/2026")); + UNSCOPED_INFO("where"); + CHECK(found.begin() == size_t(3)); + UNSCOPED_INFO("to"); + CHECK(found.end() == size_t(13)); + UNSCOPED_INFO("the month"); + CHECK(found.str(1) == std::string("12")); + UNSCOPED_INFO("the year's place"); + CHECK(found.begin(3) == size_t(9)); + UNSCOPED_INFO("its length"); + REQUIRE(found.length(3) == size_t(4)); + { + INFO("an optional group that was not there"); + CHECK_FALSE(found.matched(4)); + CHECK_FALSE(found.matched(5)); + } + UNSCOPED_INFO("is no place"); + CHECK(found.begin(4) == ALRegexMatch::npos); + { + INFO("and nothing"); + REQUIRE(found.str(4).empty()); + REQUIRE(found.length(4) == 0); + } + UNSCOPED_INFO("past the groups is nothing"); + CHECK_FALSE(found.matched(9)); - ensure("the whole only, as asked", date.search("12/25/2026", &found, 0, false, 0)); - ensure_equals("one", found.size(), size_t(1)); - ensure("and the groups not", !found.matched(1)); + UNSCOPED_INFO("the whole only, as asked"); + CHECK(date.search("12/25/2026", &found, 0, false, 0)); + UNSCOPED_INFO("one"); + REQUIRE(found.size() == size_t(1)); + UNSCOPED_INFO("and the groups not"); + CHECK_FALSE(found.matched(1)); - ensure("an empty group took part", ALRegex("a()b").match("ab", &found) && found.matched(1) && found.length(1) == 0 && found.begin(1) == 1); + { + INFO("an empty group took part"); + CHECK(ALRegex("a()b").match("ab", &found)); + CHECK(found.matched(1)); + REQUIRE(found.length(1) == 0); + CHECK(found.begin(1) == 1); + } // More groups than are kept in place. std::string pattern, text; @@ -156,113 +232,184 @@ namespace tut text += static_cast('a' + i % 26); } const ALRegex many(pattern); - ensure_equals("thirty groups", many.groups(), 30); - ensure("matches", many.match(text, &found)); - ensure_equals("all of them", found.size(), size_t(31)); - ensure_equals("the last", found.str(30), std::string(1, text[29])); - ensure_equals("and where", found.begin(30), size_t(29)); + UNSCOPED_INFO("thirty groups"); + CHECK(many.groups() == 30); + UNSCOPED_INFO("matches"); + CHECK(many.match(text, &found)); + UNSCOPED_INFO("all of them"); + REQUIRE(found.size() == size_t(31)); + UNSCOPED_INFO("the last"); + CHECK(found.str(30) == std::string(1, text[29])); + UNSCOPED_INFO("and where"); + CHECK(found.begin(30) == size_t(29)); - ensure("a miss leaves nothing", !date.search("none", &found) && found.size() == 0 && !found.matched()); + { + INFO("a miss leaves nothing"); + CHECK_FALSE(date.search("none", &found)); + REQUIRE(found.size() == 0); + CHECK_FALSE(found.matched()); + } } - template<> template<> - void alregex_object::test<6>() + TEST_CASE("alregex: forEach and replaceEach visit every match and replaceAll is literal", "[alregex]") { - set_test_name("every match in turn, stopped when asked; each replaced by what is made of it, or by a literal as it is written"); const ALRegex num("[0-9]+"); std::vector seen; - ensure_equals("three", num.forEach("a1 b22 c333", [&](const ALRegexMatch& m) { seen.push_back(m.str()); return true; }), size_t(3)); - ensure("in order", seen == std::vector{ "1", "22", "333" }); + UNSCOPED_INFO("three"); + CHECK(num.forEach("a1 b22 c333", [&](const ALRegexMatch& m) { seen.push_back(m.str()); return true; }) == size_t(3)); + UNSCOPED_INFO("in order"); + CHECK(seen == std::vector{ "1", "22", "333" }); seen.clear(); num.forEach("a1 b22 c333", [&](const ALRegexMatch& m) { seen.push_back(m.str()); return seen.size() < 2; }); - ensure_equals("stopped at two", seen.size(), size_t(2)); + UNSCOPED_INFO("stopped at two"); + REQUIRE(seen.size() == size_t(2)); std::string text = "a1 b22 c333"; - ensure_equals("each", num.replaceEach(text, [](const ALRegexMatch& m) { return "<" + m.str() + ">"; }), size_t(3)); - ensure_equals("made over", text, std::string("a<1> b<22> c<333>")); + UNSCOPED_INFO("each"); + CHECK(num.replaceEach(text, [](const ALRegexMatch& m) { return "<" + m.str() + ">"; }) == size_t(3)); + UNSCOPED_INFO("made over"); + CHECK(text == std::string("a<1> b<22> c<333>")); text = "a1 b22"; - ensure_equals("literal", num.replaceAll(text, "\\1$&"), size_t(2)); - ensure_equals("as it is written", text, std::string("a\\1$& b\\1$&")); + UNSCOPED_INFO("literal"); + CHECK(num.replaceAll(text, "\\1$&") == size_t(2)); + UNSCOPED_INFO("as it is written"); + CHECK(text == std::string("a\\1$& b\\1$&")); text = "none here"; - ensure_equals("no match, no change", num.replaceEach(text, [](const ALRegexMatch&) { return std::string("x"); }), size_t(0)); - ensure_equals("as it was", text, std::string("none here")); + UNSCOPED_INFO("no match, no change"); + CHECK(num.replaceEach(text, [](const ALRegexMatch&) { return std::string("x"); }) == size_t(0)); + UNSCOPED_INFO("as it was"); + CHECK(text == std::string("none here")); text = "ab"; - ensure_equals("empty matches: one at each place", ALRegex("x*").replaceEach(text, [](const ALRegexMatch&) { return std::string("-"); }), size_t(3)); - ensure_equals("and the text between", text, std::string("-a-b-")); + UNSCOPED_INFO("empty matches: one at each place"); + CHECK(ALRegex("x*").replaceEach(text, [](const ALRegexMatch&) { return std::string("-"); }) == size_t(3)); + UNSCOPED_INFO("and the text between"); + CHECK(text == std::string("-a-b-")); size_t count = ALRegex("x*").forEach("ab", [](const ALRegexMatch&) { return true; }); - ensure_equals("and in turn", count, size_t(3)); + UNSCOPED_INFO("and in turn"); + CHECK(count == size_t(3)); } - template<> template<> - void alregex_object::test<7>() + TEST_CASE("alregex: escape makes a text match itself and nothing else whatever it holds", "[alregex]") { - set_test_name("a text escaped matches itself and nothing else, whatever it holds"); const std::string name = "Bob (Bobby) [x].* $1 \\n ^|?+{2}"; const ALRegex exact("\\b" + ALRegex::escape(name), ALRegex::ICASE); - ensure("compiles", exact.ok()); + UNSCOPED_INFO("compiles"); + CHECK(exact.ok()); // The match views the text, which outlives it here. const std::string text = "hi " + name + " there"; ALRegexMatch found; - ensure("finds it", exact.search(text, &found)); - ensure_equals("all of it", found.str(), name); - ensure("and not what it would mean", !exact.search("hi Bob Bobby")); + UNSCOPED_INFO("finds it"); + REQUIRE(exact.search(text, &found)); + UNSCOPED_INFO("all of it"); + CHECK(found.str() == name); + UNSCOPED_INFO("and not what it would mean"); + CHECK_FALSE(exact.search("hi Bob Bobby")); } - template<> template<> - void alregex_object::test<8>() + TEST_CASE("alregex: firstByteRange gives the bytes a match can begin with or none", "[alregex]") { - set_test_name("the bytes a match can begin with, before and after a word, and none where it could be any"); U8 lo = 0, hi = 0; - ensure("a hexadecimal number", ALRegex("0[xX][0-9a-fA-F]+").firstByteRange(lo, hi)); - ensure("begins with 0", lo == '0' && hi <= '1'); - ensure("a number or a point", ALRegex("[0-9]+|\\.[0-9]+").firstByteRange(lo, hi)); - ensure("from the point to 9", lo == '.' && hi >= '9' && hi <= ':'); - ensure("anything is no range", !ALRegex(".*x").firstByteRange(lo, hi)); - ensure("nor is a pattern that did not compile", !ALRegex("(?=x)").firstByteRange(lo, hi)); + UNSCOPED_INFO("a hexadecimal number"); + CHECK(ALRegex("0[xX][0-9a-fA-F]+").firstByteRange(lo, hi)); + { + INFO("begins with 0"); + CHECK(lo == '0'); + CHECK(hi <= '1'); + } + UNSCOPED_INFO("a number or a point"); + CHECK(ALRegex("[0-9]+|\\.[0-9]+").firstByteRange(lo, hi)); + { + INFO("from the point to 9"); + CHECK(lo == '.'); + CHECK(hi >= '9'); + CHECK(hi <= ':'); + } + UNSCOPED_INFO("anything is no range"); + CHECK_FALSE(ALRegex(".*x").firstByteRange(lo, hi)); + UNSCOPED_INFO("nor is a pattern that did not compile"); + CHECK_FALSE(ALRegex("(?=x)").firstByteRange(lo, hi)); // After a word, \b is before what is not one. - ensure("a boundary before a non-letter", ALRegex("\\b[^a-z]x").firstByteRange(lo, hi) == false || (lo <= '!' && hi >= '_')); - ensure("a label", ALRegex("@[A-Za-z_][A-Za-z0-9_]*").firstByteRange(lo, hi)); - ensure("begins with @", lo == '@' && hi <= 'A'); + UNSCOPED_INFO("a boundary before a non-letter"); + CHECK((ALRegex("\\b[^a-z]x").firstByteRange(lo, hi) == false || (lo <= '!' && hi >= '_'))); + UNSCOPED_INFO("a label"); + CHECK(ALRegex("@[A-Za-z_][A-Za-z0-9_]*").firstByteRange(lo, hi)); + { + INFO("begins with @"); + CHECK(lo == '@'); + CHECK(hi <= 'A'); + } } - template<> template<> - void alregex_object::test<9>() + TEST_CASE("alregex: a regex streams as its pattern and keeps its flags", "[alregex]") { - set_test_name("a regex written out is its pattern, and keeps its flags"); std::ostringstream out; out << ALRegex("a|b", ALRegex::ICASE); - ensure_equals("the pattern", out.str(), std::string("a|b")); - ensure_equals("the flags", ALRegex("a", ALRegex::ICASE | ALRegex::NO_DOT_NL).flags(), U32(ALRegex::ICASE | ALRegex::NO_DOT_NL)); + UNSCOPED_INFO("the pattern"); + CHECK(out.str() == std::string("a|b")); + UNSCOPED_INFO("the flags"); + CHECK(ALRegex("a", ALRegex::ICASE | ALRegex::NO_DOT_NL).flags() == U32(ALRegex::ICASE | ALRegex::NO_DOT_NL)); } - template<> template<> - void alregex_object::test<10>() + TEST_CASE("alregex: ALRegexSet says which of its patterns match a text in one pass", "[alregex]") { - set_test_name("a set says which of its patterns match anywhere in a text, in one pass, read as a regex reads them"); ALRegexSet set(ALRegex::ICASE); - ensure("an empty set is not made", !set.ok() && !set.compile()); - ensure_equals("the first", set.add("https?://\\S+"), 0); - ensure_equals("the second", set.add("[0-9]+"), 1); - ensure_equals("the third", set.add("^b$"), 2); + { + INFO("an empty set is not made"); + CHECK_FALSE(set.ok()); + CHECK_FALSE(set.compile()); + } + UNSCOPED_INFO("the first"); + CHECK(set.add("https?://\\S+") == 0); + UNSCOPED_INFO("the second"); + CHECK(set.add("[0-9]+") == 1); + UNSCOPED_INFO("the third"); + CHECK(set.add("^b$") == 2); std::string error; - ensure_equals("one that does not compile is not added", set.add("x(?=y)", &error), -1); - ensure("and says why", !error.empty()); - ensure_equals("three", set.size(), size_t(3)); + UNSCOPED_INFO("one that does not compile is not added"); + CHECK(set.add("x(?=y)", &error) == -1); + UNSCOPED_INFO("and says why"); + REQUIRE_FALSE(error.empty()); + UNSCOPED_INFO("three"); + REQUIRE(set.size() == size_t(3)); std::vector hits; - ensure("not made, it cannot say", !set.match("123", hits) && hits.empty()); + { + INFO("not made, it cannot say"); + REQUIRE_FALSE(set.match("123", hits)); + REQUIRE(hits.empty()); + } - ensure("made", set.compile() && set.ok()); - ensure("nothing is added after", set.add("z") == -1); - ensure("a pass", set.match("see HTTP://x.com at 10", hits)); + { + INFO("made"); + CHECK(set.compile()); + CHECK(set.ok()); + } + UNSCOPED_INFO("nothing is added after"); + CHECK(set.add("z") == -1); + UNSCOPED_INFO("a pass"); + CHECK(set.match("see HTTP://x.com at 10", hits)); std::sort(hits.begin(), hits.end()); - ensure("the Url in any case, and the number", hits == std::vector{ 0, 1 }); - ensure("^ and $ are a line's, as a regex's are", set.match("a\nb\nc", hits) && hits == std::vector{ 2 }); - ensure("none, said", set.match("nothing", hits) && hits.empty()); + UNSCOPED_INFO("the Url in any case, and the number"); + CHECK(hits == std::vector{ 0, 1 }); + { + INFO("^ and $ are a line's, as a regex's are"); + CHECK(set.match("a\nb\nc", hits)); + CHECK(hits == std::vector{ 2 }); + } + { + INFO("none, said"); + CHECK(set.match("nothing", hits)); + REQUIRE(hits.empty()); + } ALRegexSet moved = std::move(set); - ensure("moved, it goes on", moved.ok() && moved.match("7", hits) && hits == std::vector{ 1 }); + { + INFO("moved, it goes on"); + CHECK(moved.ok()); + CHECK(moved.match("7", hits)); + CHECK(hits == std::vector{ 1 }); + } } } diff --git a/indra/llcommon/tests/alsavehistory_test.cpp b/indra/llcommon/tests/alsavehistory_test.cpp index e649af4b5a..06c8668a6c 100644 --- a/indra/llcommon/tests/alsavehistory_test.cpp +++ b/indra/llcommon/tests/alsavehistory_test.cpp @@ -30,12 +30,12 @@ #include "fsyspath.h" #include "llfile.h" -#include "../test/lltut.h" +#include #include #include -namespace tut +namespace { struct alsavehistory_data { @@ -75,56 +75,71 @@ namespace tut std::string out; for (ALSavedText one : history.list(key)) { - ensure("read whole", history.load(one)); + UNSCOPED_INFO("read whole"); + REQUIRE(history.load(one)); out += (out.empty() ? "" : "|") + one.text; } return out; } }; - typedef test_group alsavehistory_group; - typedef alsavehistory_group::object alsavehistory_object; - tut::alsavehistory_group alsavehistory_test("ALSaveHistory"); - - template<> template<> - void alsavehistory_object::test<1>() + TEST_CASE_METHOD(alsavehistory_data, "ALSaveHistory: saves list newest first per key and the text is read on asking", "[ALSaveHistory]") { - set_test_name("kept, and listed newest first as far as a listing reads; the text read on asking; each key its own"); ALSaveHistory history(folder); // Within the age: dated from now. const F64 now = LLDate::now().secondsSinceEpoch(); - ensure("kept", history.keep(saved("item:a", "one", now - 60.0))); - ensure("kept", history.keep(saved("item:a", "two", now - 30.0))); - ensure("another's", history.keep(saved("item:b", "other", now - 45.0))); + UNSCOPED_INFO("kept"); + CHECK(history.keep(saved("item:a", "one", now - 60.0))); + UNSCOPED_INFO("kept"); + CHECK(history.keep(saved("item:a", "two", now - 30.0))); + UNSCOPED_INFO("another's"); + CHECK(history.keep(saved("item:b", "other", now - 45.0))); const std::vector listed = history.list("item:a"); - ensure_equals("two of its own", listed.size(), 2U); - ensure("newest first", listed[0].when.secondsSinceEpoch() > listed[1].when.secondsSinceEpoch()); - ensure("what a listing reads", listed[0].name == "Door" && listed[0].objectName == "House" && listed[0].region == "Ahern" && - listed[0].asset == LLUUID::generateNewID("two") && listed[0].bytes == 3); - ensure("and not the text", !listed[0].whole && listed[0].text.empty()); - ensure_equals("read on asking", texts(history, "item:a"), std::string("two|one")); - ensure_equals("the other key's", texts(history, "item:b"), std::string("other")); - ensure("none for a key never saved", history.list("item:c").empty()); + UNSCOPED_INFO("two of its own"); + REQUIRE(listed.size() == 2U); + UNSCOPED_INFO("newest first"); + CHECK(listed[0].when.secondsSinceEpoch() > listed[1].when.secondsSinceEpoch()); + { + INFO("what a listing reads"); + CHECK(listed[0].name == "Door"); + CHECK(listed[0].objectName == "House"); + CHECK(listed[0].region == "Ahern"); + CHECK(listed[0].asset == LLUUID::generateNewID("two")); + CHECK(listed[0].bytes == 3); + } + { + INFO("and not the text"); + CHECK_FALSE(listed[0].whole); + REQUIRE(listed[0].text.empty()); + } + UNSCOPED_INFO("read on asking"); + CHECK(texts(history, "item:a") == std::string("two|one")); + UNSCOPED_INFO("the other key's"); + CHECK(texts(history, "item:b") == std::string("other")); + UNSCOPED_INFO("none for a key never saved"); + REQUIRE(history.list("item:c").empty()); } - template<> template<> - void alsavehistory_object::test<2>() + TEST_CASE_METHOD(alsavehistory_data, "ALSaveHistory: the text kept last is not kept again but a change back is", "[ALSaveHistory]") { - set_test_name("the text kept last is not kept again; one kept before that is, since it is a change back"); ALSaveHistory history(folder); const F64 now = LLDate::now().secondsSinceEpoch(); - ensure("kept", history.keep(saved("item:a", "one", now - 60.0))); - ensure("not again", !history.keep(saved("item:a", "one", now - 50.0))); - ensure("kept", history.keep(saved("item:a", "two", now - 40.0))); - ensure("back to one is a change", history.keep(saved("item:a", "one", now - 30.0))); - ensure_equals("three", texts(history, "item:a"), std::string("one|two|one")); - ensure("no key, nothing", !history.keep(saved("", "x", now))); + UNSCOPED_INFO("kept"); + CHECK(history.keep(saved("item:a", "one", now - 60.0))); + UNSCOPED_INFO("not again"); + CHECK_FALSE(history.keep(saved("item:a", "one", now - 50.0))); + UNSCOPED_INFO("kept"); + CHECK(history.keep(saved("item:a", "two", now - 40.0))); + UNSCOPED_INFO("back to one is a change"); + CHECK(history.keep(saved("item:a", "one", now - 30.0))); + UNSCOPED_INFO("three"); + CHECK(texts(history, "item:a") == std::string("one|two|one")); + UNSCOPED_INFO("no key, nothing"); + CHECK_FALSE(history.keep(saved("", "x", now))); } - template<> template<> - void alsavehistory_object::test<3>() + TEST_CASE_METHOD(alsavehistory_data, "ALSaveHistory: the oldest go past so many for a key or past the age at a prune", "[ALSaveHistory]") { - set_test_name("past so many for a key, the oldest go as the next is kept; past the age, as it is pruned"); ALSaveHistory history(folder); history.limit(3, ALSaveHistory::MAX_AGE, ALSaveHistory::MAX_BYTES); const F64 now = LLDate::now().secondsSinceEpoch(); @@ -132,25 +147,28 @@ namespace tut { history.keep(saved("item:a", std::to_string(i), now - 100.0 + i)); } - ensure_equals("the newest three", texts(history, "item:a"), std::string("5|4|3")); + UNSCOPED_INFO("the newest three"); + CHECK(texts(history, "item:a") == std::string("5|4|3")); ALSaveHistory aged(folder + "/aged"); aged.limit(50, 10.0 * DAY, ALSaveHistory::MAX_BYTES); aged.keep(saved("item:a", "old", THEN)); aged.keep(saved("item:a", "new", THEN + 8.0 * DAY)); aged.prune(LLDate(THEN + 9.0 * DAY)); - ensure_equals("both within the age", texts(aged, "item:a"), std::string("new|old")); + UNSCOPED_INFO("both within the age"); + CHECK(texts(aged, "item:a") == std::string("new|old")); aged.prune(LLDate(THEN + 12.0 * DAY)); - ensure_equals("the old one past it", texts(aged, "item:a"), std::string("new")); + UNSCOPED_INFO("the old one past it"); + CHECK(texts(aged, "item:a") == std::string("new")); aged.prune(LLDate(THEN + 30.0 * DAY)); - ensure("and then the other", aged.list("item:a").empty()); - ensure("its folder gone with it", std::filesystem::is_empty(fsyspath(folder + "/aged"))); + UNSCOPED_INFO("and then the other"); + REQUIRE(aged.list("item:a").empty()); + UNSCOPED_INFO("its folder gone with it"); + CHECK(std::filesystem::is_empty(fsyspath(folder + "/aged"))); } - template<> template<> - void alsavehistory_object::test<4>() + TEST_CASE_METHOD(alsavehistory_data, "ALSaveHistory: past so much in all the oldest of any key go first", "[ALSaveHistory]") { - set_test_name("past so much in all, the oldest of any key go first"); ALSaveHistory history(folder); const F64 now = LLDate::now().secondsSinceEpoch(); history.keep(saved("item:a", "aaaaaaaaaa", now - 40.0)); @@ -161,34 +179,38 @@ namespace tut const size_t room = size(history.list("item:a").front()) + size(history.list("item:b").front()); history.limit(50, 1e12, room); history.prune(); - ensure_equals("the oldest gone", texts(history, "item:a"), std::string("cccccccccc")); - ensure_equals("another key's newer one kept", texts(history, "item:b"), std::string("bbbbbbbbbb")); + UNSCOPED_INFO("the oldest gone"); + CHECK(texts(history, "item:a") == std::string("cccccccccc")); + UNSCOPED_INFO("another key's newer one kept"); + CHECK(texts(history, "item:b") == std::string("bbbbbbbbbb")); } - template<> template<> - void alsavehistory_object::test<5>() + TEST_CASE_METHOD(alsavehistory_data, "ALSaveHistory: rekey moves a key's saves behind another's under the new key", "[ALSaveHistory]") { - set_test_name("a key's saves moved to another, behind what that has, saying the new key; the old one empty"); ALSaveHistory history(folder); const F64 now = LLDate::now().secondsSinceEpoch(); history.keep(saved("task:p:old", "one", now - 60.0)); history.keep(saved("task:p:old", "two", now - 50.0)); history.keep(saved("task:p:new", "three", now - 40.0)); - ensure("moved", history.rekey("task:p:old", "task:p:new")); - ensure("none left", history.list("task:p:old").empty()); - ensure_equals("all under the new, in time's order", texts(history, "task:p:new"), std::string("three|two|one")); + UNSCOPED_INFO("moved"); + CHECK(history.rekey("task:p:old", "task:p:new")); + UNSCOPED_INFO("none left"); + REQUIRE(history.list("task:p:old").empty()); + UNSCOPED_INFO("all under the new, in time's order"); + CHECK(texts(history, "task:p:new") == std::string("three|two|one")); for (const ALSavedText& one : history.list("task:p:new")) { - ensure_equals("saying the new key", one.key, std::string("task:p:new")); + UNSCOPED_INFO("saying the new key"); + REQUIRE(one.key == std::string("task:p:new")); } - ensure("nothing to move", !history.rekey("task:p:gone", "task:p:new")); - ensure("not to itself", !history.rekey("task:p:new", "task:p:new")); + UNSCOPED_INFO("nothing to move"); + CHECK_FALSE(history.rekey("task:p:gone", "task:p:new")); + UNSCOPED_INFO("not to itself"); + CHECK_FALSE(history.rekey("task:p:new", "task:p:new")); } - template<> template<> - void alsavehistory_object::test<6>() + TEST_CASE_METHOD(alsavehistory_data, "ALSaveHistory: an unreadable file is passed over and a half write is swept", "[ALSaveHistory]") { - set_test_name("a file that cannot be read is passed over, and what a write cut short is swept"); ALSaveHistory history(folder); const F64 now = LLDate::now().secondsSinceEpoch(); history.keep(saved("item:a", "one", now - 60.0)); @@ -202,39 +224,44 @@ namespace tut llofstream half(dir + "2.llsd" + std::string(ALFileWrite::BESIDE)); half << "{'key':'item:a'"; } - ensure_equals("the one readable", history.list("item:a").size(), 1U); + UNSCOPED_INFO("the one readable"); + REQUIRE(history.list("item:a").size() == 1U); history.prune(); - ensure("the half-written swept", !LLFile::isfile(dir + "2.llsd" + std::string(ALFileWrite::BESIDE))); - ensure_equals("the save still there", texts(history, "item:a"), std::string("one")); + UNSCOPED_INFO("the half-written swept"); + CHECK_FALSE(LLFile::isfile(dir + "2.llsd" + std::string(ALFileWrite::BESIDE))); + UNSCOPED_INFO("the save still there"); + CHECK(texts(history, "item:a") == std::string("one")); } - template<> template<> - void alsavehistory_object::test<7>() + TEST_CASE_METHOD(alsavehistory_data, "ALSaveHistory: count counts a key's saves by file name and not half writes", "[ALSaveHistory]") { - set_test_name("a key's saves counted by their files' names, each key its own; what is no save of the name not counted"); ALSaveHistory history(folder); const F64 now = LLDate::now().secondsSinceEpoch(); - ensure_equals("none for a key never saved", history.count("item:a"), 0U); - ensure_equals("none for no key", history.count(""), 0U); + UNSCOPED_INFO("none for a key never saved"); + REQUIRE(history.count("item:a") == 0U); + UNSCOPED_INFO("none for no key"); + REQUIRE(history.count("") == 0U); history.keep(saved("item:a", "one", now - 60.0)); history.keep(saved("item:a", "two", now - 50.0)); history.keep(saved("item:b", "other", now - 40.0)); - ensure_equals("as many as listed", history.count("item:a"), history.list("item:a").size()); - ensure_equals("two", history.count("item:a"), 2U); - ensure_equals("the other key's", history.count("item:b"), 1U); + UNSCOPED_INFO("as many as listed"); + REQUIRE(history.count("item:a") == history.list("item:a").size()); + UNSCOPED_INFO("two"); + REQUIRE(history.count("item:a") == 2U); + UNSCOPED_INFO("the other key's"); + REQUIRE(history.count("item:b") == 1U); const std::string kept = history.list("item:a").front().path; const std::string dir = kept.substr(0, kept.find_last_of('/') + 1); { llofstream half(dir + "3.llsd" + std::string(ALFileWrite::BESIDE)); half << "{'key':'item:a'"; } - ensure_equals("what a write cut short is none", history.count("item:a"), 2U); + UNSCOPED_INFO("what a write cut short is none"); + REQUIRE(history.count("item:a") == 2U); } - template<> template<> - void alsavehistory_object::test<8>() + TEST_CASE_METHOD(alsavehistory_data, "ALSaveHistory: holds finds a text by its asset or its text among a key's newest", "[ALSaveHistory]") { - set_test_name("a text kept already, by the asset its save made or by the text, among a key's newest; not past them, nor another key's"); ALSaveHistory history(folder); const F64 now = LLDate::now().secondsSinceEpoch(); history.keep(saved("item:a", "one", now - 60.0)); @@ -242,19 +269,30 @@ namespace tut history.keep(saved("item:a", "three", now - 40.0)); history.keep(saved("item:b", "other", now - 30.0)); const LLUUID elsewhere = LLUUID::generateNewID("elsewhere"); - ensure("by the asset its save made, whatever the text", history.holds("item:a", LLUUID::generateNewID("two"), "not it", 10)); + UNSCOPED_INFO("by the asset its save made, whatever the text"); + CHECK(history.holds("item:a", LLUUID::generateNewID("two"), "not it", 10)); // The same text sent again made another asset, and was not kept // again: found by the text. - ensure("by the text, under an asset no save made", history.holds("item:a", elsewhere, "one", 10)); - ensure("a text of the same length that is not one", !history.holds("item:a", elsewhere, "owe", 10)); - ensure("nor one never kept", !history.holds("item:a", elsewhere, "four", 10)); - ensure("nor without an asset", !history.holds("item:a", LLUUID::null, "four", 10)); + UNSCOPED_INFO("by the text, under an asset no save made"); + CHECK(history.holds("item:a", elsewhere, "one", 10)); + UNSCOPED_INFO("a text of the same length that is not one"); + CHECK_FALSE(history.holds("item:a", elsewhere, "owe", 10)); + UNSCOPED_INFO("nor one never kept"); + CHECK_FALSE(history.holds("item:a", elsewhere, "four", 10)); + UNSCOPED_INFO("nor without an asset"); + CHECK_FALSE(history.holds("item:a", LLUUID::null, "four", 10)); // Among the newest two only: "one" is the third. - ensure("the newest", history.holds("item:a", elsewhere, "three", 2)); - ensure("past the newest, by the text", !history.holds("item:a", elsewhere, "one", 2)); - ensure("past the newest, by the asset", !history.holds("item:a", LLUUID::generateNewID("one"), "x", 2)); - ensure("another key's is not this one's", !history.holds("item:a", LLUUID::generateNewID("other"), "other", 10)); - ensure("nothing for a key never saved", !history.holds("item:c", LLUUID::generateNewID("one"), "one", 10)); - ensure("nor for no key", !history.holds("", LLUUID::generateNewID("one"), "one", 10)); + UNSCOPED_INFO("the newest"); + CHECK(history.holds("item:a", elsewhere, "three", 2)); + UNSCOPED_INFO("past the newest, by the text"); + CHECK_FALSE(history.holds("item:a", elsewhere, "one", 2)); + UNSCOPED_INFO("past the newest, by the asset"); + CHECK_FALSE(history.holds("item:a", LLUUID::generateNewID("one"), "x", 2)); + UNSCOPED_INFO("another key's is not this one's"); + CHECK_FALSE(history.holds("item:a", LLUUID::generateNewID("other"), "other", 10)); + UNSCOPED_INFO("nothing for a key never saved"); + CHECK_FALSE(history.holds("item:c", LLUUID::generateNewID("one"), "one", 10)); + UNSCOPED_INFO("nor for no key"); + CHECK_FALSE(history.holds("", LLUUID::generateNewID("one"), "one", 10)); } } diff --git a/indra/llcommon/tests/alserialworker_test.cpp b/indra/llcommon/tests/alserialworker_test.cpp index 0d6ba7aab1..0f75a10e97 100644 --- a/indra/llcommon/tests/alserialworker_test.cpp +++ b/indra/llcommon/tests/alserialworker_test.cpp @@ -30,7 +30,7 @@ #include "../llevents.h" #include "../llsdutil.h" -#include "../test/lltut.h" +#include #include #include @@ -38,7 +38,7 @@ #include #include -namespace tut +namespace { struct alserialworker_data { @@ -71,31 +71,31 @@ namespace tut return false; } }; - typedef test_group alserialworker_group; - typedef alserialworker_group::object alserialworker_object; - alserialworker_group alserialworker_instance("alserialworker"); - template<> template<> - void alserialworker_object::test<1>() + TEST_CASE_METHOD(alserialworker_data, "alserialworker: jobs run in order on one thread of their own", "[alserialworker]") { - set_test_name("jobs run one after another, on a thread of their own"); ALSerialWorker worker("alserialworker_test_1"); for (int n = 0; n < 50; ++n) { - ensure("taken", worker.post([this, n] { note(n); })); + UNSCOPED_INFO("taken"); + REQUIRE(worker.post([this, n] { note(n); })); } - ensure("all done", waitFor(50)); + UNSCOPED_INFO("all done"); + CHECK(waitFor(50)); for (int n = 0; n < 50; ++n) { - ensure("in the order posted", done[n] == n); - ensure("on one thread, not this one", where[n] == where[0] && where[n] != std::this_thread::get_id()); + UNSCOPED_INFO("in the order posted"); + REQUIRE(done[n] == n); + { + INFO("on one thread, not this one"); + REQUIRE(where[n] == where[0]); + REQUIRE(where[n] != std::this_thread::get_id()); + } } } - template<> template<> - void alserialworker_object::test<2>() + TEST_CASE_METHOD(alserialworker_data, "alserialworker: close stops the running job and drops waiting and later ones", "[alserialworker]") { - set_test_name("closed: the running job is stopped by the owner's stop, what waits is passed over, and a job after is refused"); std::atomic running{ false }; std::atomic stopped{ false }; ALSerialWorker worker("alserialworker_test_2", [&stopped] { stopped = true; }); @@ -116,27 +116,41 @@ namespace tut { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - ensure("the first is running", running.load()); + UNSCOPED_INFO("the first is running"); + CHECK(running.load()); worker.close(); - ensure("told to stop", stopped.load() && worker.closing()); - ensure_equals("it returned, and nothing waiting ran", done.size(), size_t(1)); + { + INFO("told to stop"); + CHECK(stopped.load()); + CHECK(worker.closing()); + } + UNSCOPED_INFO("it returned, and nothing waiting ran"); + REQUIRE(done.size() == size_t(1)); bool ran = false; - ensure("refused after", !worker.post([&ran] { ran = true; }) && !ran); + { + INFO("refused after"); + CHECK_FALSE(worker.post([&ran] { ran = true; })); + CHECK_FALSE(ran); + } worker.close(); - ensure("closed twice is closed", worker.closing()); + UNSCOPED_INFO("closed twice is closed"); + CHECK(worker.closing()); } - template<> template<> - void alserialworker_object::test<3>() + TEST_CASE_METHOD(alserialworker_data, "alserialworker: the viewer starting to quit closes it at once", "[alserialworker]") { - set_test_name("the viewer starting to quit closes it at once"); ALSerialWorker worker("alserialworker_test_3"); - ensure("taken", worker.post([this] { note(1); })); - ensure("done", waitFor(1)); + UNSCOPED_INFO("taken"); + CHECK(worker.post([this] { note(1); })); + UNSCOPED_INFO("done"); + CHECK(waitFor(1)); LLEventPumps::instance().obtain("LLApp").post(llsd::map("status", "running")); - ensure("running is nothing", !worker.closing()); + UNSCOPED_INFO("running is nothing"); + CHECK_FALSE(worker.closing()); LLEventPumps::instance().obtain("LLApp").post(llsd::map("status", "quitting")); - ensure("closed", worker.closing()); - ensure("refused", !worker.post([this] { note(2); })); + UNSCOPED_INFO("closed"); + CHECK(worker.closing()); + UNSCOPED_INFO("refused"); + CHECK_FALSE(worker.post([this] { note(2); })); } } diff --git a/indra/llcommon/tests/alsimd_test.cpp b/indra/llcommon/tests/alsimd_test.cpp index 15629e19d4..bea6c47453 100644 --- a/indra/llcommon/tests/alsimd_test.cpp +++ b/indra/llcommon/tests/alsimd_test.cpp @@ -34,7 +34,7 @@ #include "linden_common.h" #include "../alsimd.h" -#include "../test/lltut.h" +#include #include #include @@ -46,8 +46,6 @@ using namespace alsimd; -namespace tut -{ namespace { struct Lanes @@ -112,7 +110,8 @@ namespace const U32 expected[4] = {x, y, z, w}; for (int i = 0; i < 4; ++i) { - ensure_equals(what + " lane " + std::to_string(i), bits_of(l.v[i]), expected[i]); + UNSCOPED_INFO(what + " lane " + std::to_string(i)); + REQUIRE(bits_of(l.v[i]) == expected[i]); } } @@ -150,9 +149,9 @@ namespace for (int i = 0; i < 4; ++i) { const double err = std::fabs((double)l.v[i] - expected[i]) / std::fabs(expected[i]); - ensure(what + " lane " + std::to_string(i) + ": " + precise(l.v[i]) + " is off " + - precise(err) + " relative from " + precise(expected[i]), - err <= relative); + UNSCOPED_INFO(what + " lane " + std::to_string(i) + ": " + precise(l.v[i]) + " is off " + + precise(err) + " relative from " + precise(expected[i])); + REQUIRE(err <= relative); } } @@ -163,10 +162,9 @@ namespace const F32 NAN_QUIET = std::numeric_limits::quiet_NaN(); const F32 DENORMAL = std::numeric_limits::denorm_min(); const F32 DENORMAL_BIG = float_of(0x007fffffu); -} -} // namespace tut +} // anonymous namespace -namespace tut +namespace { struct alsimd_data { @@ -179,23 +177,22 @@ namespace tut f32x4 a() const { return set(float_of(pa[0]), float_of(pa[1]), float_of(pa[2]), float_of(pa[3])); } f32x4 b() const { return set(float_of(pb[0]), float_of(pb[1]), float_of(pb[2]), float_of(pb[3])); } }; - typedef test_group alsimd_test; - typedef alsimd_test::object alsimd_object; - tut::alsimd_test alsimd_testcase("alsimd"); // The register is sixteen bytes at sixteen, on every target. - template<> template<> - void alsimd_object::test<1>() + TEST_CASE_METHOD(alsimd_data, "alsimd: the register is sixteen bytes at sixteen on every target", "[alsimd]") { - ensure_equals("sizeof f32x4", sizeof(f32x4), (size_t)16); - ensure_equals("alignof f32x4", alignof(f32x4), (size_t)16); - ensure_equals("sizeof mask4", sizeof(mask4), (size_t)16); - ensure_equals("sizeof i32x4", sizeof(i32x4), (size_t)16); + UNSCOPED_INFO("sizeof f32x4"); + CHECK(sizeof(f32x4) == (size_t)16); + UNSCOPED_INFO("alignof f32x4"); + CHECK(alignof(f32x4) == (size_t)16); + UNSCOPED_INFO("sizeof mask4"); + CHECK(sizeof(mask4) == (size_t)16); + UNSCOPED_INFO("sizeof i32x4"); + CHECK(sizeof(i32x4) == (size_t)16); } // Loads and stores move bits. - template<> template<> - void alsimd_object::test<2>() + TEST_CASE_METHOD(alsimd_data, "alsimd: loads and stores move bits", "[alsimd]") { alignas(16) F32 src[5] = {float_of(pa[0]), float_of(pa[1]), float_of(pa[2]), float_of(pa[3]), 99.f}; ensure_lanes_bits("load", load(src), pa[0], pa[1], pa[2], pa[3]); @@ -205,9 +202,11 @@ namespace tut alignas(16) F32 dst[8] = {}; store(dst, a()); - ensure_equals("store", std::memcmp(dst, src, 16), 0); + UNSCOPED_INFO("store"); + CHECK(std::memcmp(dst, src, 16) == 0); storeu(dst + 1, a()); - ensure_equals("storeu", std::memcmp(dst + 1, src, 16), 0); + UNSCOPED_INFO("storeu"); + CHECK(std::memcmp(dst + 1, src, 16) == 0); ensure_lanes_bits("set", a(), pa[0], pa[1], pa[2], pa[3]); ensure_lanes("set1", set1(-2.5f), -2.5f, -2.5f, -2.5f, -2.5f); @@ -215,13 +214,16 @@ namespace tut } // Lanes and permutations are bit moves. - template<> template<> - void alsimd_object::test<3>() + TEST_CASE_METHOD(alsimd_data, "alsimd: lanes and permutations are bit moves", "[alsimd]") { - ensure_equals("lane<0>", bits_of(lane<0>(a())), pa[0]); - ensure_equals("lane<1>", bits_of(lane<1>(a())), pa[1]); - ensure_equals("lane<2>", bits_of(lane<2>(a())), pa[2]); - ensure_equals("lane<3>", bits_of(lane<3>(a())), pa[3]); + UNSCOPED_INFO("lane<0>"); + CHECK(bits_of(lane<0>(a())) == pa[0]); + UNSCOPED_INFO("lane<1>"); + CHECK(bits_of(lane<1>(a())) == pa[1]); + UNSCOPED_INFO("lane<2>"); + CHECK(bits_of(lane<2>(a())) == pa[2]); + UNSCOPED_INFO("lane<3>"); + CHECK(bits_of(lane<3>(a())) == pa[3]); ensure_lanes_bits("splat<0>", splat<0>(a()), pa[0], pa[0], pa[0], pa[0]); ensure_lanes_bits("splat<1>", splat<1>(a()), pa[1], pa[1], pa[1], pa[1]); @@ -249,8 +251,7 @@ namespace tut } // Arithmetic is one rounding per lane. - template<> template<> - void alsimd_object::test<4>() + TEST_CASE_METHOD(alsimd_data, "alsimd: arithmetic is one rounding per lane", "[alsimd]") { const F32 xs[4] = {1.5f, -3.25f, 1e-3f, 65535.f}; const F32 ys[4] = {2.75f, 0.125f, 3.f, -7.f}; @@ -266,10 +267,14 @@ namespace tut const Lanes d = lanes_of(sub(x, y)); const Lanes p = lanes_of(mul(x, y)); const Lanes q = lanes_of(div(x, y)); - ensure_equals("add", bits_of(s.v[i]), bits_of(sum)); - ensure_equals("sub", bits_of(d.v[i]), bits_of(diff)); - ensure_equals("mul", bits_of(p.v[i]), bits_of(prod)); - ensure("div within an ulp", ulps_between(q.v[i], quot) <= 1); + UNSCOPED_INFO("add"); + REQUIRE(bits_of(s.v[i]) == bits_of(sum)); + UNSCOPED_INFO("sub"); + REQUIRE(bits_of(d.v[i]) == bits_of(diff)); + UNSCOPED_INFO("mul"); + REQUIRE(bits_of(p.v[i]) == bits_of(prod)); + UNSCOPED_INFO("div within an ulp"); + REQUIRE(ulps_between(q.v[i], quot) <= 1); } // A negative zero as its bits, in and out: /fp:fast lets the compiler @@ -279,8 +284,10 @@ namespace tut bits_of(-1.f), bits_of(2.f), 0x80000000u, 0u); ensure_lanes_bits("abs", abs(or_(set(1.f, -2.f, 0.f, -INF), and_(sign, mask_lane<2>()))), bits_of(1.f), bits_of(2.f), 0u, bits_of(INF)); - ensure_equals("neg keeps a NaN payload", bits_of(lane<2>(neg(a()))), pa[2] ^ 0x80000000u); - ensure_equals("abs keeps a NaN payload", bits_of(lane<2>(abs(neg(a())))), pa[2]); + UNSCOPED_INFO("neg keeps a NaN payload"); + CHECK(bits_of(lane<2>(neg(a()))) == (pa[2] ^ 0x80000000u)); + UNSCOPED_INFO("abs keeps a NaN payload"); + CHECK(bits_of(lane<2>(abs(neg(a())))) == pa[2]); // Denormals pass through: the ops do not rely on flush-to-zero. ensure_lanes("denormal mul", mul(set(DENORMAL, DENORMAL_BIG, 0.f, 0.f), set1(1.f)), DENORMAL, DENORMAL_BIG, 0.f, 0.f); @@ -289,8 +296,7 @@ namespace tut // Fused multiply-add: exact against fma where the machine fuses, exact // against two roundings where it does not. - template<> template<> - void alsimd_object::test<5>() + TEST_CASE_METHOD(alsimd_data, "alsimd: fused multiply-add matches fma or two roundings as the machine does", "[alsimd]") { const F32 as[4] = {1.5f, -3.25f, 1e-3f, 65535.f}; const F32 bs[4] = {2.75f, 0.125f, 3.f, -7.f}; @@ -321,17 +327,20 @@ namespace tut e_fnmadd = cs[i] - p; e_lane = pl + cs[i]; } - ensure_equals("fmadd", bits_of(r_fmadd.v[i]), bits_of(e_fmadd)); - ensure_equals("fmsub", bits_of(r_fmsub.v[i]), bits_of(e_fmsub)); - ensure_equals("fnmadd", bits_of(r_fnmadd.v[i]), bits_of(e_fnmadd)); - ensure_equals("fmadd_lane", bits_of(r_lane.v[i]), bits_of(e_lane)); + UNSCOPED_INFO("fmadd"); + REQUIRE(bits_of(r_fmadd.v[i]) == bits_of(e_fmadd)); + UNSCOPED_INFO("fmsub"); + REQUIRE(bits_of(r_fmsub.v[i]) == bits_of(e_fmsub)); + UNSCOPED_INFO("fnmadd"); + REQUIRE(bits_of(r_fnmadd.v[i]) == bits_of(e_fnmadd)); + UNSCOPED_INFO("fmadd_lane"); + REQUIRE(bits_of(r_lane.v[i]) == bits_of(e_lane)); } } // min, max and sqrt on ordinary values. A NaN or a zero of either sign // in min and max is the backend's to decide and is not tested. - template<> template<> - void alsimd_object::test<6>() + TEST_CASE_METHOD(alsimd_data, "alsimd: min and max and sqrt on ordinary values", "[alsimd]") { const f32x4 x = set(1.f, -2.f, 3.5f, -INF); const f32x4 y = set(0.5f, -1.f, 3.5f, 4.f); @@ -343,14 +352,14 @@ namespace tut const Lanes l = lanes_of(s); for (int i = 0; i < 4; ++i) { - ensure_equals("sqrt", bits_of(l.v[i]), bits_of(std::sqrt(sq[i]))); + UNSCOPED_INFO("sqrt"); + REQUIRE(bits_of(l.v[i]) == bits_of(std::sqrt(sq[i]))); } } // The estimates, within what each promises, across the magnitudes a // length or a quantization range takes. - template<> template<> - void alsimd_object::test<7>() + TEST_CASE_METHOD(alsimd_data, "alsimd: the estimates are within what each promises", "[alsimd]") { const F32 xs[4] = {1.f, 0.0001f, 12345.678f, 3e20f}; double e_rsqrt[4], e_rcp[4]; @@ -385,8 +394,7 @@ namespace tut // halves, the negatives and the integers already there. A result of zero // keeps the sign of its input from SSE4 up and on NEON; the SSE2 forms // below that go through an integer and lose it. - template<> template<> - void alsimd_object::test<8>() + TEST_CASE_METHOD(alsimd_data, "alsimd: round to nearest even and floor and ceil including the halves", "[alsimd]") { const F32 xs[8] = {2.5f, 3.5f, -2.5f, -3.5f, 0.25f, -1.75f, 1234567.f, -7.f}; for (int base = 0; base < 8; base += 4) @@ -400,11 +408,16 @@ namespace tut for (int i = 0; i < 4; ++i) { const F32 v = xs[base + i]; - ensure_equals("round " + std::to_string(v), bits_of(r.v[i]), bits_of(std::nearbyint(v))); - ensure_equals("floor " + std::to_string(v), bits_of(f.v[i]), bits_of(std::floor(v))); - ensure_equals("ceil " + std::to_string(v), bits_of(c.v[i]), bits_of(std::ceil(v))); - ensure_equals("cvt_round " + std::to_string(v), int_lane(cr, i), (S32)std::nearbyint(v)); - ensure_equals("cvt_trunc " + std::to_string(v), int_lane(ct, i), (S32)v); + UNSCOPED_INFO("round " + std::to_string(v)); + REQUIRE(bits_of(r.v[i]) == bits_of(std::nearbyint(v))); + UNSCOPED_INFO("floor " + std::to_string(v)); + REQUIRE(bits_of(f.v[i]) == bits_of(std::floor(v))); + UNSCOPED_INFO("ceil " + std::to_string(v)); + REQUIRE(bits_of(c.v[i]) == bits_of(std::ceil(v))); + UNSCOPED_INFO("cvt_round " + std::to_string(v)); + REQUIRE(int_lane(cr, i) == (S32)std::nearbyint(v)); + UNSCOPED_INFO("cvt_trunc " + std::to_string(v)); + REQUIRE(int_lane(ct, i) == (S32)v); } } const f32x4 near_zero = set(-0.f, -0.25f, 0.f, -0.5f); @@ -422,12 +435,12 @@ namespace tut ensure_lanes("to_float", to_float(ints(0, -1, 16777216, -12345678)), 0.f, -1.f, 16777216.f, -12345678.f); const Lanes back = lanes_of(cvt_trunc(set(0.f, -1.f, 16777216.f, -12345678.f))); - ensure_equals("cvt_trunc round trip", int_lane(back, 3), -12345678); + UNSCOPED_INFO("cvt_trunc round trip"); + CHECK(int_lane(back, 3) == -12345678); } // Bit operations move bits. - template<> template<> - void alsimd_object::test<9>() + TEST_CASE_METHOD(alsimd_data, "alsimd: bit operations move bits", "[alsimd]") { ensure_lanes_bits("and", and_(a(), b()), pa[0] & pb[0], pa[1] & pb[1], pa[2] & pb[2], pa[3] & pb[3]); ensure_lanes_bits("andnot", andnot(a(), b()), ~pa[0] & pb[0], ~pa[1] & pb[1], ~pa[2] & pb[2], ~pa[3] & pb[3]); @@ -439,8 +452,7 @@ namespace tut // Masks: the constants, the comparisons with a NaN in play, and what // reads them. - template<> template<> - void alsimd_object::test<10>() + TEST_CASE_METHOD(alsimd_data, "alsimd: mask constants and comparisons with a NaN and what reads them", "[alsimd]") { ensure_lanes_bits("mask_none", mask_none(), MASK_OFF, MASK_OFF, MASK_OFF, MASK_OFF); ensure_lanes_bits("mask_all", mask_all(), MASK_ON, MASK_ON, MASK_ON, MASK_ON); @@ -469,28 +481,41 @@ namespace tut ensure_lanes_bits("select inverted", select(mask_not(m), a(), b()), pb[0], pa[1], pa[2], pa[3]); ensure_lanes_bits("select xyz", select(mask_xyz(), a(), b()), pa[0], pa[1], pa[2], pb[3]); - ensure_equals("bits none", bits(mask_none()), 0u); - ensure_equals("bits all", bits(mask_all()), 0xFu); - ensure_equals("bits lane 2", bits(mask_lane<2>()), 4u); - ensure_equals("bits xyz", bits(mask_xyz()), 7u); - ensure_equals("bits cmpge", bits(cmpge(x, y)), 0xAu); - - ensure("any none", !any(mask_none())); - ensure("any lane 3", any(mask_lane<3>())); - ensure("all all", all(mask_all())); - ensure("all xyz is not all", !all(mask_xyz())); - ensure("any3 lane 3 is not any3", !any3(mask_lane<3>())); - ensure("any3 lane 1", any3(mask_lane<1>())); - ensure("all3 xyz", all3(mask_xyz())); - ensure("all3 with lane 1 clear", !all3(mask_not(mask_lane<1>()))); - ensure("all3 with only lane 3 clear", all3(mask_not(mask_lane<3>()))); + UNSCOPED_INFO("bits none"); + CHECK(bits(mask_none()) == 0u); + UNSCOPED_INFO("bits all"); + CHECK(bits(mask_all()) == 0xFu); + UNSCOPED_INFO("bits lane 2"); + CHECK(bits(mask_lane<2>()) == 4u); + UNSCOPED_INFO("bits xyz"); + CHECK(bits(mask_xyz()) == 7u); + UNSCOPED_INFO("bits cmpge"); + CHECK(bits(cmpge(x, y)) == 0xAu); + + UNSCOPED_INFO("any none"); + CHECK_FALSE(any(mask_none())); + UNSCOPED_INFO("any lane 3"); + CHECK(any(mask_lane<3>())); + UNSCOPED_INFO("all all"); + CHECK(all(mask_all())); + UNSCOPED_INFO("all xyz is not all"); + CHECK_FALSE(all(mask_xyz())); + UNSCOPED_INFO("any3 lane 3 is not any3"); + CHECK_FALSE(any3(mask_lane<3>())); + UNSCOPED_INFO("any3 lane 1"); + CHECK(any3(mask_lane<1>())); + UNSCOPED_INFO("all3 xyz"); + CHECK(all3(mask_xyz())); + UNSCOPED_INFO("all3 with lane 1 clear"); + CHECK_FALSE(all3(mask_not(mask_lane<1>()))); + UNSCOPED_INFO("all3 with only lane 3 clear"); + CHECK(all3(mask_not(mask_lane<3>()))); } // Dot products sum rounded products in one order on every backend, so // they are exact against that order; the cross product rounds one // product and fuses the other where the machine fuses. - template<> template<> - void alsimd_object::test<11>() + TEST_CASE_METHOD(alsimd_data, "alsimd: dot products sum in one order and cross3 fuses where the machine does", "[alsimd]") { const F32 as[4] = {1.5f, -3.25f, 0.001f, 4.f}; const F32 bs[4] = {2.75f, 0.125f, 3.f, -7.f}; @@ -529,9 +554,11 @@ namespace tut const Lanes c = lanes_of(cross3(a, b)); for (int i = 0; i < 3; ++i) { - ensure_equals("cross3 lane " + std::to_string(i), bits_of(c.v[i]), bits_of(expected[i])); + UNSCOPED_INFO("cross3 lane " + std::to_string(i)); + REQUIRE(bits_of(c.v[i]) == bits_of(expected[i])); } - ensure_equals("cross3 w", c.v[3], 0.f); + UNSCOPED_INFO("cross3 w"); + CHECK(c.v[3] == 0.f); // The cross product of two axes is the third. ensure_lanes("x cross y", cross3(set(1.f, 0.f, 0.f, 0.f), set(0.f, 1.f, 0.f, 0.f)), 0.f, 0.f, 1.f, 0.f); @@ -539,39 +566,48 @@ namespace tut } // The feature macros describe the build they came from. - template<> template<> - void alsimd_object::test<12>() + TEST_CASE_METHOD(alsimd_data, "alsimd: the feature macros describe the build they came from", "[alsimd]") { - ensure("one architecture", AL_SIMD_X86 + AL_SIMD_NEON == 1); - ensure("a width", AL_SIMD_WIDTH == 4 || AL_SIMD_WIDTH == 8 || AL_SIMD_WIDTH == 16); + UNSCOPED_INFO("one architecture"); + CHECK(AL_SIMD_X86 + AL_SIMD_NEON == 1); + UNSCOPED_INFO("a width"); + CHECK((AL_SIMD_WIDTH == 4 || AL_SIMD_WIDTH == 8 || AL_SIMD_WIDTH == 16)); if constexpr (AL_SIMD_AVX512) { - ensure("AVX-512 implies AVX2", AL_SIMD_AVX2 == 1); - ensure("AVX-512 is sixteen wide", AL_SIMD_WIDTH == 16); + UNSCOPED_INFO("AVX-512 implies AVX2"); + CHECK(AL_SIMD_AVX2 == 1); + UNSCOPED_INFO("AVX-512 is sixteen wide"); + CHECK(AL_SIMD_WIDTH == 16); } if constexpr (AL_SIMD_AVX2) { - ensure("AVX2 implies AVX", AL_SIMD_AVX == 1); - ensure("AVX2 implies FMA", AL_SIMD_FMA == 1); + UNSCOPED_INFO("AVX2 implies AVX"); + CHECK(AL_SIMD_AVX == 1); + UNSCOPED_INFO("AVX2 implies FMA"); + CHECK(AL_SIMD_FMA == 1); } if constexpr (AL_SIMD_AVX) { - ensure("AVX implies SSE4", AL_SIMD_SSE4 == 1); - ensure("AVX is at least eight wide", AL_SIMD_WIDTH >= 8); + UNSCOPED_INFO("AVX implies SSE4"); + CHECK(AL_SIMD_SSE4 == 1); + UNSCOPED_INFO("AVX is at least eight wide"); + CHECK(AL_SIMD_WIDTH >= 8); } if constexpr (AL_SIMD_NEON) { - ensure("NEON has FMA", AL_SIMD_FMA == 1); - ensure("NEON is four wide", AL_SIMD_WIDTH == 4); + UNSCOPED_INFO("NEON has FMA"); + CHECK(AL_SIMD_FMA == 1); + UNSCOPED_INFO("NEON is four wide"); + CHECK(AL_SIMD_WIDTH == 4); } - ensure("the level is not above the flags", AL_ISA_LEVEL <= 2 || (AL_ISA_LEVEL == 3 && AL_SIMD_AVX2) || (AL_ISA_LEVEL == 4 && AL_SIMD_AVX512)); + UNSCOPED_INFO("the level is not above the flags"); + CHECK((AL_ISA_LEVEL <= 2 || (AL_ISA_LEVEL == 3 && AL_SIMD_AVX2) || (AL_ISA_LEVEL == 4 && AL_SIMD_AVX512))); } // The aligned copy moves every byte and no other, at every size the // three loops can be entered with, both ends at every sixteen-byte // offset within a line, and at the sizes the slabs come in. - template<> template<> - void alsimd_object::test<13>() + TEST_CASE_METHOD(alsimd_data, "alsimd: copy_aligned16 moves every byte and no other at every entry size", "[alsimd]") { constexpr size_t LARGEST = 4 * 1024 * 1024; constexpr size_t GUARD = 64; @@ -608,9 +644,12 @@ namespace tut std::memset(dst - 16, 0xEE, size + 32); copy_aligned16(reinterpret_cast(dst), reinterpret_cast(src), size); const std::string what = "copy of " + std::to_string(size) + " at " + std::to_string(src_off) + "/" + std::to_string(dst_off); - ensure(what + " moved every byte", std::memcmp(dst, src, size) == 0); - ensure(what + " left the byte before", dst[-1] == 0xEE); - ensure(what + " left the byte after", dst[size] == 0xEE); + UNSCOPED_INFO(what + " moved every byte"); + REQUIRE(std::memcmp(dst, src, size) == 0); + UNSCOPED_INFO(what + " left the byte before"); + REQUIRE(dst[-1] == 0xEE); + UNSCOPED_INFO(what + " left the byte after"); + REQUIRE(dst[size] == 0xEE); } } } diff --git a/indra/llcommon/tests/alwatchedfile_test.cpp b/indra/llcommon/tests/alwatchedfile_test.cpp index 852488ccc9..d69b12e4f4 100644 --- a/indra/llcommon/tests/alwatchedfile_test.cpp +++ b/indra/llcommon/tests/alwatchedfile_test.cpp @@ -33,7 +33,7 @@ #include "../lluuid.h" #include "../workqueue.h" -#include "../test/lltut.h" +#include #include #include @@ -42,7 +42,7 @@ #include #include -namespace tut +namespace { namespace fs = std::filesystem; @@ -138,116 +138,128 @@ namespace tut } }; - typedef test_group alwatchedfile_group; - typedef alwatchedfile_group::object alwatchedfile_object; - alwatchedfile_group alwatchedfile_instance("alwatchedfile"); - - template<> template<> - void alwatchedfile_object::test<1>() + TEST_CASE_METHOD(alwatchedfile_data, "alwatchedfile: a write is heard once when it holds still; what was there is not", "[alwatchedfile]") { - set_test_name("what is there when the watch begins is seen, and a write is heard once, after it holds still"); put("print(1)\n", 100); ALWatchedFile watched(file, hear()); - ensure("nothing yet", !looked(watched)); - ensure("nobody told", heard.empty()); + UNSCOPED_INFO("nothing yet"); + CHECK_FALSE(looked(watched)); + UNSCOPED_INFO("nobody told"); + REQUIRE(heard.empty()); put("print(2)\n", 200); - ensure("the first look finds it moving", !watched.check()); - ensure("the next finds it still", watched.check()); - ensure_equals("told once", heard.size(), size_t(1)); - ensure_equals("of the file", heard[0], file); - ensure_equals("which held what was written", held[0], std::string("print(2)\n")); - ensure("and not again", !looked(watched)); + UNSCOPED_INFO("the first look finds it moving"); + CHECK_FALSE(watched.check()); + UNSCOPED_INFO("the next finds it still"); + CHECK(watched.check()); + UNSCOPED_INFO("told once"); + REQUIRE(heard.size() == size_t(1)); + UNSCOPED_INFO("of the file"); + CHECK(heard[0] == file); + UNSCOPED_INFO("which held what was written"); + CHECK(held[0] == std::string("print(2)\n")); + UNSCOPED_INFO("and not again"); + CHECK_FALSE(looked(watched)); } - template<> template<> - void alwatchedfile_object::test<2>() + TEST_CASE_METHOD(alwatchedfile_data, "alwatchedfile: two writes in one second are two changes of either size", "[alwatchedfile]") { - set_test_name("two writes in one second are two changes, of one size or another"); put("a = 1\n", 100); ALWatchedFile watched(file, hear()); put("a = 2\n", 300); - ensure("the first", looked(watched)); + UNSCOPED_INFO("the first"); + CHECK(looked(watched)); // The same size, the same second: the time alone tells them apart. put("a = 3\n", 500); - ensure("the second", looked(watched)); + UNSCOPED_INFO("the second"); + CHECK(looked(watched)); // The same time, a size of its own. put("a = 40\n", 500); - ensure("the third", looked(watched)); - ensure_equals("each heard", heard.size(), size_t(3)); - ensure_equals("the last as it was written", held[2], std::string("a = 40\n")); + UNSCOPED_INFO("the third"); + CHECK(looked(watched)); + UNSCOPED_INFO("each heard"); + REQUIRE(heard.size() == size_t(3)); + UNSCOPED_INFO("the last as it was written"); + CHECK(held[2] == std::string("a = 40\n")); } - template<> template<> - void alwatchedfile_object::test<3>() + TEST_CASE_METHOD(alwatchedfile_data, "alwatchedfile: the owner's own write is not heard but the next one from outside is", "[alwatchedfile]") { - set_test_name("a write of the owner's own is seen, and the next write from outside is heard all the same"); put("one\n", 100); ALWatchedFile watched(file, hear()); put("two\n", 200); - ensure("from outside", looked(watched)); + UNSCOPED_INFO("from outside"); + CHECK(looked(watched)); // The owner's own, in the same second: a flag waiting on the next // change would never be taken by a write a time by the second // missed, and would take the editor's save after it. put("three\n", 300); watched.seen(); - ensure("the owner's own is no change", !looked(watched)); + UNSCOPED_INFO("the owner's own is no change"); + CHECK_FALSE(looked(watched)); put("four\n", 400); - ensure("the editor's save after it is heard", looked(watched)); - ensure_equals("two heard", heard.size(), size_t(2)); - ensure_equals("the editor's", held[1], std::string("four\n")); + UNSCOPED_INFO("the editor's save after it is heard"); + CHECK(looked(watched)); + UNSCOPED_INFO("two heard"); + REQUIRE(heard.size() == size_t(2)); + UNSCOPED_INFO("the editor's"); + CHECK(held[1] == std::string("four\n")); } - template<> template<> - void alwatchedfile_object::test<4>() + TEST_CASE_METHOD(alwatchedfile_data, "alwatchedfile: a file caught half written is not read until it holds still", "[alwatchedfile]") { - set_test_name("a file caught half written is not read until it holds still"); put("local x = 1\n", 100); ALWatchedFile watched(file, hear()); // An editor saving in two steps: emptied, then filled. put("", 200); - ensure("emptied: not still yet", !watched.check()); + UNSCOPED_INFO("emptied: not still yet"); + CHECK_FALSE(watched.check()); put("local x = 2\n", 300); - ensure("filled: moved again", !watched.check()); - ensure("still", watched.check()); - ensure_equals("heard once", heard.size(), size_t(1)); - ensure_equals("filled", held[0], std::string("local x = 2\n")); + UNSCOPED_INFO("filled: moved again"); + CHECK_FALSE(watched.check()); + UNSCOPED_INFO("still"); + CHECK(watched.check()); + UNSCOPED_INFO("heard once"); + REQUIRE(heard.size() == size_t(1)); + UNSCOPED_INFO("filled"); + CHECK(held[0] == std::string("local x = 2\n")); } - template<> template<> - void alwatchedfile_object::test<5>() + TEST_CASE_METHOD(alwatchedfile_data, "alwatchedfile: a file gone or come back or put back older is each a change", "[alwatchedfile]") { - set_test_name("a file gone is a change, one come back is another, and one put back older is a third"); put("kept\n", 500); ALWatchedFile watched(file, hear()); std::error_code ec; fs::remove(fsyspath(file), ec); - ensure("gone", looked(watched)); + UNSCOPED_INFO("gone"); + CHECK(looked(watched)); put("back\n", 600); - ensure("back", looked(watched)); + UNSCOPED_INFO("back"); + CHECK(looked(watched)); // Older than it was: a backup put back, which a later time only // would not see. fs::last_write_time(fsyspath(file), second, ec); - ensure("older", looked(watched)); - ensure_equals("three heard", heard.size(), size_t(3)); + UNSCOPED_INFO("older"); + CHECK(looked(watched)); + UNSCOPED_INFO("three heard"); + REQUIRE(heard.size() == size_t(3)); } - template<> template<> - void alwatchedfile_object::test<6>() + TEST_CASE_METHOD(alwatchedfile_data, "alwatchedfile: a file that is not there yet is heard when it comes", "[alwatchedfile]") { - set_test_name("a file that is not there yet is heard when it comes"); ALWatchedFile watched(file, hear()); - ensure("nothing there, nothing heard", !looked(watched)); + UNSCOPED_INFO("nothing there, nothing heard"); + CHECK_FALSE(looked(watched)); put("made\n", 100); - ensure("made", looked(watched)); - ensure_equals("its text", held.at(0), std::string("made\n")); + UNSCOPED_INFO("made"); + CHECK(looked(watched)); + UNSCOPED_INFO("its text"); + CHECK(held.at(0) == std::string("made\n")); } - template<> template<> - void alwatchedfile_object::test<7>() + TEST_CASE_METHOD(alwatchedfile_data, "alwatchedfile: the owner may let go of the watch as it is told", "[alwatchedfile]") { - set_test_name("the owner may let go of the watch as it is told"); put("before\n", 100); std::unique_ptr watched; std::string told; @@ -258,16 +270,18 @@ namespace tut }); put("after\n", 200); ALWatchedFile& watch = *watched; - ensure("moving", !watch.check()); - ensure("heard, and let go", watch.check()); - ensure("gone", !watched); - ensure_equals("told of the file all the same", told, file); + UNSCOPED_INFO("moving"); + CHECK_FALSE(watch.check()); + UNSCOPED_INFO("heard, and let go"); + CHECK(watch.check()); + UNSCOPED_INFO("gone"); + REQUIRE_FALSE(watched); + UNSCOPED_INFO("told of the file all the same"); + CHECK(told == file); } - template<> template<> - void alwatchedfile_object::test<8>() + TEST_CASE_METHOD(alwatchedfile_data, "alwatchedfile: a polled file is looked at off thread and told on the main loop", "[alwatchedfile]") { - set_test_name("a polled file is looked at on the watcher's thread, and its owner told on the main loop"); put("print(1)\n", 100); const std::thread::id here = std::this_thread::get_id(); std::thread::id told_on; @@ -278,19 +292,21 @@ namespace tut }); watched.poll(0.05f); put("print(2)\n", 200); - ensure("heard", pumped([&]() { return !heard.empty(); })); - ensure_equals("what was written", held.at(0), std::string("print(2)\n")); - ensure("told on the main loop's thread", told_on == here); + UNSCOPED_INFO("heard"); + REQUIRE(pumped([&]() { return !heard.empty(); })); + UNSCOPED_INFO("what was written"); + CHECK(held.at(0) == std::string("print(2)\n")); + UNSCOPED_INFO("told on the main loop's thread"); + CHECK(told_on == here); // Still: not told again, however many looks go by. LLTimer timer; pumped([&]() { return timer.getElapsedTimeF32() > 0.5f; }); - ensure_equals("told once", heard.size(), size_t(1)); + UNSCOPED_INFO("told once"); + REQUIRE(heard.size() == size_t(1)); } - template<> template<> - void alwatchedfile_object::test<9>() + TEST_CASE_METHOD(alwatchedfile_data, "alwatchedfile: a watch let go of is not told; the last may be let go of as it is told", "[alwatchedfile]") { - set_test_name("a file let go of is not told, and the others are; the owner may let go of the last as it is told"); const std::string other = fsyspath(fsyspath(folder) / fsyspath("sl_script_other.lsl")).string(); put("one\n", 100); { @@ -311,60 +327,75 @@ namespace tut llofstream out(other, std::ios::binary | std::ios::trunc); out << "default { state_entry() {} }\n"; } - ensure("heard", pumped([&]() { return !heard.empty(); })); - ensure("and let go", !kept); + UNSCOPED_INFO("heard"); + REQUIRE(pumped([&]() { return !heard.empty(); })); + UNSCOPED_INFO("and let go"); + REQUIRE_FALSE(kept); LLTimer timer; pumped([&]() { return timer.getElapsedTimeF32() > 0.4f; }); - ensure_equals("the one kept, only", heard.size(), size_t(1)); - ensure_equals("which is the file", heard[0], file); + UNSCOPED_INFO("the one kept, only"); + REQUIRE(heard.size() == size_t(1)); + UNSCOPED_INFO("which is the file"); + CHECK(heard[0] == file); } - template<> template<> - void alwatchedfile_object::test<10>() + TEST_CASE_METHOD(alwatchedfile_data, "alwatchedfile: ALFileStamp holds a file's size and time and nothing for a folder", "[alwatchedfile]") { - set_test_name("a stamp: a file's size and time, and nothing for a folder or nothing at all"); put("12345", 250); const ALFileStamp stamp = ALFileStamp::of(file); - ensure("there", stamp.exists); - ensure_equals("its size", stamp.size, std::uintmax_t(5)); + UNSCOPED_INFO("there"); + CHECK(stamp.exists); + UNSCOPED_INFO("its size"); + REQUIRE(stamp.size == std::uintmax_t(5)); put("12345", 251); - ensure("a millisecond later is another time", !(ALFileStamp::of(file) == stamp)); - ensure("a folder is no file", !ALFileStamp::of(folder).exists); - ensure("nothing is nothing", !ALFileStamp::of(file + ".gone").exists); + UNSCOPED_INFO("a millisecond later is another time"); + CHECK_FALSE(ALFileStamp::of(file) == stamp); + UNSCOPED_INFO("a folder is no file"); + CHECK_FALSE(ALFileStamp::of(folder).exists); + UNSCOPED_INFO("nothing is nothing"); + CHECK_FALSE(ALFileStamp::of(file + ".gone").exists); } - template<> template<> - void alwatchedfile_object::test<11>() + TEST_CASE_METHOD(alwatchedfile_data, "alwatchedfile: a watch made from an earlier stamp starts from what it found", "[alwatchedfile]") { - set_test_name("a watch made from a look made before takes what that look found, and asks the disk nothing itself"); put("print(1)\n", 100); const ALFileStamp before = ALFileStamp::of(file); // Written after the look and before the watch is made: heard, since // the watch is from the look, not from the file as it is now. put("print(2)\n", 200); ALWatchedFile watched(file, before, hear()); - ensure("as the look found it", watched.stamp() == before); - ensure("what came after it heard", looked(watched)); - ensure_equals("once", heard.size(), size_t(1)); - ensure_equals("as it is now", held.at(0), std::string("print(2)\n")); + UNSCOPED_INFO("as the look found it"); + CHECK(watched.stamp() == before); + UNSCOPED_INFO("what came after it heard"); + CHECK(looked(watched)); + UNSCOPED_INFO("once"); + REQUIRE(heard.size() == size_t(1)); + UNSCOPED_INFO("as it is now"); + CHECK(held.at(0) == std::string("print(2)\n")); // From a look that found the file as it is: nothing to hear. ALWatchedFile current(file, ALFileStamp::of(file), hear()); - ensure("nothing", !looked(current)); + UNSCOPED_INFO("nothing"); + CHECK_FALSE(looked(current)); // From a look that found nothing there -- the stamp of no file, // which a look of its own here would not have found: the file there // now is heard, which shows the watch took the look it was given. ALWatchedFile none(file, ALFileStamp(), hear()); - ensure("nothing there at the look", !none.there()); - ensure("the file there now heard", looked(none)); - ensure_equals("twice in all", heard.size(), size_t(2)); + UNSCOPED_INFO("nothing there at the look"); + CHECK_FALSE(none.there()); + UNSCOPED_INFO("the file there now heard"); + CHECK(looked(none)); + UNSCOPED_INFO("twice in all"); + REQUIRE(heard.size() == size_t(2)); // And seen() takes the file as it is, whatever look it began from. put("print(3)\n", 300); ALWatchedFile owned(file, before, hear()); owned.seen(); - ensure("the owner's own is no change", !looked(owned)); - ensure_equals("still twice", heard.size(), size_t(2)); + UNSCOPED_INFO("the owner's own is no change"); + CHECK_FALSE(looked(owned)); + UNSCOPED_INFO("still twice"); + REQUIRE(heard.size() == size_t(2)); } } diff --git a/indra/llcommon/tests/apply_test.cpp b/indra/llcommon/tests/apply_test.cpp index 6bb4e23b00..1f9ee69b5f 100644 --- a/indra/llcommon/tests/apply_test.cpp +++ b/indra/llcommon/tests/apply_test.cpp @@ -25,12 +25,12 @@ // for ensure_equals(std::vector): must come BEFORE lltut.h #include "StringVec.h" -#include "../test/lltut.h" +#include /***************************************************************************** * TUT *****************************************************************************/ -namespace tut +namespace { namespace statics { @@ -69,46 +69,66 @@ namespace tut const LLSD::URI& uri, const LLSD::Binary& bin) { called = true; - ensure_equals( "b mismatch", b, statics::b); - ensure_equals( "i mismatch", i, statics::i); - ensure_equals( "f mismatch", f, statics::f); - ensure_equals( "s mismatch", s, statics::s); - ensure_equals( "uu mismatch", uu, statics::uu); - ensure_equals( "dt mismatch", dt, statics::dt); - ensure_equals("uri mismatch", uri, statics::uri); - ensure_equals("bin mismatch", bin, statics::bin); + UNSCOPED_INFO("b mismatch"); + REQUIRE(b == statics::b); + UNSCOPED_INFO("i mismatch"); + REQUIRE(i == statics::i); + UNSCOPED_INFO("f mismatch"); + REQUIRE(f == statics::f); + UNSCOPED_INFO("s mismatch"); + REQUIRE(s == statics::s); + UNSCOPED_INFO("uu mismatch"); + REQUIRE(uu == statics::uu); + UNSCOPED_INFO("dt mismatch"); + REQUIRE(dt == statics::dt); + UNSCOPED_INFO("uri mismatch"); + REQUIRE(uri.asString() == statics::uri.asString()); + UNSCOPED_INFO("bin mismatch"); + REQUIRE(bin == statics::bin); } static void strings(std::string s0, std::string s1, std::string s2, std::string s3, std::string s4) { called = true; - ensure_equals("s0 mismatch", s0, statics::quick[0]); - ensure_equals("s1 mismatch", s1, statics::quick[1]); - ensure_equals("s2 mismatch", s2, statics::quick[2]); - ensure_equals("s3 mismatch", s3, statics::quick[3]); - ensure_equals("s4 mismatch", s4, statics::quick[4]); + UNSCOPED_INFO("s0 mismatch"); + REQUIRE(s0 == statics::quick[0]); + UNSCOPED_INFO("s1 mismatch"); + REQUIRE(s1 == statics::quick[1]); + UNSCOPED_INFO("s2 mismatch"); + REQUIRE(s2 == statics::quick[2]); + UNSCOPED_INFO("s3 mismatch"); + REQUIRE(s3 == statics::quick[3]); + UNSCOPED_INFO("s4 mismatch"); + REQUIRE(s4 == statics::quick[4]); } static void ints(int i0, int i1, int i2, int i3, int i4) { called = true; - ensure_equals("i0 mismatch", i0, statics::fibs[0]); - ensure_equals("i1 mismatch", i1, statics::fibs[1]); - ensure_equals("i2 mismatch", i2, statics::fibs[2]); - ensure_equals("i3 mismatch", i3, statics::fibs[3]); - ensure_equals("i4 mismatch", i4, statics::fibs[4]); + UNSCOPED_INFO("i0 mismatch"); + REQUIRE(i0 == statics::fibs[0]); + UNSCOPED_INFO("i1 mismatch"); + REQUIRE(i1 == statics::fibs[1]); + UNSCOPED_INFO("i2 mismatch"); + REQUIRE(i2 == statics::fibs[2]); + UNSCOPED_INFO("i3 mismatch"); + REQUIRE(i3 == statics::fibs[3]); + UNSCOPED_INFO("i4 mismatch"); + REQUIRE(i4 == statics::fibs[4]); } static void sdfunc(const LLSD& sd) { called = true; - ensure_equals("sd mismatch", sd.asInteger(), statics::i); + UNSCOPED_INFO("sd mismatch"); + REQUIRE(sd.asInteger() == statics::i); } static void intfunc(int i) { called = true; - ensure_equals("i mismatch", i, statics::i); + UNSCOPED_INFO("i mismatch"); + REQUIRE(i == statics::i); } static void voidfunc() @@ -140,78 +160,66 @@ namespace tut statics::collected.clear(); } }; - typedef test_group apply_group; - typedef apply_group::object object; - apply_group applygrp("apply"); - template<> template<> - void object::test<1>() + TEST_CASE_METHOD(apply_data, "apply: apply(tuple) passes each element as an argument", "[apply]") { - set_test_name("apply(tuple)"); LL::apply(statics::various, std::make_tuple(statics::b, statics::i, statics::f, statics::s, statics::uu, statics::dt, statics::uri, statics::bin)); - ensure("apply(tuple) failed", statics::called); + UNSCOPED_INFO("apply(tuple) failed"); + CHECK(statics::called); } - template<> template<> - void object::test<2>() + TEST_CASE_METHOD(apply_data, "apply: apply(array) passes each element as an argument", "[apply]") { - set_test_name("apply(array)"); LL::apply(statics::ints, statics::fibs); - ensure("apply(array) failed", statics::called); + UNSCOPED_INFO("apply(array) failed"); + CHECK(statics::called); } - template<> template<> - void object::test<3>() + TEST_CASE_METHOD(apply_data, "apply: apply(vector) passes each element as an argument", "[apply]") { - set_test_name("apply(vector)"); LL::apply(statics::strings, statics::quick); - ensure("apply(vector) failed", statics::called); + UNSCOPED_INFO("apply(vector) failed"); + CHECK(statics::called); } // The various apply(LLSD) tests exercise only the success cases because // the failure cases trigger assert() fail, which is hard to catch. - template<> template<> - void object::test<4>() + TEST_CASE_METHOD(apply_data, "apply: apply(LLSD()) calls a function of no arguments", "[apply]") { - set_test_name("apply(LLSD())"); LL::apply(statics::voidfunc, LLSD()); - ensure("apply(LLSD()) failed", statics::called); + UNSCOPED_INFO("apply(LLSD()) failed"); + CHECK(statics::called); } - template<> template<> - void object::test<5>() + TEST_CASE_METHOD(apply_data, "apply: apply with an LLSD scalar converts it for an int parameter", "[apply]") { - set_test_name("apply(fn(int), LLSD scalar)"); LL::apply(statics::intfunc, LLSD(statics::i)); - ensure("apply(fn(int), LLSD scalar) failed", statics::called); + UNSCOPED_INFO("apply(fn(int), LLSD scalar) failed"); + CHECK(statics::called); } - template<> template<> - void object::test<6>() + TEST_CASE_METHOD(apply_data, "apply: apply with an LLSD scalar passes it to an LLSD parameter", "[apply]") { - set_test_name("apply(fn(LLSD), LLSD scalar)"); // This test verifies that LLSDParam doesn't send the compiler // into infinite recursion when the target is itself LLSD. LL::apply(statics::sdfunc, LLSD(statics::i)); - ensure("apply(fn(LLSD), LLSD scalar) failed", statics::called); + UNSCOPED_INFO("apply(fn(LLSD), LLSD scalar) failed"); + CHECK(statics::called); } - template<> template<> - void object::test<7>() + TEST_CASE_METHOD(apply_data, "apply: apply(LLSD array) converts each element to its parameter", "[apply]") { - set_test_name("apply(LLSD array)"); LL::apply(statics::various, llsd::array(statics::b, statics::i, statics::f, statics::s, statics::uu, statics::dt, statics::uri, statics::bin)); - ensure("apply(LLSD array) failed", statics::called); + UNSCOPED_INFO("apply(LLSD array) failed"); + CHECK(statics::called); } - template<> template<> - void object::test<8>() + TEST_CASE_METHOD(apply_data, "apply: VAPPLY() expands an array into a variadic call", "[apply]") { - set_test_name("VAPPLY()"); // Make a std::array from statics::quick. We can't call a // variadic function with a data structure of dynamic length. std::array strray; @@ -222,7 +230,9 @@ namespace tut // LL::apply(statics::collect, strray); // That's what VAPPLY() is for. VAPPLY(statics::collect, strray); - ensure("VAPPLY() failed", statics::called); - ensure_equals("collected mismatch", statics::collected, statics::quick); + UNSCOPED_INFO("VAPPLY() failed"); + CHECK(statics::called); + UNSCOPED_INFO("collected mismatch"); + CHECK(statics::collected == statics::quick); } -} // namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/bitpack_test.cpp b/indra/llcommon/tests/bitpack_test.cpp index 2810f926af..b7d3a15fc6 100644 --- a/indra/llcommon/tests/bitpack_test.cpp +++ b/indra/llcommon/tests/bitpack_test.cpp @@ -30,21 +30,14 @@ #include "../llbitpack.h" -#include "../test/lltut.h" +#include +#include "altestmatchers.h" -namespace tut +namespace { - struct bit_pack - { - }; - typedef test_group bit_pack_t; - typedef bit_pack_t::object bit_pack_object_t; - tut::bit_pack_t tut_bit_pack("LLBitPack"); - // pack -> unpack - template<> template<> - void bit_pack_object_t::test<1>() + TEST_CASE("LLBitPack: a string packed then unpacked comes back unchanged", "[LLBitPack]") { U8 packbuffer[255]; U8 unpackbuffer[255]; @@ -60,13 +53,14 @@ namespace tut LLBitPack bitunpack(packbuffer, pack_bufsize*8); unpack_bufsize = bitunpack.bitUnpack(unpackbuffer, len*8); - ensure("bitPack: unpack size should be same as string size prior to pack", len == unpack_bufsize); - ensure_memory_matches("str->bitPack->bitUnpack should be equal to string", str, len, unpackbuffer, unpack_bufsize); + UNSCOPED_INFO("bitPack: unpack size should be same as string size prior to pack"); + CHECK(len == unpack_bufsize); + UNSCOPED_INFO("str->bitPack->bitUnpack should be equal to string"); + CHECK_THAT(ll_test::Memory(str, len), ll_test::MemoryMatches(unpackbuffer, unpack_bufsize)); } // pack large, unpack in individual bytes - template<> template<> - void bit_pack_object_t::test<2>() + TEST_CASE("LLBitPack: a packed string unpacks a byte at a time", "[LLBitPack]") { U8 packbuffer[255]; U8 unpackbuffer[255]; @@ -81,24 +75,30 @@ namespace tut LLBitPack bitunpack(packbuffer, pack_bufsize*8); bitunpack.bitUnpack(&unpackbuffer[0], 8); - ensure("bitPack: individual unpack: 0", unpackbuffer[0] == (U8) str[0]); + UNSCOPED_INFO("bitPack: individual unpack: 0"); + CHECK(unpackbuffer[0] == (U8) str[0]); bitunpack.bitUnpack(&unpackbuffer[0], 8); - ensure("bitPack: individual unpack: 1", unpackbuffer[0] == (U8) str[1]); + UNSCOPED_INFO("bitPack: individual unpack: 1"); + CHECK(unpackbuffer[0] == (U8) str[1]); bitunpack.bitUnpack(&unpackbuffer[0], 8); - ensure("bitPack: individual unpack: 2", unpackbuffer[0] == (U8) str[2]); + UNSCOPED_INFO("bitPack: individual unpack: 2"); + CHECK(unpackbuffer[0] == (U8) str[2]); bitunpack.bitUnpack(&unpackbuffer[0], 8); - ensure("bitPack: individual unpack: 3", unpackbuffer[0] == (U8) str[3]); + UNSCOPED_INFO("bitPack: individual unpack: 3"); + CHECK(unpackbuffer[0] == (U8) str[3]); bitunpack.bitUnpack(&unpackbuffer[0], 8); - ensure("bitPack: individual unpack: 4", unpackbuffer[0] == (U8) str[4]); + UNSCOPED_INFO("bitPack: individual unpack: 4"); + CHECK(unpackbuffer[0] == (U8) str[4]); bitunpack.bitUnpack(&unpackbuffer[0], 8); - ensure("bitPack: individual unpack: 5", unpackbuffer[0] == (U8) str[5]); + UNSCOPED_INFO("bitPack: individual unpack: 5"); + CHECK(unpackbuffer[0] == (U8) str[5]); bitunpack.bitUnpack(unpackbuffer, 8*4); // Life - ensure_memory_matches("bitPack: 4 bytes unpack:", unpackbuffer, 4, str+6, 4); + UNSCOPED_INFO("bitPack: 4 bytes unpack:"); + REQUIRE_THAT(ll_test::Memory(unpackbuffer, 4), ll_test::MemoryMatches(str+6, 4)); } // U32 packing - template<> template<> - void bit_pack_object_t::test<3>() + TEST_CASE("LLBitPack: a U32 packed then unpacked comes back unchanged", "[LLBitPack]") { U8 packbuffer[255]; int pack_bufsize = 0; @@ -113,6 +113,7 @@ namespace tut // since packing and unpacking is done on same machine in the unit test run, // endianness should not matter bitunpack.bitUnpack((U8*) &res, sizeof(res)*8); - ensure("U32->bitPack->bitUnpack->U32 should be equal", num == res); + UNSCOPED_INFO("U32->bitPack->bitUnpack->U32 should be equal"); + CHECK(num == res); } } diff --git a/indra/llcommon/tests/classic_callback_test.cpp b/indra/llcommon/tests/classic_callback_test.cpp index ec255ed1c7..25bc17b3dd 100644 --- a/indra/llcommon/tests/classic_callback_test.cpp +++ b/indra/llcommon/tests/classic_callback_test.cpp @@ -19,7 +19,7 @@ // std headers // external library headers // other Linden headers -#include "../test/lltut.h" +#include namespace { @@ -91,19 +91,10 @@ struct OtherCallback /***************************************************************************** * TUT *****************************************************************************/ -namespace tut +namespace { - struct classic_callback_data - { - }; - typedef test_group classic_callback_group; - typedef classic_callback_group::object object; - classic_callback_group classic_callbackgrp("classic_callback"); - - template<> template<> - void object::test<1>() + TEST_CASE("classic_callback: ClassicCallback adapts C++ callables to a C callback", "[classic_callback]") { - set_test_name("ClassicCallback"); // engage someAPI(MyCallback()) auto ccb{ makeClassicCallback(MyCallback()) }; someAPI(ccb.get_callback(), ccb.get_userdata()); @@ -113,13 +104,15 @@ namespace tut MyCallback mcb; ClassicCallback ccb2(mcb); someAPI(ccb2.get_callback(), ccb2.get_userdata()); - ensure_equals("failed to call through ClassicCallback", mcb.mMsg, "called"); + UNSCOPED_INFO("failed to call through ClassicCallback"); + CHECK(mcb.mMsg == "called"); // try with HeapClassicCallback mcb.mMsg.clear(); auto hcbp{ makeHeapClassicCallback(mcb) }; someAPI(hcbp->get_callback(), hcbp->get_userdata()); - ensure_equals("failed to call through HeapClassicCallback", mcb.mMsg, "called"); + UNSCOPED_INFO("failed to call through HeapClassicCallback"); + CHECK(mcb.mMsg == "called"); // lambda // The tricky thing here is that a lambda is an unspecified type, so @@ -130,7 +123,8 @@ namespace tut [&mcb](const char* msg, void*) { mcb.callback_with_extra("extra", msg); })); someAPI(xcb.get_callback(), xcb.get_userdata()); - ensure_equals("failed to call lambda", mcb.mMsg, "extra called"); + UNSCOPED_INFO("failed to call lambda"); + CHECK(mcb.mMsg == "extra called"); // engage otherAPI(OtherCallback()) OtherCallback ocb; @@ -141,9 +135,13 @@ namespace tut // effect. auto occb{ makeClassicCallback(std::move(ocb)) }; std::string result{ otherAPI(occb.get_callback(), occb.get_userdata()) }; - ensure_equals("failed to return callback result", result, "hello back!"); - ensure_equals("failed to set int", sData.mi, 17); - ensure_equals("failed to set string", sData.ms, "hello world"); - ensure_equals("failed to set double", sData.mf, 3.0); + UNSCOPED_INFO("failed to return callback result"); + CHECK(result == "hello back!"); + UNSCOPED_INFO("failed to set int"); + CHECK(sData.mi == 17); + UNSCOPED_INFO("failed to set string"); + CHECK(sData.ms == "hello world"); + UNSCOPED_INFO("failed to set double"); + CHECK(sData.mf == 3.0); } -} // namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/commonmisc_test.cpp b/indra/llcommon/tests/commonmisc_test.cpp index 80be26dfeb..4e8deb84b8 100644 --- a/indra/llcommon/tests/commonmisc_test.cpp +++ b/indra/llcommon/tests/commonmisc_test.cpp @@ -44,19 +44,75 @@ #include "../u64.h" #include "../llhash.h" -#include "../test/lltut.h" +#include -namespace tut +namespace { - struct sd_data +/* + template<> template<> + void sd_object::test<16>() { - }; - typedef test_group sd_test; - typedef sd_test::object sd_object; - tut::sd_test sd("LLSD"); + } +*/ +} - template<> template<> - void sd_object::test<1>() +#if 0 +'{\'task_id\':u1fd77b79-a8e7-25a5-9454-02a4d948ba1c}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t00082000\n\t\tcreator_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t00000000-0000-0000-0000-000000000000\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t10284\n\ttotal_crc\t35\n\ttype\t1\n\ttask_valid\t2\n\ttravel_access\t21\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t0\t0\t0\n\toldpos\t0\t0\t0\n\trotation\t4.371139183945160766597837e-08\t1\t4.371139183945160766597837e-08\t0\n\tvelocity\t0\t0\t0\n\tangvel\t0\t0\t0\n\tscale\t0.2816932\t0.2816932\t0.2816932\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t80\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t6\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t1132625972249870\n\tbirthtime\t1132625953120694\n\treztime\t1132625953120694\n\tparceltime\t1132625953120694\n\ttax_rate\t1.01615\n\tnamevalue\tAttachmentOrientation VEC3 RW DS -3.141593, 0.000000, -3.141593\n\tnamevalue\tAttachmentOffset VEC3 RW DS 0.000000, 0.000000, 0.000000\n\tnamevalue\tAttachPt U32 RW S 5\n\tnamevalue\tAttachItemID STRING RW SV 1f9975c0-2951-1b93-dd83-46e2b932fcc8\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\torig_asset_id\t52019cdd-b464-ba19-e66d-3da751fef9da\n\torig_item_id\t1f9975c0-2951-1b93-dd83-46e2b932fcc8\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n' +#endif + +namespace +{ +} + +namespace +{ + // llstrtou64 + // seems to be deprecated - could not find it being used + // anywhere in the tarball - skipping unit tests for now +} + + +namespace +{ + TEST_CASE("LLHash: llhash depends on the string and not on its pointer", "[LLHash]") + { + const char * str1 = "test string one"; + const char * same_as_str1 = "test string one"; + + size_t hash1 = llhash(str1); + size_t same_as_hash1 = llhash(same_as_str1); + + + UNSCOPED_INFO("Hashes from identical strings should be equal"); + CHECK(hash1 == same_as_hash1); + + char str[100]; + strcpy( str, "Another test" ); + + size_t hash2 = llhash(str); + + strcpy( str, "Different string, same pointer" ); + + size_t hash3 = llhash(str); + + UNSCOPED_INFO("Hashes from same pointer but different string should not be equal"); + CHECK(hash2 != hash3); + } + + TEST_CASE("LLMemoryStream: operator>> reads whitespace-separated words", "[LLMemoryStream]") + { + const char HELLO_WORLD[] = "hello world"; + LLMemoryStream mem((U8*)&HELLO_WORLD[0], static_cast(strlen(HELLO_WORLD))); /* Flawfinder: ignore */ + std::string hello; + std::string world; + mem >> hello >> world; + UNSCOPED_INFO("first word"); + CHECK(hello == std::string("hello")); + UNSCOPED_INFO("second word"); + CHECK(world == std::string("world")); + } + + TEST_CASE("LLSD: fromNotation parses a map from an LLMemoryStream", "[LLSD]") { std::ostringstream resp; resp << "{'connect':true, 'position':[r128,r128,r128], 'look_at':[r0,r1,r0], 'agent_access':'M', 'region_x':i8192, 'region_y':i8192}"; @@ -64,17 +120,23 @@ namespace tut LLMemoryStream mstr((U8*)str.c_str(), static_cast(str.size())); LLSD response; S32 count = LLSDSerialize::fromNotation(response, mstr, str.size()); - ensure("stream parsed", response.isDefined()); - ensure_equals("stream parse count", count, 13); - ensure_equals("sd type", response.type(), LLSD::TypeMap); - ensure_equals("map element count", response.size(), 6); - ensure_equals("value connect", response["connect"].asBoolean(), true); - ensure_equals("value region_x", response["region_x"].asInteger(),8192); - ensure_equals("value region_y", response["region_y"].asInteger(),8192); + UNSCOPED_INFO("stream parsed"); + REQUIRE(response.isDefined()); + UNSCOPED_INFO("stream parse count"); + CHECK(count == 13); + UNSCOPED_INFO("sd type"); + CHECK(response.type() == LLSD::TypeMap); + UNSCOPED_INFO("map element count"); + REQUIRE(response.size() == 6); + UNSCOPED_INFO("value connect"); + CHECK(response["connect"].asBoolean() == true); + UNSCOPED_INFO("value region_x"); + CHECK(response["region_x"].asInteger() == 8192); + UNSCOPED_INFO("value region_y"); + CHECK(response["region_y"].asInteger() == 8192); } - template<> template<> - void sd_object::test<2>() + TEST_CASE("LLSD: binary writes to notation as b(6) and parses back", "[LLSD]") { const std::string decoded("random"); //const std::string encoded("cmFuZG9t\n"); @@ -89,22 +151,25 @@ namespace tut sd = buf; std::stringstream str; S32 count = LLSDSerialize::toNotation(sd, str); - ensure_equals("output count", count, 1); + UNSCOPED_INFO("output count"); + CHECK(count == 1); std::string actual(str.str()); - ensure_equals("formatted binary encoding", actual, streamed); + UNSCOPED_INFO("formatted binary encoding"); + CHECK(actual == streamed); sd.clear(); LLSDSerialize::fromNotation(sd, str, str.str().size()); std::vector after; after = sd.asBinary(); - ensure_equals("binary decoded size", after.size(), decoded.size()); - ensure("binary decoding", (0 == memcmp( + UNSCOPED_INFO("binary decoded size"); + REQUIRE(after.size() == decoded.size()); + UNSCOPED_INFO("binary decoding"); + REQUIRE(memcmp( &after[0], decoded.c_str(), - decoded.size()))); + decoded.size()) == 0); } - template<> template<> - void sd_object::test<3>() + TEST_CASE("LLSD: random binary round-trips through notation", "[LLSD]") { for(S32 i = 0; i < 100; ++i) { @@ -121,22 +186,25 @@ namespace tut std::stringstream str; S32 count = LLSDSerialize::toNotation(sd, str); sd.clear(); - ensure_equals("format count", count, 1); + UNSCOPED_INFO("format count"); + REQUIRE(count == 1); LLSD sd2; count = LLSDSerialize::fromNotation(sd2, str, str.str().size()); - ensure_equals("parse count", count, 1); + UNSCOPED_INFO("parse count"); + REQUIRE(count == 1); buf_t dest = sd2.asBinary(); str.str(""); str << "binary encoding size " << i; - ensure_equals(str.str().c_str(), dest.size(), source.size()); + UNSCOPED_INFO(str.str().c_str()); + REQUIRE(dest.size() == source.size()); str.str(""); str << "binary encoding " << i; - ensure(str.str().c_str(), (source == dest)); + UNSCOPED_INFO(str.str().c_str()); + REQUIRE(source == dest); } } - template<> template<> - void sd_object::test<4>() + TEST_CASE("LLSD: an escaped string with newlines and quotes parses back", "[LLSD]") { std::ostringstream ostr; ostr << "{'task_id':u1fd77b79-a8e7-25a5-9454-02a4d948ba1c}\n" @@ -150,12 +218,13 @@ namespace tut sd, serialized, serialized.str().size()); - ensure_equals("parse count", count, 1); - ensure_equals("String streaming", sd.asString(), expected); + UNSCOPED_INFO("parse count"); + CHECK(count == 1); + UNSCOPED_INFO("String streaming"); + CHECK(sd.asString() == expected); } - template<> template<> - void sd_object::test<5>() + TEST_CASE("LLSD: random raw binary in b(size) notation parses", "[LLSD]") { for(S32 i = 0; i < 100; ++i) { @@ -174,15 +243,17 @@ namespace tut str << "\""; LLSD sd; S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size()); - ensure_equals("binary parse", count, 1); + UNSCOPED_INFO("binary parse"); + REQUIRE(count == 1); buf_t actual = sd.asBinary(); - ensure_equals("binary size", actual.size(), (size_t)size); - ensure("binary data", (0 == memcmp(&source[0], &actual[0], size))); + UNSCOPED_INFO("binary size"); + REQUIRE(actual.size() == (size_t)size); + UNSCOPED_INFO("binary data"); + REQUIRE(memcmp(&source[0], &actual[0], size) == 0); } } - template<> template<> - void sd_object::test<6>() + TEST_CASE("LLSD: a raw string in s(size) notation keeps quotes and whitespace", "[LLSD]") { std::string expected("'{\"task_id\":u1fd77b79-a8e7-25a5-9454-02a4d948ba1c}'\t\n\t\t"); std::stringstream str; @@ -191,14 +262,16 @@ namespace tut str << "'"; LLSD sd; S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size()); - ensure_equals("parse count", count, 1); + UNSCOPED_INFO("parse count"); + CHECK(count == 1); std::string actual = sd.asString(); - ensure_equals("string sizes", actual.size(), expected.size()); - ensure_equals("string content", actual, expected); + UNSCOPED_INFO("string sizes"); + REQUIRE(actual.size() == expected.size()); + UNSCOPED_INFO("string content"); + CHECK(actual == expected); } - template<> template<> - void sd_object::test<7>() + TEST_CASE("LLSD: fromNotation parses a map of mixed scalar and array values", "[LLSD]") { std::string msg("come on in"); std::stringstream stream; @@ -211,19 +284,27 @@ namespace tut sd, stream, stream.str().size()); - ensure_equals("parse count", count, 12); - ensure_equals("bool value", sd["connect"].asBoolean(), true); - ensure_equals("message value", sd["message"].asString(), msg); - ensure_equals("pos x", sd["position"][0].asReal(), 45.65); - ensure_equals("pos y", sd["position"][1].asReal(), 100.1); - ensure_equals("pos z", sd["position"][2].asReal(), 25.5); - ensure_equals("look x", sd["look_at"][0].asReal(), 0.0); - ensure_equals("look y", sd["look_at"][1].asReal(), 1.0); - ensure_equals("look z", sd["look_at"][2].asReal(), 0.0); + UNSCOPED_INFO("parse count"); + CHECK(count == 12); + UNSCOPED_INFO("bool value"); + CHECK(sd["connect"].asBoolean() == true); + UNSCOPED_INFO("message value"); + CHECK(sd["message"].asString() == msg); + UNSCOPED_INFO("pos x"); + CHECK(sd["position"][0].asReal() == 45.65); + UNSCOPED_INFO("pos y"); + CHECK(sd["position"][1].asReal() == 100.1); + UNSCOPED_INFO("pos z"); + CHECK(sd["position"][2].asReal() == 25.5); + UNSCOPED_INFO("look x"); + CHECK(sd["look_at"][0].asReal() == 0.0); + UNSCOPED_INFO("look y"); + CHECK(sd["look_at"][1].asReal() == 1.0); + UNSCOPED_INFO("look z"); + CHECK(sd["look_at"][2].asReal() == 0.0); } - template<> template<> - void sd_object::test<8>() + TEST_CASE("LLSD: one-character and empty strings parse in a map", "[LLSD]") { std::stringstream resp; resp << "{'label':'short string test', 'singlechar':'a', 'empty':'', 'endoftest':'end' }"; @@ -232,15 +313,19 @@ namespace tut response, resp, resp.str().size()); - ensure_equals("parse count", count, 5); - ensure_equals("sd type", response.type(), LLSD::TypeMap); - ensure_equals("map element count", response.size(), 4); - ensure_equals("singlechar", response["singlechar"].asString(), "a"); - ensure_equals("empty", response["empty"].asString(), ""); + UNSCOPED_INFO("parse count"); + CHECK(count == 5); + UNSCOPED_INFO("sd type"); + CHECK(response.type() == LLSD::TypeMap); + UNSCOPED_INFO("map element count"); + REQUIRE(response.size() == 4); + UNSCOPED_INFO("singlechar"); + CHECK(response["singlechar"].asString() == "a"); + UNSCOPED_INFO("empty"); + CHECK(response["empty"].asString() == ""); } - template<> template<> - void sd_object::test<9>() + TEST_CASE("LLSD: one-byte binary and raw string values parse in a map", "[LLSD]") { std::ostringstream resp; resp << "{'label':'short binary test', 'singlebinary':b(1)\"A\", 'singlerawstring':s(1)\"A\", 'endoftest':'end' }"; @@ -248,28 +333,31 @@ namespace tut LLSD sd; LLMemoryStream mstr((U8*)str.c_str(), static_cast(str.size())); S32 count = LLSDSerialize::fromNotation(sd, mstr, str.size()); - ensure_equals("parse count", count, 5); - ensure("sd created", sd.isDefined()); - ensure_equals("sd type", sd.type(), LLSD::TypeMap); - ensure_equals("map element count", sd.size(), 4); - ensure_equals( - "label", - sd["label"].asString(), + UNSCOPED_INFO("parse count"); + CHECK(count == 5); + UNSCOPED_INFO("sd created"); + REQUIRE(sd.isDefined()); + UNSCOPED_INFO("sd type"); + CHECK(sd.type() == LLSD::TypeMap); + UNSCOPED_INFO("map element count"); + REQUIRE(sd.size() == 4); + UNSCOPED_INFO("label"); + CHECK(sd["label"].asString() == "short binary test"); std::vector bin = sd["singlebinary"].asBinary(); std::vector expected; expected.resize(1); expected[0] = 'A'; - ensure("single binary", (0 == memcmp(&bin[0], &expected[0], 1))); - ensure_equals( - "single string", - sd["singlerawstring"].asString(), + UNSCOPED_INFO("single binary"); + CHECK(memcmp(&bin[0], &expected[0], 1) == 0); + UNSCOPED_INFO("single string"); + CHECK(sd["singlerawstring"].asString() == std::string("A")); - ensure_equals("end", sd["endoftest"].asString(), "end"); + UNSCOPED_INFO("end"); + CHECK(sd["endoftest"].asString() == "end"); } - template<> template<> - void sd_object::test<10>() + TEST_CASE("LLSD: escapeString escapes single quotes so a message parses back", "[LLSD]") { std::string message("parcel '' is naughty."); @@ -278,41 +366,46 @@ namespace tut << "'}"; std::string expected_str("{'message':'parcel \\'\\' is naughty.'}"); std::string actual_str = str.str(); - ensure_equals("stream contents", actual_str, expected_str); + UNSCOPED_INFO("stream contents"); + CHECK(actual_str == expected_str); LLSD sd; S32 count = LLSDSerialize::fromNotation(sd, str, actual_str.size()); - ensure_equals("parse count", count, 2); - ensure("valid parse", sd.isDefined()); + UNSCOPED_INFO("parse count"); + CHECK(count == 2); + UNSCOPED_INFO("valid parse"); + REQUIRE(sd.isDefined()); std::string actual = sd["message"].asString(); - ensure_equals("message contents", actual, message); + UNSCOPED_INFO("message contents"); + CHECK(actual == message); } - template<> template<> - void sd_object::test<11>() + TEST_CASE("LLSD: a string of single and double quotes escapes and parses back", "[LLSD]") { std::string expected("\"\"\"\"''''''\""); std::stringstream str; str << "'" << LLSDNotationFormatter::escapeString(expected) << "'"; LLSD sd; S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size()); - ensure_equals("parse count", count, 1); - ensure_equals("string value", sd.asString(), expected); + UNSCOPED_INFO("parse count"); + CHECK(count == 1); + UNSCOPED_INFO("string value"); + CHECK(sd.asString() == expected); } - template<> template<> - void sd_object::test<12>() + TEST_CASE("LLSD: a string ending in a backslash escapes and parses back", "[LLSD]") { std::string expected("mytest\\"); std::stringstream str; str << "'" << LLSDNotationFormatter::escapeString(expected) << "'"; LLSD sd; S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size()); - ensure_equals("parse count", count, 1); - ensure_equals("string value", sd.asString(), expected); + UNSCOPED_INFO("parse count"); + CHECK(count == 1); + UNSCOPED_INFO("string value"); + CHECK(sd.asString() == expected); } - template<> template<> - void sd_object::test<13>() + TEST_CASE("LLSD: random strings escape and parse back", "[LLSD]") { for(S32 i = 0; i < 1000; ++i) { @@ -328,7 +421,8 @@ namespace tut str << "'" << LLSDNotationFormatter::escapeString(expected) << "'"; LLSD sd; S32 count = LLSDSerialize::fromNotation(sd, str, expected.size()); - ensure_equals("parse count", count, 1); + UNSCOPED_INFO("parse count"); + REQUIRE(count == 1); std::string actual = sd.asString(); /* if(actual != expected) @@ -359,12 +453,12 @@ namespace tut LL_WARNS() << "actual: " << a_str.str() << LL_ENDL; } */ - ensure_equals("string value", actual, expected); + UNSCOPED_INFO("string value"); + REQUIRE(actual == expected); } } - template<> template<> - void sd_object::test<14>() + TEST_CASE("LLSD: an instant message parameter array parses", "[LLSD]") { //#if LL_WINDOWS && _MSC_VER >= 1400 // skip_fail("Fails on VS2005 due to broken LLSDSerialize::fromNotation() parser."); @@ -374,103 +468,80 @@ namespace tut istr.str(param); LLSD param_sd; LLSDSerialize::fromNotation(param_sd, istr, param.size()); - ensure_equals("parsed type", param_sd.type(), LLSD::TypeArray); + UNSCOPED_INFO("parsed type"); + CHECK(param_sd.type() == LLSD::TypeArray); LLSD version_sd = param_sd[0]; - ensure_equals("version type", version_sd.type(), LLSD::TypeMap); - ensure("has version", version_sd.has("version")); - ensure_equals("version number", version_sd["version"].asInteger(), 1); + UNSCOPED_INFO("version type"); + CHECK(version_sd.type() == LLSD::TypeMap); + UNSCOPED_INFO("has version"); + REQUIRE(version_sd.has("version")); + UNSCOPED_INFO("version number"); + CHECK(version_sd["version"].asInteger() == 1); LLSD src_sd = param_sd[1]; - ensure_equals("src type", src_sd.type(), LLSD::TypeMap); + UNSCOPED_INFO("src type"); + CHECK(src_sd.type() == LLSD::TypeMap); LLSD dst_sd = param_sd[2]; - ensure_equals("dst type", dst_sd.type(), LLSD::TypeMap); + UNSCOPED_INFO("dst type"); + CHECK(dst_sd.type() == LLSD::TypeMap); } - template<> template<> - void sd_object::test<15>() + TEST_CASE("LLSD: an undefined value parses in a map in an array", "[LLSD]") { std::string val = "[{'failures':!,'successfuls':[u3c115e51-04f4-523c-9fa6-98aff1034730]}]"; std::istringstream istr; istr.str(val); LLSD sd; LLSDSerialize::fromNotation(sd, istr, val.size()); - ensure_equals("parsed type", sd.type(), LLSD::TypeArray); - ensure_equals("parsed size", sd.size(), 1); + UNSCOPED_INFO("parsed type"); + CHECK(sd.type() == LLSD::TypeArray); + UNSCOPED_INFO("parsed size"); + REQUIRE(sd.size() == 1); LLSD failures = sd[0]["failures"]; - ensure("no failures.", failures.isUndefined()); + UNSCOPED_INFO("no failures."); + REQUIRE(failures.isUndefined()); LLSD success = sd[0]["successfuls"]; - ensure_equals("success type", success.type(), LLSD::TypeArray); - ensure_equals("success size", success.size(), 1); - ensure_equals("success instance type", success[0].type(), LLSD::TypeUUID); + UNSCOPED_INFO("success type"); + CHECK(success.type() == LLSD::TypeArray); + UNSCOPED_INFO("success size"); + REQUIRE(success.size() == 1); + UNSCOPED_INFO("success instance type"); + CHECK(success[0].type() == LLSD::TypeUUID); } - template<> template<> - void sd_object::test<16>() + TEST_CASE("LLSD: booleans parse from f t 0 and 1 in an array and a map", "[LLSD]") { std::string val = "[f,t,0,1,{'foo':t,'bar':f}]"; std::istringstream istr; istr.str(val); LLSD sd; LLSDSerialize::fromNotation(sd, istr, val.size()); - ensure_equals("parsed type", sd.type(), LLSD::TypeArray); - ensure_equals("parsed size", sd.size(), 5); - ensure_equals("element 0 false", sd[0].asBoolean(), false); - ensure_equals("element 1 true", sd[1].asBoolean(), true); - ensure_equals("element 2 false", sd[2].asBoolean(), false); - ensure_equals("element 3 true", sd[3].asBoolean(), true); + UNSCOPED_INFO("parsed type"); + CHECK(sd.type() == LLSD::TypeArray); + UNSCOPED_INFO("parsed size"); + REQUIRE(sd.size() == 5); + UNSCOPED_INFO("element 0 false"); + CHECK(sd[0].asBoolean() == false); + UNSCOPED_INFO("element 1 true"); + CHECK(sd[1].asBoolean() == true); + UNSCOPED_INFO("element 2 false"); + CHECK(sd[2].asBoolean() == false); + UNSCOPED_INFO("element 3 true"); + CHECK(sd[3].asBoolean() == true); LLSD map = sd[4]; - ensure_equals("element 4 type", map.type(), LLSD::TypeMap); - ensure_equals("map foo type", map["foo"].type(), LLSD::TypeBoolean); - ensure_equals("map foo value", map["foo"].asBoolean(), true); - ensure_equals("map bar type", map["bar"].type(), LLSD::TypeBoolean); - ensure_equals("map bar value", map["bar"].asBoolean(), false); - } - -/* - template<> template<> - void sd_object::test<16>() - { - } -*/ -} - -#if 0 -'{\'task_id\':u1fd77b79-a8e7-25a5-9454-02a4d948ba1c}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t00082000\n\t\tcreator_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t00000000-0000-0000-0000-000000000000\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t10284\n\ttotal_crc\t35\n\ttype\t1\n\ttask_valid\t2\n\ttravel_access\t21\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t0\t0\t0\n\toldpos\t0\t0\t0\n\trotation\t4.371139183945160766597837e-08\t1\t4.371139183945160766597837e-08\t0\n\tvelocity\t0\t0\t0\n\tangvel\t0\t0\t0\n\tscale\t0.2816932\t0.2816932\t0.2816932\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t80\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t6\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t1132625972249870\n\tbirthtime\t1132625953120694\n\treztime\t1132625953120694\n\tparceltime\t1132625953120694\n\ttax_rate\t1.01615\n\tnamevalue\tAttachmentOrientation VEC3 RW DS -3.141593, 0.000000, -3.141593\n\tnamevalue\tAttachmentOffset VEC3 RW DS 0.000000, 0.000000, 0.000000\n\tnamevalue\tAttachPt U32 RW S 5\n\tnamevalue\tAttachItemID STRING RW SV 1f9975c0-2951-1b93-dd83-46e2b932fcc8\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\torig_asset_id\t52019cdd-b464-ba19-e66d-3da751fef9da\n\torig_item_id\t1f9975c0-2951-1b93-dd83-46e2b932fcc8\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n' -#endif - -namespace tut -{ - struct mem_data - { - }; - typedef test_group mem_test; - typedef mem_test::object mem_object; - tut::mem_test mem_stream("LLMemoryStream"); - - template<> template<> - void mem_object::test<1>() - { - const char HELLO_WORLD[] = "hello world"; - LLMemoryStream mem((U8*)&HELLO_WORLD[0], static_cast(strlen(HELLO_WORLD))); /* Flawfinder: ignore */ - std::string hello; - std::string world; - mem >> hello >> world; - ensure_equals("first word", hello, std::string("hello")); - ensure_equals("second word", world, std::string("world")); + UNSCOPED_INFO("element 4 type"); + CHECK(map.type() == LLSD::TypeMap); + UNSCOPED_INFO("map foo type"); + CHECK(map["foo"].type() == LLSD::TypeBoolean); + UNSCOPED_INFO("map foo value"); + CHECK(map["foo"].asBoolean() == true); + UNSCOPED_INFO("map bar type"); + CHECK(map["bar"].type() == LLSD::TypeBoolean); + UNSCOPED_INFO("map bar value"); + CHECK(map["bar"].asBoolean() == false); } -} - -namespace tut -{ - struct U64_data - { - }; - typedef test_group U64_test; - typedef U64_test::object U64_object; - tut::U64_test U64_testcase("U64_conversion"); // U64_to_str - template<> template<> - void U64_object::test<1>() + TEST_CASE("U64_conversion: U64_to_str writes the digits and truncates to the buffer", "[U64_conversion]") { U64 val; std::string val_str; @@ -482,184 +553,169 @@ namespace tut U64_to_str(val, result, sizeof(result)); result_str = (const char*) result; - ensure_equals("U64_to_str converted 1.1", val_str, result_str); + UNSCOPED_INFO("U64_to_str converted 1.1"); + CHECK(val_str == result_str); val = 0; val_str = "0"; U64_to_str(val, result, sizeof(result)); result_str = (const char*) result; - ensure_equals("U64_to_str converted 1.2", val_str, result_str); + UNSCOPED_INFO("U64_to_str converted 1.2"); + CHECK(val_str == result_str); val = U64L(18446744073709551615); // 0xFFFFFFFFFFFFFFFF val_str = "18446744073709551615"; U64_to_str(val, result, sizeof(result)); result_str = (const char*) result; - ensure_equals("U64_to_str converted 1.3", val_str, result_str); + UNSCOPED_INFO("U64_to_str converted 1.3"); + CHECK(val_str == result_str); // overflow - will result in warning at compile time val = U64L(18446744073709551615) + 1; // overflow 0xFFFFFFFFFFFFFFFF + 1 == 0 val_str = "0"; U64_to_str(val, result, sizeof(result)); result_str = (const char*) result; - ensure_equals("U64_to_str converted 1.4", val_str, result_str); + UNSCOPED_INFO("U64_to_str converted 1.4"); + CHECK(val_str == result_str); val = U64(-1); // 0xFFFFFFFFFFFFFFFF == 18446744073709551615 val_str = "18446744073709551615"; U64_to_str(val, result, sizeof(result)); result_str = (const char*) result; - ensure_equals("U64_to_str converted 1.5", val_str, result_str); + UNSCOPED_INFO("U64_to_str converted 1.5"); + CHECK(val_str == result_str); val = U64L(10000000000000000000); // testing preserving of 0s val_str = "10000000000000000000"; U64_to_str(val, result, sizeof(result)); result_str = (const char*) result; - ensure_equals("U64_to_str converted 1.6", val_str, result_str); + UNSCOPED_INFO("U64_to_str converted 1.6"); + CHECK(val_str == result_str); val = 1; // testing no leading 0s val_str = "1"; U64_to_str(val, result, sizeof(result)); result_str = (const char*) result; - ensure_equals("U64_to_str converted 1.7", val_str, result_str); + UNSCOPED_INFO("U64_to_str converted 1.7"); + CHECK(val_str == result_str); val = U64L(18446744073709551615); // testing exact sized buffer for result val_str = "18446744073709551615"; memset(result, 'A', sizeof(result)); // initialize buffer with all 'A' U64_to_str(val, result, sizeof("18446744073709551615")); //pass in the exact size result_str = (const char*) result; - ensure_equals("U64_to_str converted 1.8", val_str, result_str); + UNSCOPED_INFO("U64_to_str converted 1.8"); + CHECK(val_str == result_str); val = U64L(18446744073709551615); // testing smaller sized buffer for result val_str = "1844"; memset(result, 'A', sizeof(result)); // initialize buffer with all 'A' U64_to_str(val, result, 5); //pass in a size of 5. should only copy first 4 integers and add a null terminator result_str = (const char*) result; - ensure_equals("U64_to_str converted 1.9", val_str, result_str); + UNSCOPED_INFO("U64_to_str converted 1.9"); + CHECK(val_str == result_str); } // str_to_U64 - template<> template<> - void U64_object::test<2>() + TEST_CASE("U64_conversion: str_to_U64 reads the first run of digits and wraps on overflow", "[U64_conversion]") { U64 val; U64 result; val = U64L(18446744073709551610); // slightly less than MAX_U64 result = str_to_U64("18446744073709551610"); - ensure_equals("str_to_U64 converted 2.1", val, result); + UNSCOPED_INFO("str_to_U64 converted 2.1"); + CHECK(val == result); val = U64L(0); // empty string result = str_to_U64(LLStringUtil::null); - ensure_equals("str_to_U64 converted 2.2", val, result); + UNSCOPED_INFO("str_to_U64 converted 2.2"); + CHECK(val == result); val = U64L(0); // 0 result = str_to_U64("0"); - ensure_equals("str_to_U64 converted 2.3", val, result); + UNSCOPED_INFO("str_to_U64 converted 2.3"); + CHECK(val == result); val = U64L(18446744073709551615); // 0xFFFFFFFFFFFFFFFF result = str_to_U64("18446744073709551615"); - ensure_equals("str_to_U64 converted 2.4", val, result); + UNSCOPED_INFO("str_to_U64 converted 2.4"); + CHECK(val == result); // overflow - will result in warning at compile time val = U64L(18446744073709551615) + 1; // overflow 0xFFFFFFFFFFFFFFFF + 1 == 0 result = str_to_U64("18446744073709551616"); - ensure_equals("str_to_U64 converted 2.5", val, result); + UNSCOPED_INFO("str_to_U64 converted 2.5"); + CHECK(val == result); val = U64L(1234); // process till first non-integral character result = str_to_U64("1234A5678"); - ensure_equals("str_to_U64 converted 2.6", val, result); + UNSCOPED_INFO("str_to_U64 converted 2.6"); + CHECK(val == result); val = U64L(5678); // skip all non-integral characters result = str_to_U64("ABCD5678"); - ensure_equals("str_to_U64 converted 2.7", val, result); + UNSCOPED_INFO("str_to_U64 converted 2.7"); + CHECK(val == result); // should it skip negative sign and process // rest of string or return 0 val = U64L(1234); // skip initial negative sign result = str_to_U64("-1234"); - ensure_equals("str_to_U64 converted 2.8", val, result); + UNSCOPED_INFO("str_to_U64 converted 2.8"); + CHECK(val == result); val = U64L(5678); // stop at negative sign in the middle result = str_to_U64("5678-1234"); - ensure_equals("str_to_U64 converted 2.9", val, result); + UNSCOPED_INFO("str_to_U64 converted 2.9"); + CHECK(val == result); val = U64L(0); // no integers result = str_to_U64("AaCD"); - ensure_equals("str_to_U64 converted 2.10", val, result); + UNSCOPED_INFO("str_to_U64 converted 2.10"); + CHECK(val == result); } // U64_to_F64 - template<> template<> - void U64_object::test<3>() + TEST_CASE("U64_conversion: U64_to_F64 converts values from zero to the largest U64", "[U64_conversion]") { F64 val; F64 result; result = 18446744073709551610.0; val = U64_to_F64(U64L(18446744073709551610)); - ensure_equals("U64_to_F64 converted 3.1", val, result); + UNSCOPED_INFO("U64_to_F64 converted 3.1"); + CHECK(val == result); result = 18446744073709551615.0; // 0xFFFFFFFFFFFFFFFF val = U64_to_F64(U64L(18446744073709551615)); - ensure_equals("U64_to_F64 converted 3.2", val, result); + UNSCOPED_INFO("U64_to_F64 converted 3.2"); + CHECK(val == result); result = 0.0; // overflow 0xFFFFFFFFFFFFFFFF + 1 == 0 // overflow - will result in warning at compile time val = U64_to_F64(U64L(18446744073709551615)+1); - ensure_equals("U64_to_F64 converted 3.3", val, result); + UNSCOPED_INFO("U64_to_F64 converted 3.3"); + CHECK(val == result); result = 0.0; // 0 val = U64_to_F64(U64L(0)); - ensure_equals("U64_to_F64 converted 3.4", val, result); + UNSCOPED_INFO("U64_to_F64 converted 3.4"); + CHECK(val == result); result = 1.0; // odd val = U64_to_F64(U64L(1)); - ensure_equals("U64_to_F64 converted 3.5", val, result); + UNSCOPED_INFO("U64_to_F64 converted 3.5"); + CHECK(val == result); result = 2.0; // even val = U64_to_F64(U64L(2)); - ensure_equals("U64_to_F64 converted 3.6", val, result); + UNSCOPED_INFO("U64_to_F64 converted 3.6"); + CHECK(val == result); result = F64(0x7FFFFFFFFFFFFFFF) * 1.0; // 0x7FFFFFFFFFFFFFFF val = U64_to_F64(U64L(0x7FFFFFFFFFFFFFFF)); - ensure_equals("U64_to_F64 converted 3.7", val, result); - } - - // llstrtou64 - // seems to be deprecated - could not find it being used - // anywhere in the tarball - skipping unit tests for now -} - - -namespace tut -{ - struct hash_data - { - }; - typedef test_group hash_test; - typedef hash_test::object hash_object; - tut::hash_test hash_tester("LLHash"); - - template<> template<> - void hash_object::test<1>() - { - const char * str1 = "test string one"; - const char * same_as_str1 = "test string one"; - - size_t hash1 = llhash(str1); - size_t same_as_hash1 = llhash(same_as_str1); - - - ensure("Hashes from identical strings should be equal", hash1 == same_as_hash1); - - char str[100]; - strcpy( str, "Another test" ); - - size_t hash2 = llhash(str); - - strcpy( str, "Different string, same pointer" ); - - size_t hash3 = llhash(str); - - ensure("Hashes from same pointer but different string should not be equal", hash2 != hash3); + UNSCOPED_INFO("U64_to_F64 converted 3.7"); + CHECK(val == result); } } diff --git a/indra/llcommon/tests/lazyeventapi_test.cpp b/indra/llcommon/tests/lazyeventapi_test.cpp index f2c2bf168e..133068c927 100644 --- a/indra/llcommon/tests/lazyeventapi_test.cpp +++ b/indra/llcommon/tests/lazyeventapi_test.cpp @@ -17,7 +17,8 @@ // std headers // external library headers // other Linden headers -#include "../test/lltut.h" +#include +#include #include "llevents.h" #include "llsdutil.h" @@ -69,7 +70,7 @@ class MyRegistrar: public LL::LazyEventAPI /***************************************************************************** * TUT *****************************************************************************/ -namespace tut +namespace { struct lazyeventapi_data { @@ -84,36 +85,29 @@ namespace tut LLEventPumps::deleteSingleton(); } }; - typedef test_group lazyeventapi_group; - typedef lazyeventapi_group::object object; - lazyeventapi_group lazyeventapigrp("lazyeventapi"); - template<> template<> - void object::test<1>() + TEST_CASE_METHOD(lazyeventapi_data, "lazyeventapi: a LazyEventAPI creates its listener when its pump is used", "[lazyeventapi]") { - set_test_name("LazyEventAPI"); // this is where the magic (should) happen // 'register' still a keyword until C++17 MyRegistrar regster; LLEventPumps::instance().obtain("Test").post(llsd::map("op", "set", "data", "hey")); - ensure_equals("failed to set data", data.asString(), "hey"); + UNSCOPED_INFO("failed to set data"); + CHECK(data.asString() == "hey"); } - template<> template<> - void object::test<2>() + TEST_CASE_METHOD(lazyeventapi_data, "lazyeventapi: without a LazyEventAPI no listener answers its pump", "[lazyeventapi]") { - set_test_name("No LazyEventAPI"); // Because the MyRegistrar declaration in test<1>() is local, because // it has been destroyed, we fully expect NOT to reach a MyListener // instance with this post. LLEventPumps::instance().obtain("Test").post(llsd::map("op", "set", "data", "moot")); - ensure("accidentally set data", ! data.isDefined()); + UNSCOPED_INFO("accidentally set data"); + REQUIRE_FALSE(data.isDefined()); } - template<> template<> - void object::test<3>() + TEST_CASE_METHOD(lazyeventapi_data, "lazyeventapi: instance_snapshot finds a LazyEventAPI and its metadata", "[lazyeventapi]") { - set_test_name("LazyEventAPI metadata"); MyRegistrar regster; // Of course we have 'regster' in hand; we don't need to search for // it. But this next test verifies that we can find (all) LazyEventAPI @@ -124,19 +118,28 @@ namespace tut for (const auto& registrar : LL::LazyEventAPIBase::instance_snapshot()) if ((found = dynamic_cast(®istrar))) break; - ensure("Failed to find MyRegistrar via LLInstanceTracker", found); + UNSCOPED_INFO("Failed to find MyRegistrar via LLInstanceTracker"); + REQUIRE(found); - ensure_equals("wrong API name", found->getName(), "Test"); - ensure_contains("wrong API desc", found->getDesc(), "test LLEventAPI"); - ensure_equals("wrong API field", found->getDispatchKey(), "op"); + UNSCOPED_INFO("wrong API name"); + CHECK(found->getName() == "Test"); + UNSCOPED_INFO("wrong API desc"); + CHECK_THAT(found->getDesc(), Catch::Matchers::ContainsSubstring("test LLEventAPI")); + UNSCOPED_INFO("wrong API field"); + CHECK(found->getDispatchKey() == "op"); // Normally we'd just iterate over *found. But for test purposes, // actually capture the range of NameDesc pairs in a vector. std::vector ops{ found->begin(), found->end() }; - ensure_equals("failed to find operations", ops.size(), 1); - ensure_equals("wrong operation name", ops[0].first, "set"); - ensure_contains("wrong operation desc", ops[0].second, "set operation"); + UNSCOPED_INFO("failed to find operations"); + REQUIRE(ops.size() == 1); + UNSCOPED_INFO("wrong operation name"); + CHECK(ops[0].first == "set"); + UNSCOPED_INFO("wrong operation desc"); + CHECK_THAT(ops[0].second, Catch::Matchers::ContainsSubstring("set operation")); LLSD metadata{ found->getMetadata(ops[0].first) }; - ensure_equals("bad metadata name", metadata["name"].asString(), ops[0].first); - ensure_equals("bad metadata desc", metadata["desc"].asString(), ops[0].second); + UNSCOPED_INFO("bad metadata name"); + CHECK(metadata["name"].asString() == ops[0].first); + UNSCOPED_INFO("bad metadata desc"); + CHECK(metadata["desc"].asString() == ops[0].second); } -} // namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/llapp_test.cpp b/indra/llcommon/tests/llapp_test.cpp index adcbedee4b..021ad62a29 100644 --- a/indra/llcommon/tests/llapp_test.cpp +++ b/indra/llcommon/tests/llapp_test.cpp @@ -25,14 +25,15 @@ * $/LicenseInfo$ */ -#include +#include #include "linden_common.h" #include "llapp.h" -#include "lltut.h" +#include "alteststateguard.h" -namespace tut + +namespace { struct application { @@ -51,15 +52,16 @@ namespace tut ~application() { delete mApp; + // An LLApp ends LLCommon with it, the APR pool and LLTrace's + // master recorder, and this binary is alone so that one can: + // the next case starts from what this one leaves. + ll_test::StateGuard::accept("the LLApp"); + ll_test::StateGuard::accept("the LLTrace master recorder"); + ll_test::StateGuard::accept("the APR pool"); } }; - typedef test_group application_t; - typedef application_t::object application_object_t; - tut::application_t tut_application("application"); - - template<> template<> - void application_object_t::test<1>() + TEST_CASE_METHOD(application, "application: getOption returns the default option data", "[application]") { LLSD defaults; defaults["template"] = "../../../scripts/messages/message_template.msg"; @@ -68,20 +70,22 @@ namespace tut mApp->setOptionData(LLApp::PRIORITY_DEFAULT, defaults); LLSD datadir_sd = mApp->getOption("datadir"); - ensure_equals("data type", datadir_sd.type(), LLSD::TypeString); - ensure_equals( - "data value", datadir_sd.asString(), std::string("data")); + UNSCOPED_INFO("data type"); + CHECK(datadir_sd.type() == LLSD::TypeString); + UNSCOPED_INFO("data value"); + CHECK(datadir_sd.asString() == std::string("data")); } - template<> template<> - void application_object_t::test<2>() + TEST_CASE_METHOD(application, "application: a runtime override beats the general configuration", "[application]") { LLSD options; options["boolean-test"] = true; mApp->setOptionData(LLApp::PRIORITY_GENERAL_CONFIGURATION, options); - ensure("bool set", mApp->getOption("boolean-test").asBoolean()); + UNSCOPED_INFO("bool set"); + CHECK(mApp->getOption("boolean-test").asBoolean()); options["boolean-test"] = false; mApp->setOptionData(LLApp::PRIORITY_RUNTIME_OVERRIDE, options); - ensure("bool unset", !mApp->getOption("boolean-test").asBoolean()); + UNSCOPED_INFO("bool unset"); + CHECK_FALSE(mApp->getOption("boolean-test").asBoolean()); } } diff --git a/indra/llcommon/tests/llbase64_test.cpp b/indra/llcommon/tests/llbase64_test.cpp index 69b062fc0c..14a5636d0e 100644 --- a/indra/llcommon/tests/llbase64_test.cpp +++ b/indra/llcommon/tests/llbase64_test.cpp @@ -32,46 +32,38 @@ #include "../llbase64.h" #include "../lluuid.h" -#include "../test/lltut.h" +#include -namespace tut +namespace { - struct base64_data - { - }; - typedef test_group base64_test; - typedef base64_test::object base64_object; - tut::base64_test base64("LLBase64"); - - template<> template<> - void base64_object::test<1>() + TEST_CASE("LLBase64: encode of no bytes and of a null and a set UUID", "[LLBase64]") { std::string result; result = LLBase64::encode(NULL, 0); - ensure("encode nothing", (result == "") ); + UNSCOPED_INFO("encode nothing"); + CHECK(result == ""); LLUUID nothing; result = LLBase64::encode(¬hing.mData[0], UUID_BYTES); - ensure("encode blank uuid", - (result == "AAAAAAAAAAAAAAAAAAAAAA==") ); + UNSCOPED_INFO("encode blank uuid"); + CHECK(result == "AAAAAAAAAAAAAAAAAAAAAA=="); LLUUID id("526a1e07-a19d-baed-84c4-ff08a488d15e"); result = LLBase64::encode(&id.mData[0], UUID_BYTES); - ensure("encode random uuid", - (result == "UmoeB6Gduu2ExP8IpIjRXg==") ); + UNSCOPED_INFO("encode random uuid"); + CHECK(result == "UmoeB6Gduu2ExP8IpIjRXg=="); } - template<> template<> - void base64_object::test<2>() + TEST_CASE("LLBase64: encode of 40 arbitrary bytes", "[LLBase64]") { std::string result; U8 blob[40] = { 115, 223, 172, 255, 140, 70, 49, 125, 236, 155, 45, 199, 101, 17, 164, 131, 230, 19, 80, 64, 112, 53, 135, 98, 237, 12, 26, 72, 126, 14, 145, 143, 118, 196, 11, 177, 132, 169, 195, 134 }; result = LLBase64::encode(&blob[0], 40); - ensure("encode 40 bytes", - (result == "c9+s/4xGMX3smy3HZRGkg+YTUEBwNYdi7QwaSH4OkY92xAuxhKnDhg==") ); + UNSCOPED_INFO("encode 40 bytes"); + CHECK(result == "c9+s/4xGMX3smy3HZRGkg+YTUEBwNYdi7QwaSH4OkY92xAuxhKnDhg=="); } } diff --git a/indra/llcommon/tests/llcond_test.cpp b/indra/llcommon/tests/llcond_test.cpp index f2a302ed13..d4b27d559c 100644 --- a/indra/llcommon/tests/llcond_test.cpp +++ b/indra/llcommon/tests/llcond_test.cpp @@ -17,51 +17,44 @@ // std headers // external library headers // other Linden headers -#include "../test/lltut.h" +#include #include "llcoros.h" /***************************************************************************** * TUT *****************************************************************************/ -namespace tut +namespace { struct llcond_data { LLScalarCond cond{0}; }; - typedef test_group llcond_group; - typedef llcond_group::object object; - llcond_group llcondgrp("llcond"); - template<> template<> - void object::test<1>() + TEST_CASE_METHOD(llcond_data, "llcond: a wait for the value it already holds returns at once", "[llcond]") { - 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)); + UNSCOPED_INFO("wait_for_equal() failed"); + CHECK(cond.wait_for_equal(F32Milliseconds(1), 1)); + UNSCOPED_INFO("wait_for_unequal() should have failed"); + CHECK_FALSE(cond.wait_for_unequal(F32Milliseconds(1), 1)); } - template<> template<> - void object::test<2>() + TEST_CASE_METHOD(llcond_data, "llcond: two coroutines take turns through set_all and wait_equal", "[llcond]") { - 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); + REQUIRE(cond.get() == 0); cond.set_all(1); cond.wait_equal(2); - ensure_equals(cond.get(), 2); + REQUIRE(cond.get() == 2); cond.set_all(3); }); // Main coroutine is resumed only when the lambda waits. - ensure_equals(cond.get(), 1); + CHECK(cond.get() == 1); cond.set_all(2); cond.wait_equal(3); } -} // namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/lldate_test.cpp b/indra/llcommon/tests/lldate_test.cpp index 9a4116e4a4..c6f31dc8e5 100644 --- a/indra/llcommon/tests/lldate_test.cpp +++ b/indra/llcommon/tests/lldate_test.cpp @@ -31,7 +31,7 @@ #include "../llstring.h" #include "../lldate.h" -#include "../test/lltut.h" +#include #define VALID_DATE "2003-04-30T04:00:00Z" @@ -65,14 +65,6 @@ namespace tut { - struct date_test - { - - }; - typedef test_group date_test_t; - typedef date_test_t::object date_test_object_t; - tut::date_test_t tut_date_test("LLDate"); - // Compile-time verification that LLDate's default ctor, accessors, and // comparison operators are constexpr. static_assert(LLDate{}.isNull(), "default LLDate is null at compile time"); @@ -84,58 +76,69 @@ namespace tut static_assert(LLDate{} >= LLDate{}, "default LLDate >= itself"); /* format validation */ - template<> template<> - void date_test_object_t::test<1>() + TEST_CASE("LLDate: fromString accepts valid dates and rejects malformed ones", "[LLDate]") { LLDate date(VALID_DATE); std::string expected_string; bool result; expected_string = VALID_DATE; - ensure_equals("Valid Date failed" , expected_string, date.asString()); + UNSCOPED_INFO("Valid Date failed"); + CHECK(expected_string == date.asString()); result = date.fromString(VALID_DATE_LEAP); expected_string = VALID_DATE_LEAP; - ensure_equals("VALID_DATE_LEAP failed" , expected_string, date.asString()); + UNSCOPED_INFO("VALID_DATE_LEAP failed"); + CHECK(expected_string == date.asString()); result = date.fromString(VALID_DATE_HOUR_BOUNDARY); expected_string = VALID_DATE_HOUR_BOUNDARY; - ensure_equals("VALID_DATE_HOUR_BOUNDARY failed" , expected_string, date.asString()); + UNSCOPED_INFO("VALID_DATE_HOUR_BOUNDARY failed"); + CHECK(expected_string == date.asString()); result = date.fromString(VALID_DATE_FRACTIONAL_SECS); expected_string = VALID_DATE_FRACTIONAL_SECS; - ensure_equals("VALID_DATE_FRACTIONAL_SECS failed" , expected_string, date.asString()); + UNSCOPED_INFO("VALID_DATE_FRACTIONAL_SECS failed"); + CHECK(expected_string == date.asString()); result = date.fromString(INVALID_DATE_MISSING_YEAR); - ensure_equals("INVALID_DATE_MISSING_YEAR should have failed" , result, false); + UNSCOPED_INFO("INVALID_DATE_MISSING_YEAR should have failed"); + CHECK(result == false); result = date.fromString(INVALID_DATE_MISSING_MONTH); - ensure_equals("INVALID_DATE_MISSING_MONTH should have failed" , result, false); + UNSCOPED_INFO("INVALID_DATE_MISSING_MONTH should have failed"); + CHECK(result == false); result = date.fromString(INVALID_DATE_MISSING_DATE); - ensure_equals("INVALID_DATE_MISSING_DATE should have failed" , result, false); + UNSCOPED_INFO("INVALID_DATE_MISSING_DATE should have failed"); + CHECK(result == false); result = date.fromString(INVALID_DATE_MISSING_T); - ensure_equals("INVALID_DATE_MISSING_T should have failed" , result, false); + UNSCOPED_INFO("INVALID_DATE_MISSING_T should have failed"); + CHECK(result == false); result = date.fromString(INVALID_DATE_MISSING_HOUR); - ensure_equals("INVALID_DATE_MISSING_HOUR should have failed" , result, false); + UNSCOPED_INFO("INVALID_DATE_MISSING_HOUR should have failed"); + CHECK(result == false); result = date.fromString(INVALID_DATE_MISSING_MIN); - ensure_equals("INVALID_DATE_MISSING_MIN should have failed" , result, false); + UNSCOPED_INFO("INVALID_DATE_MISSING_MIN should have failed"); + CHECK(result == false); result = date.fromString(INVALID_DATE_MISSING_SEC); - ensure_equals("INVALID_DATE_MISSING_SEC should have failed" , result, false); + UNSCOPED_INFO("INVALID_DATE_MISSING_SEC should have failed"); + CHECK(result == false); result = date.fromString(INVALID_DATE_MISSING_Z); - ensure_equals("INVALID_DATE_MISSING_Z should have failed" , result, false); + UNSCOPED_INFO("INVALID_DATE_MISSING_Z should have failed"); + CHECK(result == false); result = date.fromString(INVALID_DATE_EMPTY); - ensure_equals("INVALID_DATE_EMPTY should have failed" , result, false); + UNSCOPED_INFO("INVALID_DATE_EMPTY should have failed"); + CHECK(result == false); } /* Invalid Value Handling */ - template<> template<> - void date_test_object_t::test<2>() + TEST_CASE("LLDate: fromString rejects out-of-range field values", "[LLDate]") { #if LL_DATE_PARSER_CHECKS_BOUNDARY LLDate date; @@ -143,72 +146,81 @@ namespace tut bool result; result = date.fromString(INVALID_DATE_24HOUR_BOUNDARY); - ensure_equals("INVALID_DATE_24HOUR_BOUNDARY should have failed" , result, false); - ensure_equals("INVALID_DATE_24HOUR_BOUNDARY date still set to old value on failure!" , date.secondsSinceEpoch(), 0); + UNSCOPED_INFO("INVALID_DATE_24HOUR_BOUNDARY should have failed"); + REQUIRE(result == false); + UNSCOPED_INFO("INVALID_DATE_24HOUR_BOUNDARY date still set to old value on failure!"); + REQUIRE(date.secondsSinceEpoch() == 0); result = date.fromString(INVALID_DATE_LEAP); - ensure_equals("INVALID_DATE_LEAP should have failed" , result, false); + UNSCOPED_INFO("INVALID_DATE_LEAP should have failed"); + REQUIRE(result == false); result = date.fromString(INVALID_DATE_HOUR); - ensure_equals("INVALID_DATE_HOUR should have failed" , result, false); + UNSCOPED_INFO("INVALID_DATE_HOUR should have failed"); + REQUIRE(result == false); result = date.fromString(INVALID_DATE_MIN); - ensure_equals("INVALID_DATE_MIN should have failed" , result, false); + UNSCOPED_INFO("INVALID_DATE_MIN should have failed"); + REQUIRE(result == false); result = date.fromString(INVALID_DATE_SEC); - ensure_equals("INVALID_DATE_SEC should have failed" , result, false); + UNSCOPED_INFO("INVALID_DATE_SEC should have failed"); + REQUIRE(result == false); result = date.fromString(INVALID_DATE_YEAR); - ensure_equals("INVALID_DATE_YEAR should have failed" , result, false); + UNSCOPED_INFO("INVALID_DATE_YEAR should have failed"); + REQUIRE(result == false); result = date.fromString(INVALID_DATE_MONTH); - ensure_equals("INVALID_DATE_MONTH should have failed" , result, false); + UNSCOPED_INFO("INVALID_DATE_MONTH should have failed"); + REQUIRE(result == false); result = date.fromString(INVALID_DATE_DAY); - ensure_equals("INVALID_DATE_DAY should have failed" , result, false); + UNSCOPED_INFO("INVALID_DATE_DAY should have failed"); + REQUIRE(result == false); #endif } /* API checks */ - template<> template<> - void date_test_object_t::test<3>() + TEST_CASE("LLDate: fromStream reads a date", "[LLDate]") { LLDate date; std::istringstream stream(VALID_DATE); std::string expected_string = VALID_DATE; date.fromStream(stream); - ensure_equals("fromStream failed", date.asString(), expected_string); + UNSCOPED_INFO("fromStream failed"); + CHECK(date.asString() == expected_string); } - template<> template<> - void date_test_object_t::test<4>() + TEST_CASE("LLDate: the copy constructor copies the date", "[LLDate]") { LLDate date1(VALID_DATE); LLDate date2(date1); - ensure_equals("LLDate(const LLDate& date) constructor failed", date1.asString(), date2.asString()); + UNSCOPED_INFO("LLDate(const LLDate& date) constructor failed"); + CHECK(date1.asString() == date2.asString()); } - template<> template<> - void date_test_object_t::test<5>() + TEST_CASE("LLDate: a date built from secondsSinceEpoch equals the original", "[LLDate]") { LLDate date1(VALID_DATE); LLDate date2(date1.secondsSinceEpoch()); - ensure_equals("secondsSinceEpoch not equal",date1.secondsSinceEpoch(), date2.secondsSinceEpoch()); - ensure_equals("LLDate created using secondsSinceEpoch not equal", date1.asString(), date2.asString()); + UNSCOPED_INFO("secondsSinceEpoch not equal"); + CHECK(date1.secondsSinceEpoch() == date2.secondsSinceEpoch()); + UNSCOPED_INFO("LLDate created using secondsSinceEpoch not equal"); + CHECK(date1.asString() == date2.asString()); } - template<> template<> - void date_test_object_t::test<6>() + TEST_CASE("LLDate: operator<< writes the date string", "[LLDate]") { LLDate date(VALID_DATE); std::ostringstream stream; stream << date; std::string expected_str = VALID_DATE; - ensure_equals("ostringstream failed", expected_str, stream.str()); + UNSCOPED_INFO("ostringstream failed"); + CHECK(expected_str == stream.str()); } - template<> template<> - void date_test_object_t::test<7>() + TEST_CASE("LLDate: operator>> reads a date that operator<< writes back the same", "[LLDate]") { LLDate date; std::istringstream stream(VALID_DATE); @@ -217,60 +229,76 @@ namespace tut std::ostringstream out_stream; out_stream << date; - ensure_equals("<< failed", date.asString(),expected_str); - ensure_equals("<< to >> failed", stream.str(),out_stream.str()); + UNSCOPED_INFO("<< failed"); + CHECK(date.asString() == expected_str); + UNSCOPED_INFO("<< to >> failed"); + CHECK(stream.str() == out_stream.str()); } // Comparison operators (now constexpr in the header) — runtime coverage. - template<> template<> - void date_test_object_t::test<8>() + TEST_CASE("LLDate: comparison operators order dates by their seconds", "[LLDate]") { LLDate earlier(1000.0); LLDate later(2000.0); LLDate also_earlier(1000.0); - ensure("earlier < later", earlier < later); - ensure("later > earlier", later > earlier); - ensure("earlier <= later", earlier <= later); - ensure("later >= earlier", later >= earlier); - ensure("equal <=", earlier <= also_earlier); - ensure("equal >=", earlier >= also_earlier); - ensure("equal ==", earlier == also_earlier); - ensure("differ !=", earlier != later); - ensure("!(equal <)", !(earlier < also_earlier)); - ensure("!(equal >)", !(earlier > also_earlier)); + UNSCOPED_INFO("earlier < later"); + CHECK(earlier < later); + UNSCOPED_INFO("later > earlier"); + CHECK(later > earlier); + UNSCOPED_INFO("earlier <= later"); + CHECK(earlier <= later); + UNSCOPED_INFO("later >= earlier"); + CHECK(later >= earlier); + UNSCOPED_INFO("equal <="); + CHECK(earlier <= also_earlier); + UNSCOPED_INFO("equal >="); + CHECK(earlier >= also_earlier); + UNSCOPED_INFO("equal =="); + CHECK(earlier == also_earlier); + UNSCOPED_INFO("differ !="); + CHECK(earlier != later); + UNSCOPED_INFO("!(equal <)"); + CHECK_FALSE(earlier < also_earlier); + UNSCOPED_INFO("!(equal >)"); + CHECK_FALSE(earlier > also_earlier); } // isNull / notNull / setter round-trip. - template<> template<> - void date_test_object_t::test<9>() + TEST_CASE("LLDate: isNull and notNull follow the seconds set", "[LLDate]") { LLDate d; - ensure("default isNull", d.isNull()); - ensure("default !notNull", !d.notNull()); + UNSCOPED_INFO("default isNull"); + CHECK(d.isNull()); + UNSCOPED_INFO("default !notNull"); + CHECK_FALSE(d.notNull()); d.secondsSinceEpoch(1234.5); - ensure("after set, !isNull", !d.isNull()); - ensure("after set, notNull", d.notNull()); - ensure_equals("getter round-trips", d.secondsSinceEpoch(), 1234.5); + UNSCOPED_INFO("after set, !isNull"); + CHECK_FALSE(d.isNull()); + UNSCOPED_INFO("after set, notNull"); + CHECK(d.notNull()); + UNSCOPED_INFO("getter round-trips"); + CHECK(d.secondsSinceEpoch() == 1234.5); d.secondsSinceEpoch(0.0); - ensure("zero -> isNull again", d.isNull()); + UNSCOPED_INFO("zero -> isNull again"); + CHECK(d.isNull()); } // toLocalDateString / toHTTPDateString must not crash on the epoch value // (regression for the thread-safe localtime_utc/gmtime_utc rewrite). - template<> template<> - void date_test_object_t::test<10>() + TEST_CASE("LLDate: the date string formatters give output for the epoch value", "[LLDate]") { LLDate d; - ensure("toLocalDateString does not throw", - d.toLocalDateString(std::string("%Y-%m-%d")).size() > 0); - ensure("toHTTPDateString does not throw", - d.toHTTPDateString(std::string("%Y-%m-%d")).size() > 0); + UNSCOPED_INFO("toLocalDateString does not throw"); + REQUIRE(d.toLocalDateString(std::string("%Y-%m-%d")).size() > 0); + UNSCOPED_INFO("toHTTPDateString does not throw"); + REQUIRE(d.toHTTPDateString(std::string("%Y-%m-%d")).size() > 0); // RFC1123 wraps toHTTPDateString; verifies the day-of-week token // is present (a strftime success indicator that doesn't pin locale). std::string rfc = d.asRFC1123(); - ensure("asRFC1123 produces output", rfc.size() > 0); + UNSCOPED_INFO("asRFC1123 produces output"); + REQUIRE(rfc.size() > 0); } } diff --git a/indra/llcommon/tests/lldeadmantimer_test.cpp b/indra/llcommon/tests/lldeadmantimer_test.cpp index 01e6e8e2f7..36ce6df34f 100644 --- a/indra/llcommon/tests/lldeadmantimer_test.cpp +++ b/indra/llcommon/tests/lldeadmantimer_test.cpp @@ -30,7 +30,8 @@ #include "../llsd.h" #include "../lltimer.h" -#include "../test/lltut.h" +#include +#include "altestmatchers.h" // Convert between floating point time deltas and U64 time deltas. // Reflects an implementation detail inside lldeadmantimer.cpp @@ -46,7 +47,7 @@ static F64 u64_time_to_float(LLDeadmanTimer::time_type delta) } -namespace tut +namespace { struct deadmantimer_test @@ -58,13 +59,8 @@ struct deadmantimer_test } }; -typedef test_group deadmantimer_group_t; -typedef deadmantimer_group_t::object deadmantimer_object_t; -tut::deadmantimer_group_t deadmantimer_instance("LLDeadmanTimer"); - // Basic construction test and isExpired() call -template<> template<> -void deadmantimer_object_t::test<1>() +TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: a new timer is not expired and isExpired changes nothing", "[LLDeadmanTimer]") { { // Without cpu metrics @@ -72,10 +68,14 @@ void deadmantimer_object_t::test<1>() U64 count(U64L(8)); LLDeadmanTimer timer(10.0, false); - ensure_equals("WOCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count), false); - ensure_approximately_equals("WOCM t1 - isExpired() does not modify started", started, F64(42.0), 2); - ensure_approximately_equals("WOCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2); - ensure_equals("WOCM t1 - isExpired() does not modify count", count, U64L(8)); + UNSCOPED_INFO("WOCM isExpired() returns false after ctor()"); + CHECK(timer.isExpired(0, started, stopped, count) == false); + UNSCOPED_INFO("WOCM t1 - isExpired() does not modify started"); + CHECK_THAT(started, ll_test::WithinBits(F64(42.0), 2)); + UNSCOPED_INFO("WOCM t1 - isExpired() does not modify stopped"); + CHECK_THAT(stopped, ll_test::WithinBits(F64(97.0), 2)); + UNSCOPED_INFO("WOCM t1 - isExpired() does not modify count"); + CHECK(count == U64L(8)); } { @@ -84,19 +84,24 @@ void deadmantimer_object_t::test<1>() U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); LLDeadmanTimer timer(10.0, true); - ensure_equals("WCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false); - ensure_approximately_equals("WCM t1 - isExpired() does not modify started", started, F64(42.0), 2); - ensure_approximately_equals("WCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2); - ensure_equals("WCM t1 - isExpired() does not modify count", count, U64L(8)); - ensure_equals("WCM t1 - isExpired() does not modify user_cpu", user_cpu, U64L(29000)); - ensure_equals("WCM t1 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000)); + UNSCOPED_INFO("WCM isExpired() returns false after ctor()"); + CHECK(timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu) == false); + UNSCOPED_INFO("WCM t1 - isExpired() does not modify started"); + CHECK_THAT(started, ll_test::WithinBits(F64(42.0), 2)); + UNSCOPED_INFO("WCM t1 - isExpired() does not modify stopped"); + CHECK_THAT(stopped, ll_test::WithinBits(F64(97.0), 2)); + UNSCOPED_INFO("WCM t1 - isExpired() does not modify count"); + CHECK(count == U64L(8)); + UNSCOPED_INFO("WCM t1 - isExpired() does not modify user_cpu"); + CHECK(user_cpu == U64L(29000)); + UNSCOPED_INFO("WCM t1 - isExpired() does not modify sys_cpu"); + CHECK(sys_cpu == U64L(57000)); } } // Construct with zero horizon - not useful generally but will be useful in testing -template<> template<> -void deadmantimer_object_t::test<2>() +TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: an unstarted zero-horizon timer is not expired", "[LLDeadmanTimer]") { { // Without cpu metrics @@ -104,8 +109,8 @@ void deadmantimer_object_t::test<2>() U64 count(U64L(8)); LLDeadmanTimer timer(0.0, false); // Zero is pre-expired - ensure_equals("WOCM isExpired() still returns false with 0.0 time ctor()", - timer.isExpired(0, started, stopped, count), false); + UNSCOPED_INFO("WOCM isExpired() still returns false with 0.0 time ctor()"); + CHECK(timer.isExpired(0, started, stopped, count) == false); } { @@ -114,16 +119,15 @@ void deadmantimer_object_t::test<2>() U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); LLDeadmanTimer timer(0.0, true); // Zero is pre-expired - ensure_equals("WCM isExpired() still returns false with 0.0 time ctor()", - timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false); + UNSCOPED_INFO("WCM isExpired() still returns false with 0.0 time ctor()"); + CHECK(timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu) == false); } } // "pre-expired" timer - starting a timer with a 0.0 horizon will result in // expiration on first test. -template<> template<> -void deadmantimer_object_t::test<3>() +TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: a started zero-horizon timer expires at the first test", "[LLDeadmanTimer]") { { // Without cpu metrics @@ -132,9 +136,10 @@ void deadmantimer_object_t::test<3>() LLDeadmanTimer timer(0.0, false); timer.start(0); - ensure_equals("WOCM isExpired() returns true with 0.0 horizon time", - timer.isExpired(0, started, stopped, count), true); - ensure_approximately_equals("WOCM expired timer with no bell ringing has stopped == started", started, stopped, 8); + UNSCOPED_INFO("WOCM isExpired() returns true with 0.0 horizon time"); + CHECK(timer.isExpired(0, started, stopped, count) == true); + UNSCOPED_INFO("WOCM expired timer with no bell ringing has stopped == started"); + CHECK_THAT(started, ll_test::WithinBits(stopped, 8)); } { // With cpu metrics @@ -143,16 +148,16 @@ void deadmantimer_object_t::test<3>() LLDeadmanTimer timer(0.0, true); timer.start(0); - ensure_equals("WCM isExpired() returns true with 0.0 horizon time", - timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), true); - ensure_approximately_equals("WCM expired timer with no bell ringing has stopped == started", started, stopped, 8); + UNSCOPED_INFO("WCM isExpired() returns true with 0.0 horizon time"); + CHECK(timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu) == true); + UNSCOPED_INFO("WCM expired timer with no bell ringing has stopped == started"); + CHECK_THAT(started, ll_test::WithinBits(stopped, 8)); } } // "pre-expired" timer - bell rings are ignored as we're already expired. -template<> template<> -void deadmantimer_object_t::test<4>() +TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: a pre-expired timer ignores the bell", "[LLDeadmanTimer]") { { // Without cpu metrics @@ -162,9 +167,10 @@ void deadmantimer_object_t::test<4>() timer.start(0); timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1); - ensure_equals("WOCM isExpired() returns true with 0.0 horizon time after bell ring", - timer.isExpired(0, started, stopped, count), true); - ensure_approximately_equals("WOCM ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8); + UNSCOPED_INFO("WOCM isExpired() returns true with 0.0 horizon time after bell ring"); + CHECK(timer.isExpired(0, started, stopped, count) == true); + UNSCOPED_INFO("WOCM ringBell has no impact on expired timer leaving stopped == started"); + CHECK_THAT(started, ll_test::WithinBits(stopped, 8)); } { // With cpu metrics @@ -174,16 +180,16 @@ void deadmantimer_object_t::test<4>() timer.start(0); timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1); - ensure_equals("WCM isExpired() returns true with 0.0 horizon time after bell ring", - timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), true); - ensure_approximately_equals("WCM ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8); + UNSCOPED_INFO("WCM isExpired() returns true with 0.0 horizon time after bell ring"); + CHECK(timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu) == true); + UNSCOPED_INFO("WCM ringBell has no impact on expired timer leaving stopped == started"); + CHECK_THAT(started, ll_test::WithinBits(stopped, 8)); } } // start(0) test - unexpired timer reports unexpired -template<> template<> -void deadmantimer_object_t::test<5>() +TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: start(0) leaves a timer unexpired and isExpired changes nothing", "[LLDeadmanTimer]") { { // Without cpu metrics @@ -192,11 +198,14 @@ void deadmantimer_object_t::test<5>() LLDeadmanTimer timer(10.0, false); timer.start(0); - ensure_equals("WOCM isExpired() returns false after starting with 10.0 horizon time", - timer.isExpired(0, started, stopped, count), false); - ensure_approximately_equals("WOCM t5 - isExpired() does not modify started", started, F64(42.0), 2); - ensure_approximately_equals("WOCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2); - ensure_equals("WOCM t5 - isExpired() does not modify count", count, U64L(8)); + UNSCOPED_INFO("WOCM isExpired() returns false after starting with 10.0 horizon time"); + CHECK(timer.isExpired(0, started, stopped, count) == false); + UNSCOPED_INFO("WOCM t5 - isExpired() does not modify started"); + CHECK_THAT(started, ll_test::WithinBits(F64(42.0), 2)); + UNSCOPED_INFO("WOCM t5 - isExpired() does not modify stopped"); + CHECK_THAT(stopped, ll_test::WithinBits(F64(97.0), 2)); + UNSCOPED_INFO("WOCM t5 - isExpired() does not modify count"); + CHECK(count == U64L(8)); } { // With cpu metrics @@ -205,20 +214,24 @@ void deadmantimer_object_t::test<5>() LLDeadmanTimer timer(10.0, true); timer.start(0); - ensure_equals("WCM isExpired() returns false after starting with 10.0 horizon time", - timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false); - ensure_approximately_equals("WCM t5 - isExpired() does not modify started", started, F64(42.0), 2); - ensure_approximately_equals("WCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2); - ensure_equals("WCM t5 - isExpired() does not modify count", count, U64L(8)); - ensure_equals("WCM t5 - isExpired() does not modify user_cpu", user_cpu, U64L(29000)); - ensure_equals("WCM t5 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000)); + UNSCOPED_INFO("WCM isExpired() returns false after starting with 10.0 horizon time"); + CHECK(timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu) == false); + UNSCOPED_INFO("WCM t5 - isExpired() does not modify started"); + CHECK_THAT(started, ll_test::WithinBits(F64(42.0), 2)); + UNSCOPED_INFO("WCM t5 - isExpired() does not modify stopped"); + CHECK_THAT(stopped, ll_test::WithinBits(F64(97.0), 2)); + UNSCOPED_INFO("WCM t5 - isExpired() does not modify count"); + CHECK(count == U64L(8)); + UNSCOPED_INFO("WCM t5 - isExpired() does not modify user_cpu"); + CHECK(user_cpu == U64L(29000)); + UNSCOPED_INFO("WCM t5 - isExpired() does not modify sys_cpu"); + CHECK(sys_cpu == U64L(57000)); } } // start() test - start in the past but not beyond 1 horizon -template<> template<> -void deadmantimer_object_t::test<6>() +TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: a timer started less than a horizon ago is not expired", "[LLDeadmanTimer]") { { // Without cpu metrics @@ -233,11 +246,14 @@ void deadmantimer_object_t::test<6>() LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0)); timer.start(the_past); - ensure_equals("WOCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past", - timer.isExpired(now, started, stopped, count), false); - ensure_approximately_equals("WOCM t6 - isExpired() does not modify started", started, F64(42.0), 2); - ensure_approximately_equals("WOCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2); - ensure_equals("WOCM t6 - isExpired() does not modify count", count, U64L(8)); + UNSCOPED_INFO("WOCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past"); + CHECK(timer.isExpired(now, started, stopped, count) == false); + UNSCOPED_INFO("WOCM t6 - isExpired() does not modify started"); + CHECK_THAT(started, ll_test::WithinBits(F64(42.0), 2)); + UNSCOPED_INFO("WOCM t6 - isExpired() does not modify stopped"); + CHECK_THAT(stopped, ll_test::WithinBits(F64(97.0), 2)); + UNSCOPED_INFO("WOCM t6 - isExpired() does not modify count"); + CHECK(count == U64L(8)); } { // With cpu metrics @@ -252,20 +268,24 @@ void deadmantimer_object_t::test<6>() LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0)); timer.start(the_past); - ensure_equals("WCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past", - timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); - ensure_approximately_equals("WCM t6 - isExpired() does not modify started", started, F64(42.0), 2); - ensure_approximately_equals("WCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2); - ensure_equals("t6 - isExpired() does not modify count", count, U64L(8)); - ensure_equals("WCM t6 - isExpired() does not modify user_cpu", user_cpu, U64L(29000)); - ensure_equals("WCM t6 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000)); + UNSCOPED_INFO("WCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past"); + CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == false); + UNSCOPED_INFO("WCM t6 - isExpired() does not modify started"); + CHECK_THAT(started, ll_test::WithinBits(F64(42.0), 2)); + UNSCOPED_INFO("WCM t6 - isExpired() does not modify stopped"); + CHECK_THAT(stopped, ll_test::WithinBits(F64(97.0), 2)); + UNSCOPED_INFO("t6 - isExpired() does not modify count"); + CHECK(count == U64L(8)); + UNSCOPED_INFO("WCM t6 - isExpired() does not modify user_cpu"); + CHECK(user_cpu == U64L(29000)); + UNSCOPED_INFO("WCM t6 - isExpired() does not modify sys_cpu"); + CHECK(sys_cpu == U64L(57000)); } } // start() test - start in the past but well beyond 1 horizon -template<> template<> -void deadmantimer_object_t::test<7>() +TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: a timer started more than a horizon ago is expired", "[LLDeadmanTimer]") { { // Without cpu metrics @@ -280,9 +300,10 @@ void deadmantimer_object_t::test<7>() LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0)); timer.start(the_past); - ensure_equals("WOCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past", - timer.isExpired(now,started, stopped, count), true); - ensure_approximately_equals("WOCM t7 - starting before horizon still gives equal started / stopped", started, stopped, 8); + UNSCOPED_INFO("WOCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past"); + CHECK(timer.isExpired(now,started, stopped, count) == true); + UNSCOPED_INFO("WOCM t7 - starting before horizon still gives equal started / stopped"); + CHECK_THAT(started, ll_test::WithinBits(stopped, 8)); } { // With cpu metrics @@ -297,16 +318,16 @@ void deadmantimer_object_t::test<7>() LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0)); timer.start(the_past); - ensure_equals("WCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past", - timer.isExpired(now,started, stopped, count, user_cpu, sys_cpu), true); - ensure_approximately_equals("WOCM t7 - starting before horizon still gives equal started / stopped", started, stopped, 8); + UNSCOPED_INFO("WCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past"); + CHECK(timer.isExpired(now,started, stopped, count, user_cpu, sys_cpu) == true); + UNSCOPED_INFO("WOCM t7 - starting before horizon still gives equal started / stopped"); + CHECK_THAT(started, ll_test::WithinBits(stopped, 8)); } } // isExpired() test - results are read-once. Probes after first true are false. -template<> template<> -void deadmantimer_object_t::test<8>() +TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: isExpired reports an expiry once and then changes nothing", "[LLDeadmanTimer]") { { // Without cpu metrics @@ -321,17 +342,20 @@ void deadmantimer_object_t::test<8>() LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0)); timer.start(the_past); - ensure_equals("WOCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past", - timer.isExpired(now, started, stopped, count), true); + UNSCOPED_INFO("WOCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past"); + CHECK(timer.isExpired(now, started, stopped, count) == true); started = 42.0; stopped = 97.0; count = U64L(8); - ensure_equals("WOCM t8 - second isExpired() returns false after true", - timer.isExpired(now, started, stopped, count), false); - ensure_approximately_equals("WOCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2); - ensure_approximately_equals("WOCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2); - ensure_equals("WOCM t8 - 2nd isExpired() does not modify count", count, U64L(8)); + UNSCOPED_INFO("WOCM t8 - second isExpired() returns false after true"); + CHECK(timer.isExpired(now, started, stopped, count) == false); + UNSCOPED_INFO("WOCM t8 - 2nd isExpired() does not modify started"); + CHECK_THAT(started, ll_test::WithinBits(F64(42.0), 2)); + UNSCOPED_INFO("WOCM t8 - 2nd isExpired() does not modify stopped"); + CHECK_THAT(stopped, ll_test::WithinBits(F64(97.0), 2)); + UNSCOPED_INFO("WOCM t8 - 2nd isExpired() does not modify count"); + CHECK(count == U64L(8)); } { // With cpu metrics @@ -346,28 +370,32 @@ void deadmantimer_object_t::test<8>() LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0)); timer.start(the_past); - ensure_equals("WCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past", - timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true); + UNSCOPED_INFO("WCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past"); + CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == true); started = 42.0; stopped = 97.0; count = U64L(8); user_cpu = 29000; sys_cpu = 57000; - ensure_equals("WCM t8 - second isExpired() returns false after true", - timer.isExpired(now, started, stopped, count), false); - ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2); - ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2); - ensure_equals("WCM t8 - 2nd isExpired() does not modify count", count, U64L(8)); - ensure_equals("WCM t8 - 2nd isExpired() does not modify user_cpu", user_cpu, U64L(29000)); - ensure_equals("WCM t8 - 2nd isExpired() does not modify sys_cpu", sys_cpu, U64L(57000)); + UNSCOPED_INFO("WCM t8 - second isExpired() returns false after true"); + CHECK(timer.isExpired(now, started, stopped, count) == false); + UNSCOPED_INFO("WCM t8 - 2nd isExpired() does not modify started"); + CHECK_THAT(started, ll_test::WithinBits(F64(42.0), 2)); + UNSCOPED_INFO("WCM t8 - 2nd isExpired() does not modify stopped"); + CHECK_THAT(stopped, ll_test::WithinBits(F64(97.0), 2)); + UNSCOPED_INFO("WCM t8 - 2nd isExpired() does not modify count"); + CHECK(count == U64L(8)); + UNSCOPED_INFO("WCM t8 - 2nd isExpired() does not modify user_cpu"); + CHECK(user_cpu == U64L(29000)); + UNSCOPED_INFO("WCM t8 - 2nd isExpired() does not modify sys_cpu"); + CHECK(sys_cpu == U64L(57000)); } } // ringBell() test - see that we can keep a timer from expiring -template<> template<> -void deadmantimer_object_t::test<9>() +TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: ringing the bell keeps a timer from expiring", "[LLDeadmanTimer]") { { // Without cpu metrics @@ -399,18 +427,22 @@ void deadmantimer_object_t::test<9>() timer.ringBell(now, 1); now += float_time_to_u64(1.0); timer.ringBell(now, 1); - ensure_equals("WOCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings", - timer.isExpired(now, started, stopped, count), false); + UNSCOPED_INFO("WOCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings"); + CHECK(timer.isExpired(now, started, stopped, count) == false); F64 last_good_ring(u64_time_to_float(now)); // Jump forward and expire now += float_time_to_u64(10.0); - ensure_equals("WOCM t9 - 5.0 horizon timer expires on 10-second jump", - timer.isExpired(now, started, stopped, count), true); - ensure_approximately_equals("WOCM t9 - started matches start() time", started, real_start, 4); - ensure_approximately_equals("WOCM t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4); - ensure_equals("WOCM t9 - 10 good ringBell()s", count, U64L(10)); - ensure_equals("WOCM t9 - single read only", timer.isExpired(now, started, stopped, count), false); + UNSCOPED_INFO("WOCM t9 - 5.0 horizon timer expires on 10-second jump"); + CHECK(timer.isExpired(now, started, stopped, count) == true); + UNSCOPED_INFO("WOCM t9 - started matches start() time"); + CHECK_THAT(started, ll_test::WithinBits(real_start, 4)); + UNSCOPED_INFO("WOCM t9 - stopped matches last ringBell() time"); + CHECK_THAT(stopped, ll_test::WithinBits(last_good_ring, 4)); + UNSCOPED_INFO("WOCM t9 - 10 good ringBell()s"); + CHECK(count == U64L(10)); + UNSCOPED_INFO("WOCM t9 - single read only"); + CHECK(timer.isExpired(now, started, stopped, count) == false); } { // With cpu metrics @@ -442,25 +474,28 @@ void deadmantimer_object_t::test<9>() timer.ringBell(now, 1); now += float_time_to_u64(1.0); timer.ringBell(now, 1); - ensure_equals("WCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings", - timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); + UNSCOPED_INFO("WCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings"); + CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == false); F64 last_good_ring(u64_time_to_float(now)); // Jump forward and expire now += float_time_to_u64(10.0); - ensure_equals("WCM t9 - 5.0 horizon timer expires on 10-second jump", - timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true); - ensure_approximately_equals("WCM t9 - started matches start() time", started, real_start, 4); - ensure_approximately_equals("WCM t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4); - ensure_equals("WCM t9 - 10 good ringBell()s", count, U64L(10)); - ensure_equals("WCM t9 - single read only", timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); + UNSCOPED_INFO("WCM t9 - 5.0 horizon timer expires on 10-second jump"); + CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == true); + UNSCOPED_INFO("WCM t9 - started matches start() time"); + CHECK_THAT(started, ll_test::WithinBits(real_start, 4)); + UNSCOPED_INFO("WCM t9 - stopped matches last ringBell() time"); + CHECK_THAT(stopped, ll_test::WithinBits(last_good_ring, 4)); + UNSCOPED_INFO("WCM t9 - 10 good ringBell()s"); + CHECK(count == U64L(10)); + UNSCOPED_INFO("WCM t9 - single read only"); + CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == false); } } // restart after expiration test - verify that restarts behave well -template<> template<> -void deadmantimer_object_t::test<10>() +TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: a timer restarted after expiring times the new run", "[LLDeadmanTimer]") { { // Without cpu metrics @@ -492,18 +527,22 @@ void deadmantimer_object_t::test<10>() timer.ringBell(now, 1); now += float_time_to_u64(1.0); timer.ringBell(now, 1); - ensure_equals("WOCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings", - timer.isExpired(now, started, stopped, count), false); + UNSCOPED_INFO("WOCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings"); + CHECK(timer.isExpired(now, started, stopped, count) == false); F64 last_good_ring(u64_time_to_float(now)); // Jump forward and expire now += float_time_to_u64(10.0); - ensure_equals("WOCM t10 - 5.0 horizon timer expires on 10-second jump", - timer.isExpired(now, started, stopped, count), true); - ensure_approximately_equals("WOCM t10 - started matches start() time", started, real_start, 4); - ensure_approximately_equals("WOCM t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4); - ensure_equals("WOCM t10 - 10 good ringBell()s", count, U64L(10)); - ensure_equals("WOCM t10 - single read only", timer.isExpired(now, started, stopped, count), false); + UNSCOPED_INFO("WOCM t10 - 5.0 horizon timer expires on 10-second jump"); + CHECK(timer.isExpired(now, started, stopped, count) == true); + UNSCOPED_INFO("WOCM t10 - started matches start() time"); + CHECK_THAT(started, ll_test::WithinBits(real_start, 4)); + UNSCOPED_INFO("WOCM t10 - stopped matches last ringBell() time"); + CHECK_THAT(stopped, ll_test::WithinBits(last_good_ring, 4)); + UNSCOPED_INFO("WOCM t10 - 10 good ringBell()s"); + CHECK(count == U64L(10)); + UNSCOPED_INFO("WOCM t10 - single read only"); + CHECK(timer.isExpired(now, started, stopped, count) == false); // Jump forward and restart now += float_time_to_u64(1.0); @@ -527,19 +566,22 @@ void deadmantimer_object_t::test<10>() timer.ringBell(now, 1); now += float_time_to_u64(1.0); timer.ringBell(now, 1); - ensure_equals("WOCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings", - timer.isExpired(now, started, stopped, count), false); + UNSCOPED_INFO("WOCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings"); + CHECK(timer.isExpired(now, started, stopped, count) == false); last_good_ring = u64_time_to_float(now); // Jump forward and expire now += float_time_to_u64(10.0); - ensure_equals("WOCM t10 - 5.0 horizon timer expires on 8-second jump", - timer.isExpired(now, started, stopped, count), true); - ensure_approximately_equals("WOCM t10 - 2nd started matches start() time", started, real_start, 4); - ensure_approximately_equals("WOCM t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4); - ensure_equals("WOCM t10 - 8 good ringBell()s", count, U64L(8)); - ensure_equals("WOCM t10 - single read only - 2nd start", - timer.isExpired(now, started, stopped, count), false); + UNSCOPED_INFO("WOCM t10 - 5.0 horizon timer expires on 8-second jump"); + CHECK(timer.isExpired(now, started, stopped, count) == true); + UNSCOPED_INFO("WOCM t10 - 2nd started matches start() time"); + CHECK_THAT(started, ll_test::WithinBits(real_start, 4)); + UNSCOPED_INFO("WOCM t10 - 2nd stopped matches last ringBell() time"); + CHECK_THAT(stopped, ll_test::WithinBits(last_good_ring, 4)); + UNSCOPED_INFO("WOCM t10 - 8 good ringBell()s"); + CHECK(count == U64L(8)); + UNSCOPED_INFO("WOCM t10 - single read only - 2nd start"); + CHECK(timer.isExpired(now, started, stopped, count) == false); } { // With cpu metrics @@ -572,18 +614,22 @@ void deadmantimer_object_t::test<10>() timer.ringBell(now, 1); now += float_time_to_u64(1.0); timer.ringBell(now, 1); - ensure_equals("WCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings", - timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); + UNSCOPED_INFO("WCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings"); + CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == false); F64 last_good_ring(u64_time_to_float(now)); // Jump forward and expire now += float_time_to_u64(10.0); - ensure_equals("WCM t10 - 5.0 horizon timer expires on 10-second jump", - timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true); - ensure_approximately_equals("WCM t10 - started matches start() time", started, real_start, 4); - ensure_approximately_equals("WCM t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4); - ensure_equals("WCM t10 - 10 good ringBell()s", count, U64L(10)); - ensure_equals("WCM t10 - single read only", timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); + UNSCOPED_INFO("WCM t10 - 5.0 horizon timer expires on 10-second jump"); + CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == true); + UNSCOPED_INFO("WCM t10 - started matches start() time"); + CHECK_THAT(started, ll_test::WithinBits(real_start, 4)); + UNSCOPED_INFO("WCM t10 - stopped matches last ringBell() time"); + CHECK_THAT(stopped, ll_test::WithinBits(last_good_ring, 4)); + UNSCOPED_INFO("WCM t10 - 10 good ringBell()s"); + CHECK(count == U64L(10)); + UNSCOPED_INFO("WCM t10 - single read only"); + CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == false); // Jump forward and restart now += float_time_to_u64(1.0); @@ -607,22 +653,25 @@ void deadmantimer_object_t::test<10>() timer.ringBell(now, 1); now += float_time_to_u64(1.0); timer.ringBell(now, 1); - ensure_equals("WCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings", - timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); + UNSCOPED_INFO("WCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings"); + CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == false); last_good_ring = u64_time_to_float(now); // Jump forward and expire now += float_time_to_u64(10.0); - ensure_equals("WCM t10 - 5.0 horizon timer expires on 8-second jump", - timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true); - ensure_approximately_equals("WCM t10 - 2nd started matches start() time", started, real_start, 4); - ensure_approximately_equals("WCM t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4); - ensure_equals("WCM t10 - 8 good ringBell()s", count, U64L(8)); - ensure_equals("WCM t10 - single read only - 2nd start", - timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); + UNSCOPED_INFO("WCM t10 - 5.0 horizon timer expires on 8-second jump"); + CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == true); + UNSCOPED_INFO("WCM t10 - 2nd started matches start() time"); + CHECK_THAT(started, ll_test::WithinBits(real_start, 4)); + UNSCOPED_INFO("WCM t10 - 2nd stopped matches last ringBell() time"); + CHECK_THAT(stopped, ll_test::WithinBits(last_good_ring, 4)); + UNSCOPED_INFO("WCM t10 - 8 good ringBell()s"); + CHECK(count == U64L(8)); + UNSCOPED_INFO("WCM t10 - single read only - 2nd start"); + CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == false); } } -} // end namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/lldependencies_test.cpp b/indra/llcommon/tests/lldependencies_test.cpp index b13576a985..f1b51dcfee 100644 --- a/indra/llcommon/tests/lldependencies_test.cpp +++ b/indra/llcommon/tests/lldependencies_test.cpp @@ -57,7 +57,7 @@ std::ostream& display(std::ostream& out, const T& value) // ...but display std::string enclosed in double quotes. template<> -std::ostream& display(std::ostream& out, const std::string& value) +[[maybe_unused]] std::ostream& display(std::ostream& out, const std::string& value) { out << '"' << value << '"'; return out; @@ -105,7 +105,7 @@ std::ostream& operator<<(std::ostream& out, const std::set& set) /***************************************************************************** * Now we can #include lltut.h *****************************************************************************/ -#include "../test/lltut.h" +#include /***************************************************************************** * Other helpers @@ -155,49 +155,45 @@ bool is_empty(const RANGE& range) /***************************************************************************** * tut test group *****************************************************************************/ -namespace tut +namespace { - struct deps_data - { - }; - typedef test_group deps_group; - typedef deps_group::object deps_object; - tut::deps_group depsgr("LLDependencies"); - - template<> template<> - void deps_object::test<1>() + TEST_CASE("LLDependencies: sort orders keys by their before and after constraints", "[LLDependencies]") { StringDeps deps; StringList empty; // The quick brown fox jumps over the lazy yellow dog. // (note, "The" and "the" are distinct, else this test wouldn't work) deps.add("lazy"); - ensure_equals(sorted_keys(deps), StringList{"lazy"}); + CHECK(sorted_keys(deps) == StringList{"lazy"}); deps.add("jumps"); - ensure("found lazy", deps.get("lazy")); - ensure("not found dog.", ! deps.get("dog.")); + UNSCOPED_INFO("found lazy"); + REQUIRE(deps.get("lazy")); + UNSCOPED_INFO("not found dog."); + REQUIRE_FALSE(deps.get("dog.")); // NOTE: Maybe it's overkill to test each of these intermediate // results before all the interdependencies have been specified. My // thought is simply that if the order changes, I'd like to know why. // A change to the implementation of boost::topological_sort() would // be an acceptable reason, and you can simply update the expected // test output. - ensure_equals(sorted_keys(deps), StringList{ "lazy", "jumps" }); + CHECK(sorted_keys(deps) == StringList{ "lazy", "jumps" }); deps.add("The", 0, empty, { "fox", "dog." }); // Test key accessors - ensure("empty before deps for missing key", is_empty(deps.get_before_range("bogus"))); - ensure("empty before deps for jumps", is_empty(deps.get_before_range("jumps"))); - ensure_equals(instance_from_range< std::set >(deps.get_before_range("The")), std::set{ "dog.", "fox" }); + UNSCOPED_INFO("empty before deps for missing key"); + CHECK(is_empty(deps.get_before_range("bogus"))); + UNSCOPED_INFO("empty before deps for jumps"); + CHECK(is_empty(deps.get_before_range("jumps"))); + CHECK(instance_from_range< std::set >(deps.get_before_range("The")) == std::set{ "dog.", "fox" }); // resume building dependencies - ensure_equals(sorted_keys(deps), StringList{ "lazy", "jumps", "The" }); + CHECK(sorted_keys(deps) == StringList{ "lazy", "jumps", "The" }); deps.add("the", 0, { "The" }); - ensure_equals(sorted_keys(deps), StringList{ "lazy", "jumps", "The", "the" }); + CHECK(sorted_keys(deps) == StringList{ "lazy", "jumps", "The", "the" }); deps.add("fox", 0, { "The" }, { "jumps" }); - ensure_equals(sorted_keys(deps), StringList{ "lazy", "The", "the", "fox", "jumps" }); + CHECK(sorted_keys(deps) == StringList{ "lazy", "The", "the", "fox", "jumps" }); deps.add("the", 0, { "The" }); // same, see if cache works - ensure_equals(sorted_keys(deps), StringList{ "lazy", "The", "the", "fox", "jumps" }); + CHECK(sorted_keys(deps) == StringList{ "lazy", "The", "the", "fox", "jumps" }); deps.add("jumps", 0, empty, { "over" }); // update jumps deps - ensure_equals(sorted_keys(deps), StringList{ "lazy", "The", "the", "fox", "jumps" }); + CHECK(sorted_keys(deps) == StringList{ "lazy", "The", "the", "fox", "jumps" }); /*==========================================================================*| // It drives me nuts that this test doesn't work in the test // framework, because -- for reasons unknown -- running the test @@ -223,27 +219,26 @@ namespace tut } |*==========================================================================*/ deps.add("dog.", 0, { "yellow", "lazy" }); - ensure_equals(instance_from_range< std::set >(deps.get_after_range("dog.")), std::set{ "lazy", "yellow" }); - ensure_equals(sorted_keys(deps), StringList{ "lazy", "The", "the", "fox", "jumps", "dog." }); + CHECK(instance_from_range< std::set >(deps.get_after_range("dog.")) == std::set{ "lazy", "yellow" }); + CHECK(sorted_keys(deps) == StringList{ "lazy", "The", "the", "fox", "jumps", "dog." }); deps.add("quick", 0, { "The" }, { "fox", "brown" }); - ensure_equals(sorted_keys(deps), StringList{ "lazy", "The", "the", "quick", "fox", "jumps", "dog." }); + CHECK(sorted_keys(deps) == StringList{ "lazy", "The", "the", "quick", "fox", "jumps", "dog." }); deps.add("over", 0, { "jumps" }, { "yellow", "the" }); - ensure_equals(sorted_keys(deps), StringList{ "lazy", "The", "quick", "fox", "jumps", "over", "the", "dog." }); + CHECK(sorted_keys(deps) == StringList{ "lazy", "The", "quick", "fox", "jumps", "over", "the", "dog." }); deps.add("yellow", 0, { "the" }, { "lazy" }); - ensure_equals(sorted_keys(deps), StringList{ "The", "quick", "fox", "jumps", "over", "the", "yellow", "lazy", "dog." }); + CHECK(sorted_keys(deps) == StringList{ "The", "quick", "fox", "jumps", "over", "the", "yellow", "lazy", "dog." }); deps.add("brown"); // By now the dependencies are pretty well in place. A change to THIS // order should be viewed with suspicion. - ensure_equals(sorted_keys(deps), StringList{ "The", "quick", "brown", "fox", "jumps", "over", "the", "yellow", "lazy", "dog." }); + CHECK(sorted_keys(deps) == StringList{ "The", "quick", "brown", "fox", "jumps", "over", "the", "yellow", "lazy", "dog." }); StringList keys(StringList{ "The", "brown", "dog.", "fox", "jumps", "lazy", "over", "quick", "the", "yellow" }); - ensure_equals(instance_from_range(deps.get_key_range()), keys); - ensure_equals(instance_from_range(make_transform_range(deps.get_range(), extract_key)), keys); + CHECK(instance_from_range(deps.get_key_range()) == keys); + CHECK(instance_from_range(make_transform_range(deps.get_range(), extract_key)) == keys); // blow off get_node_range() because they're all LLDependenciesEmpty instances } - template<> template<> - void deps_object::test<2>() + TEST_CASE("LLDependencies: range and iterator accessors reach each key and its int node", "[LLDependencies]") { typedef LLDependencies NameIndexDeps; NameIndexDeps nideps; @@ -252,43 +247,44 @@ namespace tut nideps.add("ghi", 3); nideps.add("abc", 1, { "def" }); NameIndexDeps::range range(nideps.get_range()); - ensure_equals(range.begin()->first, "abc"); - ensure_equals(range.begin()->second, 1); + CHECK(range.begin()->first == "abc"); + CHECK(range.begin()->second == 1); range.begin()->second = 0; range.begin()->second = 1; NameIndexDeps::const_range const_range(const_nideps.get_range()); NameIndexDeps::const_iterator const_iterator(const_range.begin()); ++const_iterator; - ensure_equals(const_iterator->first, "def"); - ensure_equals(const_iterator->second, 2); + CHECK(const_iterator->first == "def"); + CHECK(const_iterator->second == 2); // NameIndexDeps::node_range node_range(nideps.get_node_range()); // ensure_equals(instance_from_range >(node_range), make< std::vector >(list_of(1,2,3))); // *node_range.begin() = 0; // *node_range.begin() = 1; NameIndexDeps::const_node_range const_node_range(const_nideps.get_node_range()); - ensure_equals(instance_from_range>(const_node_range), std::vector{ 1, 2, 3 }); + CHECK(instance_from_range>(const_node_range) == std::vector{ 1, 2, 3 }); NameIndexDeps::const_key_range const_key_range(const_nideps.get_key_range()); - ensure_equals(instance_from_range(const_key_range), StringList{ "abc", "def", "ghi" }); + CHECK(instance_from_range(const_key_range) == StringList{ "abc", "def", "ghi" }); NameIndexDeps::sorted_range sorted(const_nideps.sort()); NameIndexDeps::sorted_iterator sortiter(sorted.begin()); - ensure_equals(sortiter->first, "ghi"); - ensure_equals(sortiter->second, 3); + CHECK(sortiter->first == "ghi"); + CHECK(sortiter->second == 3); // test all iterator-flavored versions of get_after_range() StringList def{"def"}; - ensure("empty abc before list", is_empty(nideps.get_before_range(nideps.get_range().begin()))); - ensure_equals(instance_from_range(nideps.get_after_range(nideps.get_range().begin())), + UNSCOPED_INFO("empty abc before list"); + CHECK(is_empty(nideps.get_before_range(nideps.get_range().begin()))); + CHECK(instance_from_range(nideps.get_after_range(nideps.get_range().begin())) == def); - ensure_equals(instance_from_range(const_nideps.get_after_range(const_nideps.get_range().begin())), + CHECK(instance_from_range(const_nideps.get_after_range(const_nideps.get_range().begin())) == def); // ensure_equals(instance_from_range(nideps.get_after_range(nideps.get_node_range().begin())), // def); - ensure_equals(instance_from_range(const_nideps.get_after_range(const_nideps.get_node_range().begin())), + CHECK(instance_from_range(const_nideps.get_after_range(const_nideps.get_node_range().begin())) == def); - ensure_equals(instance_from_range(nideps.get_after_range(nideps.get_key_range().begin())), + CHECK(instance_from_range(nideps.get_after_range(nideps.get_key_range().begin())) == def); // advance from "ghi" to "def", which must come after "ghi" ++sortiter; - ensure_equals(instance_from_range(const_nideps.get_after_range(sortiter)), StringList{ "ghi" }); + CHECK(instance_from_range(const_nideps.get_after_range(sortiter)) == StringList{ "ghi" }); } -} // namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/lldoubledispatch_test.cpp b/indra/llcommon/tests/lldoubledispatch_test.cpp index bafa7a7404..06c5acb979 100644 --- a/indra/llcommon/tests/lldoubledispatch_test.cpp +++ b/indra/llcommon/tests/lldoubledispatch_test.cpp @@ -41,7 +41,7 @@ #include // external library headers // other Linden headers -#include "lltut.h" +#include namespace { @@ -123,7 +123,7 @@ std::string asteroidStation(Asteroid& rock, SpaceStation& dock) } // anonymous namespace /*------------------------------- Test code --------------------------------*/ -namespace tut +namespace { struct dispatch_data { @@ -152,12 +152,8 @@ namespace tut CommercialShip dummyCommercial; SpaceStation dummyStation; }; - typedef test_group dispatch_group; - typedef dispatch_group::object dispatch_object; - tut::dispatch_group ddgr("double dispatch"); - template<> template<> - void dispatch_object::test<1>() + TEST_CASE_METHOD(dispatch_data, "double dispatch: symmetric Type entries match subclasses in either order", "[double_dispatch]") { // Describe param types using explicit DD::Type objects // (order-sensitive add() variant) @@ -168,24 +164,23 @@ namespace tut dispatcher.add(DD::Type(), DD::Type(), asteroidStation, true); // Try colliding them. - ensure_equals(dispatcher(*home, *tug), // reverse params, SpaceShip subclass + CHECK(dispatcher(*home, *tug) == // reverse params, SpaceShip subclass "shipStation"); - ensure_equals(dispatcher(*patrol, *home), // forward params, SpaceShip subclass + CHECK(dispatcher(*patrol, *home) == // forward params, SpaceShip subclass "shipStation"); - ensure_equals(dispatcher(*obstacle, *home), // forward params + CHECK(dispatcher(*obstacle, *home) == // forward params "asteroidStation"); - ensure_equals(dispatcher(*home, *obstacle), // reverse params + CHECK(dispatcher(*home, *obstacle) == // reverse params "asteroidStation"); - ensure_equals(dispatcher(*tug, *obstacle), // forward params, SpaceShip subclass + CHECK(dispatcher(*tug, *obstacle) == // forward params, SpaceShip subclass "shipAsteroid"); - ensure_equals(dispatcher(*obstacle, *patrol), // reverse params, SpaceShip subclass + CHECK(dispatcher(*obstacle, *patrol) == // reverse params, SpaceShip subclass // won't use militaryShipAsteroid() because it was added // in wrong order "shipAsteroid"); } - template<> template<> - void dispatch_object::test<2>() + TEST_CASE_METHOD(dispatch_data, "double dispatch: a specific Type entry added before a general one wins", "[double_dispatch]") { // Describe param types using explicit DD::Type objects // (order-sensitive add() variant) @@ -195,12 +190,11 @@ namespace tut dispatcher.add(DD::Type(), DD::Type(), shipStation, true); dispatcher.add(DD::Type(), DD::Type(), asteroidStation, true); - ensure_equals(dispatcher(*patrol, *obstacle), "militaryShipAsteroid"); - ensure_equals(dispatcher(*tug, *obstacle), "shipAsteroid"); + CHECK(dispatcher(*patrol, *obstacle) == "militaryShipAsteroid"); + CHECK(dispatcher(*tug, *obstacle) == "shipAsteroid"); } - template<> template<> - void dispatch_object::test<3>() + TEST_CASE_METHOD(dispatch_data, "double dispatch: prototype entries match the most specific pair in their order only", "[double_dispatch]") { // Describe param types with actual prototype instances // (order-insensitive add() variant) @@ -209,13 +203,12 @@ namespace tut dispatcher.add(dummyShip, dummyStation, shipStation); dispatcher.add(dummyAsteroid, dummyStation, asteroidStation); - ensure_equals(dispatcher(*patrol, *obstacle), "militaryShipAsteroid"); - ensure_equals(dispatcher(*tug, *obstacle), "shipAsteroid"); - ensure_equals(dispatcher(*obstacle, *patrol), ""); + CHECK(dispatcher(*patrol, *obstacle) == "militaryShipAsteroid"); + CHECK(dispatcher(*tug, *obstacle) == "shipAsteroid"); + CHECK(dispatcher(*obstacle, *patrol) == ""); } - template<> template<> - void dispatch_object::test<4>() + TEST_CASE_METHOD(dispatch_data, "double dispatch: prototype entries match the most specific in any add order", "[double_dispatch]") { // Describe param types with actual prototype instances // (order-insensitive add() variant) @@ -226,19 +219,18 @@ namespace tut dispatcher.add(dummyShip, dummyStation, shipStation); dispatcher.add(dummyAsteroid, dummyStation, asteroidStation); - ensure_equals(dispatcher(*patrol, *obstacle), "militaryShipAsteroid"); - ensure_equals(dispatcher(*tug, *obstacle), "shipAsteroid"); + CHECK(dispatcher(*patrol, *obstacle) == "militaryShipAsteroid"); + CHECK(dispatcher(*tug, *obstacle) == "shipAsteroid"); } - template<> template<> - void dispatch_object::test<5>() + TEST_CASE_METHOD(dispatch_data, "double dispatch: the templated add matches the most specific in any add order", "[double_dispatch]") { dispatcher.add(shipAsteroid); dispatcher.add(militaryShipAsteroid); dispatcher.add(shipStation); dispatcher.add(asteroidStation); - ensure_equals(dispatcher(*patrol, *obstacle), "militaryShipAsteroid"); - ensure_equals(dispatcher(*tug, *obstacle), "shipAsteroid"); + CHECK(dispatcher(*patrol, *obstacle) == "militaryShipAsteroid"); + CHECK(dispatcher(*tug, *obstacle) == "shipAsteroid"); } -} // namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp index ce48c34a5a..76f54fcdd1 100644 --- a/indra/llcommon/tests/llerror_test.cpp +++ b/indra/llcommon/tests/llerror_test.cpp @@ -35,7 +35,8 @@ #include "../llerrorcontrol.h" #include "../llsd.h" -#include "../test/lltut.h" +#include +#include namespace { @@ -129,7 +130,8 @@ namespace tut std::ostringstream test_name; test_name << "testing message " << n << ", not enough messages"; - tut::ensure(test_name.str(), n < countMessages()); + UNSCOPED_INFO(test_name.str()); + REQUIRE(n < countMessages()); return mMessages[n]; } @@ -188,14 +190,16 @@ namespace tut void ensure_message_count(int expectedCount) { - ensure_equals("message count", countMessages(), expectedCount); + UNSCOPED_INFO("message count"); + REQUIRE(countMessages() == expectedCount); } std::string message_field(int msgnum, LogFieldIndex fieldnum) { std::ostringstream test_name; test_name << "testing message " << msgnum << ", not enough messages"; - tut::ensure(test_name.str(), msgnum < countMessages()); + UNSCOPED_INFO(test_name.str()); + REQUIRE(msgnum < countMessages()); std::string msg(message(msgnum)); @@ -247,7 +251,8 @@ namespace tut std::ostringstream test_name; test_name << "testing message " << msgnum << " field " << FieldName[fieldnum] << "\n message: \"" << message(msgnum) << "\"\n "; - ensure_equals(test_name.str(), message_field(msgnum, fieldnum), expectedText); + UNSCOPED_INFO(test_name.str()); + REQUIRE(message_field(msgnum, fieldnum) == expectedText); } void ensure_message_does_not_contain(int n, const std::string& expectedText) @@ -255,17 +260,12 @@ namespace tut std::ostringstream test_name; test_name << "testing message " << n; - ensure_does_not_contain(test_name.str(), message(n), expectedText); + UNSCOPED_INFO(test_name.str()); + REQUIRE_THAT(message(n), !Catch::Matchers::ContainsSubstring(expectedText)); } }; - typedef test_group ErrorTestGroup; - typedef ErrorTestGroup::object ErrorTestObject; - - ErrorTestGroup errorTestGroup("error"); - - template<> template<> - void ErrorTestObject::test<1>() + TEST_CASE_METHOD(ErrorTestData, "error: messages reach the recorder in order", "[error]") // basic test of output { LL_INFOS() << "test" << LL_ENDL; @@ -289,12 +289,11 @@ namespace namespace tut { - template<> template<> - void ErrorTestObject::test<2>() + TEST_CASE_METHOD(ErrorTestData, "error: the default level filters messages", "[error]") // messages are filtered based on default level { #ifdef LL_DISABLE_DEBUG_LOGGING - skip("Debug messages disabled"); + SKIP("Debug messages disabled"); #endif LLError::setDefaultLevel(LLError::LEVEL_DEBUG); writeSome(); @@ -335,12 +334,11 @@ namespace tut ensure_message_count(10); } - template<> template<> - void ErrorTestObject::test<3>() + TEST_CASE_METHOD(ErrorTestData, "error: the output names the level of each message", "[error]") // error type string in output { #ifdef LL_DISABLE_DEBUG_LOGGING - skip("Debug messages disabled"); + SKIP("Debug messages disabled"); #endif writeSome(); ensure_message_field_equals(0, LEVEL_FIELD, "DEBUG"); @@ -350,8 +348,7 @@ namespace tut ensure_message_count(4); } - template<> template<> - void ErrorTestObject::test<4>() + TEST_CASE_METHOD(ErrorTestData, "error: abbreviateFile strips the source root and leaves other paths", "[error]") // file abbreviation { std::string prev, abbreviateFile = __FILE__; @@ -370,12 +367,11 @@ namespace tut // argument unchanged, THEN check. } while (abbreviateFile != prev); - ensure_ends_with("file name abbreviation", - abbreviateFile, - "llcommon/tests/llerror_test.cpp" - ); - ensure_does_not_contain("file name abbreviation", - abbreviateFile, "indra"); + UNSCOPED_INFO("file name abbreviation"); + CHECK_THAT(abbreviateFile, + Catch::Matchers::EndsWith("llcommon/tests/llerror_test.cpp")); + UNSCOPED_INFO("file name abbreviation"); + CHECK_THAT(abbreviateFile, !Catch::Matchers::ContainsSubstring("indra")); std::string someFile = #if LL_WINDOWS @@ -386,8 +382,8 @@ namespace tut ; std::string someAbbreviation = LLError::abbreviateFile(someFile); - ensure_equals("non-indra file abbreviation", - someAbbreviation, someFile); + UNSCOPED_INFO("non-indra file abbreviation"); + CHECK(someAbbreviation == someFile); } } @@ -505,7 +501,7 @@ namespace std::stringstream ss; ss << message << ": " << "expected to find a copy of '" << expected << "' in actual '" << actual << "'"; - throw tut::failure(ss.str().c_str()); + FAIL(ss.str().c_str()); } } @@ -551,12 +547,11 @@ namespace namespace tut { - template<> template<> - void ErrorTestObject::test<5>() + TEST_CASE_METHOD(ErrorTestData, "error: multiline output leaves backslashes and line breaks unescaped", "[error]") // backslash, return, and newline are not escaped with backslashes { #ifdef LL_DISABLE_DEBUG_LOGGING - skip("Debug messages disabled"); + SKIP("Debug messages disabled"); #endif LLError::setDefaultLevel(LLError::LEVEL_DEBUG); setWantsMultiline(true); @@ -573,9 +568,8 @@ namespace tut namespace tut { - template<> template<> - // class/function information in output - void ErrorTestObject::test<6>() + // class/function information in output + TEST_CASE_METHOD(ErrorTestData, "error: messages name the function and class they came from", "[error]") { testLogName(mRecorder, logFromGlobal); testLogName(mRecorder, logFromStatic); @@ -623,9 +617,8 @@ namespace namespace tut { - template<> template<> - // handle nested logging - void ErrorTestObject::test<7>() + // handle nested logging + TEST_CASE_METHOD(ErrorTestData, "error: logging while building a message logs both", "[error]") { outerLogger(); ensure_message_field_equals(0, MSG_FIELD, "inside"); @@ -638,9 +631,8 @@ namespace tut ensure_message_count(4); } - template<> template<> - // special handling of LL_ERRS() calls - void ErrorTestObject::test<8>() + // special handling of LL_ERRS() calls + TEST_CASE_METHOD(ErrorTestData, "error: LL_ERRS logs its location and calls the fatal function", "[error]") { std::string location = errorReturningLocation(); @@ -648,7 +640,8 @@ namespace tut ensure_message_field_equals(0, MSG_FIELD, "die"); ensure_message_count(1); - ensure("fatal callback called", fatalWasCalled); + UNSCOPED_INFO("fatal callback called"); + CHECK(fatalWasCalled); } } @@ -667,9 +660,8 @@ namespace namespace tut { - template<> template<> - // time in output (for recorders that need it) - void ErrorTestObject::test<9>() + // time in output (for recorders that need it) + TEST_CASE_METHOD(ErrorTestData, "error: a recorder gets the time only when it asks for it", "[error]") { LLError::setTimeFunction(roswell); @@ -684,9 +676,8 @@ namespace tut ensure_message_field_equals(1, TIME_FIELD, roswell()); } - template<> template<> - // output order - void ErrorTestObject::test<10>() + // output order + TEST_CASE_METHOD(ErrorTestData, "error: fields come in time level tags location function message order", "[error]") { LLError::setTimeFunction(roswell); setWantsTime(true); @@ -695,14 +686,13 @@ namespace tut function; writeReturningLocationAndFunction(location, function); - ensure_equals("order is time level tags location function message", - message(0), + UNSCOPED_INFO("order is time level tags location function message"); + CHECK(message(0) == roswell() + " INFO " + "# " /* no tag */ + location + " " + function + " : " + "apple"); } - template<> template<> - // multiple recorders - void ErrorTestObject::test<11>() + // multiple recorders + TEST_CASE_METHOD(ErrorTestData, "error: every recorder gets each message with its own time setting", "[error]") { LLError::RecorderPtr altRecorder(new TestRecorder()); LLError::addRecorder(altRecorder); @@ -710,8 +700,10 @@ namespace tut LL_INFOS() << "boo" << LL_ENDL; ensure_message_field_equals(0, MSG_FIELD, "boo"); - ensure_equals("alt recorder count", std::dynamic_pointer_cast(altRecorder)->countMessages(), 1); - ensure_contains("alt recorder message 0", std::dynamic_pointer_cast(altRecorder)->message(0), "boo"); + UNSCOPED_INFO("alt recorder count"); + CHECK(std::dynamic_pointer_cast(altRecorder)->countMessages() == 1); + UNSCOPED_INFO("alt recorder message 0"); + CHECK_THAT(std::dynamic_pointer_cast(altRecorder)->message(0), Catch::Matchers::ContainsSubstring("boo")); LLError::setTimeFunction(roswell); @@ -724,10 +716,14 @@ namespace tut std::string when = roswell(); ensure_message_does_not_contain(1, when); - ensure_equals("alt recorder count", std::dynamic_pointer_cast(altRecorder)->countMessages(), 2); - ensure_does_not_contain("alt recorder message 1", std::dynamic_pointer_cast(altRecorder)->message(1), when); - ensure_equals("another recorder count", std::dynamic_pointer_cast(anotherRecorder)->countMessages(), 1); - ensure_contains("another recorder message 0", std::dynamic_pointer_cast(anotherRecorder)->message(0), when); + UNSCOPED_INFO("alt recorder count"); + CHECK(std::dynamic_pointer_cast(altRecorder)->countMessages() == 2); + UNSCOPED_INFO("alt recorder message 1"); + CHECK_THAT(std::dynamic_pointer_cast(altRecorder)->message(1), !Catch::Matchers::ContainsSubstring(when)); + UNSCOPED_INFO("another recorder count"); + CHECK(std::dynamic_pointer_cast(anotherRecorder)->countMessages() == 1); + UNSCOPED_INFO("another recorder message 0"); + CHECK_THAT(std::dynamic_pointer_cast(anotherRecorder)->message(0), Catch::Matchers::ContainsSubstring(when)); LLError::removeRecorder(altRecorder); LLError::removeRecorder(anotherRecorder); @@ -758,9 +754,8 @@ class TestBeta namespace tut { - template<> template<> - // filtering by class - void ErrorTestObject::test<12>() + // filtering by class + TEST_CASE_METHOD(ErrorTestData, "error: a class level overrides the default level", "[error]") { LLError::setDefaultLevel(LLError::LEVEL_WARN); LLError::setClassLevel("TestBeta", LLError::LEVEL_INFO); @@ -776,9 +771,8 @@ namespace tut ensure_message_count(5); } - template<> template<> - // filtering by function, and that it will override class filtering - void ErrorTestObject::test<13>() + // filtering by function, and that it will override class filtering + TEST_CASE_METHOD(ErrorTestData, "error: a function level overrides the class level", "[error]") { LLError::setDefaultLevel(LLError::LEVEL_DEBUG); LLError::setClassLevel("TestBeta", LLError::LEVEL_WARN); @@ -791,10 +785,9 @@ namespace tut ensure_message_count(2); } - template<> template<> - // filtering by file - // and that it is overridden by both class and function filtering - void ErrorTestObject::test<14>() + // filtering by file + // and that it is overridden by both class and function filtering + TEST_CASE_METHOD(ErrorTestData, "error: a file level yields to class and function levels", "[error]") { LLError::setDefaultLevel(LLError::LEVEL_DEBUG); LLError::setFileLevel(LLError::abbreviateFile(__FILE__), @@ -813,39 +806,43 @@ namespace tut ensure_message_count(3); } - template<> template<> - // proper cached, efficient lookup of filtering - void ErrorTestObject::test<15>() + // proper cached, efficient lookup of filtering + TEST_CASE_METHOD(ErrorTestData, "error: filter lookups are cached until the levels change", "[error]") { LLError::setDefaultLevel(LLError::LEVEL_NONE); TestAlpha::doInfo(); ensure_message_count(0); - ensure_equals("first check", LLError::shouldLogCallCount(), 1); + UNSCOPED_INFO("first check"); + CHECK(LLError::shouldLogCallCount() == 1); TestAlpha::doInfo(); ensure_message_count(0); - ensure_equals("second check", LLError::shouldLogCallCount(), 1); + UNSCOPED_INFO("second check"); + CHECK(LLError::shouldLogCallCount() == 1); LLError::setClassLevel("TestAlpha", LLError::LEVEL_DEBUG); TestAlpha::doInfo(); ensure_message_count(1); - ensure_equals("third check", LLError::shouldLogCallCount(), 2); + UNSCOPED_INFO("third check"); + CHECK(LLError::shouldLogCallCount() == 2); TestAlpha::doInfo(); ensure_message_count(2); - ensure_equals("fourth check", LLError::shouldLogCallCount(), 2); + UNSCOPED_INFO("fourth check"); + CHECK(LLError::shouldLogCallCount() == 2); LLError::setClassLevel("TestAlpha", LLError::LEVEL_WARN); TestAlpha::doInfo(); ensure_message_count(2); - ensure_equals("fifth check", LLError::shouldLogCallCount(), 3); + UNSCOPED_INFO("fifth check"); + CHECK(LLError::shouldLogCallCount() == 3); TestAlpha::doInfo(); ensure_message_count(2); - ensure_equals("sixth check", LLError::shouldLogCallCount(), 3); + UNSCOPED_INFO("sixth check"); + CHECK(LLError::shouldLogCallCount() == 3); } - template<> template<> - // configuration from LLSD - void ErrorTestObject::test<16>() + // configuration from LLSD + TEST_CASE_METHOD(ErrorTestData, "error: configure sets levels from LLSD and can be run again", "[error]") { LLSD config; config["print-location"] = true; @@ -895,12 +892,11 @@ namespace tut namespace tut { - template<> template<> - void ErrorTestObject::test<17>() + TEST_CASE_METHOD(ErrorTestData, "error: single-line output escapes backslashes and line breaks", "[error]") // backslash, return, and newline are escaped with backslashes { #ifdef LL_DISABLE_DEBUG_LOGGING - skip("Debug messages disabled"); + SKIP("Debug messages disabled"); #endif LLError::setDefaultLevel(LLError::LEVEL_DEBUG); writeMsgNeedsEscaping(); @@ -925,12 +921,11 @@ namespace namespace tut { - template<> template<> - void ErrorTestObject::test<18>() + TEST_CASE_METHOD(ErrorTestData, "error: a space in a tag is an error", "[error]") // space character is not allowed in a tag { #ifdef LL_DISABLE_DEBUG_LOGGING - skip("Debug messages disabled"); + SKIP("Debug messages disabled"); #endif LLError::setDefaultLevel(LLError::LEVEL_DEBUG); fatalWasCalled = false; @@ -939,7 +934,8 @@ namespace tut std::string expected = "Space is not allowed in a log tag at " + location; ensure_message_field_equals(0, LEVEL_FIELD, "ERROR"); ensure_message_field_equals(0, MSG_FIELD, expected); - ensure("fatal callback called", fatalWasCalled); + UNSCOPED_INFO("fatal callback called"); + CHECK(fatalWasCalled); } } diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index a16acb3307..9de231f97f 100644 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -37,8 +37,10 @@ #include #include -#include "../test/lltut.h" +#include +#include "altestmatchers.h" #include "../test/lltestapp.h" +#include "alteststateguard.h" #include "llsd.h" #include "llsdutil.h" #include "llevents.h" @@ -93,7 +95,8 @@ class ImmediateAPI /// ran ~LLApp after each, and that ends LLCommon -- the APR pool, the LLTrace /// master recorder -- for every test after it in the process. So there is one /// app for the process, never destroyed, which each test sees start running -/// and stop. +/// and stop. Making it changes the LLApp and LLTrace's master recorder on +/// purpose, so the state guard takes those as they are then. class CoroTestApp: public LLTestApp { public: @@ -110,7 +113,13 @@ class CoroTestApp: public LLTestApp private: static CoroTestApp& app() { - static CoroTestApp* sApp = new CoroTestApp; + static CoroTestApp* sApp = nullptr; + if (!sApp) + { + sApp = new CoroTestApp; + ll_test::StateGuard::accept("the LLApp"); + ll_test::StateGuard::accept("the LLTrace master recorder"); + } return *sApp; } }; @@ -120,7 +129,7 @@ class CoroTestApp: public LLTestApp /***************************************************************************** * TUT *****************************************************************************/ -namespace tut +namespace { struct lleventcoro_data { @@ -137,9 +146,6 @@ namespace tut void postAndWait1(); void coroPumpPost(); }; - typedef test_group coroutine_group; - typedef coroutine_group::object object; - coroutine_group coroutinegrp("coroutine"); void lleventcoro_data::explicit_wait(std::shared_ptr>& cbp) { @@ -164,15 +170,14 @@ namespace tut debug("about to suspend"); stringdata = future.get(); mSync.bump(); - ensure_equals("Got it", stringdata, "received"); + UNSCOPED_INFO("Got it"); + REQUIRE(stringdata == "received"); } END } - template<> template<> - void object::test<1>() + TEST_CASE_METHOD(lleventcoro_data, "coroutine: a coroutine suspended on a future wakes when its promise is set", "[coroutine]") { - set_test_name("explicit_wait"); DEBUG; // Construct the coroutine instance that will run explicit_wait. @@ -188,7 +193,7 @@ namespace tut // 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"); + CHECK(stringdata == "received"); } void lleventcoro_data::waitForEventOn1() @@ -202,10 +207,8 @@ namespace tut END } - template<> template<> - void object::test<2>() + TEST_CASE_METHOD(lleventcoro_data, "coroutine: suspendUntilEventOn() wakes on a post to the named pump", "[coroutine]") { - set_test_name("waitForEventOn1"); DEBUG; LLCoros::instance().launch("test<2>", [this](){ waitForEventOn1(); }); mSync.bump(); @@ -214,7 +217,7 @@ namespace tut // give waitForEventOn1() a chance to run mSync.yield(); debug("back from send"); - ensure_equals(result.asString(), "received"); + CHECK(result.asString() == "received"); } void lleventcoro_data::coroPump() @@ -230,10 +233,8 @@ namespace tut END } - template<> template<> - void object::test<3>() + TEST_CASE_METHOD(lleventcoro_data, "coroutine: LLCoroEventPump::suspend() wakes on a post to its pump", "[coroutine]") { - set_test_name("coroPump"); DEBUG; LLCoros::instance().launch("test<3>", [this](){ coroPump(); }); mSync.bump(); @@ -242,7 +243,7 @@ namespace tut // give coroPump() a chance to run mSync.yield(); debug("back from send"); - ensure_equals(result.asString(), "received"); + CHECK(result.asString() == "received"); } void lleventcoro_data::postAndWait1() @@ -259,13 +260,11 @@ namespace tut END } - template<> template<> - void object::test<4>() + TEST_CASE_METHOD(lleventcoro_data, "coroutine: postAndSuspend() gets an immediate reply", "[coroutine]") { - set_test_name("postAndWait1"); DEBUG; LLCoros::instance().launch("test<4>", [this](){ postAndWait1(); }); - ensure_equals(result.asInteger(), 18); + CHECK(result.asInteger() == 18); } void lleventcoro_data::coroPumpPost() @@ -281,13 +280,11 @@ namespace tut END } - template<> template<> - void object::test<5>() + TEST_CASE_METHOD(lleventcoro_data, "coroutine: LLCoroEventPump::postAndSuspend() gets an immediate reply", "[coroutine]") { - set_test_name("coroPumpPost"); DEBUG; LLCoros::instance().launch("test<5>", [this](){ coroPumpPost(); }); - ensure_equals(result.asInteger(), 18); + CHECK(result.asInteger() == 18); } // A coroutine consumes what a PUMP holds for it, one value a wait. @@ -302,7 +299,8 @@ namespace tut 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); + UNSCOPED_INFO("should not have consumed first"); + REQUIRE_FALSE(consumed); // now launch the coro LL_DEBUGS() << "test() launching listener coro" << LL_ENDL; running = true; @@ -330,7 +328,8 @@ namespace tut 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); + UNSCOPED_INFO("should have consumed second"); + REQUIRE(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 @@ -339,7 +338,8 @@ namespace tut 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); + UNSCOPED_INFO("should NOT yet have consumed third"); + REQUIRE_FALSE(consumed); // now just wait for coro to finish -- which it eventually will, given // that all its suspend calls have short timeouts. while (running) @@ -348,22 +348,19 @@ namespace tut llcoro::suspendUntilTimeout(0.1f); } // okay, verify expected results - ensure_equals("should have received three values", data, - llsd::array(first, second, third)); + UNSCOPED_INFO("should have received three values"); + REQUIRE_THAT(data, + ll_test::LLSDEquals(llsd::array(first, second, third))); LL_DEBUGS() << "test() done" << LL_ENDL; } - template<> template<> - void object::test<6>() + TEST_CASE_METHOD(lleventcoro_data, "coroutine: LLEventMailDrop gives a coroutine one value a wait", "[coroutine]") { - set_test_name("LLEventMailDrop"); check_coro_consumes(); } - template<> template<> - void object::test<7>() + TEST_CASE_METHOD(lleventcoro_data, "coroutine: LLEventLogProxyFor gives a coroutine one value a wait", "[coroutine]") { - set_test_name("LLEventLogProxyFor"); check_coro_consumes< LLEventLogProxyFor >(); } } diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp index ada009b99b..6e8a7ab1bc 100644 --- a/indra/llcommon/tests/lleventdispatcher_test.cpp +++ b/indra/llcommon/tests/lleventdispatcher_test.cpp @@ -17,7 +17,8 @@ // std headers // external library headers // other Linden headers -#include "../test/lltut.h" +#include +#include "altestmatchers.h" #include "lleventfilter.h" #include "llsd.h" #include "llsdutil.h" @@ -326,8 +327,8 @@ namespace tut { static void ensure_has(const std::string& outer, const std::string& inner) { - ensure(stringize("'", outer, "' does not contain '", inner, "'"), - outer.find(inner) != std::string::npos); + UNSCOPED_INFO(stringize("'", outer, "' does not contain '", inner, "'")); + REQUIRE(outer.find(inner) != std::string::npos); } template @@ -636,12 +637,12 @@ namespace tut for (LLEventDispatcher::NameDesc nd: work) { DescMap::iterator found = forgotten.find(nd.first); - ensure(stringize("LLEventDispatcher records function '", nd.first, - "' we didn't enter"), - found != forgotten.end()); - ensure_equals(stringize("LLEventDispatcher desc '", nd.second, - "' doesn't match what we entered: '", found->second, "'"), - nd.second, found->second); + UNSCOPED_INFO(stringize("LLEventDispatcher records function '", nd.first, + "' we didn't enter")); + REQUIRE(found != forgotten.end()); + UNSCOPED_INFO(stringize("LLEventDispatcher desc '", nd.second, + "' doesn't match what we entered: '", found->second, "'")); + REQUIRE(nd.second == found->second); // found in our map the name from LLEventDispatcher, good, erase // our map entry forgotten.erase(found); @@ -656,15 +657,17 @@ namespace tut out << delim << fme.first; delim = ", "; } - throw failure(out.str()); + FAIL(out.str()); } } Vars* varsfor(const std::string& name) { VarsMap::const_iterator found = funcvars.find(name); - ensure(stringize("No Vars* for ", name), found != funcvars.end()); - ensure(stringize("NULL Vars* for ", name), found->second); + UNSCOPED_INFO(stringize("No Vars* for ", name)); + REQUIRE(found != funcvars.end()); + UNSCOPED_INFO(stringize("NULL Vars* for ", name)); + REQUIRE(found->second); return found->second; } @@ -693,7 +696,8 @@ namespace tut LLSD getMetadata(const std::string& name) { LLSD meta(work.getMetadata(name)); - ensure(stringize("No metadata for ", name), meta.isDefined()); + UNSCOPED_INFO(stringize("No metadata for ", name)); + REQUIRE(meta.isDefined()); return meta; } @@ -723,7 +727,8 @@ namespace tut out << msg << ": "; } out << "expected " << expected << ", actual " << actual; - ensure(out.str(), llsd_equals(actual, expected, bits)); + UNSCOPED_INFO(out.str()); + REQUIRE(llsd_equals(actual, expected, bits)); } void ensure_llsd(const LLSD& actual, const LLSD& expected, U32 bits) @@ -731,9 +736,6 @@ namespace tut ensure_llsd("", actual, expected, bits); } }; - typedef test_group lleventdispatcher_group; - typedef lleventdispatcher_group::object object; - lleventdispatcher_group lleventdispatchergrp("lleventdispatcher"); // Call cases: // - (try_call | call) (explicit name | event key) (real | bogus) name @@ -756,10 +758,8 @@ namespace tut // - (Free function | non-static method), map style, (no | arbitrary) params, // (empty | full | partial (array | map)) defaults - template<> template<> - void object::test<1>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: map-style registration with non-array params", "[lleventdispatcher]") { - set_test_name("map-style registration with non-array params"); // Pass "param names" as scalar or as map LLSD attempts(llsd::array(17, LLSDMap("pi", 3.14)("two", 2))); for (LLSD ae: inArray(attempts)) @@ -771,20 +771,16 @@ namespace tut } } - template<> template<> - void object::test<2>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: map-style registration with badly-formed defaults", "[lleventdispatcher]") { - set_test_name("map-style registration with badly-formed defaults"); std::string threw = catch_what([this](){ work.add("freena_err", "freena", freena, llsd::array("a", "b"), 17); }); ensure_has(threw, "must be a map or an array"); } - template<> template<> - void object::test<3>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: map-style registration with too many array defaults", "[lleventdispatcher]") { - set_test_name("map-style registration with too many array defaults"); std::string threw = catch_what([this](){ work.add("freena_err", "freena", freena, llsd::array("a", "b"), @@ -793,10 +789,8 @@ namespace tut ensure_has(threw, "shorter than"); } - template<> template<> - void object::test<4>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: map-style registration with too many map defaults", "[lleventdispatcher]") { - set_test_name("map-style registration with too many map defaults"); std::string threw = catch_what([this](){ work.add("freena_err", "freena", freena, llsd::array("a", "b"), @@ -807,57 +801,54 @@ namespace tut ensure_has(threw, "bar"); } - template<> template<> - void object::test<5>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: query all lists every registered name and desc", "[lleventdispatcher]") { - set_test_name("query all"); verify_descs(); } - template<> template<> - void object::test<6>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: query all after remove() omits only the removed name", "[lleventdispatcher]") { - set_test_name("query all with remove()"); - ensure("remove('bogus') returned true", ! work.remove("bogus")); - ensure("remove('real') returned false", work.remove("free1")); + UNSCOPED_INFO("remove('bogus') returned true"); + CHECK_FALSE(work.remove("bogus")); + UNSCOPED_INFO("remove('real') returned false"); + CHECK(work.remove("free1")); // Of course, remove that from 'descs' too... descs.erase("free1"); verify_descs(); } - template<> template<> - void object::test<7>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: getDispatchKey() returns the key it was built with", "[lleventdispatcher]") { - set_test_name("getDispatchKey()"); - ensure_equals(work.getDispatchKey(), "op"); + CHECK(work.getDispatchKey() == "op"); } - template<> template<> - void object::test<8>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: query Callables with/out required params", "[lleventdispatcher]") { - set_test_name("query Callables with/out required params"); LLSD names(llsd::array("free1", "Dmethod1", "Dcmethod1", "method1")); for (LLSD nm: inArray(names)) { LLSD metadata(getMetadata(nm)); - ensure_equals("name mismatch", metadata["name"], nm); - ensure_equals(metadata["desc"].asString(), descs[nm]); - ensure("should not have required structure", metadata["required"].isUndefined()); - ensure("should not have optional", metadata["optional"].isUndefined()); + UNSCOPED_INFO("name mismatch"); + REQUIRE_THAT(metadata["name"], ll_test::LLSDEquals(nm)); + REQUIRE(metadata["desc"].asString() == descs[nm]); + UNSCOPED_INFO("should not have required structure"); + REQUIRE(metadata["required"].isUndefined()); + UNSCOPED_INFO("should not have optional"); + REQUIRE(metadata["optional"].isUndefined()); std::string name_req(nm.asString() + "_req"); metadata = getMetadata(name_req); - ensure_equals(metadata["name"].asString(), name_req); - ensure_equals(metadata["desc"].asString(), descs[name_req]); - ensure_equals("required mismatch", required, metadata["required"]); - ensure("should not have optional", metadata["optional"].isUndefined()); + REQUIRE(metadata["name"].asString() == name_req); + REQUIRE(metadata["desc"].asString() == descs[name_req]); + UNSCOPED_INFO("required mismatch"); + REQUIRE_THAT(required, ll_test::LLSDEquals(metadata["required"])); + UNSCOPED_INFO("should not have optional"); + REQUIRE(metadata["optional"].isUndefined()); } } - template<> template<> - void object::test<9>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: query array-style functions/methods", "[lleventdispatcher]") { - set_test_name("query array-style functions/methods"); // Associate each registered name with expected arity. LLSD expected(llsd::array (llsd::array @@ -876,38 +867,37 @@ namespace tut for (LLSD nm: inArray(names)) { LLSD metadata(getMetadata(nm)); - ensure_equals("name mismatch", metadata["name"], nm); - ensure_equals(metadata["desc"].asString(), descs[nm]); - ensure_equals(stringize("mismatched required for ", nm.asString()), - metadata["required"], req); - ensure("should not have optional", metadata["optional"].isUndefined()); + UNSCOPED_INFO("name mismatch"); + REQUIRE_THAT(metadata["name"], ll_test::LLSDEquals(nm)); + REQUIRE(metadata["desc"].asString() == descs[nm]); + UNSCOPED_INFO(stringize("mismatched required for ", nm.asString())); + REQUIRE_THAT(metadata["required"], ll_test::LLSDEquals(req)); + UNSCOPED_INFO("should not have optional"); + REQUIRE(metadata["optional"].isUndefined()); } } } - template<> template<> - void object::test<10>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: query map-style no-params functions/methods", "[lleventdispatcher]") { - set_test_name("query map-style no-params functions/methods"); // - (Free function | non-static method), map style, no params (ergo // no defaults) LLSD names(llsd::array("free0_map", "smethod0_map", "method0_map")); for (LLSD nm: inArray(names)) { LLSD metadata(getMetadata(nm)); - ensure_equals("name mismatch", metadata["name"], nm); - ensure_equals(metadata["desc"].asString(), descs[nm]); - ensure("should not have required", - (metadata["required"].isUndefined() || metadata["required"].size() == 0)); - ensure("should not have optional", metadata["optional"].isUndefined()); + UNSCOPED_INFO("name mismatch"); + REQUIRE_THAT(metadata["name"], ll_test::LLSDEquals(nm)); + REQUIRE(metadata["desc"].asString() == descs[nm]); + UNSCOPED_INFO("should not have required"); + REQUIRE((metadata["required"].isUndefined() || metadata["required"].size() == 0)); + UNSCOPED_INFO("should not have optional"); + REQUIRE(metadata["optional"].isUndefined()); } } - template<> template<> - void object::test<11>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: full array defaults and full map defaults give the same metadata", "[lleventdispatcher]") { - set_test_name("query map-style arbitrary-params functions/methods: " - "full array defaults vs. full map defaults"); // With functions registered with no defaults ("_allreq" suffixes), // there is of course no difference between array defaults and map // defaults. (We don't even bother registering with LLSD::emptyArray() @@ -932,20 +922,20 @@ namespace tut // remove for comparing the rest. LLSD ameta(getMetadata(adft)); LLSD mmeta(getMetadata(mdft)); - ensure_equals("adft name", adft, ameta["name"]); - ensure_equals("mdft name", mdft, mmeta["name"]); + UNSCOPED_INFO("adft name"); + REQUIRE_THAT(adft, ll_test::LLSDEquals(ameta["name"])); + UNSCOPED_INFO("mdft name"); + REQUIRE_THAT(mdft, ll_test::LLSDEquals(mmeta["name"])); ameta.erase("name"); mmeta.erase("name"); - ensure_equals(stringize("metadata for ", adft.asString(), - " vs. ", mdft.asString()), - ameta, mmeta); + UNSCOPED_INFO(stringize("metadata for ", adft.asString(), + " vs. ", mdft.asString())); + REQUIRE_THAT(ameta, ll_test::LLSDEquals(mmeta)); } } - template<> template<> - void object::test<12>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: query map-style arbitrary-params functions/methods", "[lleventdispatcher]") { - set_test_name("query map-style arbitrary-params functions/methods"); // - (Free function | non-static method), map style, arbitrary params, // (empty | full | partial (array | map)) defaults @@ -1044,46 +1034,46 @@ namespace tut for (LLSD nm: inArray(names)) { LLSD metadata(getMetadata(nm)); - ensure_equals("name mismatch", metadata["name"], nm); - ensure_equals(nm.asString(), metadata["desc"].asString(), descs[nm]); - ensure_equals(stringize(nm, " required mismatch"), - metadata["required"], required); - ensure_equals(stringize(nm, " optional mismatch"), - metadata["optional"], optional); + UNSCOPED_INFO("name mismatch"); + REQUIRE_THAT(metadata["name"], ll_test::LLSDEquals(nm)); + UNSCOPED_INFO(nm.asString()); + REQUIRE(metadata["desc"].asString() == descs[nm]); + UNSCOPED_INFO(stringize(nm, " required mismatch")); + REQUIRE_THAT(metadata["required"], ll_test::LLSDEquals(required)); + UNSCOPED_INFO(stringize(nm, " optional mismatch")); + REQUIRE_THAT(metadata["optional"], ll_test::LLSDEquals(optional)); } } } - template<> template<> - void object::test<13>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: try_call() returns whether it found the name", "[lleventdispatcher]") { - set_test_name("try_call()"); - ensure("try_call(bogus name, LLSD()) returned true", ! work.try_call("freek", LLSD())); - ensure("try_call(bogus name) returned true", ! work.try_call(LLSDMap("op", "freek"))); - ensure("try_call(real name, LLSD()) returned false", work.try_call("free0_array", LLSD())); - ensure("try_call(real name) returned false", work.try_call(LLSDMap("op", "free0_map"))); + UNSCOPED_INFO("try_call(bogus name, LLSD()) returned true"); + CHECK_FALSE(work.try_call("freek", LLSD())); + UNSCOPED_INFO("try_call(bogus name) returned true"); + CHECK_FALSE(work.try_call(LLSDMap("op", "freek"))); + UNSCOPED_INFO("try_call(real name, LLSD()) returned false"); + CHECK(work.try_call("free0_array", LLSD())); + UNSCOPED_INFO("try_call(real name) returned false"); + CHECK(work.try_call(LLSDMap("op", "free0_map"))); } - template<> template<> - void object::test<14>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: call with bad name", "[lleventdispatcher]") { - set_test_name("call with bad name"); call_exc("freek", LLSD(), "not found"); std::string threw = call_exc("", LLSDMap("op", "freek"), "bad"); ensure_has(threw, "op"); ensure_has(threw, "freek"); } - template<> template<> - void object::test<15>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: call with event key", "[lleventdispatcher]") { - set_test_name("call with event key"); // We don't need a separate test for operator()(string, LLSD) with // valid name, because all the rest of the tests exercise that case. // The one we don't exercise elsewhere is operator()(LLSD) with valid // name, so here it is. work(LLSDMap("op", "free0_map")); - ensure_equals(g.i, 17); + CHECK(g.i == 17); } // Cannot be defined inside function body... remind me again why we use C++... :-P @@ -1096,10 +1086,8 @@ namespace tut }; } // anonymous namespace - template<> template<> - void object::test<16>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: call Callables", "[lleventdispatcher]") { - set_test_name("call Callables"); CallablesTriple tests[] = { { "free1", "free1_req", g.llsd }, @@ -1119,22 +1107,21 @@ namespace tut // Should be able to pass 'answer' to Callables registered // without 'required'. work(tr.name, answer); - ensure_equals("answer mismatch", tr.llsd, answer); + UNSCOPED_INFO("answer mismatch"); + REQUIRE_THAT(tr.llsd, ll_test::LLSDEquals(answer)); // Should NOT be able to pass 'answer' to Callables registered // with 'required'. call_logerr(tr.name_req, answer, "bad request"); // But SHOULD be able to pass 'matching' to Callables registered // with 'required'. work(tr.name_req, matching); - ensure_equals("matching mismatch", tr.llsd, matching); + UNSCOPED_INFO("matching mismatch"); + REQUIRE_THAT(tr.llsd, ll_test::LLSDEquals(matching)); } } - template<> template<> - void object::test<17>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: passing wrong args to (map | array)-style registrations", "[lleventdispatcher]") { - set_test_name("passing wrong args to (map | array)-style registrations"); - // Pass scalar/map to array-style functions, scalar/array to map-style // functions. It seems pointless to repeat this with every variation: // (free function | non-static method), (no | arbitrary) args. We @@ -1155,10 +1142,8 @@ namespace tut // call_exc("free0_map", llsd::array("a", "b"), map_exc); } - template<> template<> - void object::test<18>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: call no-args functions", "[lleventdispatcher]") { - set_test_name("call no-args functions"); LLSD names(llsd::array ("free0_array", "free0_map", "smethod0_array", "smethod0_map", @@ -1172,10 +1157,10 @@ namespace tut // calling these things several different times in the same // test method, manually reset the Vars between each. *vars = Vars(); - ensure_equals(vars->i, 0); + REQUIRE(vars->i == 0); // call function with empty array (or LLSD(), should be equivalent) work(name, LLSD()); - ensure_equals(vars->i, 17); + REQUIRE(vars->i == 17); } } @@ -1185,10 +1170,8 @@ namespace tut LLSDMap("a", "smethodna_array")("b", "smethodnb_array"), LLSDMap("a", "methodna_array") ("b", "methodnb_array"))); - template<> template<> - void object::test<19>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: call array-style functions with wrong-length arrays", "[lleventdispatcher]") { - set_test_name("call array-style functions with wrong-length arrays"); // Could have different wrong-length arrays for *na and for *nb, but // since they both take 5 params... LLSD tooshort(llsd::array("this", "array", "too", "short")); @@ -1207,10 +1190,8 @@ namespace tut } } - template<> template<> - void object::test<20>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: call array-style functions with right-size arrays", "[lleventdispatcher]") { - set_test_name("call array-style functions with right-size arrays"); std::vector binary; for (size_t h(0x01), i(0); i < 5; h+= 0x22, ++i) { @@ -1245,24 +1226,23 @@ namespace tut } } - template<> template<> - void object::test<21>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: passing LLSD() to a const char* param sends NULL", "[lleventdispatcher]") { - set_test_name("verify that passing LLSD() to const char* sends NULL"); - - ensure_equals("Vars::cp init", v.cp, ""); + UNSCOPED_INFO("Vars::cp init"); + CHECK(v.cp == ""); work("methodna_map_mdft", LLSDMap("cp", LLSD())); - ensure_equals("passing LLSD()", v.cp, "NULL"); + UNSCOPED_INFO("passing LLSD()"); + CHECK(v.cp == "NULL"); work("methodna_map_mdft", LLSDMap("cp", "")); - ensure_equals("passing \"\"", v.cp, "''"); + UNSCOPED_INFO("passing \"\""); + CHECK(v.cp == "''"); work("methodna_map_mdft", LLSDMap("cp", "non-NULL")); - ensure_equals("passing \"non-NULL\"", v.cp, "'non-NULL'"); + UNSCOPED_INFO("passing \"non-NULL\""); + CHECK(v.cp == "'non-NULL'"); } - template<> template<> - void object::test<22>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: call map-style functions with (full | oversized) (arrays | maps)", "[lleventdispatcher]") { - set_test_name("call map-style functions with (full | oversized) (arrays | maps)"); const char binary[] = "\x99\x88\x77\x66\x55"; LLSD array_full(LLSDMap ("a", llsd::array(false, 255, 98.6, 1024.5, "pointer")) @@ -1276,11 +1256,11 @@ namespace tut "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", - array_full["b"][2].asDate(), dft_array_full["b"][2].asDate()); + UNSCOPED_INFO("Timestamps too close"); + CHECK(array_full["b"][2].asDate() != dft_array_full["b"][2].asDate()); // We /insist/ that LLUUID::generateNewID() do so. - ensure_not_equals("UUID collision", - array_full["b"][1].asUUID(), dft_array_full["b"][1].asUUID()); + UNSCOPED_INFO("UUID collision"); + CHECK(array_full["b"][1].asUUID() != dft_array_full["b"][1].asUUID()); LLSD map_full, map_overfull; for (LLSD::String a: ab) { @@ -1379,65 +1359,57 @@ namespace tut }; } // anonymous namespace - template<> template<> - void object::test<23>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: string result", "[lleventdispatcher]") { - set_test_name("string result"); DispatchResult service; LLSD result{ service("strfunc", "a string") }; - ensure_equals("strfunc() mismatch", result.asString(), "got a string"); + UNSCOPED_INFO("strfunc() mismatch"); + CHECK(result.asString() == "got a string"); } - template<> template<> - void object::test<24>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: void result", "[lleventdispatcher]") { - set_test_name("void result"); DispatchResult service; LLSD result{ service("voidfunc", LLSD()) }; - ensure("voidfunc() returned defined", result.isUndefined()); + UNSCOPED_INFO("voidfunc() returned defined"); + REQUIRE(result.isUndefined()); } - template<> template<> - void object::test<25>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: Integer result", "[lleventdispatcher]") { - set_test_name("Integer result"); DispatchResult service; LLSD result{ service("intfunc", -17) }; - ensure_equals("intfunc() mismatch", result.asInteger(), 17); + UNSCOPED_INFO("intfunc() mismatch"); + CHECK(result.asInteger() == 17); } - template<> template<> - void object::test<26>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: LLSD echo", "[lleventdispatcher]") { - set_test_name("LLSD echo"); DispatchResult service; LLSD result{ service("llsdfunc", llsd::map("op", "llsdfunc", "reqid", 17)) }; - ensure_equals("llsdfunc() mismatch", result, - llsd::map("op", "llsdfunc", "reqid", 17, "with", "string")); + UNSCOPED_INFO("llsdfunc() mismatch"); + CHECK_THAT(result, + ll_test::LLSDEquals(llsd::map("op", "llsdfunc", "reqid", 17, "with", "string"))); } - template<> template<> - void object::test<27>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: map LLSD result", "[lleventdispatcher]") { - set_test_name("map LLSD result"); DispatchResult service; LLSD result{ service("mapfunc", llsd::array(-12, "value")) }; - ensure_equals("mapfunc() mismatch", result, llsd::map("i", 12, "str", "got value")); + UNSCOPED_INFO("mapfunc() mismatch"); + CHECK_THAT(result, ll_test::LLSDEquals(llsd::map("i", 12, "str", "got value"))); } - template<> template<> - void object::test<28>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: array LLSD result", "[lleventdispatcher]") { - set_test_name("array LLSD result"); DispatchResult service; LLSD result{ service("arrayfunc", llsd::array(-8, "word")) }; - ensure_equals("arrayfunc() mismatch", result, llsd::array(8, "got word")); + UNSCOPED_INFO("arrayfunc() mismatch"); + CHECK_THAT(result, ll_test::LLSDEquals(llsd::array(8, "got word"))); } - template<> template<> - void object::test<29>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: a listener error without a reply pump throws", "[lleventdispatcher]") { - set_test_name("listener error, no reply"); DispatchResult service; tut::call_exc( [&service]() @@ -1445,23 +1417,21 @@ namespace tut "nosuchfunc"); } - template<> template<> - void object::test<30>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: listener error with reply", "[lleventdispatcher]") { - set_test_name("listener error with reply"); DispatchResult service; LLCaptureListener result; service.post(llsd::map("op", "nosuchfunc", "reqid", 17, "reply", result.getName())); LLSD reply{ result.get() }; - ensure("no reply", reply.isDefined()); - ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17); + UNSCOPED_INFO("no reply"); + REQUIRE(reply.isDefined()); + UNSCOPED_INFO("reqid not echoed"); + CHECK(reply["reqid"].asInteger() == 17); ensure_has(reply["error"].asString(), "nosuchfunc"); } - template<> template<> - void object::test<31>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: listener call to void function", "[lleventdispatcher]") { - set_test_name("listener call to void function"); DispatchResult service; LLCaptureListener result; result.set("non-empty"); @@ -1471,14 +1441,13 @@ namespace tut "op", func, "reqid", 17, "reply", result.getName())); - ensure_equals("reply from " + func, result.get().asString(), "non-empty"); + UNSCOPED_INFO("reply from " + func); + REQUIRE(result.get().asString() == "non-empty"); } } - template<> template<> - void object::test<32>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: listener call to string function", "[lleventdispatcher]") { - set_test_name("listener call to string function"); DispatchResult service; LLCaptureListener result; service.post(llsd::map( @@ -1487,14 +1456,14 @@ namespace tut "reqid", 17, "reply", result.getName())); LLSD reply{ result.get() }; - ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17); - ensure_equals("bad reply from strfunc", reply["data"].asString(), "got a string"); + UNSCOPED_INFO("reqid not echoed"); + CHECK(reply["reqid"].asInteger() == 17); + UNSCOPED_INFO("bad reply from strfunc"); + CHECK(reply["data"].asString() == "got a string"); } - template<> template<> - void object::test<33>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: listener call to map function", "[lleventdispatcher]") { - set_test_name("listener call to map function"); DispatchResult service; LLCaptureListener result; service.post(llsd::map( @@ -1503,15 +1472,16 @@ namespace tut "reqid", 17, "reply", result.getName())); LLSD reply{ result.get() }; - ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17); - ensure_equals("bad i from mapfunc", reply["i"].asInteger(), 7); - ensure_equals("bad str from mapfunc", reply["str"], "got value"); + UNSCOPED_INFO("reqid not echoed"); + CHECK(reply["reqid"].asInteger() == 17); + UNSCOPED_INFO("bad i from mapfunc"); + CHECK(reply["i"].asInteger() == 7); + UNSCOPED_INFO("bad str from mapfunc"); + CHECK(reply["str"] == "got value"); } - template<> template<> - void object::test<34>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: batched map success", "[lleventdispatcher]") { - set_test_name("batched map success"); DispatchResult service; LLCaptureListener result; service.post(llsd::map( @@ -1523,22 +1493,20 @@ namespace tut "reqid", 17, "reply", result.getName())); LLSD reply{ result.get() }; - ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17); + UNSCOPED_INFO("reqid not echoed"); + CHECK(reply["reqid"].asInteger() == 17); reply.erase("reqid"); - ensure_equals( - "bad map batch", - reply, - llsd::map( + UNSCOPED_INFO("bad map batch"); + CHECK_THAT(reply, + ll_test::LLSDEquals(llsd::map( "strfunc", "got some string", "intfunc", -2, "voidfunc", LLSD(), - "arrayfunc", llsd::array(5, "got other string"))); + "arrayfunc", llsd::array(5, "got other string")))); } - template<> template<> - void object::test<35>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: batched map error", "[lleventdispatcher]") { - set_test_name("batched map error"); DispatchResult service; LLCaptureListener result; service.post(llsd::map( @@ -1552,26 +1520,24 @@ namespace tut "reqid", 17, "reply", result.getName())); LLSD reply{ result.get() }; - ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17); + UNSCOPED_INFO("reqid not echoed"); + CHECK(reply["reqid"].asInteger() == 17); reply.erase("reqid"); auto error{ reply["error"].asString() }; reply.erase("error"); ensure_has(error, "badfunc"); ensure_has(error, "missing"); - ensure_equals( - "bad partial batch", - reply, - llsd::map( + UNSCOPED_INFO("bad partial batch"); + CHECK_THAT(reply, + ll_test::LLSDEquals(llsd::map( "strfunc", "got some string", "intfunc", -2, "voidfunc", LLSD(), - "arrayfunc", llsd::array(5, "got other string"))); + "arrayfunc", llsd::array(5, "got other string")))); } - template<> template<> - void object::test<36>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: batched map exception", "[lleventdispatcher]") { - set_test_name("batched map exception"); DispatchResult service; auto error = tut::call_exc( [&service]() @@ -1591,10 +1557,8 @@ namespace tut ensure_has(error, "missing"); } - template<> template<> - void object::test<37>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: batched array success", "[lleventdispatcher]") { - set_test_name("batched array success"); DispatchResult service; LLCaptureListener result; service.post(llsd::map( @@ -1612,23 +1576,21 @@ namespace tut "reqid", 17, "reply", result.getName())); LLSD reply{ result.get() }; - ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17); + UNSCOPED_INFO("reqid not echoed"); + CHECK(reply["reqid"].asInteger() == 17); reply.erase("reqid"); - ensure_equals( - "bad array batch", - reply, - llsd::map( + UNSCOPED_INFO("bad array batch"); + CHECK_THAT(reply, + ll_test::LLSDEquals(llsd::map( "data", llsd::array( "got some string", -2, llsd::array(5, "got other string"), - LLSD()))); + LLSD())))); } - template<> template<> - void object::test<38>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: batched array error", "[lleventdispatcher]") { - set_test_name("batched array error"); DispatchResult service; LLCaptureListener result; service.post(llsd::map( @@ -1646,20 +1608,20 @@ namespace tut "reqid", 17, "reply", result.getName())); LLSD reply{ result.get() }; - ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17); + UNSCOPED_INFO("reqid not echoed"); + CHECK(reply["reqid"].asInteger() == 17); reply.erase("reqid"); auto error{ reply["error"] }; reply.erase("error"); ensure_has(error, "[1]"); ensure_has(error, "unsupported"); - ensure_equals("bad array batch", reply, - llsd::map("data", llsd::array("got some string"))); + UNSCOPED_INFO("bad array batch"); + CHECK_THAT(reply, + ll_test::LLSDEquals(llsd::map("data", llsd::array("got some string")))); } - template<> template<> - void object::test<39>() + TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: batched array exception", "[lleventdispatcher]") { - set_test_name("batched array exception"); DispatchResult service; auto error = tut::call_exc( [&service]() diff --git a/indra/llcommon/tests/lleventfilter_test.cpp b/indra/llcommon/tests/lleventfilter_test.cpp index e8dd37ee60..ad92ab7728 100644 --- a/indra/llcommon/tests/lleventfilter_test.cpp +++ b/indra/llcommon/tests/lleventfilter_test.cpp @@ -34,7 +34,9 @@ // std headers // external library headers // other Linden headers -#include "../test/lltut.h" +#include +#include +#include "altestmatchers.h" #include "stringize.h" #include "llsdutil.h" #include "listener.h" @@ -160,7 +162,7 @@ class TestEventThrottle: public LLEventThrottleBase /***************************************************************************** * TUT *****************************************************************************/ -namespace tut +namespace { struct filter_data { @@ -179,18 +181,13 @@ namespace tut void check_listener(const std::string& desc, const Listener& listener, const LLSD& got) { - ensure_equals(STRINGIZE(listener << ' ' << desc), - listener.getLastEvent(), got); + UNSCOPED_INFO((STRINGIZE(listener << ' ' << desc))); + REQUIRE_THAT(listener.getLastEvent(), ll_test::LLSDEquals(got)); } }; - typedef test_group filter_group; - typedef filter_group::object filter_object; - filter_group filtergrp("lleventfilter"); - template<> template<> - void filter_object::test<1>() + TEST_CASE_METHOD(filter_data, "lleventfilter: LLEventMatching passes only events that match its pattern", "[lleventfilter]") { - set_test_name("LLEventMatching"); LLEventPump& driver(pumps.obtain("driver")); listener0.reset(0); // Listener isn't derived from LLEventTrackable specifically to test @@ -221,10 +218,8 @@ namespace tut check_listener("filtered", listener1, data); } - template<> template<> - void filter_object::test<2>() + TEST_CASE_METHOD(filter_data, "lleventfilter: LLEventTimeout::actionAfter() acts only when it times out", "[lleventfilter]") { - set_test_name("LLEventTimeout::actionAfter()"); LLEventPump& driver(pumps.obtain("driver")); TestEventTimeout filter(driver); listener0.reset(0); @@ -292,10 +287,8 @@ namespace tut check_listener("no timeout 6", listener1, LLSD(0)); } - template<> template<> - void filter_object::test<3>() + TEST_CASE_METHOD(filter_data, "lleventfilter: LLEventTimeout::eventAfter() posts only when it times out", "[lleventfilter]") { - set_test_name("LLEventTimeout::eventAfter()"); LLEventPump& driver(pumps.obtain("driver")); TestEventTimeout filter(driver); listener0.reset(0); @@ -329,10 +322,8 @@ namespace tut check_listener("no timeout 3", listener0, LLSD(0)); } - template<> template<> - void filter_object::test<4>() + TEST_CASE_METHOD(filter_data, "lleventfilter: LLEventTimeout::errorAfter() raises an error only on timeout", "[lleventfilter]") { - set_test_name("LLEventTimeout::errorAfter()"); WrapLLErrs capture; LLEventPump& driver(pumps.obtain("driver")); TestEventTimeout filter(driver); @@ -361,7 +352,8 @@ namespace tut std::string threw = capture.catch_llerrs([this](){ mainloop.post(17); }); - ensure_contains("errorAfter() timeout exception", threw, "timeout"); + UNSCOPED_INFO("errorAfter() timeout exception"); + CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("timeout")); // Timing out cancels the timer. Verify that. listener0.reset(0); filter.forceTimeout(); @@ -369,10 +361,8 @@ namespace tut check_listener("no timeout 3", listener0, LLSD(0)); } - template<> template<> - void filter_object::test<5>() + TEST_CASE_METHOD(filter_data, "lleventfilter: LLEventThrottle delivers at most one event an interval", "[lleventfilter]") { - set_test_name("LLEventThrottle"); TestEventThrottle throttle(3); Concat cat; throttle.listen("concat", boost::ref(cat)); @@ -381,33 +371,41 @@ namespace tut // 1: post(): event immediately passed to listeners, next no sooner than 4 throttle.advance(1); throttle.post("1"); - ensure_equals("1", cat.result, "1"); // delivered immediately + UNSCOPED_INFO("1"); + CHECK(cat.result == "1"); // delivered immediately // 2: post(): deferred: waiting for 3 seconds to elapse throttle.advance(1); throttle.post("2"); - ensure_equals("2", cat.result, "1"); // "2" not yet delivered + UNSCOPED_INFO("2"); + CHECK(cat.result == "1"); // "2" not yet delivered // 3: post(): deferred throttle.advance(1); throttle.post("3"); - ensure_equals("3", cat.result, "1"); // "3" not yet delivered + UNSCOPED_INFO("3"); + CHECK(cat.result == "1"); // "3" not yet delivered // 4: no post() call, but event delivered to listeners; next no sooner than 7 throttle.advance(1); - ensure_equals("4", cat.result, "13"); // "3" delivered + UNSCOPED_INFO("4"); + CHECK(cat.result == "13"); // "3" delivered // 6: post(): deferred throttle.advance(2); throttle.post("6"); - ensure_equals("6", cat.result, "13"); // "6" not yet delivered + UNSCOPED_INFO("6"); + CHECK(cat.result == "13"); // "6" not yet delivered // 7: no post() call, but event delivered; next no sooner than 10 throttle.advance(1); - ensure_equals("7", cat.result, "136"); // "6" delivered + UNSCOPED_INFO("7"); + CHECK(cat.result == "136"); // "6" delivered // 12: post(): immediately passed to listeners, next no sooner than 15 throttle.advance(5); throttle.post(";12"); - ensure_equals("12", cat.result, "136;12"); // "12" delivered + UNSCOPED_INFO("12"); + CHECK(cat.result == "136;12"); // "12" delivered // 17: post(): immediately passed to listeners, next no sooner than 20 throttle.advance(5); throttle.post(";17"); - ensure_equals("17", cat.result, "136;12;17"); // "17" delivered + UNSCOPED_INFO("17"); + CHECK(cat.result == "136;12;17"); // "17" delivered } // A PUMP keeps what no listener consumed, and replays it on the next listen(). @@ -433,7 +431,8 @@ namespace tut pump.post("first"); } // first post() should certainly be received by listener - ensure_equals("first", data, llsd::array("first")); + UNSCOPED_INFO("first"); + REQUIRE_THAT(data, ll_test::LLSDEquals(llsd::array("first"))); // the question is, since consumed was true, did it queue the value? data = LLSD::emptyArray(); { @@ -441,7 +440,8 @@ namespace tut // listen() call LLTempBoundListener conn = pump.listen("lambda", lambda); } - ensure_equals("empty1", data, LLSD::emptyArray()); + UNSCOPED_INFO("empty1"); + REQUIRE_THAT(data, ll_test::LLSDEquals(LLSD::emptyArray())); data = LLSD::emptyArray(); // now let's NOT consume the posted data consumed = false; @@ -451,7 +451,8 @@ namespace tut pump.post("third"); } // the two events still arrive - ensure_equals("second,third1", data, llsd::array("second", "third")); + UNSCOPED_INFO("second,third1"); + REQUIRE_THAT(data, ll_test::LLSDEquals(llsd::array("second", "third"))); data = LLSD::emptyArray(); { // when we reconnect, these should be delivered again @@ -460,29 +461,27 @@ namespace tut LLTempBoundListener conn = pump.listen("lambda", lambda); } // unconsumed events were delivered again - ensure_equals("second,third2", data, llsd::array("second", "third")); + UNSCOPED_INFO("second,third2"); + REQUIRE_THAT(data, ll_test::LLSDEquals(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()); + UNSCOPED_INFO("empty2"); + REQUIRE_THAT(data, ll_test::LLSDEquals(LLSD::emptyArray())); } - template<> template<> - void filter_object::test<6>() + TEST_CASE_METHOD(filter_data, "lleventfilter: LLEventMailDrop replays unconsumed events on the next listen", "[lleventfilter]") { - set_test_name("LLEventMailDrop"); check_mail_drop_replay(); } - template<> template<> - void filter_object::test<7>() + TEST_CASE_METHOD(filter_data, "lleventfilter: LLEventLogProxyFor replays unconsumed events", "[lleventfilter]") { - set_test_name("LLEventLogProxyFor"); check_mail_drop_replay< LLEventLogProxyFor >(); } -} // namespace tut +} // anonymous namespace // (llsdutil symbols formerly pulled in via #include "llsdutil.cpp" now come from // the linked llcommon library -- including the .cpp here double-defines them and diff --git a/indra/llcommon/tests/llevents_test.cpp b/indra/llcommon/tests/llevents_test.cpp index 4232efd9ca..970111d5e9 100644 --- a/indra/llcommon/tests/llevents_test.cpp +++ b/indra/llcommon/tests/llevents_test.cpp @@ -46,7 +46,8 @@ #include // other Linden headers #include "tests/listener.h" // must PRECEDE lltut.h -#include "lltut.h" +#include +#include #include "catch_and_store_what_in.h" #include "stringize.h" @@ -59,7 +60,7 @@ static T make(const T& value) /***************************************************************************** * tut test group *****************************************************************************/ -namespace tut +namespace { struct events_data { @@ -92,28 +93,26 @@ struct events_data void check_listener(const std::string& desc, const Listener& listener, LLSD::Integer got) { - ensure_equals(STRINGIZE(listener << ' ' << desc), - listener.getLastEvent().asInteger(), got); + UNSCOPED_INFO((STRINGIZE(listener << ' ' << desc))); + REQUIRE(listener.getLastEvent().asInteger() == got); } }; -typedef test_group events_group; -typedef events_group::object events_object; -tut::events_group evgr("events"); -template<> template<> -void events_object::test<1>() +TEST_CASE_METHOD(events_data, "events: basic operations", "[events]") { - set_test_name("basic operations"); // Having to modify this to track the statically- // constructed pumps in other TUT modules in this giant monolithic test // executable isn't such a hot idea. // ensure_equals("initial pump", pumps.mPumpMap.size(), 1); size_t initial_pumps(pumps.mPumpMap.size()); LLEventPump& per_frame(pumps.obtain("per-frame")); - ensure_equals("first explicit pump", pumps.mPumpMap.size(), initial_pumps + 1); + UNSCOPED_INFO("first explicit pump"); + REQUIRE(pumps.mPumpMap.size() == initial_pumps + 1); // Verify that per_frame was instantiated as an LLEventStream. - ensure("LLEventStream leaf class", dynamic_cast (&per_frame)); - ensure("enabled", per_frame.enabled()); + UNSCOPED_INFO("LLEventStream leaf class"); + REQUIRE(dynamic_cast (&per_frame)); + UNSCOPED_INFO("enabled"); + CHECK(per_frame.enabled()); // Trivial test, but posting an event to an EventPump with no // listeners should not blow up. The test is relevant because defining // a boost::signal with a non-void return signature, using the default @@ -122,25 +121,32 @@ void events_object::test<1>() // last listener, which is meaningless if there were no listeners. per_frame.post(0); LLBoundListener connection = listener0.listenTo(per_frame); - ensure("connected", connection.connected()); - ensure("not blocked", !connection.blocked()); + UNSCOPED_INFO("connected"); + CHECK(connection.connected()); + UNSCOPED_INFO("not blocked"); + CHECK_FALSE(connection.blocked()); per_frame.post(1); check_listener("received", listener0, 1); { // block the connection LLEventPump::Blocker block(connection); - ensure("blocked", connection.blocked()); + UNSCOPED_INFO("blocked"); + CHECK(connection.blocked()); per_frame.post(2); check_listener("not updated", listener0, 1); } // unblock - ensure("unblocked", !connection.blocked()); + UNSCOPED_INFO("unblocked"); + CHECK_FALSE(connection.blocked()); per_frame.post(3); check_listener("unblocked", listener0, 3); LLBoundListener sameConnection = per_frame.getListener(listener0.getName()); - ensure("still connected", sameConnection.connected()); - ensure("still not blocked", !sameConnection.blocked()); + UNSCOPED_INFO("still connected"); + CHECK(sameConnection.connected()); + UNSCOPED_INFO("still not blocked"); + CHECK_FALSE(sameConnection.blocked()); { // block it again LLEventPump::Blocker block(sameConnection); - ensure("re-blocked", sameConnection.blocked()); + UNSCOPED_INFO("re-blocked"); + CHECK(sameConnection.blocked()); per_frame.post(4); check_listener("re-blocked", listener0, 3); } // unblock @@ -153,7 +159,7 @@ void events_object::test<1>() per_frame.listen(listener0.getName(), // note bug, dup name boost::bind(&Listener::call, boost::ref(listener1), _1)); }); - ensure_equals(threw, + CHECK(threw == std::string("DupListenerName: " "Attempt to register duplicate listener name '") + listener0.getName() + "' on " + typeid(per_frame).name() + @@ -172,8 +178,10 @@ void events_object::test<1>() check_listener("got", listener0, 7); check_listener("got", listener1, 7); per_frame.stopListening(listener0.getName()); - ensure("disconnected 0", ! connection.connected()); - ensure("disconnected 1", ! sameConnection.connected()); + UNSCOPED_INFO("disconnected 0"); + CHECK_FALSE(connection.connected()); + UNSCOPED_INFO("disconnected 1"); + CHECK_FALSE(sameConnection.connected()); per_frame.post(8); check_listener("disconnected", listener0, 7); check_listener("still connected", listener1, 8); @@ -182,10 +190,8 @@ void events_object::test<1>() check_listener("disconnected", listener1, 8); } -template<> template<> -void events_object::test<2>() +TEST_CASE_METHOD(events_data, "events: callstop() returning true", "[events]") { - set_test_name("callstop() returning true"); LLEventPump& per_frame(pumps.obtain("per-frame")); listener0.reset(0); listener1.reset(0); @@ -193,21 +199,24 @@ void events_object::test<2>() LLBoundListener bound1 = listener1.listenTo(per_frame, &Listener::call, // after listener0 make(LLEventPump::NameList{ listener0.getName() })); - ensure("enabled", per_frame.enabled()); - ensure("connected 0", bound0.connected()); - ensure("unblocked 0", !bound0.blocked()); - ensure("connected 1", bound1.connected()); - ensure("unblocked 1", !bound1.blocked()); + UNSCOPED_INFO("enabled"); + CHECK(per_frame.enabled()); + UNSCOPED_INFO("connected 0"); + CHECK(bound0.connected()); + UNSCOPED_INFO("unblocked 0"); + CHECK_FALSE(bound0.blocked()); + UNSCOPED_INFO("connected 1"); + CHECK(bound1.connected()); + UNSCOPED_INFO("unblocked 1"); + CHECK_FALSE(bound1.blocked()); per_frame.post(1); check_listener("got", listener0, 1); // Because listener0.callstop() returns true, control never reaches listener1.call(). check_listener("got", listener1, 0); } -template<> template<> -void events_object::test<3>() +TEST_CASE_METHOD(events_data, "events: an explicit LLEventStream registers itself until destroyed", "[events]") { - set_test_name("explicitly-instantiated LLEventStream"); // Explicitly instantiate an LLEventStream, and verify that it // self-registers with LLEventPumps size_t registered = pumps.mPumpMap.size(); @@ -217,36 +226,42 @@ void events_object::test<3>() LLEventStream myEventStream("stream"); localInstance = &myEventStream; LLEventPump& stream(pumps.obtain("stream")); - ensure("found named LLEventStream instance", &stream == localInstance); - ensure_equals("registered new instance", pumps.mPumpMap.size(), registered + 1); - ensure_equals("explicit instance not owned", pumps.mOurPumps.size(), owned); + UNSCOPED_INFO("found named LLEventStream instance"); + CHECK(&stream == localInstance); + UNSCOPED_INFO("registered new instance"); + REQUIRE(pumps.mPumpMap.size() == registered + 1); + UNSCOPED_INFO("explicit instance not owned"); + REQUIRE(pumps.mOurPumps.size() == owned); } // destroy myEventStream -- should unregister - ensure_equals("destroyed instance unregistered", pumps.mPumpMap.size(), registered); - ensure_equals("destroyed instance not owned", pumps.mOurPumps.size(), owned); + UNSCOPED_INFO("destroyed instance unregistered"); + REQUIRE(pumps.mPumpMap.size() == registered); + UNSCOPED_INFO("destroyed instance not owned"); + REQUIRE(pumps.mOurPumps.size() == owned); LLEventPump& stream(pumps.obtain("stream")); - ensure("new LLEventStream instance", &stream != localInstance); - ensure_equals("obtain()ed instance registered", pumps.mPumpMap.size(), registered + 1); - ensure_equals("obtain()ed instance owned", pumps.mOurPumps.size(), owned + 1); + UNSCOPED_INFO("new LLEventStream instance"); + CHECK(&stream != localInstance); + UNSCOPED_INFO("obtain()ed instance registered"); + REQUIRE(pumps.mPumpMap.size() == registered + 1); + UNSCOPED_INFO("obtain()ed instance owned"); + REQUIRE(pumps.mOurPumps.size() == owned + 1); } -template<> template<> -void events_object::test<4>() +TEST_CASE_METHOD(events_data, "events: stopListening() frees the name and a bogus name has no connection", "[events]") { - set_test_name("stopListening()"); LLEventPump& login(pumps.obtain("login")); listener0.listenTo(login); login.stopListening(listener0.getName()); // should not throw because stopListening() should have removed name listener0.listenTo(login, &Listener::callstop); LLBoundListener wrong = login.getListener("bogus"); - ensure("bogus connection disconnected", !wrong.connected()); - ensure("bogus connection blocked", wrong.blocked()); + UNSCOPED_INFO("bogus connection disconnected"); + CHECK_FALSE(wrong.connected()); + UNSCOPED_INFO("bogus connection blocked"); + CHECK(wrong.blocked()); } -template<> template<> -void events_object::test<5>() +TEST_CASE_METHOD(events_data, "events: chaining LLEventPump instances", "[events]") { - set_test_name("chaining LLEventPump instances"); LLEventPump& upstream(pumps.obtain("upstream")); // One potentially-useful construct is to chain LLEventPumps together. // Among other things, this allows you to turn subsets of listeners on @@ -268,10 +283,8 @@ void events_object::test<5>() check_listener("got filtered", listener1, 2); } -template<> template<> -void events_object::test<6>() +TEST_CASE_METHOD(events_data, "events: listener dependency order", "[events]") { - set_test_name("listener dependency order"); typedef LLEventPump::NameList NameList; LLEventPump& button(pumps.obtain("button")); Collect collector; @@ -286,7 +299,7 @@ void events_object::test<6>() button.listen("spot", boost::bind(&Collect::add, boost::ref(collector), "spot", _1)); button.post(1); - ensure_equals(collector.result, make(StringVec{ "spot", "checked", "Mary" })); + CHECK(collector.result == make(StringVec{ "spot", "checked", "Mary" })); collector.clear(); button.stopListening("Mary"); button.listen("Mary", @@ -295,7 +308,7 @@ void events_object::test<6>() // now "Mary" must come before "spot" make(NameList{ "spot" })); button.post(2); - ensure_equals(collector.result, make(StringVec{ "Mary", "spot", "checked" })); + CHECK(collector.result == make(StringVec{ "Mary", "spot", "checked" })); collector.clear(); button.stopListening("spot"); std::string threw = catch_what( @@ -309,23 +322,29 @@ void events_object::test<6>() // change; go ahead and change the test to match. // Establish that it contains: // - the name and runtime type of the LLEventPump - ensure_contains("LLEventPump type", threw, typeid(button).name()); - ensure_contains("LLEventPump name", threw, "'button'"); + UNSCOPED_INFO("LLEventPump type"); + REQUIRE_THAT(threw, Catch::Matchers::ContainsSubstring(typeid(button).name())); + UNSCOPED_INFO("LLEventPump name"); + CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("'button'")); // - the name of the new listener that caused the problem - ensure_contains("new listener name", threw, "'spot'"); + UNSCOPED_INFO("new listener name"); + CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("'spot'")); // - a synopsis of the problematic dependencies. - ensure_contains("cyclic dependencies", threw, - "\"Mary\" -> before (\"spot\")"); - ensure_contains("cyclic dependencies", threw, - "after (\"spot\") -> \"checked\""); - ensure_contains("cyclic dependencies", threw, - "after (\"Mary\", \"checked\") -> \"spot\""); + UNSCOPED_INFO("cyclic dependencies"); + CHECK_THAT(threw, + Catch::Matchers::ContainsSubstring("\"Mary\" -> before (\"spot\")")); + UNSCOPED_INFO("cyclic dependencies"); + CHECK_THAT(threw, + Catch::Matchers::ContainsSubstring("after (\"spot\") -> \"checked\"")); + UNSCOPED_INFO("cyclic dependencies"); + CHECK_THAT(threw, + Catch::Matchers::ContainsSubstring("after (\"Mary\", \"checked\") -> \"spot\"")); button.listen("yellow", boost::bind(&Collect::add, boost::ref(collector), "yellow", _1), make(NameList{ "checked" })); button.listen("shoelaces", boost::bind(&Collect::add, boost::ref(collector), "shoelaces", _1), make(NameList{ "checked" })); button.post(3); - ensure_equals(collector.result, make(StringVec{ "Mary", "checked", "yellow", "shoelaces" })); + CHECK(collector.result == make(StringVec{ "Mary", "checked", "yellow", "shoelaces" })); collector.clear(); threw = catch_what( [&button, &collector](){ @@ -337,21 +356,25 @@ void events_object::test<6>() // Same remarks about the specific wording of the exception. Just // ensure that it contains enough information to clarify the // problem and what must be done to resolve it. - ensure_contains("LLEventPump type", threw, typeid(button).name()); - ensure_contains("LLEventPump name", threw, "'button'"); - ensure_contains("new listener name", threw, "'of'"); - ensure_contains("prev listener name", threw, "'yellow'"); + UNSCOPED_INFO("LLEventPump type"); + REQUIRE_THAT(threw, Catch::Matchers::ContainsSubstring(typeid(button).name())); + UNSCOPED_INFO("LLEventPump name"); + CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("'button'")); + UNSCOPED_INFO("new listener name"); + CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("'of'")); + UNSCOPED_INFO("prev listener name"); + CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("'yellow'")); // std::cout << "Thrown Exception: " << threw << std::endl; - ensure_contains("old order", threw, "was: Mary, checked, yellow, shoelaces"); - ensure_contains("new order", threw, "now: Mary, checked, shoelaces, of, yellow"); + UNSCOPED_INFO("old order"); + CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("was: Mary, checked, yellow, shoelaces")); + UNSCOPED_INFO("new order"); + CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("now: Mary, checked, shoelaces, of, yellow")); button.post(4); - ensure_equals(collector.result, make(StringVec{ "Mary", "checked", "yellow", "shoelaces" })); + CHECK(collector.result == make(StringVec{ "Mary", "checked", "yellow", "shoelaces" })); } -template<> template<> -void events_object::test<7>() +TEST_CASE_METHOD(events_data, "events: tweaked and untweaked LLEventPump instance names", "[events]") { - set_test_name("tweaked and untweaked LLEventPump instance names"); { // nested scope // Hand-instantiate an LLEventStream... LLEventStream bob("bob"); @@ -360,17 +383,20 @@ void events_object::test<7>() // then another with a duplicate name. LLEventStream bob2("bob"); }); - ensure("Caught DupPumpName", !threw.empty()); + UNSCOPED_INFO("Caught DupPumpName"); + REQUIRE_FALSE(threw.empty()); } // delete first 'bob' LLEventStream bob("bob"); // should work, previous one unregistered LLEventStream bob1("bob", true);// allowed to tweak name - ensure_equals("tweaked LLEventStream name", bob1.getName(), "bob1"); + UNSCOPED_INFO("tweaked LLEventStream name"); + CHECK(bob1.getName() == "bob1"); std::vector > streams; for (int i = 2; i <= 10; ++i) { streams.push_back(std::shared_ptr(new LLEventStream("bob", true))); } - ensure_equals("last tweaked LLEventStream name", streams.back()->getName(), "bob10"); + UNSCOPED_INFO("last tweaked LLEventStream name"); + CHECK(streams.back()->getName() == "bob10"); } // Define a function that accepts an LLListenerOrPumpName @@ -380,10 +406,8 @@ static void eventSource(const LLListenerOrPumpName& listener) listener(17); } -template<> template<> -void events_object::test<8>() +TEST_CASE_METHOD(events_data, "events: LLListenerOrPumpName calls a listener or posts to a named pump", "[events]") { - set_test_name("LLListenerOrPumpName"); // Passing a boost::bind() expression to LLListenerOrPumpName listener0.reset(0); eventSource(boost::bind(&Listener::call, boost::ref(listener0), _1)); @@ -400,11 +424,10 @@ void events_object::test<8>() empty(17); }); - ensure("threw Empty", !threw.empty()); + UNSCOPED_INFO("threw Empty"); + REQUIRE_FALSE(threw.empty()); } -namespace -{ class TempListener: public Listener { @@ -424,12 +447,9 @@ class TempListener: public Listener bool& mLiveFlag; }; -} // anonymous namespace -template<> template<> -void events_object::test<9>() +TEST_CASE_METHOD(events_data, "events: a destroyed plain listener still looks connected", "[events]") { - set_test_name("listen(boost::bind(...TempListener...))"); // listen() can't do anything about a plain TempListener instance: // it's not managed with shared_ptr, nor is it an LLEventTrackable subclass bool live = false; @@ -437,7 +457,8 @@ void events_object::test<9>() LLBoundListener connection; { TempListener tempListener("temp", live); - ensure("TempListener constructed", live); + UNSCOPED_INFO("TempListener constructed"); + CHECK(live); connection = heaptest.listen(tempListener.getName(), boost::bind(&Listener::call, boost::ref(tempListener), @@ -446,17 +467,17 @@ void events_object::test<9>() check_listener("received", tempListener, 1); } // presumably this will make newListener go away? // verify that - ensure("TempListener destroyed", !live); + UNSCOPED_INFO("TempListener destroyed"); + CHECK_FALSE(live); // This is the case against which we can't defend. Don't even try to // post to heaptest -- that would engage Undefined Behavior. // Cautiously inspect connection... - ensure("misleadingly connected", connection.connected()); + UNSCOPED_INFO("misleadingly connected"); + CHECK(connection.connected()); // then disconnect by hand. heaptest.stopListening("temp"); } -namespace -{ class TempTrackableListener: public TempListener, public LLEventTrackable { @@ -466,18 +487,16 @@ class TempTrackableListener: public TempListener, public LLEventTrackable {} }; -} // anonymous namespace -template<> template<> -void events_object::test<10>() +TEST_CASE_METHOD(events_data, "events: destroying a bound LLEventTrackable disconnects it", "[events]") { - set_test_name("listen(boost::bind(...TempTrackableListener ref...))"); bool live = false; LLEventPump& heaptest(pumps.obtain("heaptest")); LLBoundListener connection; { TempTrackableListener tempListener("temp", live); - ensure("TempTrackableListener constructed", live); + UNSCOPED_INFO("TempTrackableListener constructed"); + CHECK(live); connection = heaptest.listen(tempListener.getName(), boost::bind(&TempTrackableListener::call, boost::ref(tempListener), _1)); @@ -485,22 +504,23 @@ void events_object::test<10>() check_listener("received", tempListener, 1); } // presumably this will make tempListener go away? // verify that - ensure("TempTrackableListener destroyed", ! live); - ensure("implicit disconnect", ! connection.connected()); + UNSCOPED_INFO("TempTrackableListener destroyed"); + CHECK_FALSE(live); + UNSCOPED_INFO("implicit disconnect"); + CHECK_FALSE(connection.connected()); // now just make sure we don't blow up trying to access a freed object! heaptest.post(2); } -template<> template<> -void events_object::test<11>() +TEST_CASE_METHOD(events_data, "events: deleting a bound LLEventTrackable pointer disconnects it", "[events]") { - set_test_name("listen(boost::bind(...TempTrackableListener pointer...))"); bool live = false; LLEventPump& heaptest(pumps.obtain("heaptest")); LLBoundListener connection; { TempTrackableListener* newListener(new TempTrackableListener("temp", live)); - ensure("TempTrackableListener constructed", live); + UNSCOPED_INFO("TempTrackableListener constructed"); + CHECK(live); connection = heaptest.listen(newListener->getName(), boost::bind(&TempTrackableListener::call, newListener, _1)); @@ -510,10 +530,12 @@ void events_object::test<11>() delete newListener; } // verify that - ensure("TempTrackableListener destroyed", ! live); - ensure("implicit disconnect", ! connection.connected()); + UNSCOPED_INFO("TempTrackableListener destroyed"); + CHECK_FALSE(live); + UNSCOPED_INFO("implicit disconnect"); + CHECK_FALSE(connection.connected()); // now just make sure we don't blow up trying to access a freed object! heaptest.post(2); } -} // namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/llfile_test.cpp b/indra/llcommon/tests/llfile_test.cpp index 46ea565b45..e4cecb9cfa 100644 --- a/indra/llcommon/tests/llfile_test.cpp +++ b/indra/llcommon/tests/llfile_test.cpp @@ -26,13 +26,13 @@ * $/LicenseInfo$ */ -#include -#include "lltut.h" +#include +#include "altestmatchers.h" #include "linden_common.h" #include "llfile.h" #include "lluuid.h" -namespace tut +namespace { static void clear_entire_dir(const std::filesystem::path& dir_path) { @@ -73,56 +73,69 @@ namespace tut clear_entire_dir(get_testdir_unicode(tempdir, unique)); } }; - typedef test_group llfile_test_t; - typedef llfile_test_t::object llfile_test_object_t; - tut::llfile_test_t tut_llfile_test("llfile_test"); - template<> template<> - void llfile_test_object_t::test<1>() + TEST_CASE_METHOD(llfile_test, "llfile_test: exists isdir and isfile follow mkdir write and remove", "[llfile_test]") { // Test creating directories and files and deleting them and checking if the // relevant status functions work as expected - ensure("LLFile::tmpdir() empty", !tempdir.empty()); - ensure("LLFile::tmpdir() doesn't exist", LLFile::exists(tempdir)); - ensure("LLFile::tmpdir() is not a directory", LLFile::isdir(tempdir)); - ensure("LLFile::tmpdir() should not be a file", !LLFile::isfile(tempdir)); + UNSCOPED_INFO("LLFile::tmpdir() empty"); + REQUIRE_FALSE(tempdir.empty()); + UNSCOPED_INFO("LLFile::tmpdir() doesn't exist"); + CHECK(LLFile::exists(tempdir)); + UNSCOPED_INFO("LLFile::tmpdir() is not a directory"); + CHECK(LLFile::isdir(tempdir)); + UNSCOPED_INFO("LLFile::tmpdir() should not be a file"); + CHECK_FALSE(LLFile::isfile(tempdir)); // Make sure there is nothing left from a previous test run clear_entire_dir(testdir); - ensure("llfile_test should not exist anymore", !LLFile::exists(testdir)); + UNSCOPED_INFO("llfile_test should not exist anymore"); + CHECK_FALSE(LLFile::exists(testdir)); int rc = LLFile::mkdir(testdir); - ensure("LLFile::mkdir() failed", rc == 0); - ensure("llfile_test should be a directory", LLFile::isdir(testdir)); + UNSCOPED_INFO("LLFile::mkdir() failed"); + CHECK(rc == 0); + UNSCOPED_INFO("llfile_test should be a directory"); + CHECK(LLFile::isdir(testdir)); rc = LLFile::mkdir(testdir); - ensure("LLFile::mkdir() should not fail when the directory already exists", rc == 0); + UNSCOPED_INFO("LLFile::mkdir() should not fail when the directory already exists"); + CHECK(rc == 0); std::filesystem::path testfile1 = testdir; testfile1.append("llfile_test.dat"); - ensure("llfile_test1.dat should not yet exist", !LLFile::exists(testfile1)); + UNSCOPED_INFO("llfile_test1.dat should not yet exist"); + CHECK_FALSE(LLFile::exists(testfile1)); const char* testdata = "testdata"; S64 bytes = LLFile::write(testfile1, testdata, 0, sizeof(testdata)); - ensure("LLFile::write() did not write correctly", bytes == sizeof(testdata)); + UNSCOPED_INFO("LLFile::write() did not write correctly"); + CHECK(bytes == sizeof(testdata)); rc = LLFile::remove(testfile1); - ensure("LLFile::remove() for file test_file.dat", rc == 0); - ensure("llfile_test.dat should not exist anymore", !LLFile::exists(testfile1)); - ensure("llfile_test.dat should not be a file", !LLFile::isfile(testfile1)); - ensure("llfile_test.dat should not be a directory", !LLFile::isdir(testfile1)); - ensure("llfile_test.dat should not be a symlink", !LLFile::islink(testfile1)); + UNSCOPED_INFO("LLFile::remove() for file test_file.dat"); + CHECK(rc == 0); + UNSCOPED_INFO("llfile_test.dat should not exist anymore"); + CHECK_FALSE(LLFile::exists(testfile1)); + UNSCOPED_INFO("llfile_test.dat should not be a file"); + CHECK_FALSE(LLFile::isfile(testfile1)); + UNSCOPED_INFO("llfile_test.dat should not be a directory"); + CHECK_FALSE(LLFile::isdir(testfile1)); + UNSCOPED_INFO("llfile_test.dat should not be a symlink"); + CHECK_FALSE(LLFile::islink(testfile1)); rc = LLFile::remove(testdir); - ensure("LLFile::remove() for directory llfile_test failed", rc == 0); - ensure("llfile_test should not exist anymore", !LLFile::exists(testdir)); + UNSCOPED_INFO("LLFile::remove() for directory llfile_test failed"); + CHECK(rc == 0); + UNSCOPED_INFO("llfile_test should not exist anymore"); + CHECK_FALSE(LLFile::exists(testdir)); } - template<> template<> - void llfile_test_object_t::test<2>() + TEST_CASE_METHOD(llfile_test, "llfile_test: the static write size getContents and read agree on a file", "[llfile_test]") { // High level static file IO functions to read and write data files LLFile::mkdir(testdir); - ensure("llfile_test should exist", LLFile::isdir(testdir)); + UNSCOPED_INFO("llfile_test should exist"); + CHECK(LLFile::isdir(testdir)); std::filesystem::path testfile1 = testdir; testfile1.append("llfile_test.dat"); @@ -131,42 +144,57 @@ namespace tut std::string testdata2("datateststuff"); std::time_t current = time(nullptr); S64 bytes = LLFile::write(testfile1, testdata1.c_str(), 0, testdata1.length()); - ensure("LLFile::write() did not write correctly", bytes == testdata1.length()); - ensure("llfile_test.dat should exist", LLFile::exists(testfile1)); - ensure("llfile_test.dat should be a file", LLFile::isfile(testfile1)); - ensure("llfile_test.dat should not be a directory", !LLFile::isdir(testfile1)); + UNSCOPED_INFO("LLFile::write() did not write correctly"); + REQUIRE(bytes == testdata1.length()); + UNSCOPED_INFO("llfile_test.dat should exist"); + CHECK(LLFile::exists(testfile1)); + UNSCOPED_INFO("llfile_test.dat should be a file"); + CHECK(LLFile::isfile(testfile1)); + UNSCOPED_INFO("llfile_test.dat should not be a directory"); + CHECK_FALSE(LLFile::isdir(testfile1)); bytes = LLFile::size(testfile1); - ensure("LLFile::size() did not return the correct size", bytes == testdata1.length()); + UNSCOPED_INFO("LLFile::size() did not return the correct size"); + REQUIRE(bytes == testdata1.length()); std::string data = LLFile::getContents(testfile1); - ensure("LLFile::getContents() did not return the correct size data", data.length() == testdata1.length()); - ensure_memory_matches("LLFile::getContents() did not read correct data", testdata1.c_str(), (U32)testdata1.length(), data.c_str(), (U32)data.length()); + UNSCOPED_INFO("LLFile::getContents() did not return the correct size data"); + REQUIRE(data.length() == testdata1.length()); + UNSCOPED_INFO("LLFile::getContents() did not read correct data"); + REQUIRE_THAT(ll_test::Memory(testdata1.c_str(), (U32)testdata1.length()), ll_test::MemoryMatches(data.c_str(), (U32)data.length())); char buffer[1024]; bytes = LLFile::read(testfile1, buffer, 0, testdata1.length()); - ensure("LLFile:read() did not return the correct size", bytes == testdata1.length()); - ensure_memory_matches("LLFile::read() did not read correct data", testdata1.c_str(), (U32)bytes, buffer, (U32)bytes); + UNSCOPED_INFO("LLFile:read() did not return the correct size"); + REQUIRE(bytes == testdata1.length()); + UNSCOPED_INFO("LLFile::read() did not read correct data"); + REQUIRE_THAT(ll_test::Memory(testdata1.c_str(), (U32)bytes), ll_test::MemoryMatches(buffer, (U32)bytes)); // What if we try to read more data than there is in the file? bytes = LLFile::read(testfile1, buffer, 0, bytes + 10); - ensure("LLFile:read() did not correctly stop on eof", bytes == testdata1.length()); - ensure_memory_matches("LLFile::read() did not read correct data", testdata1.c_str(), (U32)bytes, buffer, (U32)bytes); + UNSCOPED_INFO("LLFile:read() did not correctly stop on eof"); + REQUIRE(bytes == testdata1.length()); + UNSCOPED_INFO("LLFile::read() did not read correct data"); + REQUIRE_THAT(ll_test::Memory(testdata1.c_str(), (U32)bytes), ll_test::MemoryMatches(buffer, (U32)bytes)); // Let's append more data bytes = LLFile::write(testfile1, testdata2.c_str(), -1, testdata2.length()); - ensure("LLFile::write() did not write correctly", bytes == testdata2.length()); + UNSCOPED_INFO("LLFile::write() did not write correctly"); + REQUIRE(bytes == testdata2.length()); bytes = LLFile::size(testfile1); - ensure("LLFile::size() did not return the correct size", bytes == testdata1.length() + testdata2.length()); + UNSCOPED_INFO("LLFile::size() did not return the correct size"); + REQUIRE(bytes == testdata1.length() + testdata2.length()); bytes = LLFile::read(testfile1, buffer, 0, bytes); - ensure("LLFile:read() did not read correct number of bytes", bytes == testdata1.length() + testdata2.length()); - ensure_memory_matches("LLFile:read() did not read correct testdata1", testdata1.c_str(), (U32)testdata1.length(), buffer, (U32)testdata1.length()); - ensure_memory_matches("LLFile:read() did not read correct testdata2", testdata2.c_str(), (U32)testdata2.length(), buffer + testdata1.length(), (U32)testdata2.length()); + UNSCOPED_INFO("LLFile:read() did not read correct number of bytes"); + REQUIRE(bytes == testdata1.length() + testdata2.length()); + UNSCOPED_INFO("LLFile:read() did not read correct testdata1"); + REQUIRE_THAT(ll_test::Memory(testdata1.c_str(), (U32)testdata1.length()), ll_test::MemoryMatches(buffer, (U32)testdata1.length())); + UNSCOPED_INFO("LLFile:read() did not read correct testdata2"); + REQUIRE_THAT(ll_test::Memory(testdata2.c_str(), (U32)testdata2.length()), ll_test::MemoryMatches(buffer + testdata1.length(), (U32)testdata2.length())); } - template<> template<> - void llfile_test_object_t::test<3>() + TEST_CASE_METHOD(llfile_test, "llfile_test: an LLFile writes reads seeks and tells", "[llfile_test]") { const size_t numints = 1024; @@ -183,59 +211,84 @@ namespace tut std::error_code ec; LLFile fileout(testfile, LLFile::out, ec); - ensure("LLFile constructor did not open correctly", (bool)fileout); - ensure("error_code from LLFile constructor should not indicate an error", !ec); + UNSCOPED_INFO("LLFile constructor did not open correctly"); + REQUIRE((bool)fileout); + UNSCOPED_INFO("error_code from LLFile constructor should not indicate an error"); + REQUIRE_FALSE(ec); if (fileout) { S64 length = fileout.size(ec); - ensure("freshly created file should be empty", length == 0); - ensure("error_code from LLFile::size() should not indicate an error", !ec); + UNSCOPED_INFO("freshly created file should be empty"); + CHECK(length == 0); + UNSCOPED_INFO("error_code from LLFile::size() should not indicate an error"); + REQUIRE_FALSE(ec); S64 bytes = fileout.write(data, sizeof(data), ec); - ensure("LLFile::write() did not write correctly", bytes == sizeof(data)); - ensure("error_code from LLFile::write() should not indicate an error", !ec); + UNSCOPED_INFO("LLFile::write() did not write correctly"); + CHECK(bytes == sizeof(data)); + UNSCOPED_INFO("error_code from LLFile::write() should not indicate an error"); + REQUIRE_FALSE(ec); bytes = fileout.write(data, sizeof(data), ec); - ensure("LLFile::write() did not write correctly", bytes == sizeof(data)); - ensure("error_code from LLFile::write() should not indicate an error", !ec); + UNSCOPED_INFO("LLFile::write() did not write correctly"); + CHECK(bytes == sizeof(data)); + UNSCOPED_INFO("error_code from LLFile::write() should not indicate an error"); + REQUIRE_FALSE(ec); bytes = fileout.size(ec); - ensure("LLFile::size() returned wrong size", bytes == 2 * sizeof(data)); - ensure("error_code from LLFile::size() should not indicate an error", !ec); + UNSCOPED_INFO("LLFile::size() returned wrong size"); + CHECK(bytes == 2 * sizeof(data)); + UNSCOPED_INFO("error_code from LLFile::size() should not indicate an error"); + REQUIRE_FALSE(ec); fileout.close(); } LLFile filein(testfile, LLFile::in, ec); - ensure("LLFile constructor did not open correctly", (bool)filein); - ensure("error_code from LLFile constructor should not indicate an error", !ec); + UNSCOPED_INFO("LLFile constructor did not open correctly"); + REQUIRE((bool)filein); + UNSCOPED_INFO("error_code from LLFile constructor should not indicate an error"); + REQUIRE_FALSE(ec); if (filein) { S64 length = filein.size(ec); - ensure("LLFile::size() returned wrong size", length == 2 * sizeof(data)); - ensure("error_code from LLFile::size() should not indicate an error", !ec); + UNSCOPED_INFO("LLFile::size() returned wrong size"); + CHECK(length == 2 * sizeof(data)); + UNSCOPED_INFO("error_code from LLFile::size() should not indicate an error"); + REQUIRE_FALSE(ec); char* buffer = (char*)malloc(length); S64 bytes = filein.read(buffer, length, ec); - ensure("LLFile::read() did not read correctly", bytes == length); - ensure("error_code from LLFile::read() should not indicate an error", !ec); - ensure_memory_matches("LLFile:read() did not read correct data1", data, (U32)sizeof(data), buffer, (U32)sizeof(data)); - ensure_memory_matches("LLFile:read() did not read correct data2", data, (U32)sizeof(data), buffer + sizeof(data), (U32)sizeof(data)); + UNSCOPED_INFO("LLFile::read() did not read correctly"); + CHECK(bytes == length); + UNSCOPED_INFO("error_code from LLFile::read() should not indicate an error"); + REQUIRE_FALSE(ec); + UNSCOPED_INFO("LLFile:read() did not read correct data1"); + REQUIRE_THAT(ll_test::Memory(data, (U32)sizeof(data)), ll_test::MemoryMatches(buffer, (U32)sizeof(data))); + UNSCOPED_INFO("LLFile:read() did not read correct data2"); + REQUIRE_THAT(ll_test::Memory(data, (U32)sizeof(data)), ll_test::MemoryMatches(buffer + sizeof(data), (U32)sizeof(data))); S64 offset = filein.tell(ec); - ensure("LLFile::tell() returned a bad offset", offset == length); - ensure("error_code from LLFile::read() should not indicate an error", !ec); + UNSCOPED_INFO("LLFile::tell() returned a bad offset"); + CHECK(offset == length); + UNSCOPED_INFO("error_code from LLFile::read() should not indicate an error"); + REQUIRE_FALSE(ec); offset = sizeof(data) / 2; int rc = filein.seek(offset, ec); - ensure("LLFile::seek() indicated an error", rc == 0); - ensure("error_code from LLFile::seek() should not indicate an error", !ec); + UNSCOPED_INFO("LLFile::seek() indicated an error"); + CHECK(rc == 0); + UNSCOPED_INFO("error_code from LLFile::seek() should not indicate an error"); + REQUIRE_FALSE(ec); bytes = filein.read(buffer, 2 * sizeof(data), ec); - ensure("LLFile::read() did not read correctly", bytes == sizeof(data) + offset); - ensure("error_code from LLFile::read() should not indicate an error", !ec); - ensure_memory_matches("LLFile:read() did not read correct data3", (char*)data + offset, (U32)offset, buffer, (U32)offset); - ensure_memory_matches("LLFile:read() did not read correct data4", (char*)data, (U32)sizeof(data), buffer + offset, (U32)sizeof(data)); + UNSCOPED_INFO("LLFile::read() did not read correctly"); + CHECK(bytes == sizeof(data) + offset); + UNSCOPED_INFO("error_code from LLFile::read() should not indicate an error"); + REQUIRE_FALSE(ec); + UNSCOPED_INFO("LLFile:read() did not read correct data3"); + REQUIRE_THAT(ll_test::Memory((char*)data + offset, (U32)offset), ll_test::MemoryMatches(buffer, (U32)offset)); + UNSCOPED_INFO("LLFile:read() did not read correct data4"); + REQUIRE_THAT(ll_test::Memory((char*)data, (U32)sizeof(data)), ll_test::MemoryMatches(buffer + offset, (U32)sizeof(data))); filein.close(); free(buffer); } } - template<> template<> - void llfile_test_object_t::test<4>() + TEST_CASE_METHOD(llfile_test, "llfile_test: LLFile refuses conflicting flags and missing paths", "[llfile_test]") { // Testing the LLFile class implementation with wrong paths and parameters std::filesystem::path testfile = testdir; @@ -248,29 +301,36 @@ namespace tut std::error_code ec; LLFile file(testfile, LLFile::out | LLFile::noreplace, ec); - ensure("LLFile constructor should not have opened the already existing file", !file); - ensure("error_code from LLFile constructor should indicate an error", (bool)ec); + UNSCOPED_INFO("LLFile constructor should not have opened the already existing file"); + CHECK(!file); + UNSCOPED_INFO("error_code from LLFile constructor should indicate an error"); + REQUIRE((bool)ec); LLFile::remove(testfile); file = LLFile(testfile, LLFile::out | LLFile::app | LLFile::trunc, ec); - ensure("LLFile constructor should not have opened the file with conflicting flags", !file); - ensure("error_code from LLFile constructor should indicate an error", (bool)ec); + UNSCOPED_INFO("LLFile constructor should not have opened the file with conflicting flags"); + CHECK(!file); + UNSCOPED_INFO("error_code from LLFile constructor should indicate an error"); + REQUIRE((bool)ec); file = LLFile(testfile, LLFile::out | LLFile::app | LLFile::noreplace, ec); - ensure("LLFile constructor should not have opened the file with conflicting flags", !file); - ensure("error_code from LLFile constructor should indicate an error", (bool)ec); + UNSCOPED_INFO("LLFile constructor should not have opened the file with conflicting flags"); + CHECK(!file); + UNSCOPED_INFO("error_code from LLFile constructor should indicate an error"); + REQUIRE((bool)ec); testfile = testdir; testfile.append("llfile_test"); testfile.append("llfile_test.bin"); file = LLFile(testfile, LLFile::in, ec); - ensure("LLFile constructor should not have been able to open the file in the non-existing directory", !file); - ensure("error_code from LLFile constructor should indicate an error", (bool)ec); + UNSCOPED_INFO("LLFile constructor should not have been able to open the file in the non-existing directory"); + CHECK(!file); + UNSCOPED_INFO("error_code from LLFile constructor should indicate an error"); + REQUIRE((bool)ec); } - template<> template<> - void llfile_test_object_t::test<5>() + TEST_CASE_METHOD(llfile_test, "llfile_test: mkdir write read and remove work with Unicode names", "[llfile_test]") { // Test file and directory operations with Unicode paths and filenames std::filesystem::path testdir_unicode = get_testdir_unicode(tempdir, unique); @@ -282,150 +342,223 @@ namespace tut // Clean up any previous test artifacts clear_entire_dir(testdir_unicode); - ensure("Unicode test directory should not exist", !LLFile::exists(testdir_unicode)); + UNSCOPED_INFO("Unicode test directory should not exist"); + CHECK_FALSE(LLFile::exists(testdir_unicode)); // Create the Unicode directory int rc = LLFile::mkdir(testdir_unicode); - ensure("LLFile::mkdir() failed for Unicode directory", rc == 0); - ensure("Unicode test directory should exist", LLFile::isdir(testdir_unicode)); + UNSCOPED_INFO("LLFile::mkdir() failed for Unicode directory"); + CHECK(rc == 0); + UNSCOPED_INFO("Unicode test directory should exist"); + CHECK(LLFile::isdir(testdir_unicode)); - ensure("Unicode test file should not exist", !LLFile::exists(testfile_unicode)); + UNSCOPED_INFO("Unicode test file should not exist"); + CHECK_FALSE(LLFile::exists(testfile_unicode)); // Write to the Unicode file const char* testdata = "unicode_testdata"; S64 bytes = LLFile::write(testfile_unicode, testdata, 0, strlen(testdata)); - ensure("Unicode test file should exist", LLFile::exists(testfile_unicode)); - ensure("Unicode test file should be a file", LLFile::isfile(testfile_unicode)); - ensure("LLFile::write() did not write correctly to Unicode file", bytes == (S64)strlen(testdata)); + UNSCOPED_INFO("Unicode test file should exist"); + CHECK(LLFile::exists(testfile_unicode)); + UNSCOPED_INFO("Unicode test file should be a file"); + CHECK(LLFile::isfile(testfile_unicode)); + UNSCOPED_INFO("LLFile::write() did not write correctly to Unicode file"); + CHECK(bytes == (S64)strlen(testdata)); // Read back the data char buffer[64] = {}; bytes = LLFile::read(testfile_unicode, buffer, 0, sizeof(buffer)); - ensure("LLFile::read() did not read correctly from Unicode file", bytes == (S64)strlen(testdata)); - ensure_memory_matches("LLFile::read() did not read correct Unicode data", testdata, (U32)bytes, buffer, (U32)bytes); + UNSCOPED_INFO("LLFile::read() did not read correctly from Unicode file"); + CHECK(bytes == (S64)strlen(testdata)); + UNSCOPED_INFO("LLFile::read() did not read correct Unicode data"); + REQUIRE_THAT(ll_test::Memory(testdata, (U32)bytes), ll_test::MemoryMatches(buffer, (U32)bytes)); // Remove the file and directory rc = LLFile::remove(testfile_unicode); - ensure("LLFile::remove() failed for Unicode file", rc == 0); - ensure("Unicode test file should not exist after removal", !LLFile::exists(testfile_unicode)); + UNSCOPED_INFO("LLFile::remove() failed for Unicode file"); + CHECK(rc == 0); + UNSCOPED_INFO("Unicode test file should not exist after removal"); + CHECK_FALSE(LLFile::exists(testfile_unicode)); rc = LLFile::remove(testdir_unicode); - ensure("LLFile::remove() failed for Unicode directory", rc == 0); - ensure("Unicode test directory should not exist after removal", !LLFile::exists(testdir_unicode)); + UNSCOPED_INFO("LLFile::remove() failed for Unicode directory"); + CHECK(rc == 0); + UNSCOPED_INFO("Unicode test directory should not exist after removal"); + CHECK_FALSE(LLFile::exists(testdir_unicode)); } - template<> template<> - void llfile_test_object_t::test<6>() + TEST_CASE_METHOD(llfile_test, "llfile_test: isSafeFileName accepts ordinary file names", "[llfile_test]") { // Ordinary file names pass isSafeFileName() - ensure("xml", LLFile::isSafeFileName("lsl_keywords.xml")); - ensure("two extensions", LLFile::isSafeFileName("secondlife.d.luau")); - ensure("no extension", LLFile::isSafeFileName("keywords")); - ensure("inner dots", LLFile::isSafeFileName("a..b")); - ensure("inner space", LLFile::isSafeFileName("my file.txt")); - ensure("hidden", LLFile::isSafeFileName(".luaurc")); - ensure("leading dots", LLFile::isSafeFileName("..name")); - ensure("unicode", LLFile::isSafeFileName("\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB.bin")); + UNSCOPED_INFO("xml"); + CHECK(LLFile::isSafeFileName("lsl_keywords.xml")); + UNSCOPED_INFO("two extensions"); + CHECK(LLFile::isSafeFileName("secondlife.d.luau")); + UNSCOPED_INFO("no extension"); + CHECK(LLFile::isSafeFileName("keywords")); + UNSCOPED_INFO("inner dots"); + CHECK(LLFile::isSafeFileName("a..b")); + UNSCOPED_INFO("inner space"); + CHECK(LLFile::isSafeFileName("my file.txt")); + UNSCOPED_INFO("hidden"); + CHECK(LLFile::isSafeFileName(".luaurc")); + UNSCOPED_INFO("leading dots"); + CHECK(LLFile::isSafeFileName("..name")); + UNSCOPED_INFO("unicode"); + CHECK(LLFile::isSafeFileName("\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB.bin")); } - template<> template<> - void llfile_test_object_t::test<7>() + TEST_CASE_METHOD(llfile_test, "llfile_test: isSafeFileName refuses empty and dot names and ones Windows trims", "[llfile_test]") { // Nothing, the directory itself or its parent, and names Windows would // shorten when creating them - ensure("empty", !LLFile::isSafeFileName("")); - ensure("dot", !LLFile::isSafeFileName(".")); - ensure("dot dot", !LLFile::isSafeFileName("..")); - ensure("three dots", !LLFile::isSafeFileName("...")); - ensure("trailing dot", !LLFile::isSafeFileName("name.")); - ensure("trailing space", !LLFile::isSafeFileName("name.xml ")); + UNSCOPED_INFO("empty"); + CHECK_FALSE(LLFile::isSafeFileName("")); + UNSCOPED_INFO("dot"); + CHECK_FALSE(LLFile::isSafeFileName(".")); + UNSCOPED_INFO("dot dot"); + CHECK_FALSE(LLFile::isSafeFileName("..")); + UNSCOPED_INFO("three dots"); + CHECK_FALSE(LLFile::isSafeFileName("...")); + UNSCOPED_INFO("trailing dot"); + CHECK_FALSE(LLFile::isSafeFileName("name.")); + UNSCOPED_INFO("trailing space"); + CHECK_FALSE(LLFile::isSafeFileName("name.xml ")); } - template<> template<> - void llfile_test_object_t::test<8>() + TEST_CASE_METHOD(llfile_test, "llfile_test: isSafeFileName refuses path separators of either platform", "[llfile_test]") { // Separators of either platform, at either end or inside - ensure("parent posix", !LLFile::isSafeFileName("../../evil.xml")); - ensure("parent windows", !LLFile::isSafeFileName("..\\..\\evil.xml")); - ensure("absolute posix", !LLFile::isSafeFileName("/etc/evil")); - ensure("absolute windows", !LLFile::isSafeFileName("\\Windows\\evil.dll")); - ensure("unc", !LLFile::isSafeFileName("\\\\host\\share\\evil")); - ensure("subdirectory posix", !LLFile::isSafeFileName("sub/evil.xml")); - ensure("subdirectory windows", !LLFile::isSafeFileName("sub\\evil.xml")); - ensure("trailing separator", !LLFile::isSafeFileName("evil/")); + UNSCOPED_INFO("parent posix"); + CHECK_FALSE(LLFile::isSafeFileName("../../evil.xml")); + UNSCOPED_INFO("parent windows"); + CHECK_FALSE(LLFile::isSafeFileName("..\\..\\evil.xml")); + UNSCOPED_INFO("absolute posix"); + CHECK_FALSE(LLFile::isSafeFileName("/etc/evil")); + UNSCOPED_INFO("absolute windows"); + CHECK_FALSE(LLFile::isSafeFileName("\\Windows\\evil.dll")); + UNSCOPED_INFO("unc"); + CHECK_FALSE(LLFile::isSafeFileName("\\\\host\\share\\evil")); + UNSCOPED_INFO("subdirectory posix"); + CHECK_FALSE(LLFile::isSafeFileName("sub/evil.xml")); + UNSCOPED_INFO("subdirectory windows"); + CHECK_FALSE(LLFile::isSafeFileName("sub\\evil.xml")); + UNSCOPED_INFO("trailing separator"); + CHECK_FALSE(LLFile::isSafeFileName("evil/")); } - template<> template<> - void llfile_test_object_t::test<9>() + TEST_CASE_METHOD(llfile_test, "llfile_test: isSafeFileName refuses colons reserved characters and controls", "[llfile_test]") { // Drive and stream colons, the rest of what Windows refuses in a name, // and control characters, a NUL above all: a C string would cut a // harmless prefix off a harmful name there - ensure("drive absolute", !LLFile::isSafeFileName("C:\\evil.dll")); - ensure("drive relative", !LLFile::isSafeFileName("C:evil.dll")); - ensure("stream", !LLFile::isSafeFileName("name.xml:evil")); - ensure("star", !LLFile::isSafeFileName("a*b")); - ensure("question", !LLFile::isSafeFileName("a?b")); - ensure("quote", !LLFile::isSafeFileName("a\"b")); - ensure("less", !LLFile::isSafeFileName("ab")); - ensure("pipe", !LLFile::isSafeFileName("a|b")); - ensure("tab", !LLFile::isSafeFileName("a\tb")); - ensure("newline", !LLFile::isSafeFileName("a\nb")); - ensure("unit separator", !LLFile::isSafeFileName("a\x1F" "b")); - ensure("only nul", !LLFile::isSafeFileName(std::string(1, '\0'))); - ensure("inner nul", !LLFile::isSafeFileName(std::string("name.xml\0evil", 13))); - ensure("trailing nul", !LLFile::isSafeFileName(std::string("name.xml\0", 9))); + UNSCOPED_INFO("drive absolute"); + CHECK_FALSE(LLFile::isSafeFileName("C:\\evil.dll")); + UNSCOPED_INFO("drive relative"); + CHECK_FALSE(LLFile::isSafeFileName("C:evil.dll")); + UNSCOPED_INFO("stream"); + CHECK_FALSE(LLFile::isSafeFileName("name.xml:evil")); + UNSCOPED_INFO("star"); + CHECK_FALSE(LLFile::isSafeFileName("a*b")); + UNSCOPED_INFO("question"); + CHECK_FALSE(LLFile::isSafeFileName("a?b")); + UNSCOPED_INFO("quote"); + CHECK_FALSE(LLFile::isSafeFileName("a\"b")); + UNSCOPED_INFO("less"); + CHECK_FALSE(LLFile::isSafeFileName("ab")); + UNSCOPED_INFO("pipe"); + CHECK_FALSE(LLFile::isSafeFileName("a|b")); + UNSCOPED_INFO("tab"); + CHECK_FALSE(LLFile::isSafeFileName("a\tb")); + UNSCOPED_INFO("newline"); + CHECK_FALSE(LLFile::isSafeFileName("a\nb")); + UNSCOPED_INFO("unit separator"); + CHECK_FALSE(LLFile::isSafeFileName("a\x1F" "b")); + UNSCOPED_INFO("only nul"); + CHECK_FALSE(LLFile::isSafeFileName(std::string(1, '\0'))); + UNSCOPED_INFO("inner nul"); + CHECK_FALSE(LLFile::isSafeFileName(std::string("name.xml\0evil", 13))); + UNSCOPED_INFO("trailing nul"); + CHECK_FALSE(LLFile::isSafeFileName(std::string("name.xml\0", 9))); } - template<> template<> - void llfile_test_object_t::test<10>() + TEST_CASE_METHOD(llfile_test, "llfile_test: isSafeFileName allows up to 255 bytes", "[llfile_test]") { // The length limit is inclusive - ensure("at limit", LLFile::isSafeFileName(std::string(255, 'a'))); - ensure("over limit", !LLFile::isSafeFileName(std::string(256, 'a'))); + UNSCOPED_INFO("at limit"); + CHECK(LLFile::isSafeFileName(std::string(255, 'a'))); + UNSCOPED_INFO("over limit"); + CHECK_FALSE(LLFile::isSafeFileName(std::string(256, 'a'))); } - template<> template<> - void llfile_test_object_t::test<11>() + TEST_CASE_METHOD(llfile_test, "llfile_test: isSafeFileName refuses Windows device names", "[llfile_test]") { // Windows device names, in any case, with or without an extension - ensure("con", !LLFile::isSafeFileName("CON")); - ensure("prn", !LLFile::isSafeFileName("PRN")); - ensure("aux", !LLFile::isSafeFileName("AUX")); - ensure("nul", !LLFile::isSafeFileName("NUL")); - ensure("conin", !LLFile::isSafeFileName("CONIN$")); - ensure("conout", !LLFile::isSafeFileName("CONOUT$")); - ensure("com0", !LLFile::isSafeFileName("COM0")); - ensure("com9", !LLFile::isSafeFileName("COM9")); - ensure("lpt0", !LLFile::isSafeFileName("LPT0")); - ensure("lpt9", !LLFile::isSafeFileName("LPT9")); - ensure("com superscript one", !LLFile::isSafeFileName("COM\xC2\xB9")); - ensure("lpt superscript three", !LLFile::isSafeFileName("LPT\xC2\xB3")); - - ensure("lower case", !LLFile::isSafeFileName("con")); - ensure("mixed case", !LLFile::isSafeFileName("cOm1")); - ensure("lower case conout", !LLFile::isSafeFileName("conout$")); - - ensure("extension", !LLFile::isSafeFileName("NUL.xml")); - ensure("two extensions", !LLFile::isSafeFileName("nul.tar.gz")); - ensure("superscript with extension", !LLFile::isSafeFileName("com\xC2\xB2.txt")); - ensure("spaces before extension", !LLFile::isSafeFileName("CON .xml")); + UNSCOPED_INFO("con"); + CHECK_FALSE(LLFile::isSafeFileName("CON")); + UNSCOPED_INFO("prn"); + CHECK_FALSE(LLFile::isSafeFileName("PRN")); + UNSCOPED_INFO("aux"); + CHECK_FALSE(LLFile::isSafeFileName("AUX")); + UNSCOPED_INFO("nul"); + CHECK_FALSE(LLFile::isSafeFileName("NUL")); + UNSCOPED_INFO("conin"); + CHECK_FALSE(LLFile::isSafeFileName("CONIN$")); + UNSCOPED_INFO("conout"); + CHECK_FALSE(LLFile::isSafeFileName("CONOUT$")); + UNSCOPED_INFO("com0"); + CHECK_FALSE(LLFile::isSafeFileName("COM0")); + UNSCOPED_INFO("com9"); + CHECK_FALSE(LLFile::isSafeFileName("COM9")); + UNSCOPED_INFO("lpt0"); + CHECK_FALSE(LLFile::isSafeFileName("LPT0")); + UNSCOPED_INFO("lpt9"); + CHECK_FALSE(LLFile::isSafeFileName("LPT9")); + UNSCOPED_INFO("com superscript one"); + CHECK_FALSE(LLFile::isSafeFileName("COM\xC2\xB9")); + UNSCOPED_INFO("lpt superscript three"); + CHECK_FALSE(LLFile::isSafeFileName("LPT\xC2\xB3")); + + UNSCOPED_INFO("lower case"); + CHECK_FALSE(LLFile::isSafeFileName("con")); + UNSCOPED_INFO("mixed case"); + CHECK_FALSE(LLFile::isSafeFileName("cOm1")); + UNSCOPED_INFO("lower case conout"); + CHECK_FALSE(LLFile::isSafeFileName("conout$")); + + UNSCOPED_INFO("extension"); + CHECK_FALSE(LLFile::isSafeFileName("NUL.xml")); + UNSCOPED_INFO("two extensions"); + CHECK_FALSE(LLFile::isSafeFileName("nul.tar.gz")); + UNSCOPED_INFO("superscript with extension"); + CHECK_FALSE(LLFile::isSafeFileName("com\xC2\xB2.txt")); + UNSCOPED_INFO("spaces before extension"); + CHECK_FALSE(LLFile::isSafeFileName("CON .xml")); } - template<> template<> - void llfile_test_object_t::test<12>() + TEST_CASE_METHOD(llfile_test, "llfile_test: isSafeFileName accepts names that only look like devices", "[llfile_test]") { // Names that only look like devices are files - ensure("longer word", LLFile::isSafeFileName("CONSOLE.xml")); - ensure("two digits", LLFile::isSafeFileName("COM10")); - ensure("no digit", LLFile::isSafeFileName("LPT")); - ensure("device prefix", LLFile::isSafeFileName("nul_keywords.xml")); - ensure("device suffix", LLFile::isSafeFileName("icon.xml")); - ensure("device extension", LLFile::isSafeFileName("keywords.con")); - ensure("leading space", LLFile::isSafeFileName(" CON")); - ensure("only spaces before extension", LLFile::isSafeFileName(" .xml")); - ensure("hidden device", LLFile::isSafeFileName(".con")); + UNSCOPED_INFO("longer word"); + CHECK(LLFile::isSafeFileName("CONSOLE.xml")); + UNSCOPED_INFO("two digits"); + CHECK(LLFile::isSafeFileName("COM10")); + UNSCOPED_INFO("no digit"); + CHECK(LLFile::isSafeFileName("LPT")); + UNSCOPED_INFO("device prefix"); + CHECK(LLFile::isSafeFileName("nul_keywords.xml")); + UNSCOPED_INFO("device suffix"); + CHECK(LLFile::isSafeFileName("icon.xml")); + UNSCOPED_INFO("device extension"); + CHECK(LLFile::isSafeFileName("keywords.con")); + UNSCOPED_INFO("leading space"); + CHECK(LLFile::isSafeFileName(" CON")); + UNSCOPED_INFO("only spaces before extension"); + CHECK(LLFile::isSafeFileName(" .xml")); + UNSCOPED_INFO("hidden device"); + CHECK(LLFile::isSafeFileName(".con")); } -} // namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/llframetimer_test.cpp b/indra/llcommon/tests/llframetimer_test.cpp index 2af7be4fe5..73f9466fb3 100644 --- a/indra/llcommon/tests/llframetimer_test.cpp +++ b/indra/llcommon/tests/llframetimer_test.cpp @@ -30,9 +30,10 @@ #include "../llframetimer.h" #include "../llsd.h" -#include "../test/lltut.h" +#include +#include "altestmatchers.h" -namespace tut +namespace { struct frametimer_test { @@ -41,48 +42,39 @@ namespace tut LLFrameTimer::updateFrameTime(); } }; - typedef test_group frametimer_group_t; - typedef frametimer_group_t::object frametimer_object_t; - tut::frametimer_group_t frametimer_instance("LLFrameTimer"); - template<> template<> - void frametimer_object_t::test<1>() + TEST_CASE_METHOD(frametimer_test, "LLFrameTimer: expiresAt returns the time setExpiryAt set", "[LLFrameTimer]") { F64 seconds_since_epoch = LLFrameTimer::getTotalSeconds(); LLFrameTimer timer; timer.setExpiryAt(seconds_since_epoch); F64 expires_at = timer.expiresAt(); - ensure_distance( - "set expiry matches get expiry", - expires_at, - seconds_since_epoch, - 0.001); + UNSCOPED_INFO("set expiry matches get expiry"); + CHECK_THAT(expires_at, + ll_test::WithinDistance(seconds_since_epoch, + 0.001)); } - template<> template<> - void frametimer_object_t::test<2>() + TEST_CASE_METHOD(frametimer_test, "LLFrameTimer: setExpiryAt moves an expiry already set", "[LLFrameTimer]") { F64 seconds_since_epoch = LLFrameTimer::getTotalSeconds(); seconds_since_epoch += 10.0; LLFrameTimer timer; timer.setExpiryAt(seconds_since_epoch); F64 expires_at = timer.expiresAt(); - ensure_distance( - "set expiry matches get expiry 1", - expires_at, - seconds_since_epoch, - 0.001); + UNSCOPED_INFO("set expiry matches get expiry 1"); + CHECK_THAT(expires_at, + ll_test::WithinDistance(seconds_since_epoch, + 0.001)); seconds_since_epoch += 10.0; timer.setExpiryAt(seconds_since_epoch); expires_at = timer.expiresAt(); - ensure_distance( - "set expiry matches get expiry 2", - expires_at, - seconds_since_epoch, - 0.001); + UNSCOPED_INFO("set expiry matches get expiry 2"); + CHECK_THAT(expires_at, + ll_test::WithinDistance(seconds_since_epoch, + 0.001)); } - template<> template<> - void frametimer_object_t::test<3>() + TEST_CASE_METHOD(frametimer_test, "LLFrameTimer: an expiry two seconds out passes within ten 200 ms sleeps", "[LLFrameTimer]") { clock_t t1 = clock(); ms_sleep(200); @@ -109,27 +101,26 @@ namespace tut LLFrameTimer::updateFrameTime(); iterations_until_expiration++; } - ensure("timer took too long to expire", iterations_until_expiration <= 10); + UNSCOPED_INFO("timer took too long to expire"); + CHECK(iterations_until_expiration <= 10); } - template<> template<> - void frametimer_object_t::test<4>() + TEST_CASE_METHOD(frametimer_test, "LLFrameTimer: an expiry counts from now and not from the timer's last reset", "[LLFrameTimer]") { - set_test_name("an expiry counts from now, not from the timer's last reset"); LLFrameTimer timer; // Longer than the expiry passes between the timer starting and the // expiry being set. ms_sleep(300); LLFrameTimer::updateFrameTime(); timer.setTimerExpirySec(0.2f); - ensure("not expired the moment it is set", !timer.hasExpired()); - ensure("the whole expiry still to run", timer.getTimeToExpireF32() > 0.19f); + UNSCOPED_INFO("not expired the moment it is set"); + CHECK_FALSE(timer.hasExpired()); + UNSCOPED_INFO("the whole expiry still to run"); + CHECK(timer.getTimeToExpireF32() > 0.19f); } - template<> template<> - void frametimer_object_t::test<5>() + TEST_CASE_METHOD(frametimer_test, "LLFrameTimer: a stopped timer keeps the time it ran and resumes from it", "[LLFrameTimer]") { - set_test_name("a stopped timer keeps the time it ran, and resumes from it"); // Far enough from the start of the run that the absolute start time // cannot pass for the time run. ms_sleep(300); @@ -140,19 +131,21 @@ namespace tut LLFrameTimer::updateFrameTime(); const F32 ran = timer.getElapsedTimeF32(); timer.stop(); - ensure("stopped", !timer.getStarted()); - ensure_approximately_equals("the time it ran", timer.getElapsedTimeF32(), ran, 12); + UNSCOPED_INFO("stopped"); + CHECK_FALSE(timer.getStarted()); + UNSCOPED_INFO("the time it ran"); + CHECK_THAT(timer.getElapsedTimeF32(), ll_test::WithinBits(ran, 12)); ms_sleep(100); LLFrameTimer::updateFrameTime(); - ensure_approximately_equals("not running while stopped", timer.getElapsedTimeF32(), ran, 12); + UNSCOPED_INFO("not running while stopped"); + CHECK_THAT(timer.getElapsedTimeF32(), ll_test::WithinBits(ran, 12)); timer.unpause(); - ensure_approximately_equals("resumed from where it stopped", timer.getElapsedTimeF32(), ran, 12); + UNSCOPED_INFO("resumed from where it stopped"); + CHECK_THAT(timer.getElapsedTimeF32(), ll_test::WithinBits(ran, 12)); } - template<> template<> - void frametimer_object_t::test<6>() + TEST_CASE_METHOD(frametimer_test, "LLFrameTimer: a reset while paused zeroes the time run and leaves it paused", "[LLFrameTimer]") { - set_test_name("a reset while paused zeroes the time run and leaves it paused"); ms_sleep(300); LLFrameTimer::updateFrameTime(); LLFrameTimer timer; @@ -161,16 +154,22 @@ namespace tut LLFrameTimer::updateFrameTime(); timer.pause(); timer.reset(); - ensure("still paused", !timer.getStarted()); - ensure_approximately_equals("no time run", timer.getElapsedTimeF32(), 0.f, 12); + UNSCOPED_INFO("still paused"); + CHECK_FALSE(timer.getStarted()); + UNSCOPED_INFO("no time run"); + CHECK_THAT(timer.getElapsedTimeF32(), ll_test::WithinBits(0.f, 12)); ms_sleep(100); LLFrameTimer::updateFrameTime(); - ensure_approximately_equals("not running while paused", timer.getElapsedTimeF32(), 0.f, 12); + UNSCOPED_INFO("not running while paused"); + CHECK_THAT(timer.getElapsedTimeF32(), ll_test::WithinBits(0.f, 12)); timer.unpause(); - ensure_approximately_equals("runs on from zero", timer.getElapsedTimeF32(), 0.f, 12); + UNSCOPED_INFO("runs on from zero"); + CHECK_THAT(timer.getElapsedTimeF32(), ll_test::WithinBits(0.f, 12)); timer.pause(); timer.start(); - ensure("started", timer.getStarted()); - ensure_approximately_equals("start() runs from zero, paused or not", timer.getElapsedTimeF32(), 0.f, 12); + UNSCOPED_INFO("started"); + CHECK(timer.getStarted()); + UNSCOPED_INFO("start() runs from zero, paused or not"); + CHECK_THAT(timer.getElapsedTimeF32(), ll_test::WithinBits(0.f, 12)); } } diff --git a/indra/llcommon/tests/llheteromap_test.cpp b/indra/llcommon/tests/llheteromap_test.cpp index 7d7a3f96f0..4953a326df 100644 --- a/indra/llcommon/tests/llheteromap_test.cpp +++ b/indra/llcommon/tests/llheteromap_test.cpp @@ -21,7 +21,7 @@ // (pacify clang) static std::ostream& operator<<(std::ostream& out, const std::set& strset); // other Linden headers -#include "../test/lltut.h" +#include static std::string clog; static std::set dlog; @@ -108,7 +108,7 @@ struct Chowdah /***************************************************************************** * TUT *****************************************************************************/ -namespace tut +namespace { struct llheteromap_data { @@ -118,15 +118,9 @@ namespace tut dlog.clear(); } }; - typedef test_group llheteromap_group; - typedef llheteromap_group::object object; - llheteromap_group llheteromapgrp("llheteromap"); - template<> template<> - void object::test<1>() + TEST_CASE_METHOD(llheteromap_data, "llheteromap: obtain creates each type once and the map destroys them all", "[llheteromap]") { - set_test_name("create, get, delete"); - { LLHeteroMap map; @@ -145,24 +139,24 @@ namespace tut { // verify each instance Chalk& chalk = map.obtain(); - ensure_equals(chalk.name, "Chalk"); + CHECK(chalk.name == "Chalk"); Cheese& cheese = map.obtain(); - ensure_equals(cheese.name, "Cheese"); + CHECK(cheese.name == "Cheese"); Chowdah& chowdah = map.obtain(); - ensure_equals(chowdah.name, "Chowdah"); + CHECK(chowdah.name == "Chowdah"); } } // destroy map // Chalk, Cheese and Chowdah should have been created in specific order - ensure_equals(clog, "aeo"); + CHECK(clog == "aeo"); // We don't care what order they're destroyed in, as long as each is // appropriately destroyed. std::set dtorset; for (const char* cp = "aeo"; *cp; ++cp) dtorset.insert(std::string(1, *cp)); - ensure_equals(dlog, dtorset); + CHECK(dlog == dtorset); } -} // namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/llhttpdate_test.cpp b/indra/llcommon/tests/llhttpdate_test.cpp index 023987f171..1443a58f85 100644 --- a/indra/llcommon/tests/llhttpdate_test.cpp +++ b/indra/llcommon/tests/llhttpdate_test.cpp @@ -26,7 +26,8 @@ */ #include "linden_common.h" -#include "lltut.h" +#include +#include #include "lldate.h" #include "llcommon.h" @@ -35,7 +36,7 @@ #include #include -namespace tut +namespace { struct httpdate_data { @@ -47,30 +48,26 @@ namespace tut } LLDate some_date; }; - typedef test_group httpdate_test; - typedef httpdate_test::object httpdate_object; - tut::httpdate_test httpdate("httpdate"); - template<> template<> - void httpdate_object::test<1>() + TEST_CASE_METHOD(httpdate_data, "httpdate: asRFC1123 formats the epoch", "[httpdate]") { static std::string epoch_expected = "Thursday, 01 Jan 1970 00:00:00 GMT" ; - ensure("Check Epoch in RFC 1123", ( epoch_expected == some_date.asRFC1123())); + UNSCOPED_INFO("Check Epoch in RFC 1123"); + CHECK(epoch_expected == some_date.asRFC1123()); } - template<> template<> - void httpdate_object::test<2>() + TEST_CASE_METHOD(httpdate_data, "httpdate: asRFC1123 formats a timestamp", "[httpdate]") { static std::string expected = "Wednesday, 18 Jul 2007 22:17:24 GMT" ; some_date = LLDate(1184797044.037586); - ensure("Check some timestamp in RFC 1123", ( expected == some_date.asRFC1123())); + UNSCOPED_INFO("Check some timestamp in RFC 1123"); + CHECK(expected == some_date.asRFC1123()); } // This test of course most generic.. runs off current time - template<> template<> - void httpdate_object::test<3>() + TEST_CASE_METHOD(httpdate_data, "httpdate: asRFC1123 of the current time matches strftime", "[httpdate]") { //F64 sometime = LLFrameTimer::getTotalSeconds(); @@ -96,11 +93,10 @@ namespace tut strftime(expected, 255, "%A, %d %b %Y %H:%M:%S GMT", result); actual = some_date.asRFC1123(); // probably not a good idea to use strcmp but this is just a unit test - ensure("Current time in RFC 1123", (strcmp(expected, actual.c_str()) == 0)); + UNSCOPED_INFO("Current time in RFC 1123"); + CHECK(std::string_view(expected) == std::string_view(actual.c_str())); } - namespace - { // LC_TIME, LLStringUtil's locale and the one LLDate last set LC_TIME // to, put back as they were however the test ends. LLDate sets LC_TIME // itself when the locale it saw last changes, so one date formatted in @@ -118,7 +114,6 @@ namespace tut setlocale(LC_TIME, mTimeLocale.c_str()); } }; - } // anonymous namespace static void test_date_string(const std::string &locale, struct tm *t, const std::string &fmt, const std::string &expected) @@ -126,11 +121,11 @@ namespace tut std::string result = LLDate::toHTTPDateString(t, fmt); LLStringUtil::toLower(result); std::string label = std::string("toHTTPDateString - ") + locale; - ensure_equals(label.c_str(), result, expected); + UNSCOPED_INFO(label.c_str()); + REQUIRE(result == expected); } - template<> template<> - void httpdate_object::test<4>() + TEST_CASE_METHOD(httpdate_data, "httpdate: toHTTPDateString formats in English and French locales", "[httpdate]") { // test localization of http dates const char *en_locale = "en_US.UTF-8"; @@ -143,7 +138,7 @@ namespace tut setlocale(LC_TIME, en_locale); if (strcmp(setlocale(LC_TIME, NULL), en_locale) != 0) { - skip("Cannot set English locale"); + SKIP("Cannot set English locale"); } LLStringUtil::setLocale(en_locale); @@ -160,7 +155,7 @@ namespace tut setlocale(LC_TIME, fr_locale); if (strcmp(setlocale(LC_TIME, NULL), fr_locale) != 0) { - skip("Cannot set French locale"); + SKIP("Cannot set French locale"); } LLStringUtil::setLocale(fr_locale); diff --git a/indra/llcommon/tests/llinitparam_test.cpp b/indra/llcommon/tests/llinitparam_test.cpp index 0096b98fe3..3a7ee1a2cb 100644 --- a/indra/llcommon/tests/llinitparam_test.cpp +++ b/indra/llcommon/tests/llinitparam_test.cpp @@ -34,7 +34,7 @@ #include "llsd.h" #include "llsdparam.h" -#include "../test/lltut.h" +#include // // Types under test. These live at namespace scope because a block's parameter @@ -235,132 +235,134 @@ struct PointChoice : public LLInitParam::ChoiceBlock } // anonymous namespace -namespace tut +namespace { - struct llinitparam_data - { - }; - typedef test_group llinitparam_group; - typedef llinitparam_group::object object; - llinitparam_group llinitparamgrp("llinitparam"); - - template<> template<> - void object::test<1>() + TEST_CASE("llinitparam: fresh block reports defaults and nothing provided", "[llinitparam]") { - set_test_name("fresh block reports defaults and nothing provided"); - SimpleBlock block; - ensure("nothing provided yet", !block.name.isProvided()); - ensure("nothing provided yet", !block.count.isProvided()); - ensure_equals("declared default survives", block.count(), 7); - ensure_equals("declared default survives", block.flag(), false); - ensure("block itself is not provided", !block.isProvided()); + UNSCOPED_INFO("nothing provided yet"); + CHECK_FALSE(block.name.isProvided()); + UNSCOPED_INFO("nothing provided yet"); + CHECK_FALSE(block.count.isProvided()); + UNSCOPED_INFO("declared default survives"); + CHECK(block.count() == 7); + UNSCOPED_INFO("declared default survives"); + CHECK(block.flag() == false); + UNSCOPED_INFO("block itself is not provided"); + CHECK_FALSE(block.isProvided()); } - template<> template<> - void object::test<2>() + TEST_CASE("llinitparam: assignment marks a parameter provided", "[llinitparam]") { - set_test_name("assignment marks a parameter provided"); - SimpleBlock block; block.count = 42; - ensure("assignment provides", block.count.isProvided()); - ensure_equals("assignment stores", block.count(), 42); - ensure("sibling untouched", !block.flag.isProvided()); - ensure("block became provided", block.isProvided()); + UNSCOPED_INFO("assignment provides"); + CHECK(block.count.isProvided()); + UNSCOPED_INFO("assignment stores"); + CHECK(block.count() == 42); + UNSCOPED_INFO("sibling untouched"); + CHECK_FALSE(block.flag.isProvided()); + UNSCOPED_INFO("block became provided"); + CHECK(block.isProvided()); } - template<> template<> - void object::test<3>() + TEST_CASE("llinitparam: Mandatory gates validation and Optional does not", "[llinitparam]") { - set_test_name("Mandatory gates validation, Optional does not"); - SimpleBlock block; - ensure("missing mandatory fails validation", !block.validateBlock(false)); + UNSCOPED_INFO("missing mandatory fails validation"); + CHECK_FALSE(block.validateBlock(false)); block.name = std::string("widget"); - ensure("supplied mandatory passes validation", block.validateBlock(false)); + UNSCOPED_INFO("supplied mandatory passes validation"); + CHECK(block.validateBlock(false)); } - template<> template<> - void object::test<4>() + TEST_CASE("llinitparam: derived block sees its own and its base's parameters", "[llinitparam]") { - set_test_name("derived block sees its own and its base's parameters"); - DerivedBlock block; block.count = 3; // declared on SimpleBlock block.scale = 2.f; // declared on DerivedBlock - ensure_equals("base parameter readable", block.count(), 3); - ensure_equals("derived parameter readable", block.scale(), 2.f); + UNSCOPED_INFO("base parameter readable"); + CHECK(block.count() == 3); + UNSCOPED_INFO("derived parameter readable"); + CHECK(block.scale() == 2.f); // The base's Mandatory is aggregated into the derived table, so it // still gates the derived block's validation. - ensure("inherited mandatory still required", !block.validateBlock(false)); + UNSCOPED_INFO("inherited mandatory still required"); + CHECK_FALSE(block.validateBlock(false)); block.name = std::string("derived"); - ensure("inherited mandatory satisfied", block.validateBlock(false)); + UNSCOPED_INFO("inherited mandatory satisfied"); + CHECK(block.validateBlock(false)); } - template<> template<> - void object::test<5>() + TEST_CASE("llinitparam: named values resolve by name and by number", "[llinitparam]") { - set_test_name("named values resolve by name and by number"); - S32 value = 0; - ensure("known name resolves", TestNames::getValueFromName("two", value)); - ensure_equals("known name resolves to its value", value, 2); - ensure("unknown name does not resolve", !TestNames::getValueFromName("nine", value)); + UNSCOPED_INFO("known name resolves"); + CHECK(TestNames::getValueFromName("two", value)); + UNSCOPED_INFO("known name resolves to its value"); + CHECK(value == 2); + UNSCOPED_INFO("unknown name does not resolve"); + CHECK_FALSE(TestNames::getValueFromName("nine", value)); SimpleBlock block; block.named = std::string("one"); - ensure_equals("assignment by name sets the value", block.named(), 1); - ensure_equals("assignment by name records the name", block.named.getValueName(), std::string("one")); + UNSCOPED_INFO("assignment by name sets the value"); + CHECK(block.named() == 1); + UNSCOPED_INFO("assignment by name records the name"); + CHECK(block.named.getValueName() == std::string("one")); block.named = 2; - ensure_equals("assignment by value sets the value", block.named(), 2); - ensure("assignment by value clears the name", block.named.getValueName().empty()); + UNSCOPED_INFO("assignment by value sets the value"); + CHECK(block.named() == 2); + UNSCOPED_INFO("assignment by value clears the name"); + REQUIRE(block.named.getValueName().empty()); } - template<> template<> - void object::test<6>() + TEST_CASE("llinitparam: Multiple counts elements and enforces its range", "[llinitparam]") { - set_test_name("Multiple counts elements and enforces its range"); - OuterBlock block; - ensure("empty list is below AtLeast<2>", !block.list.isValid()); - ensure_equals("empty list has no elements", block.list.size(), size_t(0)); + UNSCOPED_INFO("empty list is below AtLeast<2>"); + CHECK_FALSE(block.list.isValid()); + UNSCOPED_INFO("empty list has no elements"); + REQUIRE(block.list.size() == size_t(0)); block.list.add(10); - ensure("one element is still below AtLeast<2>", !block.list.isValid()); + UNSCOPED_INFO("one element is still below AtLeast<2>"); + CHECK_FALSE(block.list.isValid()); block.list.add(20); - ensure("two elements satisfy AtLeast<2>", block.list.isValid()); - ensure_equals("elements are kept in order", block.list()[0](), 10); - ensure_equals("elements are kept in order", block.list()[1](), 20); + UNSCOPED_INFO("two elements satisfy AtLeast<2>"); + CHECK(block.list.isValid()); + UNSCOPED_INFO("elements are kept in order"); + CHECK(block.list()[0]() == 10); + UNSCOPED_INFO("elements are kept in order"); + CHECK(block.list()[1]() == 20); } - template<> template<> - void object::test<7>() + TEST_CASE("llinitparam: a parameter that is a block is provided by its children", "[llinitparam]") { - set_test_name("a parameter that is a block is provided by its children"); - OuterBlock block; - ensure("untouched sub-block is not provided", !block.inner.isProvided()); + UNSCOPED_INFO("untouched sub-block is not provided"); + CHECK_FALSE(block.inner.isProvided()); block.inner.a = 5; - ensure("setting a child provides the sub-block", block.inner.isProvided()); - ensure("and provides the enclosing block", block.isProvided()); - ensure_equals("child value readable", block.inner.a(), 5); - ensure_equals("sibling child keeps its default", block.inner.b(), 0); + UNSCOPED_INFO("setting a child provides the sub-block"); + CHECK(block.inner.isProvided()); + UNSCOPED_INFO("and provides the enclosing block"); + CHECK(block.isProvided()); + UNSCOPED_INFO("child value readable"); + CHECK(block.inner.a() == 5); + UNSCOPED_INFO("sibling child keeps its default"); + CHECK(block.inner.b() == 0); } - template<> template<> - void object::test<8>() + TEST_CASE("llinitparam: fillFrom yields to what is already provided and overwriteFrom does not", "[llinitparam]") { - set_test_name("fillFrom yields to what is already provided, overwriteFrom does not"); - SimpleBlock source; source.count = 100; source.flag = true; @@ -369,37 +371,41 @@ namespace tut destination.count = 1; destination.fillFrom(source); - ensure_equals("fillFrom leaves a provided value alone", destination.count(), 1); - ensure_equals("fillFrom supplies an absent value", destination.flag(), true); + UNSCOPED_INFO("fillFrom leaves a provided value alone"); + CHECK(destination.count() == 1); + UNSCOPED_INFO("fillFrom supplies an absent value"); + CHECK(destination.flag() == true); destination.overwriteFrom(source); - ensure_equals("overwriteFrom replaces a provided value", destination.count(), 100); + UNSCOPED_INFO("overwriteFrom replaces a provided value"); + CHECK(destination.count() == 100); } - template<> template<> - void object::test<9>() + TEST_CASE("llinitparam: choosing one alternative unprovides the other", "[llinitparam]") { - set_test_name("choosing one alternative unprovides the other"); - FirstChoiceOfThings choice; - ensure("first declared alternative is chosen initially", choice.number.isChosen()); + UNSCOPED_INFO("first declared alternative is chosen initially"); + CHECK(choice.number.isChosen()); choice.text = std::string("hello"); - ensure("assignment chooses", choice.text.isChosen()); - ensure("and unchooses the previous alternative", !choice.number.isChosen()); - ensure("the displaced alternative is no longer provided", !choice.number.isProvided()); - ensure_equals("the chosen alternative reads back", choice.text(), std::string("hello")); + UNSCOPED_INFO("assignment chooses"); + CHECK(choice.text.isChosen()); + UNSCOPED_INFO("and unchooses the previous alternative"); + CHECK_FALSE(choice.number.isChosen()); + UNSCOPED_INFO("the displaced alternative is no longer provided"); + CHECK_FALSE(choice.number.isProvided()); + UNSCOPED_INFO("the chosen alternative reads back"); + CHECK(choice.text() == std::string("hello")); choice.number = 3; - ensure("choosing back works", choice.number.isChosen()); - ensure("and displaces the other", !choice.text.isProvided()); + UNSCOPED_INFO("choosing back works"); + CHECK(choice.number.isChosen()); + UNSCOPED_INFO("and displaces the other"); + CHECK_FALSE(choice.text.isProvided()); } - template<> template<> - void object::test<10>() + TEST_CASE("llinitparam: scalars survive an LLSD round-trip", "[llinitparam]") { - set_test_name("scalars survive an LLSD round-trip"); - SimpleBlock written; written.name = std::string("round"); written.count = 55; @@ -409,24 +415,27 @@ namespace tut LLParamSDParser parser; parser.writeSD(sd, written); - ensure("provided scalars are written", sd.has("count")); - ensure_equals("written value is the one set", sd["count"].asInteger(), 55); + UNSCOPED_INFO("provided scalars are written"); + REQUIRE(sd.has("count")); + UNSCOPED_INFO("written value is the one set"); + CHECK(sd["count"].asInteger() == 55); SimpleBlock read; LLParamSDParser reader; reader.readSD(sd, read); - ensure_equals("string survives", read.name(), std::string("round")); - ensure_equals("integer survives", read.count(), 55); - ensure_equals("boolean survives", read.flag(), true); - ensure("reading marks parameters provided", read.count.isProvided()); + UNSCOPED_INFO("string survives"); + CHECK(read.name() == std::string("round")); + UNSCOPED_INFO("integer survives"); + CHECK(read.count() == 55); + UNSCOPED_INFO("boolean survives"); + CHECK(read.flag() == true); + UNSCOPED_INFO("reading marks parameters provided"); + CHECK(read.count.isProvided()); } - template<> template<> - void object::test<11>() + TEST_CASE("llinitparam: nested blocks and lists survive an LLSD round-trip", "[llinitparam]") { - set_test_name("nested blocks and lists survive an LLSD round-trip"); - OuterBlock written; written.inner.a = 11; written.inner.b = 22; @@ -438,24 +447,27 @@ namespace tut LLParamSDParser parser; parser.writeSD(sd, written); - ensure("sub-block is written under its own name", sd.has("inner")); - ensure_equals("sub-block child is written", sd["inner"]["a"].asInteger(), 11); + UNSCOPED_INFO("sub-block is written under its own name"); + REQUIRE(sd.has("inner")); + UNSCOPED_INFO("sub-block child is written"); + CHECK(sd["inner"]["a"].asInteger() == 11); OuterBlock read; LLParamSDParser reader; reader.readSD(sd, read); - ensure_equals("nested value survives", read.inner.a(), 11); - ensure_equals("nested value survives", read.inner.b(), 22); - ensure_equals("list length survives", read.list.size(), size_t(3)); - ensure_equals("list order survives", read.list()[2](), 3); + UNSCOPED_INFO("nested value survives"); + CHECK(read.inner.a() == 11); + UNSCOPED_INFO("nested value survives"); + CHECK(read.inner.b() == 22); + UNSCOPED_INFO("list length survives"); + REQUIRE(read.list.size() == size_t(3)); + UNSCOPED_INFO("list order survives"); + CHECK(read.list()[2]() == 3); } - template<> template<> - void object::test<12>() + TEST_CASE("llinitparam: a named value round-trips as its name and not its number", "[llinitparam]") { - set_test_name("a named value round-trips as its name, not its number"); - SimpleBlock written; written.named = std::string("two"); @@ -463,21 +475,21 @@ namespace tut LLParamSDParser parser; parser.writeSD(sd, written); - ensure_equals("the name is what gets written", sd["named"].asString(), std::string("two")); + UNSCOPED_INFO("the name is what gets written"); + CHECK(sd["named"].asString() == std::string("two")); SimpleBlock read; LLParamSDParser reader; reader.readSD(sd, read); - ensure_equals("the name resolves back to its value", read.named(), 2); - ensure_equals("and the name is remembered", read.named.getValueName(), std::string("two")); + UNSCOPED_INFO("the name resolves back to its value"); + CHECK(read.named() == 2); + UNSCOPED_INFO("and the name is remembered"); + CHECK(read.named.getValueName() == std::string("two")); } - template<> template<> - void object::test<13>() + TEST_CASE("llinitparam: a synonym reaches the same parameter", "[llinitparam]") { - set_test_name("a synonym reaches the same parameter"); - LLSD sd; sd["w"] = 64; @@ -485,15 +497,14 @@ namespace tut LLParamSDParser parser; parser.readSD(sd, block); - ensure("the synonym provided the parameter", block.width.isProvided()); - ensure_equals("through to the same storage", block.width(), 64); + UNSCOPED_INFO("the synonym provided the parameter"); + CHECK(block.width.isProvided()); + UNSCOPED_INFO("through to the same storage"); + CHECK(block.width() == 64); } - template<> template<> - void object::test<14>() + TEST_CASE("llinitparam: an Ignored parameter is accepted and discarded", "[llinitparam]") { - set_test_name("an Ignored parameter is accepted and discarded"); - LLSD sd; sd["kept"] = 5; sd["gone"] = 6; @@ -502,33 +513,34 @@ namespace tut LLParamSDParser parser; parser.readSD(sd, block); - ensure_equals("the live parameter is read", block.kept(), 5); - ensure("the block is valid despite the ignored key", block.validateBlock(false)); + UNSCOPED_INFO("the live parameter is read"); + CHECK(block.kept() == 5); + UNSCOPED_INFO("the block is valid despite the ignored key"); + CHECK(block.validateBlock(false)); } - template<> template<> - void object::test<15>() + TEST_CASE("llinitparam: a custom value can be set whole or by component", "[llinitparam]") { - set_test_name("a custom value can be set whole or by component"); - PointBlock whole; whole.point.set(TestPoint(3, 4)); - ensure_equals("whole assignment reaches the value", whole.point().mX, 3); - ensure_equals("whole assignment reaches the value", whole.point().mY, 4); - ensure_equals("and splats out to the components", whole.point.x(), 3); + UNSCOPED_INFO("whole assignment reaches the value"); + CHECK(whole.point().mX == 3); + UNSCOPED_INFO("whole assignment reaches the value"); + CHECK(whole.point().mY == 4); + UNSCOPED_INFO("and splats out to the components"); + CHECK(whole.point.x() == 3); PointBlock components; components.point.x = 8; components.point.y = 9; - ensure_equals("component assignment builds the value", components.point().mX, 8); - ensure_equals("component assignment builds the value", components.point().mY, 9); + UNSCOPED_INFO("component assignment builds the value"); + CHECK(components.point().mX == 8); + UNSCOPED_INFO("component assignment builds the value"); + CHECK(components.point().mY == 9); } - template<> template<> - void object::test<16>() + TEST_CASE("llinitparam: a custom value round-trips through its components", "[llinitparam]") { - set_test_name("a custom value round-trips through its components"); - PointBlock written; written.point.x = 12; written.point.y = 34; @@ -541,24 +553,25 @@ namespace tut LLParamSDParser reader; reader.readSD(sd, read); - ensure_equals("x survives", read.point().mX, 12); - ensure_equals("y survives", read.point().mY, 34); + UNSCOPED_INFO("x survives"); + CHECK(read.point().mX == 12); + UNSCOPED_INFO("y survives"); + CHECK(read.point().mY == 34); } - template<> template<> - void object::test<17>() + TEST_CASE("llinitparam: a Lazy sub-block stays absent until something fills it", "[llinitparam]") { - set_test_name("a Lazy sub-block stays absent until something fills it"); - // Lazy<> keeps its block on the heap rather than deriving from it, so // an untouched one costs a pointer and serialises to nothing at all. OuterBlock untouched; - ensure("lazy sub-block starts unprovided", !untouched.lazy_inner.isProvided()); + UNSCOPED_INFO("lazy sub-block starts unprovided"); + CHECK_FALSE(untouched.lazy_inner.isProvided()); LLSD empty_sd; LLParamSDParser empty_parser; empty_parser.writeSD(empty_sd, untouched); - ensure("an unfilled lazy block writes nothing", !empty_sd.has("lazy_inner")); + UNSCOPED_INFO("an unfilled lazy block writes nothing"); + REQUIRE_FALSE(empty_sd.has("lazy_inner")); LLSD sd; sd["lazy_inner"]["a"] = 77; @@ -567,63 +580,37 @@ namespace tut LLParamSDParser parser; parser.readSD(sd, filled); - ensure("parsing into it provides it", filled.lazy_inner.isProvided()); - ensure_equals("and the value is reachable", filled.lazy_inner().a(), 77); + UNSCOPED_INFO("parsing into it provides it"); + CHECK(filled.lazy_inner.isProvided()); + UNSCOPED_INFO("and the value is reachable"); + CHECK(filled.lazy_inner().a() == 77); } - template<> template<> - void object::test<18>() + TEST_CASE("llinitparam: copying a block carries values and provided-ness", "[llinitparam]") { - set_test_name("copying a block carries values and provided-ness"); - SimpleBlock original; original.name = std::string("copy me"); original.count = 99; SimpleBlock copy(original); - ensure_equals("values copy", copy.count(), 99); - ensure_equals("values copy", copy.name(), std::string("copy me")); - ensure("provided-ness copies", copy.count.isProvided()); - ensure("unprovided stays unprovided", !copy.flag.isProvided()); + UNSCOPED_INFO("values copy"); + CHECK(copy.count() == 99); + UNSCOPED_INFO("values copy"); + CHECK(copy.name() == std::string("copy me")); + UNSCOPED_INFO("provided-ness copies"); + CHECK(copy.count.isProvided()); + UNSCOPED_INFO("unprovided stays unprovided"); + CHECK_FALSE(copy.flag.isProvided()); // The copy must own its storage: changing it must not disturb the original. copy.count = 1; - ensure_equals("the original is untouched", original.count(), 99); + UNSCOPED_INFO("the original is untouched"); + CHECK(original.count() == 99); } - template<> template<> - void object::test<20>() + TEST_CASE("llinitparam: the descriptor registry sees every block that has been built", "[llinitparam]") { - set_test_name("merging reports whether it actually took anything"); - - // The return value is what tells a caller its block changed. A - // Multiple<> used to answer yes unconditionally, so any block holding - // one looked modified by a merge that moved nothing. - OuterBlock empty_source; - OuterBlock destination; - ensure("filling from an empty block changes nothing", - !destination.fillFrom(empty_source)); - ensure_equals("and leaves the list alone", destination.list.size(), size_t(0)); - - OuterBlock source; - source.list.add(1); - source.list.add(2); - ensure("filling from a block with values does change it", - destination.fillFrom(source)); - ensure_equals("which arrive in the destination", destination.list.size(), size_t(2)); - - // Already-filled destination, still-empty source. - ensure("an empty source still changes nothing", - !destination.fillFrom(empty_source)); - ensure_equals("and does not disturb what is there", destination.list.size(), size_t(2)); - } - - template<> template<> - void object::test<19>() - { - set_test_name("the descriptor registry sees every block that has been built"); - // Constructing a block is what registers its parameters, so touch one // of each kind declared above before counting. SimpleBlock simple; @@ -635,32 +622,59 @@ namespace tut const std::vector& all = LLInitParam::BlockDescriptor::getAllDescriptors(); - ensure("every constructed block type registered", all.size() >= 5); + UNSCOPED_INFO("every constructed block type registered"); + REQUIRE(all.size() >= 5); size_t named_entries = 0; for (const LLInitParam::BlockDescriptor* descriptor : all) { named_entries += descriptor->namedParams().size(); } - ensure("the tables hold the parameters we declared", named_entries >= 12); + UNSCOPED_INFO("the tables hold the parameters we declared"); + CHECK(named_entries >= 12); // DerivedBlock aggregates SimpleBlock's four parameters on top of its // own, which is the duplication the census exists to measure. - ensure_equals("a derived block carries its base's parameters too", - DerivedBlock::getBlockDescriptor().namedParams().size(), + UNSCOPED_INFO("a derived block carries its base's parameters too"); + REQUIRE(DerivedBlock::getBlockDescriptor().namedParams().size() == SimpleBlock::getBlockDescriptor().namedParams().size() + 1); const std::string report = LLInitParam::BlockDescriptor::getStatsReport(); - ensure("the census reports something", !report.empty()); - ensure("the census names what it counted", - report.find("block types registered") != std::string::npos); + UNSCOPED_INFO("the census reports something"); + REQUIRE_FALSE(report.empty()); + UNSCOPED_INFO("the census names what it counted"); + REQUIRE(report.find("block types registered") != std::string::npos); } - template<> template<> - void object::test<21>() + TEST_CASE("llinitparam: merging reports whether it actually took anything", "[llinitparam]") { - set_test_name("a value filling in its own components is not a choice"); + // The return value is what tells a caller its block changed. A + // Multiple<> used to answer yes unconditionally, so any block holding + // one looked modified by a merge that moved nothing. + OuterBlock empty_source; + OuterBlock destination; + UNSCOPED_INFO("filling from an empty block changes nothing"); + REQUIRE_FALSE(destination.fillFrom(empty_source)); + UNSCOPED_INFO("and leaves the list alone"); + REQUIRE(destination.list.size() == size_t(0)); + + OuterBlock source; + source.list.add(1); + source.list.add(2); + UNSCOPED_INFO("filling from a block with values does change it"); + CHECK(destination.fillFrom(source)); + UNSCOPED_INFO("which arrive in the destination"); + REQUIRE(destination.list.size() == size_t(2)); + // Already-filled destination, still-empty source. + UNSCOPED_INFO("an empty source still changes nothing"); + CHECK_FALSE(destination.fillFrom(empty_source)); + UNSCOPED_INFO("and does not disturb what is there"); + REQUIRE(destination.list.size() == size_t(2)); + } + + TEST_CASE("llinitparam: a value filling in its own components is not a choice", "[llinitparam]") + { // TestPoint fills x and y from its value as it is built. That must // read as the block keeping itself consistent, not as someone // supplying a point. @@ -674,33 +688,34 @@ namespace tut PointChoice choice; - ensure("nothing has been chosen yet", !choice.point.isChosen()); - ensure("nothing has been chosen yet", !choice.scalar.isChosen()); + UNSCOPED_INFO("nothing has been chosen yet"); + CHECK_FALSE(choice.point.isChosen()); + UNSCOPED_INFO("nothing has been chosen yet"); + CHECK_FALSE(choice.scalar.isChosen()); } - template<> template<> - void object::test<22>() + TEST_CASE("llinitparam: providing an alternative still chooses it", "[llinitparam]") { - set_test_name("providing an alternative still chooses it"); - PointChoice choice; choice.scalar = 5; - ensure("the provided alternative is the choice", choice.scalar.isChosen()); - ensure_equals("and reads back", choice.scalar(), 5); + UNSCOPED_INFO("the provided alternative is the choice"); + CHECK(choice.scalar.isChosen()); + UNSCOPED_INFO("and reads back"); + CHECK(choice.scalar() == 5); // Reaching into the components of the other alternative counts as // providing it, the same way assigning the whole value would. choice.point.x = 3; - ensure("the later alternative takes over", choice.point.isChosen()); - ensure("and the earlier one steps down", !choice.scalar.isChosen()); - ensure_equals("the component drives the value", choice.point().mX, 3); + UNSCOPED_INFO("the later alternative takes over"); + CHECK(choice.point.isChosen()); + UNSCOPED_INFO("and the earlier one steps down"); + CHECK_FALSE(choice.scalar.isChosen()); + UNSCOPED_INFO("the component drives the value"); + CHECK(choice.point().mX == 3); } - template<> template<> - void object::test<23>() + TEST_CASE("llinitparam: merging does not hand the choice to a value with components", "[llinitparam]") { - set_test_name("merging does not hand the choice to a value with components"); - // A choice merges as a unit: the destination takes the source's // choice, and an alternative the source never provided is passed over // rather than visited -- which matters here because visiting the point @@ -712,8 +727,11 @@ namespace tut PointChoice destination; destination.fillFrom(source); - ensure("the source's choice carried over", destination.scalar.isChosen()); - ensure("the untouched alternative did not seize it", !destination.point.isChosen()); - ensure_equals("and the value came with it", destination.scalar(), 5); + UNSCOPED_INFO("the source's choice carried over"); + CHECK(destination.scalar.isChosen()); + UNSCOPED_INFO("the untouched alternative did not seize it"); + CHECK_FALSE(destination.point.isChosen()); + UNSCOPED_INFO("and the value came with it"); + CHECK(destination.scalar() == 5); } } diff --git a/indra/llcommon/tests/llinstancetracker_test.cpp b/indra/llcommon/tests/llinstancetracker_test.cpp index ae315e45c4..7dfea355be 100644 --- a/indra/llcommon/tests/llinstancetracker_test.cpp +++ b/indra/llcommon/tests/llinstancetracker_test.cpp @@ -38,7 +38,7 @@ #include // std headers // other Linden headers -#include "../test/lltut.h" +#include namespace { @@ -79,62 +79,58 @@ struct Unkeyed: public LLInstanceTracker *****************************************************************************/ namespace tut { - struct llinstancetracker_data + TEST_CASE("llinstancetracker: Keyed instances are found by key and forgotten when destroyed", "[llinstancetracker]") { - }; - typedef test_group llinstancetracker_group; - typedef llinstancetracker_group::object object; - llinstancetracker_group llinstancetrackergrp("llinstancetracker"); - - template<> template<> - void object::test<1>() - { - ensure_equals(Keyed::instanceCount(), 0); + CHECK(Keyed::instanceCount() == 0); { Keyed one("one"); - ensure_equals(Keyed::instanceCount(), 1); + CHECK(Keyed::instanceCount() == 1); auto found = Keyed::getInstance("one"); - ensure("couldn't find stack Keyed", bool(found)); - ensure_equals("found wrong Keyed instance", found.get(), &one); + UNSCOPED_INFO("couldn't find stack Keyed"); + REQUIRE(bool(found)); + UNSCOPED_INFO("found wrong Keyed instance"); + REQUIRE(found.get() == &one); { std::unique_ptr two(new Keyed("two")); - ensure_equals(Keyed::instanceCount(), 2); + CHECK(Keyed::instanceCount() == 2); auto found = Keyed::getInstance("two"); - ensure("couldn't find heap Keyed", bool(found)); - ensure_equals("found wrong Keyed instance", found.get(), two.get()); + UNSCOPED_INFO("couldn't find heap Keyed"); + REQUIRE(bool(found)); + UNSCOPED_INFO("found wrong Keyed instance"); + REQUIRE(found.get() == two.get()); } - ensure_equals(Keyed::instanceCount(), 1); + CHECK(Keyed::instanceCount() == 1); } auto found = Keyed::getInstance("one"); - ensure("Keyed key lives too long", ! found); - ensure_equals(Keyed::instanceCount(), 0); + UNSCOPED_INFO("Keyed key lives too long"); + REQUIRE_FALSE(found); + CHECK(Keyed::instanceCount() == 0); } - template<> template<> - void object::test<2>() + TEST_CASE("llinstancetracker: Unkeyed instances are counted and their weak pointers expire", "[llinstancetracker]") { - ensure_equals(Unkeyed::instanceCount(), 0); + CHECK(Unkeyed::instanceCount() == 0); std::weak_ptr dangling; { Unkeyed one; - ensure_equals(Unkeyed::instanceCount(), 1); + CHECK(Unkeyed::instanceCount() == 1); std::weak_ptr found = one.getWeak(); - ensure(! found.expired()); + CHECK_FALSE(found.expired()); { std::unique_ptr two(new Unkeyed); - ensure_equals(Unkeyed::instanceCount(), 2); + CHECK(Unkeyed::instanceCount() == 2); } - ensure_equals(Unkeyed::instanceCount(), 1); + CHECK(Unkeyed::instanceCount() == 1); // 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("weak_ptr failed to track destruction", dangling.expired()); - ensure_equals(Unkeyed::instanceCount(), 0); + UNSCOPED_INFO("weak_ptr failed to track destruction"); + CHECK(dangling.expired()); + CHECK(Unkeyed::instanceCount() == 0); } - template<> template<> - void object::test<3>() + TEST_CASE("llinstancetracker: key_snapshot and instance_snapshot list each Keyed once", "[llinstancetracker]") { Keyed one("one"), two("two"), three("three"); // We don't want to rely on the underlying container delivering keys @@ -146,12 +142,13 @@ namespace tut StringVector keys(snap.begin(), snap.end()); std::sort(keys.begin(), keys.end()); StringVector::const_iterator ki(keys.begin()); - ensure_equals(*ki++, "one"); - ensure_equals(*ki++, "three"); - ensure_equals(*ki++, "two"); + CHECK(*ki++ == "one"); + CHECK(*ki++ == "three"); + CHECK(*ki++ == "two"); // Use ensure() here because ensure_equals would want to display // mismatched values, and frankly that wouldn't help much. - ensure("didn't reach end", ki == keys.end()); + UNSCOPED_INFO("didn't reach end"); + REQUIRE(ki == keys.end()); // Use a somewhat different approach to order independence with // instance_snapshot(): explicitly capture the instances we know in a @@ -163,13 +160,14 @@ namespace tut instances.insert(&three); for (auto& ref : Keyed::instance_snapshot()) { - ensure_equals("spurious instance", instances.erase(&ref), 1); + UNSCOPED_INFO("spurious instance"); + REQUIRE(instances.erase(&ref) == 1); } - ensure_equals("unreported instance", instances.size(), 0); + UNSCOPED_INFO("unreported instance"); + REQUIRE(instances.size() == 0); } - template<> template<> - void object::test<4>() + TEST_CASE("llinstancetracker: instance_snapshot lists each Unkeyed once", "[llinstancetracker]") { Unkeyed one, two, three; typedef std::set KeySet; @@ -181,17 +179,17 @@ namespace tut for (auto& ref : Unkeyed::instance_snapshot()) { - ensure_equals("spurious instance", instances.erase(&ref), 1); + UNSCOPED_INFO("spurious instance"); + REQUIRE(instances.erase(&ref) == 1); } - ensure_equals("unreported instance", instances.size(), 0); + UNSCOPED_INFO("unreported instance"); + REQUIRE(instances.size() == 0); } - template<> template<> - void object::test<5>() + TEST_CASE("llinstancetracker: delete Keyed with outstanding instance_snapshot", "[llinstancetracker]") { 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(); @@ -200,14 +198,12 @@ namespace tut // 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()); + REQUIRE(snapshot.begin() == snapshot.end()); } - template<> template<> - void object::test<6>() + TEST_CASE("llinstancetracker: delete Keyed with outstanding key_snapshot", "[llinstancetracker]") { 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(); @@ -216,13 +212,11 @@ namespace tut // 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()); + REQUIRE(snapshot.begin() == snapshot.end()); } - template<> template<> - void object::test<7>() + TEST_CASE("llinstancetracker: delete Unkeyed with outstanding instance_snapshot", "[llinstancetracker]") { - set_test_name("delete Unkeyed with outstanding instance_snapshot"); std::string what; Unkeyed* unkeyed = new Unkeyed; // capture a snapshot but do not yet traverse it @@ -232,13 +226,11 @@ namespace tut // 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()); + REQUIRE(snapshot.begin() == snapshot.end()); } - template<> template<> - void object::test<8>() + TEST_CASE("llinstancetracker: a throwing subclass constructor leaves no instance behind", "[llinstancetracker]") { - set_test_name("exception in subclass ctor"); typedef std::set InstanceSet; InstanceSet existing; // We can't use the iterator-range InstanceSet constructor because @@ -271,7 +263,8 @@ namespace tut // its constructor exception. for (auto& ref : Unkeyed::instance_snapshot()) { - ensure("failed to remove instance", existing.find(&ref) != existing.end()); + UNSCOPED_INFO("failed to remove instance"); + REQUIRE(existing.find(&ref) != existing.end()); } } } // namespace tut diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp index d833e6185f..b3e3194c54 100644 --- a/indra/llcommon/tests/llleap_test.cpp +++ b/indra/llcommon/tests/llleap_test.cpp @@ -18,9 +18,12 @@ #include // external library headers // other Linden headers -#include "../test/lltut.h" +#include +#include +#include "altestname.h" #include "../test/namedtempfile.h" #include "../test/catch_and_store_what_in.h" +#include "altestfailure.h" #include "wrapllerrs.h" // CaptureLog #include "llevents.h" #include "llprocess.h" @@ -75,10 +78,10 @@ static void waitfor(const LLLeapVector& instances, int timeout=60) sleep(1); LLEventPumps::instance().obtain("mainloop").post(LLSD()); } - tut::ensure(STRINGIZE("at least 1 of " << instances.size() + UNSCOPED_INFO((STRINGIZE("at least 1 of " << instances.size() << " LLLeap instances timed out (" - << timeout << " seconds) without terminating"), - i < timeout); + << timeout << " seconds) without terminating"))); + REQUIRE(i < timeout); } static void waitfor(LLLeap* instance, int timeout=60) @@ -193,27 +196,23 @@ namespace tut reader.getPath().string().substr(0, reader.getPath().string().length()-3))), PYTHON(LLStringUtil::getenv("PYTHON")) { - ensure("Set PYTHON to interpreter pathname", !PYTHON.empty()); + UNSCOPED_INFO("Set PYTHON to interpreter pathname"); + REQUIRE_FALSE(PYTHON.empty()); } NamedExtTempFile reader; const std::string reader_module; const std::string PYTHON; }; - typedef test_group llleap_group; - typedef llleap_group::object object; - llleap_group llleapgrp("llleap"); - template<> template<> - void object::test<1>() + TEST_CASE_METHOD(llleap_data, "llleap: two LLLeap instances coexist and both terminate", "[llleap]") { - set_test_name("multiple LLLeap instances"); NamedExtTempFile script("py", "import time\n" "time.sleep(1)\n"); LLLeapVector instances; - instances.push_back(LLLeap::create(get_test_name(), + instances.push_back(LLLeap::create(ll_test::currentTestName(), StringVec{PYTHON, script.getPath().string()})->getWeak()); - instances.push_back(LLLeap::create(get_test_name(), + instances.push_back(LLLeap::create(ll_test::currentTestName(), StringVec{PYTHON, script.getPath().string()})->getWeak()); // In this case we're simply establishing that two LLLeap instances // can coexist without throwing exceptions or bombing in any other @@ -221,90 +220,82 @@ namespace tut waitfor(instances); } - template<> template<> - void object::test<2>() + TEST_CASE_METHOD(llleap_data, "llleap: a plugin's stderr lines go to the log", "[llleap]") { - set_test_name("stderr to log"); NamedExtTempFile script("py", "import sys\n" "sys.stderr.write('''Hello from Python!\n" "note partial line''')\n"); StringVec vcommand{ PYTHON, script.getPath().string() }; CaptureLog log(LLError::LEVEL_INFO); - waitfor(LLLeap::create(get_test_name(), vcommand)); + waitfor(LLLeap::create(ll_test::currentTestName(), vcommand)); log.messageWith("Hello from Python!"); log.messageWith("note partial line"); } - template<> template<> - void object::test<3>() + TEST_CASE_METHOD(llleap_data, "llleap: stdout without a length prefix is logged as bad protocol", "[llleap]") { - set_test_name("bad stdout protocol"); NamedExtTempFile script("py", "print('Hello from Python!')\n"); CaptureLog log(LLError::LEVEL_WARN); - waitfor(LLLeap::create(get_test_name(), + waitfor(LLLeap::create(ll_test::currentTestName(), StringVec{PYTHON, script.getPath().string()})); - ensure_contains("error log line", - log.messageWith("invalid protocol"), "Hello from Python!"); + UNSCOPED_INFO("error log line"); + CHECK_THAT(log.messageWith("invalid protocol"), Catch::Matchers::ContainsSubstring("Hello from Python!")); } - template<> template<> - void object::test<4>() + TEST_CASE_METHOD(llleap_data, "llleap: partial stdout left at exit is logged", "[llleap]") { - set_test_name("leftover stdout"); 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(), + waitfor(LLLeap::create(ll_test::currentTestName(), StringVec{PYTHON, script.getPath().string()})); - ensure_contains("error log line", - log.messageWith("Discarding"), "Hello from Python!"); + UNSCOPED_INFO("error log line"); + CHECK_THAT(log.messageWith("Discarding"), Catch::Matchers::ContainsSubstring("Hello from Python!")); } - template<> template<> - void object::test<5>() + TEST_CASE_METHOD(llleap_data, "llleap: a non-numeric length prefix is logged", "[llleap]") { - set_test_name("bad stdout len prefix"); NamedExtTempFile script("py", "import sys\n" "sys.stdout.write('5a2:something')\n"); CaptureLog log(LLError::LEVEL_WARN); - waitfor(LLLeap::create(get_test_name(), + waitfor(LLLeap::create(ll_test::currentTestName(), StringVec{PYTHON, script.getPath().string()})); - ensure_contains("error log line", - log.messageWith("invalid protocol"), "5a2:"); + UNSCOPED_INFO("error log line"); + CHECK_THAT(log.messageWith("invalid protocol"), Catch::Matchers::ContainsSubstring("5a2:")); } - template<> template<> - void object::test<6>() + TEST_CASE_METHOD(llleap_data, "llleap: create with an empty command throws or returns null", "[llleap]") { - set_test_name("empty plugin vector"); std::string threw = catch_what([](){ LLLeap::create("empty", StringVec()); }); - ensure_contains("LLLeap::Error", threw, "no plugin"); + UNSCOPED_INFO("LLLeap::Error"); + CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("no plugin")); // try the suppress-exception variant - ensure("bad launch returned non-NULL", ! LLLeap::create("empty", StringVec(), false)); + UNSCOPED_INFO("bad launch returned non-NULL"); + REQUIRE_FALSE(LLLeap::create("empty", StringVec(), false)); } - template<> template<> - void object::test<7>() + TEST_CASE_METHOD(llleap_data, "llleap: create with a missing executable throws or returns null", "[llleap]") { - set_test_name("bad launch"); // Synthesize bogus executable name std::string BADPYTHON(PYTHON.substr(0, PYTHON.length()-1) + "x"); CaptureLog log; std::string threw = catch_what([&BADPYTHON](){ LLLeap::create("bad exe", BADPYTHON); }); - ensure_contains("LLLeap::create() didn't throw", threw, "failed"); + UNSCOPED_INFO("LLLeap::create() didn't throw"); + CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("failed")); log.messageWith("failed"); log.messageWith(BADPYTHON); // try the suppress-exception variant - ensure("bad launch returned non-NULL", ! LLLeap::create("bad exe", BADPYTHON, false)); + UNSCOPED_INFO("bad launch returned non-NULL"); + REQUIRE_FALSE(LLLeap::create("bad exe", BADPYTHON, false)); } namespace @@ -363,19 +354,19 @@ namespace tut void ensure() const { - tut::ensure(std::string("never posted to ") + getName(), mData.isDefined()); + UNSCOPED_INFO(std::string("never posted to ") + getName()); + REQUIRE(mData.isDefined()); // Post an empty string for success, non-empty string is failure message. - tut::ensure(mData, mData.asString().empty()); + UNSCOPED_INFO(mData); + REQUIRE(mData.asString().empty()); } LLSD mData; }; } // anonymous namespace - template<> template<> - void object::test<8>() + TEST_CASE_METHOD(llleap_data, "llleap: a plugin's request gets the API's reply", "[llleap]") { - set_test_name("round trip"); AckAPI api; Result result; NamedExtTempFile script("py", @@ -388,7 +379,7 @@ namespace tut "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(), + waitfor(LLLeap::create(ll_test::currentTestName(), StringVec{PYTHON, script.getPath().string()})); result.ensure(); } @@ -408,10 +399,8 @@ namespace tut }; } // anonymous namespace - template<> template<> - void object::test<9>() + TEST_CASE_METHOD(llleap_data, "llleap: many small requests get their replies in order", "[llleap]") { - set_test_name("many small messages"); // It's not clear to me whether there's value in iterating many times // over a send/receive loop -- I don't think that will exercise any // interesting corner cases. This test first sends a large number of @@ -451,7 +440,7 @@ namespace tut " 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.getPath().string()}), + waitfor(LLLeap::create(ll_test::currentTestName(), StringVec{PYTHON, script.getPath().string()}), 300); // needs more realtime than most tests result.ensure(); } @@ -575,9 +564,9 @@ namespace tut std::cout << "test_large_message(" << size << ") succeeded" << std::endl; return success; } - catch (const failure& e) + catch (const ll_test::Failure&) { - std::cout << "test_large_message(" << size << ") failed: " << e.what() << std::endl; + std::cout << "test_large_message(" << size << ") failed" << std::endl; return ! success; } } @@ -598,9 +587,9 @@ namespace tut { test_large_message(PYTHON, reader_module, test_name, size); } - catch (const failure& e) + catch (const ll_test::Failure&) { - std::cout << "test_large_message(" << size << ") failed: " << e.what() << std::endl; + std::cout << "test_large_message(" << size << ") failed" << std::endl; // If it still fails below 4K, give up: subdividing any further is // pointless. if (size >= 4096) @@ -646,7 +635,7 @@ namespace tut << "that succeeds" << std::endl; } } - catch (const failure&) + catch (const ll_test::Failure&) { // The recursive test_or_split() call above has already // handled the exception. We don't want our caller to see @@ -656,14 +645,12 @@ namespace tut // In any case, because we reached here through failure of // our original test_large_message(size) call, ensure failure // propagates. - throw e; + throw; } } - template<> template<> - void object::test<10>() + TEST_CASE_METHOD(llleap_data, "llleap: a very large message echoes back intact", "[llleap]") { - set_test_name("very large message"); - test_or_split(PYTHON, reader_module, get_test_name(), BUFFERED_LENGTH); + test_or_split(PYTHON, reader_module, ll_test::currentTestName(), BUFFERED_LENGTH); } } // namespace tut diff --git a/indra/llcommon/tests/llmainthreadtask_test.cpp b/indra/llcommon/tests/llmainthreadtask_test.cpp index a4fab4ba2f..f8885bf3c0 100644 --- a/indra/llcommon/tests/llmainthreadtask_test.cpp +++ b/indra/llcommon/tests/llmainthreadtask_test.cpp @@ -18,7 +18,7 @@ #include // external library headers // other Linden headers -#include "../test/lltut.h" +#include #include "../test/sync.h" #include "llthread.h" // on_main_thread() #include "lleventtimer.h" @@ -27,7 +27,7 @@ /***************************************************************************** * TUT *****************************************************************************/ -namespace tut +namespace { struct llmainthreadtask_data { @@ -41,26 +41,21 @@ namespace tut on_main_thread(); } }; - typedef test_group llmainthreadtask_group; - typedef llmainthreadtask_group::object object; - llmainthreadtask_group llmainthreadtaskgrp("llmainthreadtask"); - template<> template<> - void object::test<1>() + TEST_CASE_METHOD(llmainthreadtask_data, "llmainthreadtask: dispatch on the main thread runs the task inline", "[llmainthreadtask]") { - 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); + UNSCOPED_INFO("didn't run lambda"); + CHECK(ran); + UNSCOPED_INFO("didn't return result"); + CHECK(result); } - namespace - { // LockStatic finds its one instance a process by the type's name, even // in an anonymous namespace, so the name is this test's own. struct MainThreadTaskStatic @@ -69,13 +64,10 @@ namespace tut bool ran{false}; }; typedef llthread::LockStatic LockStatic; - } // anonymous namespace - template<> template<> - void object::test<2>() + TEST_CASE_METHOD(llmainthreadtask_data, "llmainthreadtask: dispatch from another thread runs the task on the main thread", "[llmainthreadtask]") { - set_test_name("cross-thread"); - skip("This test is prone to build-time hangs"); + 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 @@ -107,8 +99,10 @@ namespace tut // 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); + UNSCOPED_INFO("shouldn't have run yet"); + CHECK_FALSE(lk->ran); + UNSCOPED_INFO("shouldn't have returned yet"); + CHECK_FALSE(result); // unlock so the task can acquire the lock lk.unlock(); // run the task -- should unblock thread, which will immediately block @@ -116,8 +110,10 @@ namespace tut 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); + UNSCOPED_INFO("should now have run"); + CHECK(LockStatic()->ran); + UNSCOPED_INFO("returned too early"); + CHECK_FALSE(result); // okay, let thread perform the assignment mSync.set(3); } @@ -136,7 +132,9 @@ namespace tut 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); + UNSCOPED_INFO("ran changed"); + CHECK(LockStatic()->ran); + UNSCOPED_INFO("didn't run on main thread"); + CHECK(result); } -} // namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/llmutex_test.cpp b/indra/llcommon/tests/llmutex_test.cpp index 040ca79b41..b8a99cd56a 100644 --- a/indra/llcommon/tests/llmutex_test.cpp +++ b/indra/llcommon/tests/llmutex_test.cpp @@ -16,13 +16,13 @@ #include "llmutex.h" -#include "../test/lltut.h" +#include #include #include #include -namespace tut +namespace { struct llmutex_data { @@ -38,69 +38,69 @@ namespace tut } }; - typedef test_group llmutex_group; - typedef llmutex_group::object object; - llmutex_group llmutexgrp("llmutex"); - - template<> template<> - void object::test<1>() + TEST_CASE_METHOD(llmutex_data, "llmutex: lock marks ownership and unlock clears it", "[llmutex]") { - set_test_name("lock marks ownership, unlock clears it"); - - ensure("unlocked mutex reports self-locked", !mMutex.isSelfLocked()); - ensure_equals("unlocked mutex has an owner", mMutex.lockingThread(), LLThread::id_t()); + UNSCOPED_INFO("unlocked mutex reports self-locked"); + CHECK_FALSE(mMutex.isSelfLocked()); + UNSCOPED_INFO("unlocked mutex has an owner"); + CHECK(mMutex.lockingThread() == LLThread::id_t()); mMutex.lock(); - ensure("locked mutex does not report self-locked", mMutex.isSelfLocked()); - ensure_equals("locking thread not recorded", mMutex.lockingThread(), LLThread::currentID()); - ensure("isLocked() false while held", mMutex.isLocked()); + UNSCOPED_INFO("locked mutex does not report self-locked"); + CHECK(mMutex.isSelfLocked()); + UNSCOPED_INFO("locking thread not recorded"); + CHECK(mMutex.lockingThread() == LLThread::currentID()); + UNSCOPED_INFO("isLocked() false while held"); + CHECK(mMutex.isLocked()); mMutex.unlock(); - ensure("still self-locked after unlock", !mMutex.isSelfLocked()); - ensure_equals("owner not cleared on unlock", mMutex.lockingThread(), LLThread::id_t()); - ensure("isLocked() true after unlock", !mMutex.isLocked()); + UNSCOPED_INFO("still self-locked after unlock"); + CHECK_FALSE(mMutex.isSelfLocked()); + UNSCOPED_INFO("owner not cleared on unlock"); + CHECK(mMutex.lockingThread() == LLThread::id_t()); + UNSCOPED_INFO("isLocked() true after unlock"); + CHECK_FALSE(mMutex.isLocked()); } - template<> template<> - void object::test<2>() + TEST_CASE_METHOD(llmutex_data, "llmutex: lock is recursive and balanced", "[llmutex]") { - set_test_name("lock is recursive and balanced"); - mMutex.lock(); mMutex.lock(); mMutex.lock(); - ensure("self-lock lost during recursion", mMutex.isSelfLocked()); + UNSCOPED_INFO("self-lock lost during recursion"); + CHECK(mMutex.isSelfLocked()); mMutex.unlock(); - ensure("released on the first of three unlocks", mMutex.isLocked()); + UNSCOPED_INFO("released on the first of three unlocks"); + CHECK(mMutex.isLocked()); mMutex.unlock(); - ensure("released on the second of three unlocks", mMutex.isLocked()); + UNSCOPED_INFO("released on the second of three unlocks"); + CHECK(mMutex.isLocked()); mMutex.unlock(); - ensure("not released after balanced unlocks", !mMutex.isLocked()); - ensure("still self-locked after balanced unlocks", !mMutex.isSelfLocked()); + UNSCOPED_INFO("not released after balanced unlocks"); + CHECK_FALSE(mMutex.isLocked()); + UNSCOPED_INFO("still self-locked after balanced unlocks"); + CHECK_FALSE(mMutex.isSelfLocked()); } - template<> template<> - void object::test<3>() + TEST_CASE_METHOD(llmutex_data, "llmutex: trylock recurses on a self-held mutex", "[llmutex]") { - set_test_name("trylock recurses on a self-held mutex"); - mMutex.lock(); - ensure("trylock failed on a mutex this thread already holds", mMutex.trylock()); + UNSCOPED_INFO("trylock failed on a mutex this thread already holds"); + CHECK(mMutex.trylock()); mMutex.unlock(); - ensure("recursive trylock did not need a matching unlock", mMutex.isLocked()); + UNSCOPED_INFO("recursive trylock did not need a matching unlock"); + CHECK(mMutex.isLocked()); mMutex.unlock(); - ensure("not released after balanced unlocks", !mMutex.isLocked()); + UNSCOPED_INFO("not released after balanced unlocks"); + CHECK_FALSE(mMutex.isLocked()); } - template<> template<> - void object::test<4>() + TEST_CASE_METHOD(llmutex_data, "llmutex: ownership does not leak across threads", "[llmutex]") { - set_test_name("ownership does not leak across threads"); - mMutex.lock(); std::atomic saw_self_locked{true}; @@ -115,8 +115,10 @@ namespace tut } }); - ensure("another thread saw our lock as its own", !saw_self_locked); - ensure("another thread acquired a held mutex", !trylock_succeeded); + UNSCOPED_INFO("another thread saw our lock as its own"); + CHECK_FALSE(saw_self_locked); + UNSCOPED_INFO("another thread acquired a held mutex"); + CHECK_FALSE(trylock_succeeded); mMutex.unlock(); @@ -130,32 +132,31 @@ namespace tut } }); - ensure("another thread could not acquire a released mutex", acquired); + UNSCOPED_INFO("another thread could not acquire a released mutex"); + CHECK(acquired); } - template<> template<> - void object::test<5>() + TEST_CASE_METHOD(llmutex_data, "llmutex: LLMutexTrylock reports and releases", "[llmutex]") { - set_test_name("LLMutexTrylock reports and releases"); - { LLMutexTrylock lock(&mMutex); - ensure("trylock scope failed on a free mutex", lock.isLocked()); - ensure("trylock scope did not take ownership", mMutex.isSelfLocked()); + UNSCOPED_INFO("trylock scope failed on a free mutex"); + CHECK(lock.isLocked()); + UNSCOPED_INFO("trylock scope did not take ownership"); + CHECK(mMutex.isSelfLocked()); } - ensure("trylock scope did not release", !mMutex.isLocked()); + UNSCOPED_INFO("trylock scope did not release"); + CHECK_FALSE(mMutex.isLocked()); // A null mutex is legal and locks nothing -- callers pass member pointers that // may not be constructed yet. LLMutexTrylock null_lock(nullptr); - ensure("null trylock scope claimed a lock", !null_lock.isLocked()); + UNSCOPED_INFO("null trylock scope claimed a lock"); + CHECK_FALSE(null_lock.isLocked()); } - template<> template<> - void object::test<6>() + TEST_CASE_METHOD(llmutex_data, "llmutex: mutual exclusion holds under contention", "[llmutex]") { - set_test_name("mutual exclusion holds under contention"); - // Deliberately a non-atomic counter: the mutex is what makes it safe, so a lost // acquisition shows up as a short count. constexpr int NUM_THREADS = 8; @@ -189,7 +190,9 @@ namespace tut thread.join(); } - ensure_equals("lost an acquisition under contention", counter, NUM_THREADS * NUM_ITERATIONS); - ensure_equals("thread claimed ownership it did not have", false_self_locks.load(), 0); + UNSCOPED_INFO("lost an acquisition under contention"); + CHECK(counter == NUM_THREADS * NUM_ITERATIONS); + UNSCOPED_INFO("thread claimed ownership it did not have"); + CHECK(false_self_locks.load() == 0); } } diff --git a/indra/llcommon/tests/llpounceable_test.cpp b/indra/llcommon/tests/llpounceable_test.cpp index 20954be5d0..6564768047 100644 --- a/indra/llcommon/tests/llpounceable_test.cpp +++ b/indra/llcommon/tests/llpounceable_test.cpp @@ -18,7 +18,7 @@ // external library headers #include // other Linden headers -#include "../test/lltut.h" +#include namespace { @@ -74,24 +74,16 @@ LLPounceable gForward; /***************************************************************************** * TUT *****************************************************************************/ -namespace tut +namespace { - struct llpounceable_data - { - }; - typedef test_group llpounceable_group; - typedef llpounceable_group::object object; - llpounceable_group llpounceablegrp("llpounceable"); - - template<> template<> - void object::test<1>() + TEST_CASE("llpounceable: LLPounceableStatic queues calls made before its constructor runs", "[llpounceable]") { - set_test_name("LLPounceableStatic out-of-order test"); // LLPounceable::callWhenReady() must work even // before LLPounceable's constructor runs. That's the whole point of // implementing it with an LLSingleton queue. This models (say) // LLPounceableStatic. - ensure("static_check should still be null", ! static_check); + UNSCOPED_INFO("static_check should still be null"); + REQUIRE_FALSE(static_check); Data myData("test<1>"); // gForward and static_check let go of myData with the test, however // it ends, rather than point past it at whatever runs next. @@ -104,13 +96,12 @@ namespace tut } } let_go; gForward = &myData; // should run setter - ensure_equals("static_check should be &myData", static_check, &myData); + UNSCOPED_INFO("static_check should be &myData"); + REQUIRE(static_check == &myData); } - template<> template<> - void object::test<2>() + TEST_CASE("llpounceable: each LLPounceableQueue instance has its own queue", "[llpounceable]") { - set_test_name("LLPounceableQueue different queues"); // We expect that LLPounceable should have // different queues because that specialization stores the queue // directly in the LLPounceable instance. @@ -118,21 +109,24 @@ namespace tut LLPounceable a, b; a.callWhenReady(boost::bind(setter, &aptr, _1)); b.callWhenReady(boost::bind(setter, &bptr, _1)); - ensure("aptr should be null", ! aptr); - ensure("bptr should be null", ! bptr); + UNSCOPED_INFO("aptr should be null"); + REQUIRE_FALSE(aptr); + UNSCOPED_INFO("bptr should be null"); + REQUIRE_FALSE(bptr); Data adata("a"), bdata("b"); a = &adata; - ensure_equals("aptr should be &adata", aptr, &adata); + UNSCOPED_INFO("aptr should be &adata"); + REQUIRE(aptr == &adata); // but we haven't yet set b - ensure("bptr should still be null", !bptr); + UNSCOPED_INFO("bptr should still be null"); + REQUIRE_FALSE(bptr); b = &bdata; - ensure_equals("bptr should be &bdata", bptr, &bdata); + UNSCOPED_INFO("bptr should be &bdata"); + REQUIRE(bptr == &bdata); } - template<> template<> - void object::test<3>() + TEST_CASE("llpounceable: each LLPounceableStatic instance has its own queue", "[llpounceable]") { - set_test_name("LLPounceableStatic different queues"); // LLPounceable should also have a distinct // queue for each instance, but that engages an additional map lookup // because there's only one LLSingleton for each T. @@ -140,21 +134,24 @@ namespace tut LLPounceable a, b; a.callWhenReady(boost::bind(setter, &aptr, _1)); b.callWhenReady(boost::bind(setter, &bptr, _1)); - ensure("aptr should be null", ! aptr); - ensure("bptr should be null", ! bptr); + UNSCOPED_INFO("aptr should be null"); + REQUIRE_FALSE(aptr); + UNSCOPED_INFO("bptr should be null"); + REQUIRE_FALSE(bptr); Data adata("a"), bdata("b"); a = &adata; - ensure_equals("aptr should be &adata", aptr, &adata); + UNSCOPED_INFO("aptr should be &adata"); + REQUIRE(aptr == &adata); // but we haven't yet set b - ensure("bptr should still be null", !bptr); + UNSCOPED_INFO("bptr should still be null"); + REQUIRE_FALSE(bptr); b = &bdata; - ensure_equals("bptr should be &bdata", bptr, &bdata); + UNSCOPED_INFO("bptr should be &bdata"); + REQUIRE(bptr == &bdata); } - template<> template<> - void object::test<4>() + TEST_CASE("llpounceable: LLPounceable looks like T", "[llpounceable]") { - set_test_name("LLPounceable looks like T"); // We want LLPounceable to be drop-in replaceable for a plain // T for read constructs. In particular, it should behave like a dumb // pointer -- and with zero abstraction cost for such usage. @@ -165,20 +162,23 @@ namespace tut LLPounceable pounceable(&a); // should be able to pass LLPounceable to function accepting T setter(&aptr, pounceable); - ensure_equals("aptr should be &a", aptr, &a); + UNSCOPED_INFO("aptr should be &a"); + REQUIRE(aptr == &a); // should be able to dereference with * - ensure_equals("deref with *", (*pounceable).mData, "a"); + UNSCOPED_INFO("deref with *"); + CHECK((*pounceable).mData == "a"); // should be able to dereference with -> - ensure_equals("deref with ->", pounceable->mData, "a"); + UNSCOPED_INFO("deref with ->"); + CHECK(pounceable->mData == "a"); // bool operations - ensure("test with operator bool()", pounceable); - ensure("test with operator !()", ! (! pounceable)); + UNSCOPED_INFO("test with operator bool()"); + REQUIRE(pounceable); + UNSCOPED_INFO("test with operator !()"); + CHECK_FALSE(! pounceable); } - template<> template<> - void object::test<5>() + TEST_CASE("llpounceable: callWhenReady() queues until non-null then calls at once", "[llpounceable]") { - set_test_name("Multiple callWhenReady() queue items"); Data *p1 = 0, *p2 = 0, *p3 = 0; Data a("a"); LLPounceable pounceable; @@ -186,43 +186,54 @@ namespace tut pounceable.callWhenReady(boost::bind(setter, &p1, _1)); pounceable.callWhenReady(boost::bind(setter, &p2, _1)); // should still be pending - ensure("p1 should be null", !p1); - ensure("p2 should be null", !p2); - ensure("p3 should be null", !p3); + UNSCOPED_INFO("p1 should be null"); + REQUIRE_FALSE(p1); + UNSCOPED_INFO("p2 should be null"); + REQUIRE_FALSE(p2); + UNSCOPED_INFO("p3 should be null"); + REQUIRE_FALSE(p3); pounceable = 0; // assigning a new empty value shouldn't flush the queue - ensure("p1 should still be null", !p1); - ensure("p2 should still be null", !p2); - ensure("p3 should still be null", !p3); + UNSCOPED_INFO("p1 should still be null"); + REQUIRE_FALSE(p1); + UNSCOPED_INFO("p2 should still be null"); + REQUIRE_FALSE(p2); + UNSCOPED_INFO("p3 should still be null"); + REQUIRE_FALSE(p3); // using whichever syntax pounceable.reset(0); // try to make ensure messages distinct... tough to pin down which // ensure() failed if multiple ensure() calls in the same test have // the same message! - ensure("p1 should again be null", !p1); - ensure("p2 should again be null", !p2); - ensure("p3 should again be null", !p3); + UNSCOPED_INFO("p1 should again be null"); + REQUIRE_FALSE(p1); + UNSCOPED_INFO("p2 should again be null"); + REQUIRE_FALSE(p2); + UNSCOPED_INFO("p3 should again be null"); + REQUIRE_FALSE(p3); pounceable.reset(&a); // should flush queue - ensure_equals("p1 should be &a", p1, &a); - ensure_equals("p2 should be &a", p2, &a); - ensure("p3 still not set", !p3); + UNSCOPED_INFO("p1 should be &a"); + REQUIRE(p1 == &a); + UNSCOPED_INFO("p2 should be &a"); + REQUIRE(p2 == &a); + UNSCOPED_INFO("p3 still not set"); + REQUIRE_FALSE(p3); // immediate call pounceable.callWhenReady(boost::bind(setter, &p3, _1)); - ensure_equals("p3 should be &a", p3, &a); + UNSCOPED_INFO("p3 should be &a"); + REQUIRE(p3 == &a); } - template<> template<> - void object::test<6>() + TEST_CASE("llpounceable: queued calls run in order and firing empties the queue", "[llpounceable]") { - set_test_name("queue order"); std::string data; LLPounceable pounceable; pounceable.callWhenReady(boost::bind(append, _1, "a")); pounceable.callWhenReady(boost::bind(append, _1, "b")); pounceable.callWhenReady(boost::bind(append, _1, "c")); pounceable = &data; - ensure_equals("callWhenReady() must preserve chronological order", - data, "abc"); + UNSCOPED_INFO("callWhenReady() must preserve chronological order"); + CHECK(data == "abc"); std::string data2; pounceable = NULL; @@ -230,16 +241,14 @@ namespace tut pounceable.callWhenReady(boost::bind(append, _1, "e")); pounceable.callWhenReady(boost::bind(append, _1, "f")); pounceable = &data2; - ensure_equals("LLPounceable must reset queue when fired", - data2, "def"); + UNSCOPED_INFO("LLPounceable must reset queue when fired"); + CHECK(data2 == "def"); } - template<> template<> - void object::test<7>() + TEST_CASE("llpounceable: an int tag does not compile (uncomment to check)", "[llpounceable]") { - set_test_name("compile-fail test, uncomment to check"); // The following declaration should fail: only LLPounceableQueue and // LLPounceableStatic should work as tags. // LLPounceable pounceable; } -} // namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp index a17247d232..f609fd0b5b 100644 --- a/indra/llcommon/tests/llprocess_test.cpp +++ b/indra/llcommon/tests/llprocess_test.cpp @@ -26,7 +26,11 @@ #include #include // other Linden headers -#include "../test/lltut.h" +#include +#include +#include "altestfailure.h" +#include "altestmatchers.h" +#include "altestname.h" #include "../test/namedtempfile.h" #include "../test/catch_and_store_what_in.h" #include "stringize.h" @@ -60,9 +64,9 @@ do { \ auto _right_val = (right); \ if (_left_val != _right_val) { \ std::string _msg = std::string(#left) + " != " + std::string(#right); \ - tut::ensure_equals(_msg, _left_val, _right_val); \ + UNSCOPED_INFO(_msg); REQUIRE_THAT(_left_val, ll_test::LLSDEquals(_right_val)); \ } else { \ - tut::ensure_equals("", _left_val, _right_val); \ + REQUIRE_THAT(_left_val, ll_test::LLSDEquals(_right_val)); \ } \ } while(0) @@ -72,11 +76,12 @@ static void aprchk_(const char* call, apr_status_t rv, apr_status_t expected=APR if (rv != expected) { std::string msg = std::string(call) + " => " + std::to_string(rv) + ": " + apr_strerror_helper(rv); - tut::ensure_equals(msg, rv, expected); + UNSCOPED_INFO(msg); + REQUIRE_THAT(rv, ll_test::LLSDEquals(expected)); } else { - tut::ensure_equals("", rv, expected); + REQUIRE_THAT(rv, ll_test::LLSDEquals(expected)); } } @@ -100,7 +105,8 @@ static std::string readfile(const std::filesystem::path& pathname, const std::st std::string output; if (!std::getline(inf, output)) { - tut::ensure("No output " + use_desc, false); + UNSCOPED_INFO("No output " + use_desc); + REQUIRE(false); } std::string more; while (std::getline(inf, more)) @@ -146,7 +152,8 @@ static void waitfor(LLProcess& proc, int timeout=60) // Pump once more after the process exits to flush any final events such as EOF. yield(0); std::string msg = "process took longer than " + std::to_string(timeout) + " seconds to terminate"; - tut::ensure(msg, i < timeout); + UNSCOPED_INFO(msg); + REQUIRE(i < timeout); } static void waitfor(LLProcess::handle h, const std::string& desc, int timeout=60) @@ -159,7 +166,8 @@ static void waitfor(LLProcess::handle h, const std::string& desc, int timeout=60 // Pump once more after the process exits to flush any final events such as EOF. yield(0); std::string msg = "process took longer than " + std::to_string(timeout) + " seconds to terminate"; - tut::ensure(msg, i < timeout); + UNSCOPED_INFO(msg); + REQUIRE(i < timeout); } namespace @@ -182,7 +190,8 @@ struct PythonProcessLauncher mScript("py", script) { auto PYTHON(LLStringUtil::getenv("PYTHON")); - tut::ensure("Set $PYTHON to the Python interpreter", !PYTHON.empty()); + UNSCOPED_INFO("Set $PYTHON to the Python interpreter"); + REQUIRE_FALSE(PYTHON.empty()); mParams.desc = desc + " script"; mParams.executable = PYTHON; @@ -196,9 +205,10 @@ struct PythonProcessLauncher { mPy = LLProcess::create(mParams); std::string msg = "Couldn't launch " + mDesc + " script"; - tut::ensure(msg, bool(mPy)); + UNSCOPED_INFO(msg); + REQUIRE(bool(mPy)); } - catch (const tut::failure&) + catch (const ll_test::Failure&) { // On Windows, if APR_LOG is set, our version of APR's // apr_create_proc() logs to the specified file. If this test @@ -320,7 +330,8 @@ namespace for (;;) { DWORD length = GetModuleFileNameA(nullptr, buffer.data(), static_cast(buffer.size())); - tut::ensure("GetModuleFileNameA() failed", length > 0); + UNSCOPED_INFO("GetModuleFileNameA() failed"); + REQUIRE(length > 0); if (length < buffer.size() && (length < buffer.size() - 1 || buffer[length] == '\0')) { @@ -422,9 +433,10 @@ namespace Sleep(AUTOKILL_HELPER_POLL_INTERVAL_MS); LLEventPumps::instance().obtain("mainloop").post(LLSD()); } - tut::ensure(STRINGIZE("expected " << expected_count + UNSCOPED_INFO((STRINGIZE("expected " << expected_count << " child pids within " << timeout - << " seconds"), false); + << " seconds"))); + REQUIRE(false); return {}; } @@ -445,7 +457,8 @@ namespace params.executable = get_current_executable_path(); params.desc = desc + " helper"; LLProcessPtr helper = LLProcess::create(params); - tut::ensure("helper launched", bool(helper)); + UNSCOPED_INFO("helper launched"); + REQUIRE(bool(helper)); std::vector pids = wait_for_helper_pids(pidfile.getPath().string(), child_count); std::vector handles; @@ -455,7 +468,8 @@ namespace // SYNCHRONIZE lets the test wait for the child to terminate, while // PROCESS_QUERY_LIMITED_INFORMATION keeps the requested access minimal. HANDLE handle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION, false, pid); - tut::ensure(STRINGIZE("opened child process handle for pid " << pid), handle != nullptr); + UNSCOPED_INFO((STRINGIZE("opened child process handle for pid " << pid))); + REQUIRE(handle != nullptr); handles.push_back(handle); } @@ -465,13 +479,13 @@ namespace } waitfor(*helper); - tut::ensure_equals("helper exited", helper->getStatus().mState, LLProcess::EXITED); + UNSCOPED_INFO("helper exited"); + REQUIRE(helper->getStatus().mState == LLProcess::EXITED); for (HANDLE handle : handles) { - tut::ensure_equals("autokilled child exited", - WaitForSingleObject(handle, AUTOKILL_CHILD_TERMINATION_TIMEOUT_MS), - WAIT_OBJECT_0); + UNSCOPED_INFO("autokilled child exited"); + REQUIRE(WaitForSingleObject(handle, AUTOKILL_CHILD_TERMINATION_TIMEOUT_MS) == WAIT_OBJECT_0); CloseHandle(handle); } } @@ -544,9 +558,6 @@ namespace tut LLAPRPool pool; }; - typedef test_group llprocess_group; - typedef llprocess_group::object object; - llprocess_group llprocessgrp("llprocess"); namespace { @@ -650,11 +661,8 @@ namespace tut } } - template<> template<> - void object::test<1>() + TEST_CASE_METHOD(llprocess_data, "llprocess: raw APR pipes read a child's output without blocking", "[llprocess]") { - set_test_name("raw APR nonblocking I/O"); - // Create a script file in a temporary place. NamedExtTempFile script("py", "from __future__ import print_function" EOL @@ -684,7 +692,8 @@ namespace tut std::vector argv; apr_proc_t child; auto PYTHON(LLStringUtil::getenv("PYTHON")); - tut::ensure("Set $PYTHON to the Python interpreter", !PYTHON.empty()); + UNSCOPED_INFO("Set $PYTHON to the Python interpreter"); + REQUIRE_FALSE(PYTHON.empty()); argv.push_back(PYTHON.c_str()); // Have to have a named copy of this std::string so its c_str() value // will persist. @@ -810,21 +819,23 @@ namespace tut ensure_equals_(wi.rc, 0); unsigned i = 0; - ensure("blocking I/O on child pipe (0)", history[i].tries); + UNSCOPED_INFO("blocking I/O on child pipe (0)"); + CHECK(history[i].tries); ensure_equals_(history[i].which, "out"); ensure_equals_(history[i].what, "stdout after wait" EOL); // ++i; // ensure_equals_(history[i].which, "out"); // ensure_equals_(history[i].what, "*eof*"); ++i; - ensure("blocking I/O on child pipe (1)", history[i].tries); + UNSCOPED_INFO("blocking I/O on child pipe (1)"); + CHECK(history[i].tries); ensure_equals_(history[i].which, "err"); ensure_equals_(history[i].what, "stderr after wait" EOL); // ++i; // ensure_equals_(history[i].which, "err"); // ensure_equals_(history[i].what, "*eof*"); } - catch (const failure&) + catch (const ll_test::Failure&) { std::cout << "History:\n"; for (const Item& item : history) @@ -849,15 +860,13 @@ namespace tut } } - template<> template<> - void object::test<2>() + TEST_CASE_METHOD(llprocess_data, "llprocess: cwd sets the child's working directory", "[llprocess]") { - set_test_name("setWorkingDirectory()"); // We want to test setWorkingDirectory(). But what directory is // guaranteed to exist on every machine, under every OS? Have to // create one. Naturally, ensure we clean it up when done. NamedTempDir tempdir; - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "from __future__ import with_statement\n" "import os, sys\n" "with open(sys.argv[1], 'w') as f:\n" @@ -870,14 +879,13 @@ namespace tut // but on the Mac, using tolower() fails because "/users" != "/Users"! expected = utf8str_tolower(expected); #endif - ensure_equals("os.getcwd()", py.run_read(), expected); + UNSCOPED_INFO("os.getcwd()"); + CHECK(py.run_read() == expected); } - template<> template<> - void object::test<3>() + TEST_CASE_METHOD(llprocess_data, "llprocess: args reach the child in order", "[llprocess]") { - set_test_name("arguments"); - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "from __future__ import with_statement, print_function\n" "import sys\n" // note nonstandard output-file arg! @@ -892,71 +900,76 @@ namespace tut std::string output(py.run_read()); boost::split_iterator li(output, boost::first_finder("\n")), lend; - ensure("didn't get first arg", li != lend); + UNSCOPED_INFO("didn't get first arg"); + CHECK(li != lend); std::string arg(li->begin(), li->end()); - ensure_equals(arg, "first arg"); + CHECK(arg == "first arg"); ++li; - ensure("didn't get second arg", li != lend); + UNSCOPED_INFO("didn't get second arg"); + CHECK(li != lend); arg.assign(li->begin(), li->end()); - ensure_equals(arg, "second arg"); + CHECK(arg == "second arg"); ++li; - ensure("didn't get output filename?!", li != lend); + UNSCOPED_INFO("didn't get output filename?!"); + CHECK(li != lend); arg.assign(li->begin(), li->end()); - ensure("output filename empty?!", ! arg.empty()); + UNSCOPED_INFO("output filename empty?!"); + REQUIRE_FALSE(arg.empty()); ++li; - ensure("too many args", li == lend); + UNSCOPED_INFO("too many args"); + CHECK(li == lend); } - template<> template<> - void object::test<4>() + TEST_CASE_METHOD(llprocess_data, "llprocess: exit(0) is reported as EXITED with code 0", "[llprocess]") { - set_test_name("exit(0)"); - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "import sys\n" "sys.exit(0)\n"); py.run(); - ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::EXITED); - ensure_equals("Status.mData", py.mPy->getStatus().mData, 0); + UNSCOPED_INFO("Status.mState"); + CHECK(py.mPy->getStatus().mState == LLProcess::EXITED); + UNSCOPED_INFO("Status.mData"); + CHECK(py.mPy->getStatus().mData == 0); } - template<> template<> - void object::test<5>() + TEST_CASE_METHOD(llprocess_data, "llprocess: exit(2) is reported as EXITED with code 2", "[llprocess]") { - set_test_name("exit(2)"); - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "import sys\n" "sys.exit(2)\n"); py.run(); - ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::EXITED); - ensure_equals("Status.mData", py.mPy->getStatus().mData, 2); + UNSCOPED_INFO("Status.mState"); + CHECK(py.mPy->getStatus().mState == LLProcess::EXITED); + UNSCOPED_INFO("Status.mData"); + CHECK(py.mPy->getStatus().mData == 2); } - template<> template<> - void object::test<6>() + TEST_CASE_METHOD(llprocess_data, "llprocess: a script syntax error exits 1 with SyntaxError on stderr", "[llprocess]") { - set_test_name("syntax_error"); - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "syntax_error:\n"); py.mParams.files.add(LLProcess::FileParam()); // inherit stdin py.mParams.files.add(LLProcess::FileParam()); // inherit stdout py.mParams.files.add(LLProcess::FileParam().type("pipe")); // pipe for stderr py.run(); - ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::EXITED); - ensure_equals("Status.mData", py.mPy->getStatus().mData, 1); + UNSCOPED_INFO("Status.mState"); + CHECK(py.mPy->getStatus().mState == LLProcess::EXITED); + UNSCOPED_INFO("Status.mData"); + CHECK(py.mPy->getStatus().mData == 1); std::istream& rpipe(py.mPy->getReadPipe(LLProcess::STDERR).get_istream()); std::vector buffer(4096); rpipe.read(&buffer[0], buffer.size()); std::streamsize got(rpipe.gcount()); - ensure("Nothing read from stderr pipe", got); + UNSCOPED_INFO("Nothing read from stderr pipe"); + CHECK(got); std::string data(&buffer[0], got); - ensure("Didn't find 'SyntaxError:'", data.find("\nSyntaxError:") != std::string::npos); + UNSCOPED_INFO("Didn't find 'SyntaxError:'"); + REQUIRE(data.find("\nSyntaxError:") != std::string::npos); } - template<> template<> - void object::test<7>() + TEST_CASE_METHOD(llprocess_data, "llprocess: kill() stops a sleeping child before it writes again", "[llprocess]") { - set_test_name("explicit kill()"); - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "from __future__ import with_statement\n" "import sys, time\n" "with open(sys.argv[1], 'w') as f:\n" @@ -978,32 +991,36 @@ namespace tut break; } // If we broke this loop because of the counter, something's wrong - ensure("script never started", i < timeout); + UNSCOPED_INFO("script never started"); + CHECK(i < timeout); // script has performed its first write and should now be sleeping. py.mPy->kill(); // wait for the script to terminate... one way or another. waitfor(*py.mPy); #if LL_WINDOWS - ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::EXITED); - ensure_equals("Status.mData", py.mPy->getStatus().mData, -1); + UNSCOPED_INFO("Status.mState"); + REQUIRE(py.mPy->getStatus().mState == LLProcess::EXITED); + UNSCOPED_INFO("Status.mData"); + REQUIRE(py.mPy->getStatus().mData == -1); #else - ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::KILLED); - ensure_equals("Status.mData", py.mPy->getStatus().mData, SIGTERM); + UNSCOPED_INFO("Status.mState"); + CHECK(py.mPy->getStatus().mState == LLProcess::KILLED); + UNSCOPED_INFO("Status.mData"); + CHECK(py.mPy->getStatus().mData == SIGTERM); #endif // If kill() failed, the script would have woken up on its own and // overwritten the file with 'bad'. But if kill() succeeded, it should // not have had that chance. - ensure_equals(get_test_name() + " script output", readfile(out.getPath()), "ok"); + UNSCOPED_INFO(ll_test::currentTestName() + " script output"); + CHECK(readfile(out.getPath()) == "ok"); } - template<> template<> - void object::test<8>() + TEST_CASE_METHOD(llprocess_data, "llprocess: destroying the LLProcess kills its child", "[llprocess]") { - set_test_name("implicit kill()"); NamedTempFile out("out", "not started"); LLProcess::handle phandle(0); { - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "from __future__ import with_statement\n" "import sys, time\n" "with open(sys.argv[1], 'w') as f:\n" @@ -1026,7 +1043,8 @@ namespace tut break; } // If we broke this loop because of the counter, something's wrong - ensure("script never started", i < timeout); + UNSCOPED_INFO("script never started"); + CHECK(i < timeout); // Script has performed its first write and should now be sleeping. // Destroy the LLProcess, which should kill the child. } @@ -1035,18 +1053,17 @@ namespace tut // If kill() failed, the script would have woken up on its own and // overwritten the file with 'bad'. But if kill() succeeded, it should // not have had that chance. - ensure_equals(get_test_name() + " script output", readfile(out.getPath()), "ok"); + UNSCOPED_INFO(ll_test::currentTestName() + " script output"); + CHECK(readfile(out.getPath()) == "ok"); } - template<> template<> - void object::test<9>() + TEST_CASE_METHOD(llprocess_data, "llprocess: autokill=false lets the child outlive the LLProcess", "[llprocess]") { - set_test_name("autokill=false"); NamedTempFile from("from", "not started"); NamedTempFile to("to", ""); LLProcess::handle phandle(0); { - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "from __future__ import with_statement\n" "import sys, time\n" "with open(sys.argv[1], 'w') as f:\n" @@ -1080,7 +1097,8 @@ namespace tut break; } // If we broke this loop because of the counter, something's wrong - ensure("script never started", i < timeout); + UNSCOPED_INFO("script never started"); + CHECK(i < timeout); // Now destroy the LLProcess, which should NOT kill the child! } // If the destructor killed the child anyway, give it time to die @@ -1095,20 +1113,19 @@ namespace tut waitfor(phandle, "autokill script"); // If the LLProcess destructor implicitly called kill(), the // script could not have written 'ack' as we expect. - ensure_equals(get_test_name() + " script output", readfile(from.getPath()), "ack"); + UNSCOPED_INFO(ll_test::currentTestName() + " script output"); + CHECK(readfile(from.getPath()) == "ack"); } - template<> template<> - void object::test<10>() + TEST_CASE_METHOD(llprocess_data, "llprocess: attached=false lets the child outlive the LLProcess despite autokill", "[llprocess]") { - set_test_name("attached=false"); // almost just like autokill=false, except set autokill=true with // attached=false. NamedTempFile from("from", "not started"); NamedTempFile to("to", ""); LLProcess::handle phandle(0); { - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "from __future__ import with_statement\n" "import sys, time\n" "with open(sys.argv[1], 'w') as f:\n" @@ -1143,7 +1160,8 @@ namespace tut break; } // If we broke this loop because of the counter, something's wrong - ensure("script never started", i < timeout); + UNSCOPED_INFO("script never started"); + CHECK(i < timeout); // Now destroy the LLProcess, which should NOT kill the child! } // If the destructor killed the child anyway, give it time to die @@ -1158,91 +1176,91 @@ namespace tut waitfor(phandle, "autokill script"); // If the LLProcess destructor implicitly called kill(), the // script could not have written 'ack' as we expect. - ensure_equals(get_test_name() + " script output", readfile(from.getPath()), "ack"); + UNSCOPED_INFO(ll_test::currentTestName() + " script output"); + CHECK(readfile(from.getPath()) == "ack"); } - template<> template<> - void object::test<11>() + TEST_CASE_METHOD(llprocess_data, "llprocess: an unknown file type is rejected and its slot named", "[llprocess]") { - set_test_name("'bogus' test"); CaptureLog recorder; - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "from __future__ import print_function\n" "print('Hello world')\n"); py.mParams.files.add(LLProcess::FileParam("bogus")); py.mPy = LLProcess::create(py.mParams); - ensure("should have rejected 'bogus'", ! py.mPy); + UNSCOPED_INFO("should have rejected 'bogus'"); + REQUIRE_FALSE(py.mPy); std::string message(recorder.messageWith("bogus")); - ensure_contains("did not name 'stdin'", message, "stdin"); + UNSCOPED_INFO("did not name 'stdin'"); + CHECK_THAT(message, Catch::Matchers::ContainsSubstring("stdin")); } - template<> template<> - void object::test<12>() + TEST_CASE_METHOD(llprocess_data, "llprocess: the unimplemented file type is rejected", "[llprocess]") { - set_test_name("'file' test"); // Replace this test with one or more real 'file' tests when we // implement 'file' support - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "from __future__ import print_function\n" "print('Hello world')\n"); py.mParams.files.add(LLProcess::FileParam()); py.mParams.files.add(LLProcess::FileParam("file")); py.mPy = LLProcess::create(py.mParams); - ensure("should have rejected 'file'", ! py.mPy); + UNSCOPED_INFO("should have rejected 'file'"); + REQUIRE_FALSE(py.mPy); } - template<> template<> - void object::test<13>() + TEST_CASE_METHOD(llprocess_data, "llprocess: the unimplemented tpipe type is rejected and its slot named", "[llprocess]") { - set_test_name("'tpipe' test"); // Replace this test with one or more real 'tpipe' tests when we // implement 'tpipe' support CaptureLog recorder; - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "from __future__ import print_function\n" "print('Hello world')\n"); py.mParams.files.add(LLProcess::FileParam()); py.mParams.files.add(LLProcess::FileParam("tpipe")); py.mPy = LLProcess::create(py.mParams); - ensure("should have rejected 'tpipe'", ! py.mPy); + UNSCOPED_INFO("should have rejected 'tpipe'"); + REQUIRE_FALSE(py.mPy); std::string message(recorder.messageWith("tpipe")); - ensure_contains("did not name 'stdout'", message, "stdout"); + UNSCOPED_INFO("did not name 'stdout'"); + CHECK_THAT(message, Catch::Matchers::ContainsSubstring("stdout")); } - template<> template<> - void object::test<14>() + TEST_CASE_METHOD(llprocess_data, "llprocess: the unimplemented npipe type is rejected and its slot named", "[llprocess]") { - set_test_name("'npipe' test"); // Replace this test with one or more real 'npipe' tests when we // implement 'npipe' support CaptureLog recorder; - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "from __future__ import print_function\n" "print('Hello world')\n"); py.mParams.files.add(LLProcess::FileParam()); py.mParams.files.add(LLProcess::FileParam()); py.mParams.files.add(LLProcess::FileParam("npipe")); py.mPy = LLProcess::create(py.mParams); - ensure("should have rejected 'npipe'", ! py.mPy); + UNSCOPED_INFO("should have rejected 'npipe'"); + REQUIRE_FALSE(py.mPy); std::string message(recorder.messageWith("npipe")); - ensure_contains("did not name 'stderr'", message, "stderr"); + UNSCOPED_INFO("did not name 'stderr'"); + CHECK_THAT(message, Catch::Matchers::ContainsSubstring("stderr")); } - template<> template<> - void object::test<15>() + TEST_CASE_METHOD(llprocess_data, "llprocess: a named internal pipe draws a warning but still launches", "[llprocess]") { - set_test_name("internal pipe name warning"); CaptureLog recorder; - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "import sys\n" "sys.exit(7)\n"); py.mParams.files.add(LLProcess::FileParam("pipe", "somename")); py.run(); // verify that it did launch anyway - ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::EXITED); - ensure_equals("Status.mData", py.mPy->getStatus().mData, 7); + UNSCOPED_INFO("Status.mState"); + CHECK(py.mPy->getStatus().mState == LLProcess::EXITED); + UNSCOPED_INFO("Status.mData"); + CHECK(py.mPy->getStatus().mData == 7); std::string message(recorder.messageWith("not yet supported")); - ensure_contains("log message did not mention internal pipe name", - message, "somename"); + UNSCOPED_INFO("log message did not mention internal pipe name"); + CHECK_THAT(message, Catch::Matchers::ContainsSubstring("somename")); } /*-------------- support for "get*Pipe() validation" test --------------*/ @@ -1252,21 +1270,21 @@ namespace tut std::string threw; \ /* Both the following calls should work. */ \ (PROCESS).GETPIPE(VALID); \ - ensure(#GETOPTPIPE "(" #VALID ") failed", bool((PROCESS).GETOPTPIPE(VALID))); \ + UNSCOPED_INFO((#GETOPTPIPE "(" #VALID ") failed")); REQUIRE((bool((PROCESS).GETOPTPIPE(VALID)))); \ /* pass obviously bogus PIPESLOT */ \ CATCH_IN(threw, LLProcess::NoPipe, (PROCESS).GETPIPE(LLProcess::FILESLOT(4))); \ - ensure_contains("didn't reject bad slot", threw, "no slot"); \ - ensure_contains("didn't mention bad slot num", threw, "4"); \ + UNSCOPED_INFO("didn't reject bad slot"); REQUIRE_THAT(threw, Catch::Matchers::ContainsSubstring("no slot")); \ + UNSCOPED_INFO("didn't mention bad slot num"); REQUIRE_THAT(threw, Catch::Matchers::ContainsSubstring("4")); \ EXPECT_FAIL_WITH_LOG(threw, (PROCESS).GETOPTPIPE(LLProcess::FILESLOT(4))); \ /* pass NOPIPE */ \ CATCH_IN(threw, LLProcess::NoPipe, (PROCESS).GETPIPE(NOPIPE)); \ - ensure_contains("didn't reject non-pipe", threw, "not a monitored"); \ + UNSCOPED_INFO("didn't reject non-pipe"); REQUIRE_THAT(threw, Catch::Matchers::ContainsSubstring("not a monitored")); \ EXPECT_FAIL_WITH_LOG(threw, (PROCESS).GETOPTPIPE(NOPIPE)); \ /* pass BADPIPE: FILESLOT isn't empty but wrong direction */ \ CATCH_IN(threw, LLProcess::NoPipe, (PROCESS).GETPIPE(BADPIPE)); \ /* sneaky: GETPIPE is getReadPipe or getWritePipe */ \ /* so skip "get" to obtain ReadPipe or WritePipe :-P */ \ - ensure_contains("didn't reject wrong pipe", threw, (#GETPIPE)+3); \ + UNSCOPED_INFO("didn't reject wrong pipe"); REQUIRE_THAT(threw, Catch::Matchers::ContainsSubstring((#GETPIPE)+3)); \ EXPECT_FAIL_WITH_LOG(threw, (PROCESS).GETOPTPIPE(BADPIPE)); \ } while (0) @@ -1278,22 +1296,20 @@ namespace tut (THREW) = catch_what([&](){ \ CODE; \ }); \ - ensure("failed to throw " #EXCEPTION ": " #CODE, ! (THREW).empty()); \ + UNSCOPED_INFO(("failed to throw " #EXCEPTION ": " #CODE)); REQUIRE((! (THREW).empty())); \ } while (0) #define EXPECT_FAIL_WITH_LOG(EXPECT, CODE) \ do \ { \ CaptureLog recorder; \ - ensure(#CODE " succeeded", ! (CODE)); \ + UNSCOPED_INFO((#CODE " succeeded")); REQUIRE((! (CODE))); \ recorder.messageWith(EXPECT); \ } while (0) - template<> template<> - void object::test<16>() + TEST_CASE_METHOD(llprocess_data, "llprocess: getReadPipe and getWritePipe reject bad slots and wrong pipes", "[llprocess]") { - set_test_name("get*Pipe() validation"); - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "from __future__ import print_function\n" "print('this output is expected')\n"); py.mParams.files.add(LLProcess::FileParam("pipe")); // pipe for stdin @@ -1310,11 +1326,9 @@ namespace tut LLProcess::STDIN); // BADPIPE } - template<> template<> - void object::test<17>() + TEST_CASE_METHOD(llprocess_data, "llprocess: a child answers over its stdin and stdout pipes", "[llprocess]") { - set_test_name("talk to stdin/stdout"); - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "from __future__ import print_function\n" "import sys, time\n" "print('ok')\n" @@ -1333,16 +1347,21 @@ namespace tut { yield(); } - ensure("script never started", i < timeout); - ensure_equals("bad wakeup from stdin/stdout script", - childout.getline(), "ok"); + UNSCOPED_INFO("script never started"); + CHECK(i < timeout); + UNSCOPED_INFO("bad wakeup from stdin/stdout script"); + CHECK(childout.getline() == "ok"); // important to get the implicit flush from std::endl py.mPy->getWritePipe().get_ostream() << "go" << std::endl; waitfor(*py.mPy); - ensure("script never replied", childout.contains("\n")); - ensure_equals("child didn't ack", childout.getline(), "ack"); - ensure_equals("bad child termination", py.mPy->getStatus().mState, LLProcess::EXITED); - ensure_equals("bad child exit code", py.mPy->getStatus().mData, 0); + UNSCOPED_INFO("script never replied"); + CHECK(childout.contains("\n")); + UNSCOPED_INFO("child didn't ack"); + CHECK(childout.getline() == "ack"); + UNSCOPED_INFO("bad child termination"); + CHECK(py.mPy->getStatus().mState == LLProcess::EXITED); + UNSCOPED_INFO("bad child exit code"); + CHECK(py.mPy->getStatus().mData == 0); } namespace @@ -1372,7 +1391,7 @@ namespace tut // we expect this lambda to contain tut::ensure() calls std::forward(code)(mHistory); } - catch (const failure&) + catch (const ll_test::Failure&) { LL_INFOS() << "event history:" << LL_ENDL; for (const LLSD& item : mHistory) @@ -1395,11 +1414,9 @@ namespace tut return false; } - template<> template<> - void object::test<18>() + TEST_CASE_METHOD(llprocess_data, "llprocess: ReadPipe events carry the data so far and its length", "[llprocess]") { - set_test_name("listen for ReadPipe events"); - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "import sys\n" "sys.stdout.write('abc')\n" "sys.stdout.flush()\n" @@ -1430,7 +1447,8 @@ namespace tut { yield(); } - ensure("couldn't get first line", i < timeout); + UNSCOPED_INFO("couldn't get first line"); + CHECK(i < timeout); // disconnect from listener listener.mConnection.disconnect(); // finish out the run @@ -1440,29 +1458,37 @@ namespace tut [](const EventListener::Listory& history) { auto li(history.begin()), lend(history.end()); - ensure("no events", li != lend); - ensure_equals("history[0]", (*li)["data"].asString(), "abc"); - ensure_equals("history[0] len", (*li)["len"].asInteger(), 3); + UNSCOPED_INFO("no events"); + REQUIRE(li != lend); + UNSCOPED_INFO("history[0]"); + REQUIRE((*li)["data"].asString() == "abc"); + UNSCOPED_INFO("history[0] len"); + REQUIRE((*li)["len"].asInteger() == 3); ++li; - ensure("only 1 event", li != lend); - ensure_equals("history[1]", (*li)["data"].asString(), "abcdef"); - ensure_equals("history[0] len", (*li)["len"].asInteger(), 6); + UNSCOPED_INFO("only 1 event"); + REQUIRE(li != lend); + UNSCOPED_INFO("history[1]"); + REQUIRE((*li)["data"].asString() == "abcdef"); + UNSCOPED_INFO("history[0] len"); + REQUIRE((*li)["len"].asInteger() == 6); ++li; - ensure("only 2 events", li != lend); - ensure_equals("history[2]", (*li)["data"].asString(), "abcdefghi" EOL); - ensure_equals("history[0] len", (*li)["len"].asInteger(), 9 + sizeof(EOL) - 1); + UNSCOPED_INFO("only 2 events"); + REQUIRE(li != lend); + UNSCOPED_INFO("history[2]"); + REQUIRE((*li)["data"].asString() == "abcdefghi" EOL); + UNSCOPED_INFO("history[0] len"); + REQUIRE((*li)["len"].asInteger() == 9 + sizeof(EOL) - 1); ++li; // We DO NOT expect a whole new event for the second line because we // disconnected. - ensure("more than 3 events", li == lend); + UNSCOPED_INFO("more than 3 events"); + REQUIRE(li == lend); }); } - template<> template<> - void object::test<19>() + TEST_CASE_METHOD(llprocess_data, "llprocess: ReadPipe posts one eof event and posts it last", "[llprocess]") { - set_test_name("ReadPipe \"eof\" event"); - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "import time\n" "time.sleep(1.5)\n"); py.mParams.files.add(LLProcess::FileParam()); // stdin @@ -1487,20 +1513,21 @@ namespace tut [](const EventListener::Listory& history) { auto rli(history.rbegin()), rlend(history.rend()); - ensure("no events", rli != rlend); - ensure("last event not \"eof\"", (*rli)["eof"].asBoolean()); + UNSCOPED_INFO("no events"); + REQUIRE(rli != rlend); + UNSCOPED_INFO("last event not \"eof\""); + REQUIRE((*rli)["eof"].asBoolean()); while (++rli != rlend) { - ensure("\"eof\" event not last", ! (*rli)["eof"].asBoolean()); + UNSCOPED_INFO("\"eof\" event not last"); + REQUIRE_FALSE((*rli)["eof"].asBoolean()); } }); } - template<> template<> - void object::test<20>() + TEST_CASE_METHOD(llprocess_data, "llprocess: setLimit caps the data an event carries but not its len", "[llprocess]") { - set_test_name("setLimit()"); - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "import sys\n" "sys.stdout.write(sys.argv[1])\n"); std::string abc("abcdefghijklmnopqrstuvwxyz"); @@ -1513,27 +1540,27 @@ namespace tut EventListener listener(childout.getPump()); // but set limit childout.setLimit(10); - ensure_equals("getLimit() after setlimit(10)", childout.getLimit(), 10); + UNSCOPED_INFO("getLimit() after setlimit(10)"); + CHECK(childout.getLimit() == 10); // okay, pump I/O to pick up output from child waitfor(*py.mPy); listener.checkHistory( [abc](const EventListener::Listory& history) { - ensure("no events", ! history.empty()); + UNSCOPED_INFO("no events"); + REQUIRE_FALSE(history.empty()); // For all we know, that data could have arrived in several different // bursts... probably not, but anyway, only check the last one. - ensure_equals("event[\"len\"]", - history.back()["len"].asInteger(), abc.length()); - ensure_equals("length of setLimit(10) data", - history.back()["data"].asString().length(), 10); + UNSCOPED_INFO("event[\"len\"]"); + REQUIRE(history.back()["len"].asInteger() == abc.length()); + UNSCOPED_INFO("length of setLimit(10) data"); + REQUIRE(history.back()["data"].asString().length() == 10); }); } - template<> template<> - void object::test<21>() + TEST_CASE_METHOD(llprocess_data, "llprocess: ReadPipe peek/contains/find search the buffered data", "[llprocess]") { - set_test_name("peek() ReadPipe data"); - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "import sys\n" "sys.stdout.write(sys.argv[1])\n"); std::string abc("abcdefghijklmnopqrstuvwxyz"); @@ -1545,77 +1572,108 @@ namespace tut // okay, pump I/O to pick up output from child waitfor(*py.mPy); // peek() with substr args - ensure_equals("peek()", childout.peek(), abc); - ensure_equals("peek(23)", childout.peek(23), abc.substr(23)); - ensure_equals("peek(5, 3)", childout.peek(5, 3), abc.substr(5, 3)); - ensure_equals("peek(27, 2)", childout.peek(27, 2), ""); - ensure_equals("peek(23, 5)", childout.peek(23, 5), "xyz"); + UNSCOPED_INFO("peek()"); + CHECK(childout.peek() == abc); + UNSCOPED_INFO("peek(23)"); + CHECK(childout.peek(23) == abc.substr(23)); + UNSCOPED_INFO("peek(5, 3)"); + CHECK(childout.peek(5, 3) == abc.substr(5, 3)); + UNSCOPED_INFO("peek(27, 2)"); + CHECK(childout.peek(27, 2) == ""); + UNSCOPED_INFO("peek(23, 5)"); + CHECK(childout.peek(23, 5) == "xyz"); // contains() -- we don't exercise as thoroughly as find() because the // contains() implementation is trivially (and visibly) based on find() - ensure("contains(\":\")", ! childout.contains(":")); - ensure("contains(':')", ! childout.contains(':')); - ensure("contains(\"d\")", childout.contains("d")); - ensure("contains('d')", childout.contains('d')); - ensure("contains(\"klm\")", childout.contains("klm")); - ensure("contains(\"klx\")", ! childout.contains("klx")); + UNSCOPED_INFO("contains(\":\")"); + CHECK_FALSE(childout.contains(":")); + UNSCOPED_INFO("contains(':')"); + CHECK_FALSE(childout.contains(':')); + UNSCOPED_INFO("contains(\"d\")"); + CHECK(childout.contains("d")); + UNSCOPED_INFO("contains('d')"); + CHECK(childout.contains('d')); + UNSCOPED_INFO("contains(\"klm\")"); + CHECK(childout.contains("klm")); + UNSCOPED_INFO("contains(\"klx\")"); + CHECK_FALSE(childout.contains("klx")); // find() - ensure("find(\":\")", childout.find(":") == LLProcess::ReadPipe::npos); - ensure("find(':')", childout.find(':') == LLProcess::ReadPipe::npos); - ensure_equals("find(\"d\")", childout.find("d"), 3); - ensure_equals("find('d')", childout.find('d'), 3); - ensure_equals("find(\"d\", 3)", childout.find("d", 3), 3); - ensure_equals("find('d', 3)", childout.find('d', 3), 3); - ensure("find(\"d\", 4)", childout.find("d", 4) == LLProcess::ReadPipe::npos); - ensure("find('d', 4)", childout.find('d', 4) == LLProcess::ReadPipe::npos); + UNSCOPED_INFO("find(\":\")"); + REQUIRE(childout.find(":") == LLProcess::ReadPipe::npos); + UNSCOPED_INFO("find(':')"); + REQUIRE(childout.find(':') == LLProcess::ReadPipe::npos); + UNSCOPED_INFO("find(\"d\")"); + CHECK(childout.find("d") == 3); + UNSCOPED_INFO("find('d')"); + CHECK(childout.find('d') == 3); + UNSCOPED_INFO("find(\"d\", 3)"); + CHECK(childout.find("d", 3) == 3); + UNSCOPED_INFO("find('d', 3)"); + CHECK(childout.find('d', 3) == 3); + UNSCOPED_INFO("find(\"d\", 4)"); + REQUIRE(childout.find("d", 4) == LLProcess::ReadPipe::npos); + UNSCOPED_INFO("find('d', 4)"); + REQUIRE(childout.find('d', 4) == LLProcess::ReadPipe::npos); // The case of offset == end and offset > end are different. In the // first case, we can form a valid (albeit empty) iterator range and // search that. In the second, guard logic in the implementation must // realize we can't form a valid iterator range. - ensure("find(\"d\", 26)", childout.find("d", 26) == LLProcess::ReadPipe::npos); - ensure("find('d', 26)", childout.find('d', 26) == LLProcess::ReadPipe::npos); - ensure("find(\"d\", 27)", childout.find("d", 27) == LLProcess::ReadPipe::npos); - ensure("find('d', 27)", childout.find('d', 27) == LLProcess::ReadPipe::npos); - ensure_equals("find(\"ghi\")", childout.find("ghi"), 6); - ensure_equals("find(\"ghi\", 6)", childout.find("ghi"), 6); - ensure("find(\"ghi\", 7)", childout.find("ghi", 7) == LLProcess::ReadPipe::npos); - ensure("find(\"ghi\", 26)", childout.find("ghi", 26) == LLProcess::ReadPipe::npos); - ensure("find(\"ghi\", 27)", childout.find("ghi", 27) == LLProcess::ReadPipe::npos); + UNSCOPED_INFO("find(\"d\", 26)"); + REQUIRE(childout.find("d", 26) == LLProcess::ReadPipe::npos); + UNSCOPED_INFO("find('d', 26)"); + REQUIRE(childout.find('d', 26) == LLProcess::ReadPipe::npos); + UNSCOPED_INFO("find(\"d\", 27)"); + REQUIRE(childout.find("d", 27) == LLProcess::ReadPipe::npos); + UNSCOPED_INFO("find('d', 27)"); + REQUIRE(childout.find('d', 27) == LLProcess::ReadPipe::npos); + UNSCOPED_INFO("find(\"ghi\")"); + CHECK(childout.find("ghi") == 6); + UNSCOPED_INFO("find(\"ghi\", 6)"); + CHECK(childout.find("ghi") == 6); + UNSCOPED_INFO("find(\"ghi\", 7)"); + REQUIRE(childout.find("ghi", 7) == LLProcess::ReadPipe::npos); + UNSCOPED_INFO("find(\"ghi\", 26)"); + REQUIRE(childout.find("ghi", 26) == LLProcess::ReadPipe::npos); + UNSCOPED_INFO("find(\"ghi\", 27)"); + REQUIRE(childout.find("ghi", 27) == LLProcess::ReadPipe::npos); } - template<> template<> - void object::test<22>() + TEST_CASE_METHOD(llprocess_data, "llprocess: postend reports a launch that failed validation", "[llprocess]") { - set_test_name("bad postend"); std::string pumpname("postend"); EventListener listener(LLEventPumps::instance().obtain(pumpname)); LLProcess::Params params; - params.desc = get_test_name(); + params.desc = ll_test::currentTestName(); params.postend = pumpname; LLProcessPtr child = LLProcess::create(params); - ensure("shouldn't have launched", ! child); + UNSCOPED_INFO("shouldn't have launched"); + REQUIRE_FALSE(child); listener.checkHistory( [¶ms](const EventListener::Listory& history) { - ensure_equals("number of postend events", history.size(), 1); + UNSCOPED_INFO("number of postend events"); + REQUIRE(history.size() == 1); LLSD postend(history.front()); - ensure("has id", ! postend.has("id")); - ensure_equals("desc", postend["desc"].asString(), std::string(params.desc)); - ensure_equals("state", postend["state"].asInteger(), LLProcess::UNSTARTED); - ensure("has data", ! postend.has("data")); + UNSCOPED_INFO("has id"); + REQUIRE_FALSE(postend.has("id")); + UNSCOPED_INFO("desc"); + REQUIRE(postend["desc"].asString() == std::string(params.desc)); + UNSCOPED_INFO("state"); + REQUIRE(postend["state"].asInteger() == LLProcess::UNSTARTED); + UNSCOPED_INFO("has data"); + REQUIRE_FALSE(postend.has("data")); std::string error(postend["string"]); // All we get from canned parameter validation is a bool, so the // "validation failed" message we ourselves generate can't mention // "executable" by name. Just check that it's nonempty. //ensure_contains("error", error, "executable"); - ensure("string", ! error.empty()); + UNSCOPED_INFO("string"); + REQUIRE_FALSE(error.empty()); }); } - template<> template<> - void object::test<23>() + TEST_CASE_METHOD(llprocess_data, "llprocess: postend reports the child's id and exit code", "[llprocess]") { - set_test_name("good postend"); - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "import sys\n" "sys.exit(35)\n"); std::string pumpname("postend"); @@ -1633,16 +1691,24 @@ namespace tut listener.checkHistory( [i, timeout, childid](const EventListener::Listory& history) { - ensure("no postend event", i < timeout); - ensure_equals("number of postend events", history.size(), 1); + UNSCOPED_INFO("no postend event"); + REQUIRE(i < timeout); + UNSCOPED_INFO("number of postend events"); + REQUIRE(history.size() == 1); LLSD postend(history.front()); - ensure_equals("id", postend["id"].asInteger(), childid); - ensure("desc empty", ! postend["desc"].asString().empty()); - ensure_equals("state", postend["state"].asInteger(), LLProcess::EXITED); - ensure_equals("data", postend["data"].asInteger(), 35); + UNSCOPED_INFO("id"); + REQUIRE(postend["id"].asInteger() == childid); + UNSCOPED_INFO("desc empty"); + REQUIRE_FALSE(postend["desc"].asString().empty()); + UNSCOPED_INFO("state"); + REQUIRE(postend["state"].asInteger() == LLProcess::EXITED); + UNSCOPED_INFO("data"); + REQUIRE(postend["data"].asInteger() == 35); std::string str(postend["string"]); - ensure_contains("string", str, "exited"); - ensure_contains("string", str, "35"); + UNSCOPED_INFO("string"); + REQUIRE_THAT(str, Catch::Matchers::ContainsSubstring("exited")); + UNSCOPED_INFO("string"); + REQUIRE_THAT(str, Catch::Matchers::ContainsSubstring("35")); }); } @@ -1666,7 +1732,8 @@ namespace tut bool postend(const LLSD&) { mTriggered = true; - ensure_equals("postend listener", mReadPipe.read(mReadPipe.size()), mExpect); + UNSCOPED_INFO("postend listener"); + REQUIRE(mReadPipe.read(mReadPipe.size()) == mExpect); return false; } @@ -1677,11 +1744,9 @@ namespace tut }; } // anonymous namespace - template<> template<> - void object::test<24>() + TEST_CASE_METHOD(llprocess_data, "llprocess: all output is readable when postend fires", "[llprocess]") { - set_test_name("all data visible at postend"); - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "import sys\n" // note, no '\n' in written data "sys.stdout.write('partial line')\n"); @@ -1694,14 +1759,13 @@ namespace tut pumpname, "partial line"); waitfor(*py.mPy); - ensure("postend never triggered", listener.mTriggered); + UNSCOPED_INFO("postend never triggered"); + CHECK(listener.mTriggered); } - template<> template<> - void object::test<25>() + TEST_CASE_METHOD(llprocess_data, "llprocess: a megabyte written to stdin reaches the child whole", "[llprocess]") { - set_test_name("large stdin write"); - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "import sys\n" "expected = int(sys.argv[1])\n" "data = sys.stdin.buffer.read(expected)\n" @@ -1728,19 +1792,22 @@ namespace tut { yield(); } - ensure("large stdin write timed out", i < timeout); - ensure("child never replied", childout.contains("\n")); - ensure_equals("large stdin ack", childout.getline(), "ok"); + UNSCOPED_INFO("large stdin write timed out"); + CHECK(i < timeout); + UNSCOPED_INFO("child never replied"); + CHECK(childout.contains("\n")); + UNSCOPED_INFO("large stdin ack"); + CHECK(childout.getline() == "ok"); waitfor(*py.mPy); - ensure_equals("bad child termination", py.mPy->getStatus().mState, LLProcess::EXITED); - ensure_equals("bad child exit code", py.mPy->getStatus().mData, 0); + UNSCOPED_INFO("bad child termination"); + CHECK(py.mPy->getStatus().mState == LLProcess::EXITED); + UNSCOPED_INFO("bad child exit code"); + CHECK(py.mPy->getStatus().mData == 0); } - template<> template<> - void object::test<26>() + TEST_CASE_METHOD(llprocess_data, "llprocess: pipes on all three standard streams work at once", "[llprocess]") { - set_test_name("all three pipes active"); - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "import sys\n" "sys.stdout.write('stdout message\\n')\n" "sys.stderr.write('stderr message\\n')\n" @@ -1762,23 +1829,26 @@ namespace tut { yield(); } - ensure("initial output timeout", i < timeout); + UNSCOPED_INFO("initial output timeout"); + CHECK(i < timeout); - ensure_equals("stdout message", childout.getline(), "stdout message"); - ensure_equals("stderr message", childerr.getline(), "stderr message"); + UNSCOPED_INFO("stdout message"); + CHECK(childout.getline() == "stdout message"); + UNSCOPED_INFO("stderr message"); + CHECK(childerr.getline() == "stderr message"); py.mPy->getWritePipe().get_ostream() << "test input" << std::endl; waitfor(*py.mPy); - ensure("script never replied", childout.contains("\n")); - ensure_equals("echo response", childout.getline(), "received: test input"); + UNSCOPED_INFO("script never replied"); + CHECK(childout.contains("\n")); + UNSCOPED_INFO("echo response"); + CHECK(childout.getline() == "received: test input"); } - template<> template<> - void object::test<27>() + TEST_CASE_METHOD(llprocess_data, "llprocess: status goes from RUNNING to EXITED", "[llprocess]") { - set_test_name("process state transitions"); - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "import time\n" "time.sleep(1)\n"); @@ -1786,21 +1856,23 @@ namespace tut // Immediately after launch LLProcess::Status status = py.mPy->getStatus(); - ensure_equals("post-launch state", status.mState, LLProcess::RUNNING); - ensure("process is running", py.mPy->isRunning()); + UNSCOPED_INFO("post-launch state"); + CHECK(status.mState == LLProcess::RUNNING); + UNSCOPED_INFO("process is running"); + CHECK(py.mPy->isRunning()); // After completion waitfor(*py.mPy); status = py.mPy->getStatus(); - ensure_equals("post-completion state", status.mState, LLProcess::EXITED); - ensure("process is not running", !py.mPy->isRunning()); + UNSCOPED_INFO("post-completion state"); + CHECK(status.mState == LLProcess::EXITED); + UNSCOPED_INFO("process is not running"); + CHECK_FALSE(py.mPy->isRunning()); } - template<> template<> - void object::test<28>() + TEST_CASE_METHOD(llprocess_data, "llprocess: setLimit holds for every event under rapid output", "[llprocess]") { - set_test_name("ReadPipe limit with rapid data"); - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "import sys\n" "for i in range(100):\n" " sys.stdout.write('line %d\\n' % i)\n" @@ -1823,17 +1895,17 @@ namespace tut for (const LLSD& event : history) { const std::string data = event["data"].asString(); - ensure("event data within limit", data.length() <= 50); + UNSCOPED_INFO("event data within limit"); + REQUIRE(data.length() <= 50); saw_data = saw_data || !data.empty(); } - ensure("saw at least one data event", saw_data); + UNSCOPED_INFO("saw at least one data event"); + REQUIRE(saw_data); }); } - template<> template<> - void object::test<29>() + TEST_CASE_METHOD(llprocess_data, "llprocess: a nonexistent executable fails with an UNSTARTED postend", "[llprocess]") { - set_test_name("nonexistent executable"); LLProcess::Params params; params.executable = "/path/to/nonexistent/executable"; std::string pumpname("postend_invalid"); @@ -1842,24 +1914,25 @@ namespace tut EventListener listener(LLEventPumps::instance().obtain(pumpname)); LLProcessPtr child = LLProcess::create(params); - ensure("should not create invalid process", !child); + UNSCOPED_INFO("should not create invalid process"); + REQUIRE_FALSE(child); listener.checkHistory( [](const EventListener::Listory& history) { - ensure_equals("got failure event", history.size(), 1); + UNSCOPED_INFO("got failure event"); + REQUIRE(history.size() == 1); LLSD event = history.front(); - ensure_equals("state is UNSTARTED", - event["state"].asInteger(), LLProcess::UNSTARTED); - ensure("has error string", !event["string"].asString().empty()); + UNSCOPED_INFO("state is UNSTARTED"); + REQUIRE(event["state"].asInteger() == LLProcess::UNSTARTED); + UNSCOPED_INFO("has error string"); + REQUIRE_FALSE(event["string"].asString().empty()); }); } - template<> template<> - void object::test<30>() + TEST_CASE_METHOD(llprocess_data, "llprocess: a running child has a valid id and handle", "[llprocess]") { - set_test_name("process ID and handle validity"); - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "import time\n" "time.sleep(1)\n"); py.launch(); @@ -1867,29 +1940,32 @@ namespace tut LLProcess::id pid = py.mPy->getProcessID(); LLProcess::handle handle = py.mPy->getProcessHandle(); - ensure("PID is valid", pid != 0); - ensure("handle is valid", handle != 0); + UNSCOPED_INFO("PID is valid"); + CHECK(pid != 0); + UNSCOPED_INFO("handle is valid"); + CHECK(handle != 0); #if LL_WINDOWS // On Windows, verify handle is a valid process handle DWORD exitCode; - ensure("GetExitCodeProcess succeeds", - GetExitCodeProcess(handle, &exitCode) != 0); - ensure_equals("process still running", exitCode, STILL_ACTIVE); + UNSCOPED_INFO("GetExitCodeProcess succeeds"); + REQUIRE((GetExitCodeProcess(handle, &exitCode) != 0)); + UNSCOPED_INFO("process still running"); + REQUIRE(exitCode == STILL_ACTIVE); #else // On POSIX, PID and handle should be the same - ensure_equals("PID equals handle", pid, handle); - ensure("process still running", py.mPy->isRunning()); + UNSCOPED_INFO("PID equals handle"); + CHECK(pid == handle); + UNSCOPED_INFO("process still running"); + CHECK(py.mPy->isRunning()); #endif waitfor(*py.mPy); } - template<> template<> - void object::test<31>() + TEST_CASE_METHOD(llprocess_data, "llprocess: ReadPipe find and peek handle the ends of the data", "[llprocess]") { - set_test_name("ReadPipe search at boundaries"); - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "import sys\n" "sys.stdout.write('abcdefghijklmnopqrstuvwxyz')\n"); py.mParams.files.add(LLProcess::FileParam()); // stdin @@ -1900,39 +1976,42 @@ namespace tut waitfor(*py.mPy); // Test find at exact end - ensure("find at end succeeds", - childout.find("xyz", 23) != LLProcess::ReadPipe::npos); - ensure("find past end returns npos", - childout.find("a", 30) == LLProcess::ReadPipe::npos); + UNSCOPED_INFO("find at end succeeds"); + REQUIRE(childout.find("xyz", 23) != LLProcess::ReadPipe::npos); + UNSCOPED_INFO("find past end returns npos"); + REQUIRE(childout.find("a", 30) == LLProcess::ReadPipe::npos); // Test empty string search - ensure("contains empty string", childout.contains("")); + UNSCOPED_INFO("contains empty string"); + CHECK(childout.contains("")); // Test single char at boundaries - ensure_equals("find 'a' at 0", childout.find('a', 0), 0); - ensure_equals("find 'z' at end", childout.find('z'), 25); + UNSCOPED_INFO("find 'a' at 0"); + CHECK(childout.find('a', 0) == 0); + UNSCOPED_INFO("find 'z' at end"); + CHECK(childout.find('z') == 25); // Test peek at boundaries - ensure_equals("peek at exact size", childout.peek(26), ""); - ensure_equals("peek past end", childout.peek(30, 10), ""); + UNSCOPED_INFO("peek at exact size"); + CHECK(childout.peek(26) == ""); + UNSCOPED_INFO("peek past end"); + CHECK(childout.peek(30, 10) == ""); } - template<> template<> - void object::test<32>() + TEST_CASE_METHOD(llprocess_data, "llprocess: ten short-lived children in a row each exit cleanly", "[llprocess]") { - set_test_name("rapid process lifecycle"); const int ITERATIONS = 10; for (int i = 0; i < ITERATIONS; ++i) { - PythonProcessLauncher py(STRINGIZE(get_test_name() << " " << i), + PythonProcessLauncher py(STRINGIZE(ll_test::currentTestName() << " " << i), "import sys\n" "sys.exit(0)\n"); py.run(); - ensure_equals("quick exit status", - py.mPy->getStatus().mState, LLProcess::EXITED); - ensure_equals("quick exit code", - py.mPy->getStatus().mData, 0); + UNSCOPED_INFO("quick exit status"); + REQUIRE(py.mPy->getStatus().mState == LLProcess::EXITED); + UNSCOPED_INFO("quick exit code"); + REQUIRE(py.mPy->getStatus().mData == 0); // Let the process object destroy } @@ -1940,11 +2019,9 @@ namespace tut yield(0); } - template<> template<> - void object::test<33>() + TEST_CASE_METHOD(llprocess_data, "llprocess: a silent child posts one empty eof event on each pipe", "[llprocess]") { - set_test_name("process with no output"); - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "import time\n" "time.sleep(1)\n" "# Produce no output\n"); @@ -1972,16 +2049,22 @@ namespace tut LLEventPumps::instance().obtain("mainloop").post(LLSD()); } - ensure_equals("stdout size", childout.size(), 0); - ensure_equals("stderr size", childerr.size(), 0); - ensure_equals("process exited", py.mPy->getStatus().mState, LLProcess::EXITED); + UNSCOPED_INFO("stdout size"); + REQUIRE(childout.size() == 0); + UNSCOPED_INFO("stderr size"); + REQUIRE(childerr.size() == 0); + UNSCOPED_INFO("process exited"); + CHECK(py.mPy->getStatus().mState == LLProcess::EXITED); auto check_eof = [](const EventListener::Listory& history, const std::string& which) { - ensure_equals(STRINGIZE(which << " events"), history.size(), 1); + UNSCOPED_INFO((STRINGIZE(which << " events"))); + REQUIRE(history.size() == 1); const LLSD& event = history.front(); - ensure(STRINGIZE(which << " eof event"), event["eof"].asBoolean()); - ensure_equals(STRINGIZE(which << " len"), event["len"].asInteger(), 0); + UNSCOPED_INFO((STRINGIZE(which << " eof event"))); + REQUIRE(event["eof"].asBoolean()); + UNSCOPED_INFO((STRINGIZE(which << " len"))); + REQUIRE(event["len"].asInteger() == 0); }; outListener.checkHistory( @@ -1997,23 +2080,22 @@ namespace tut }); } - template<> template<> - void object::test<34>() + TEST_CASE_METHOD(llprocess_data, "llprocess: tick() completes quickly after kill()", "[llprocess]") { - set_test_name("tick() completes quickly after kill()"); // Regression test: tick() must not block after kill() is called. // The old Windows code called WaitForSingleObject(..., 100) in tick(), // causing a 100 ms stall on the main thread. This test ensures that a // mainloop tick completes well under that threshold even when the child // process has been killed and may still be exiting. - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "import time\n" "time.sleep(120)\n"); py.launch(); // Wait for the process to start up yield(); - ensure("process started", py.mPy->isRunning()); + UNSCOPED_INFO("process started"); + CHECK(py.mPy->isRunning()); // Send the kill signal py.mPy->kill(); @@ -2029,30 +2111,29 @@ namespace tut auto elapsed_ms = std::chrono::duration_cast( std::chrono::steady_clock::now() - start).count(); - ensure(STRINGIZE("tick() took " << elapsed_ms << " ms, expected < 75 ms"), - elapsed_ms < 75); + UNSCOPED_INFO((STRINGIZE("tick() took " << elapsed_ms << " ms, expected < 75 ms"))); + CHECK(elapsed_ms < 75); // Let the process fully exit so cleanup is orderly waitfor(*py.mPy); } - template<> template<> - void object::test<35>() + TEST_CASE_METHOD(llprocess_data, "llprocess: LLProcess destructor completes quickly after kill()", "[llprocess]") { - set_test_name("LLProcess destructor completes quickly after kill()"); // Regression test: after an explicit kill() call the destructor must // not perform a blocking wait. The old Windows code called // WaitForSingleObject(..., 100) in the destructor, causing a 100 ms // stall on the main thread. With mKillCalled set to true, the // destructor skips the termination/wait block entirely. - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "import time\n" "time.sleep(120)\n"); py.launch(); // Wait for the process to start up yield(); - ensure("process started", py.mPy->isRunning()); + UNSCOPED_INFO("process started"); + CHECK(py.mPy->isRunning()); // Kill the process (sets mKillCalled = true) py.mPy->kill(); @@ -2063,43 +2144,37 @@ namespace tut auto elapsed_ms = std::chrono::duration_cast( std::chrono::steady_clock::now() - start).count(); - ensure(STRINGIZE("destructor took " << elapsed_ms << " ms, expected < 75 ms"), - elapsed_ms < 75); + UNSCOPED_INFO((STRINGIZE("destructor took " << elapsed_ms << " ms, expected < 75 ms"))); + CHECK(elapsed_ms < 75); } - template<> template<> - void object::test<36>() + TEST_CASE_METHOD(llprocess_data, "llprocess: autokill ends a child when its parent exits", "[llprocess]") { - set_test_name("autokill ensures child termination on parent exit"); #if !LL_WINDOWS - skip("Windows-specific test"); + SKIP("Windows-specific test"); #else - verify_autokill_on_helper_exit(get_test_name(), 1); + verify_autokill_on_helper_exit(ll_test::currentTestName(), 1); #endif } - template<> template<> - void object::test<37>() + TEST_CASE_METHOD(llprocess_data, "llprocess: autokill ends every child when their parent exits", "[llprocess]") { - set_test_name("multiple processes with autokill"); #if !LL_WINDOWS - skip("Windows-specific test"); + SKIP("Windows-specific test"); #else - verify_autokill_on_helper_exit(get_test_name(), 2); + verify_autokill_on_helper_exit(ll_test::currentTestName(), 2); #endif } - template<> template<> - void object::test<38>() + TEST_CASE_METHOD(llprocess_data, "llprocess: attached=true with autokill=false still kills on destruction", "[llprocess]") { - set_test_name("attached=true, autokill=false"); // 'attached' governs destruction of the LLProcess object, 'autokill' // governs termination of the parent, so this combination must still // kill the child on destruction. NamedTempFile out("out", "not started"); LLProcess::handle phandle(0); { - PythonProcessLauncher py(get_test_name(), + PythonProcessLauncher py(ll_test::currentTestName(), "from __future__ import with_statement\n" "import sys, time\n" "with open(sys.argv[1], 'w') as f:\n" @@ -2121,13 +2196,15 @@ namespace tut if (readfile(out.getPath(), "from kill() script") == "ok") break; } - ensure("script never started", i < timeout); + UNSCOPED_INFO("script never started"); + CHECK(i < timeout); // Script has performed its first write and should now be sleeping. // Destroy the LLProcess, which should kill the child. } waitfor(phandle, "kill() script"); // Had the destructor detached instead, the script would have woken on // its own and overwritten the file with 'bad'. - ensure_equals(get_test_name() + " script output", readfile(out.getPath()), "ok"); + UNSCOPED_INFO(ll_test::currentTestName() + " script output"); + CHECK(readfile(out.getPath()) == "ok"); } } // namespace tut diff --git a/indra/llcommon/tests/llprocessor_test.cpp b/indra/llcommon/tests/llprocessor_test.cpp index a2467d9205..dbc6b73e11 100644 --- a/indra/llcommon/tests/llprocessor_test.cpp +++ b/indra/llcommon/tests/llprocessor_test.cpp @@ -25,26 +25,15 @@ */ #include "linden_common.h" -#include "../test/lltut.h" +#include #include "../llprocessor.h" -namespace tut +namespace { - struct processor + TEST_CASE("LLProcessor: LLProcessorInfo names the CPU and gives a sane frequency", "[LLProcessor]") { - }; - - typedef test_group processor_t; - typedef processor_t::object processor_object_t; - tut::processor_t tut_processor("LLProcessor"); - - template<> template<> - void processor_object_t::test<1>() - { - set_test_name("LLProcessorInfo regression test"); - LLProcessorInfo pi; F64 freq = pi.getCPUFrequency(); //bool sse = pi.hasSSE(); @@ -54,8 +43,14 @@ namespace tut std::string brand = pi.getCPUBrandName(); //std::string steam = pi.getCPUFeatureDescription(); - ensure_not_equals("Unknown Brand name", brand, "Unknown"); - ensure_not_equals("Unknown Family name", family, "Unknown"); - ensure("Reasonable CPU Frequency > 100 && < 10000", freq > 100 && freq < 10000); + UNSCOPED_INFO("Unknown Brand name"); + CHECK(brand != "Unknown"); + UNSCOPED_INFO("Unknown Family name"); + CHECK(family != "Unknown"); + { + INFO("Reasonable CPU Frequency > 100 && < 10000"); + CHECK(freq > 100); + CHECK(freq < 10000); + } } } diff --git a/indra/llcommon/tests/llprocinfo_test.cpp b/indra/llcommon/tests/llprocinfo_test.cpp index 6a151048c2..b91a14231f 100644 --- a/indra/llcommon/tests/llprocinfo_test.cpp +++ b/indra/llcommon/tests/llprocinfo_test.cpp @@ -28,14 +28,14 @@ #include "../llprocinfo.h" -#include "../test/lltut.h" +#include #include "../lltimer.h" static const LLProcInfo::time_type bad_user(289375U), bad_system(275U); -namespace tut +namespace { struct procinfo_test @@ -45,35 +45,26 @@ struct procinfo_test } }; -typedef test_group procinfo_group_t; -typedef procinfo_group_t::object procinfo_object_t; -tut::procinfo_group_t procinfo_instance("LLProcInfo"); - - // Basic invocation works -template<> template<> -void procinfo_object_t::test<1>() +TEST_CASE_METHOD(procinfo_test, "LLProcInfo: getCPUUsage() writes both its arguments", "[LLProcInfo]") { LLProcInfo::time_type user(bad_user), system(bad_system); - set_test_name("getCPUUsage() basic function"); - LLProcInfo::getCPUUsage(user, system); - ensure_not_equals("getCPUUsage() writes to its user argument", user, bad_user); - ensure_not_equals("getCPUUsage() writes to its system argument", system, bad_system); + UNSCOPED_INFO("getCPUUsage() writes to its user argument"); + CHECK(user != bad_user); + UNSCOPED_INFO("getCPUUsage() writes to its system argument"); + CHECK(system != bad_system); } // Time increases -template<> template<> -void procinfo_object_t::test<2>() +TEST_CASE_METHOD(procinfo_test, "LLProcInfo: getCPUUsage() never goes down over time", "[LLProcInfo]") { LLProcInfo::time_type user(bad_user), system(bad_system); LLProcInfo::time_type user2(bad_user), system2(bad_system); - set_test_name("getCPUUsage() increases over time"); - LLProcInfo::getCPUUsage(user, system); for (int i(0); i < 100000; ++i) @@ -83,9 +74,11 @@ void procinfo_object_t::test<2>() LLProcInfo::getCPUUsage(user2, system2); - ensure_equals("getCPUUsage() user value doesn't decrease over time", user2 >= user, true); - ensure_equals("getCPUUsage() system value doesn't decrease over time", system2 >= system, true); + UNSCOPED_INFO("getCPUUsage() user value doesn't decrease over time"); + CHECK((user2 >= user) == true); + UNSCOPED_INFO("getCPUUsage() system value doesn't decrease over time"); + CHECK((system2 >= system) == true); } -} // end namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/llptrto_test.cpp b/indra/llcommon/tests/llptrto_test.cpp index 0f81f1688d..35c156e654 100644 --- a/indra/llcommon/tests/llptrto_test.cpp +++ b/indra/llcommon/tests/llptrto_test.cpp @@ -37,7 +37,7 @@ // other Linden headers #include "../llmemory.h" -#include "../test/lltut.h" +#include namespace { @@ -103,23 +103,15 @@ namespace /***************************************************************************** * tut test group *****************************************************************************/ -namespace tut +namespace { - struct llptrto_data - { - }; - typedef test_group llptrto_group; - typedef llptrto_group::object llptrto_object; - llptrto_group llptrtogrp("LLPtrTo"); - // The checks above are static_asserts, made when this file compiles. Here, // what LLPtrTo names for a refcounted class holds a reference, as an // LLPointer does. - template<> template<> - void llptrto_object::test<1>() + TEST_CASE("LLPtrTo: LLPtrTo of a refcounted class holds a reference", "[LLPtrTo]") { - set_test_name("LLPtrTo of a refcounted class holds a reference"); LLPtrTo::type ptr(new RCFoo); - ensure_equals("one reference", ptr->getNumRefs(), 1); + UNSCOPED_INFO("one reference"); + CHECK(ptr->getNumRefs() == 1); } } diff --git a/indra/llcommon/tests/llrand_test.cpp b/indra/llcommon/tests/llrand_test.cpp index fca8275c6c..9137dfa6c2 100644 --- a/indra/llcommon/tests/llrand_test.cpp +++ b/indra/llcommon/tests/llrand_test.cpp @@ -26,7 +26,7 @@ */ #include "linden_common.h" -#include "../test/lltut.h" +#include #include "../test/alrandomreseed.h" #include "../llrand.h" @@ -50,9 +50,11 @@ static 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)); + UNSCOPED_INFO(failmsg); + REQUIRE(value >= low); failmsg = stringize(name, " < ", high, " (", value, ')'); - tut::ensure(failmsg, (value < high)); + UNSCOPED_INFO(failmsg); + REQUIRE(value < high); } namespace @@ -85,7 +87,8 @@ namespace stat += off * off / expected; } const F64 limit = chi_square_limit(counts.size() - 1); - tut::ensure(stringize(name, ": chi-square ", stat, " under ", limit), stat < limit); + UNSCOPED_INFO(stringize(name, ": chi-square ", stat, " under ", limit)); + REQUIRE(stat < limit); } // That draws uniform over [0, 1) have its mean, 1/2, and variance, 1/12. @@ -98,8 +101,10 @@ namespace // fourth central moment, 1/80, less its square, over n. const F64 mean_bound = ONE_IN_A_MILLION_Z * std::sqrt(1.0 / 12.0 / n); const F64 variance_bound = ONE_IN_A_MILLION_Z * std::sqrt((1.0 / 80.0 - 1.0 / 144.0) / n); - tut::ensure(stringize(name, ": mean ", mean), std::fabs(mean - 0.5) < mean_bound); - tut::ensure(stringize(name, ": variance ", variance), std::fabs(variance - 1.0 / 12.0) < variance_bound); + UNSCOPED_INFO(stringize(name, ": mean ", mean)); + REQUIRE(std::fabs(mean - 0.5) < mean_bound); + UNSCOPED_INFO(stringize(name, ": variance ", variance)); + REQUIRE(std::fabs(variance - 1.0 / 12.0) < variance_bound); } // Every kind of draw, interleaved, so a sequence differs if any of them @@ -130,7 +135,7 @@ namespace } } -namespace tut +namespace { struct random { @@ -138,12 +143,7 @@ namespace tut ~random() { ll_test::reseedRandomFromEntropy(); } }; - typedef test_group random_t; - typedef random_t::object random_object_t; - tut::random_t tut_random("LLSeedRand"); - - template<> template<> - void random_object_t::test<1>() + TEST_CASE_METHOD(random, "LLSeedRand: frand() draws at least 0 and under 1", "[LLSeedRand]") { for(S32 ii = 0; ii < 100000; ++ii) { @@ -151,8 +151,7 @@ namespace tut } } - template<> template<> - void random_object_t::test<2>() + TEST_CASE_METHOD(random, "LLSeedRand: drand() draws at least 0 and under 1", "[LLSeedRand]") { for(S32 ii = 0; ii < 100000; ++ii) { @@ -160,8 +159,7 @@ namespace tut } } - template<> template<> - void random_object_t::test<3>() + TEST_CASE_METHOD(random, "LLSeedRand: frand(2.0f) draws at least 0 and under 2", "[LLSeedRand]") { for(S32 ii = 0; ii < 100000; ++ii) { @@ -169,8 +167,7 @@ namespace tut } } - template<> template<> - void random_object_t::test<4>() + TEST_CASE_METHOD(random, "LLSeedRand: frand(-7.0) draws at most 0 and over -7", "[LLSeedRand]") { for(S32 ii = 0; ii < 100000; ++ii) { @@ -180,8 +177,7 @@ namespace tut } } - template<> template<> - void random_object_t::test<5>() + TEST_CASE_METHOD(random, "LLSeedRand: drand(-2.0) draws at most 0 and over -2", "[LLSeedRand]") { for(S32 ii = 0; ii < 100000; ++ii) { @@ -189,8 +185,7 @@ namespace tut } } - template<> template<> - void random_object_t::test<6>() + TEST_CASE_METHOD(random, "LLSeedRand: rand(100) draws at least 0 and under 100", "[LLSeedRand]") { for(S32 ii = 0; ii < 100000; ++ii) { @@ -198,8 +193,7 @@ namespace tut } } - template<> template<> - void random_object_t::test<7>() + TEST_CASE_METHOD(random, "LLSeedRand: rand(-127) draws at most 0 and over -127", "[LLSeedRand]") { for(S32 ii = 0; ii < 100000; ++ii) { @@ -210,22 +204,26 @@ namespace tut // Tests 8 to 15 hold for any generator under llrand. Tests 16 to 18 are // what the generator itself draws, and change when it does. - template<> template<> - void random_object_t::test<8>() + TEST_CASE_METHOD(random, "LLSeedRand: extents of zero or one give zero and the widest extents stay in range", "[LLSeedRand]") { - set_test_name("an extent of zero or one gives zero, and the widest extents hold their draws"); ll_rand_seed(8); for (S32 ii = 0; ii < 10000; ++ii) { - ensure_equals("rand(0)", ll_rand(0), 0); - ensure_equals("rand(1)", ll_rand(1), 0); - ensure_equals("rand(-1)", ll_rand(-1), 0); - ensure_equals("frand(0)", ll_frand(0.f), 0.f); - ensure_equals("drand(0)", ll_drand(0.0), 0.0); + UNSCOPED_INFO("rand(0)"); + REQUIRE(ll_rand(0) == 0); + UNSCOPED_INFO("rand(1)"); + REQUIRE(ll_rand(1) == 0); + UNSCOPED_INFO("rand(-1)"); + REQUIRE(ll_rand(-1) == 0); + UNSCOPED_INFO("frand(0)"); + REQUIRE(ll_frand(0.f) == 0.f); + UNSCOPED_INFO("drand(0)"); + REQUIRE(ll_drand(0.0) == 0.0); ensure_in_range("rand(INT_MAX)", ll_rand(INT_MAX), 0, INT_MAX); const S32 lowest = ll_rand(INT_MIN); - ensure(stringize("rand(INT_MIN) in (INT_MIN, 0] (", lowest, ')'), lowest > INT_MIN && lowest <= 0); + UNSCOPED_INFO(stringize("rand(INT_MIN) in (INT_MIN, 0] (", lowest, ')')); + REQUIRE((lowest > INT_MIN && lowest <= 0)); ensure_in_range("frand(FLT_MAX)", ll_frand(std::numeric_limits::max()), 0.f, std::numeric_limits::max()); ensure_in_range("frand(FLT_MIN)", ll_frand(std::numeric_limits::min()), 0.f, std::numeric_limits::min()); @@ -234,30 +232,29 @@ namespace tut } } - template<> template<> - void random_object_t::test<9>() + TEST_CASE_METHOD(random, "LLSeedRand: a seed repeats its draws on any thread and another seed differs", "[LLSeedRand]") { - set_test_name("a seed draws the same again, on any thread, and another seed draws otherwise"); ll_rand_seed(1234); const std::vector first = draw_each(16); ll_rand_seed(1234); - ensure("the same seed draws the same", draw_each(16) == first); + UNSCOPED_INFO("the same seed draws the same"); + CHECK(draw_each(16) == first); std::vector elsewhere; std::thread([&elsewhere] { ll_rand_seed(1234); elsewhere = draw_each(16); }).join(); - ensure("on another thread too", elsewhere == first); + UNSCOPED_INFO("on another thread too"); + CHECK(elsewhere == first); ll_rand_seed(1235); - ensure("another seed draws otherwise", draw_each(16) != first); + UNSCOPED_INFO("another seed draws otherwise"); + CHECK(draw_each(16) != first); } - template<> template<> - void random_object_t::test<10>() + TEST_CASE_METHOD(random, "LLSeedRand: rand(n) draws each of its n values as often as the others", "[LLSeedRand]") { - set_test_name("rand(n) draws each of its n values as often as the others"); ll_rand_seed(10); const U64 draws = 256000; for (const S32 n : { 2, 3, 7, 100, 256 }) @@ -278,10 +275,8 @@ namespace tut ensure_uniform("rand(-7)", counts, draws); } - template<> template<> - void random_object_t::test<11>() + TEST_CASE_METHOD(random, "LLSeedRand: frand() and drand() spread evenly over the unit interval", "[LLSeedRand]") { - set_test_name("frand() and drand() spread evenly over [0, 1)"); ll_rand_seed(11); const U64 draws = 1 << 20; std::vector frand_bins(64); @@ -306,10 +301,8 @@ namespace tut ensure_unit_moments("drand", drand_sum, drand_squares, draws); } - template<> template<> - void random_object_t::test<12>() + TEST_CASE_METHOD(random, "LLSeedRand: rand_u32() draws each byte evenly and sets each bit half the time", "[LLSeedRand]") { - set_test_name("rand_u32() draws each of its bytes evenly and sets each bit half the time"); ll_rand_seed(12); const U64 draws = 256000; std::vector> bytes(4, std::vector(256)); @@ -336,24 +329,25 @@ namespace tut } } - template<> template<> - void random_object_t::test<13>() + TEST_CASE_METHOD(random, "LLSeedRand: a thread that was never seeded draws apart from every other", "[LLSeedRand]") { - set_test_name("a thread that was never seeded draws apart from every other"); std::vector one, two; std::thread first([&one] { one = draw_drand(8); }); std::thread second([&two] { two = draw_drand(8); }); first.join(); second.join(); const std::vector here = draw_drand(8); - ensure("two threads draw apart", one != two); - ensure("and apart from this one", one != here && two != here); + UNSCOPED_INFO("two threads draw apart"); + CHECK(one != two); + { + INFO("and apart from this one"); + CHECK(one != here); + CHECK(two != here); + } } - template<> template<> - void random_object_t::test<14>() + TEST_CASE_METHOD(random, "LLSeedRand: seeding and drawing on another thread leaves this thread's draws alone", "[LLSeedRand]") { - set_test_name("seeding and drawing on another thread leaves this thread's draws alone"); ll_rand_seed(14); std::vector drawn = draw_drand(4); std::thread([] { @@ -364,67 +358,94 @@ namespace tut drawn.insert(drawn.end(), after.begin(), after.end()); ll_rand_seed(14); - ensure("this thread's draws went on as if alone", draw_drand(8) == drawn); + UNSCOPED_INFO("this thread's draws went on as if alone"); + CHECK(draw_drand(8) == drawn); } - template<> template<> - void random_object_t::test<15>() + TEST_CASE_METHOD(random, "LLSeedRand: unit values scale short of the extent and integers truncate toward zero", "[LLSeedRand]") { - set_test_name("a unit value scales to each extent, an integer truncated toward zero, and never reaches it"); using namespace ALRandMap; const F64 top53 = 1.0 - 0x1p-53; const F64 top48 = 1.0 - 0x1p-48; - ensure_equals("0 of 100", extentS32(0.0, 100), 0); - ensure_equals("a half of 100", extentS32(0.5, 100), 50); - ensure_equals("the top of 100, to 48 bits", extentS32(top48, 100), 99); - ensure_equals("the top of 100, to 53 bits", extentS32(top53, 100), 99); - ensure_equals("the top of INT_MAX", extentS32(top53, INT_MAX), INT_MAX - 1); - ensure_equals("the top of 2^30", extentS32(top53, 1 << 30), (1 << 30) - 1); - ensure_equals("a half of -7, toward zero", extentS32(0.5, -7), -3); - ensure_equals("the top of -7", extentS32(top53, -7), -6); - ensure_equals("the top of INT_MIN", extentS32(top53, INT_MIN), INT_MIN + 1); - ensure_equals("a unit of 1 gives 0, not the extent", extentS32(1.0, 100), 0); - ensure_equals("nor the negative extent", extentS32(1.0, -100), 0); - - ensure_equals("a half of 2", extentReal(0.5f, 2.f), 1.f); - ensure_equals("a half of -7", extentReal(0.5f, -7.f), -3.5f); - ensure_equals("0 of 0", extentReal(0.5, 0.0), 0.0); - ensure("the top F32 of 3 stays under 3", extentReal(1.f - 0x1p-24f, 3.f) < 3.f); - ensure("the top F64 of 3 stays under 3", extentReal(top53, 3.0) < 3.0); - ensure("the top F64 of -3 stays over -3", extentReal(top53, -3.0) > -3.0); - ensure_equals("a unit of 1 gives 0, not the extent", extentReal(1.f, 2.f), 0.f); - ensure_equals("nor the negative extent", extentReal(1.0, -2.0), 0.0); + UNSCOPED_INFO("0 of 100"); + CHECK(extentS32(0.0, 100) == 0); + UNSCOPED_INFO("a half of 100"); + CHECK(extentS32(0.5, 100) == 50); + UNSCOPED_INFO("the top of 100, to 48 bits"); + CHECK(extentS32(top48, 100) == 99); + UNSCOPED_INFO("the top of 100, to 53 bits"); + CHECK(extentS32(top53, 100) == 99); + UNSCOPED_INFO("the top of INT_MAX"); + CHECK(extentS32(top53, INT_MAX) == INT_MAX - 1); + UNSCOPED_INFO("the top of 2^30"); + CHECK(extentS32(top53, 1 << 30) == (1 << 30) - 1); + UNSCOPED_INFO("a half of -7, toward zero"); + CHECK(extentS32(0.5, -7) == -3); + UNSCOPED_INFO("the top of -7"); + CHECK(extentS32(top53, -7) == -6); + UNSCOPED_INFO("the top of INT_MIN"); + CHECK(extentS32(top53, INT_MIN) == INT_MIN + 1); + UNSCOPED_INFO("a unit of 1 gives 0, not the extent"); + CHECK(extentS32(1.0, 100) == 0); + UNSCOPED_INFO("nor the negative extent"); + CHECK(extentS32(1.0, -100) == 0); + + UNSCOPED_INFO("a half of 2"); + CHECK(extentReal(0.5f, 2.f) == 1.f); + UNSCOPED_INFO("a half of -7"); + CHECK(extentReal(0.5f, -7.f) == -3.5f); + UNSCOPED_INFO("0 of 0"); + CHECK(extentReal(0.5, 0.0) == 0.0); + UNSCOPED_INFO("the top F32 of 3 stays under 3"); + CHECK(extentReal(1.f - 0x1p-24f, 3.f) < 3.f); + UNSCOPED_INFO("the top F64 of 3 stays under 3"); + CHECK(extentReal(top53, 3.0) < 3.0); + UNSCOPED_INFO("the top F64 of -3 stays over -3"); + CHECK(extentReal(top53, -3.0) > -3.0); + UNSCOPED_INFO("a unit of 1 gives 0, not the extent"); + CHECK(extentReal(1.f, 2.f) == 0.f); + UNSCOPED_INFO("nor the negative extent"); + CHECK(extentReal(1.0, -2.0) == 0.0); } - template<> template<> - void random_object_t::test<16>() + TEST_CASE_METHOD(random, "LLSeedRand: unitF64/unitF32/bitsU32 take exactly a draw's top 53/24/32 bits", "[LLSeedRand]") { - set_test_name("a draw's top 53 bits are the F64, its top 24 the F32 and its top 32 the U32, each exact"); using namespace ALRandMap; const U64 all = ~U64(0); - ensure_equals("0", unitF64(0), 0.0); - ensure_equals("a half", unitF64(U64(1) << 63), 0.5); - ensure_equals("the top", unitF64(all), 1.0 - 0x1p-53); - ensure_equals("the low 11 bits are passed over", unitF64(0x7FF), 0.0); - ensure_equals("the lowest bit used", unitF64(0x800), 0x1p-53); - - ensure_equals("0", unitF32(0), 0.f); - ensure_equals("a half", unitF32(U64(1) << 63), 0.5f); - ensure_equals("the top, which does not round up to 1", unitF32(all), 1.f - 0x1p-24f); - ensure_equals("the low 40 bits are passed over", unitF32((U64(1) << 40) - 1), 0.f); - ensure_equals("the lowest bit used", unitF32(U64(1) << 40), 0x1p-24f); - - ensure_equals("the bits of 0", bitsU32(0), 0u); - ensure_equals("the top 32 bits", bitsU32(0x123456789ABCDEF0), 0x12345678u); - ensure_equals("the bits of the top", bitsU32(all), 0xFFFFFFFFu); + UNSCOPED_INFO("0"); + CHECK(unitF64(0) == 0.0); + UNSCOPED_INFO("a half"); + CHECK(unitF64(U64(1) << 63) == 0.5); + UNSCOPED_INFO("the top"); + CHECK(unitF64(all) == 1.0 - 0x1p-53); + UNSCOPED_INFO("the low 11 bits are passed over"); + CHECK(unitF64(0x7FF) == 0.0); + UNSCOPED_INFO("the lowest bit used"); + CHECK(unitF64(0x800) == 0x1p-53); + + UNSCOPED_INFO("0"); + CHECK(unitF32(0) == 0.f); + UNSCOPED_INFO("a half"); + CHECK(unitF32(U64(1) << 63) == 0.5f); + UNSCOPED_INFO("the top, which does not round up to 1"); + CHECK(unitF32(all) == 1.f - 0x1p-24f); + UNSCOPED_INFO("the low 40 bits are passed over"); + CHECK(unitF32((U64(1) << 40) - 1) == 0.f); + UNSCOPED_INFO("the lowest bit used"); + CHECK(unitF32(U64(1) << 40) == 0x1p-24f); + + UNSCOPED_INFO("the bits of 0"); + CHECK(bitsU32(0) == 0u); + UNSCOPED_INFO("the top 32 bits"); + CHECK(bitsU32(0x123456789ABCDEF0) == 0x12345678u); + UNSCOPED_INFO("the bits of the top"); + CHECK(bitsU32(all) == 0xFFFFFFFFu); } - template<> template<> - void random_object_t::test<17>() + TEST_CASE_METHOD(random, "LLSeedRand: seed 42 draws what std::mt19937_64 does under every standard library", "[LLSeedRand]") { - set_test_name("seed 42 draws what std::mt19937_64 does, from every standard library"); const F64 drand[] = { 0x1.82a3befaddcbcp-1, 0x1.472f1f73724ap-1, 0x1.81192cfe1cbcfp-1, 0x1.171621fc50d68p-3, 0x1.ce79451c9a6c3p-1, 0x1.814dc629c7f48p-4, 0x1.262e1432cba84p-1, 0x1.7dd645e8fadeep-2 }; const F32 frand[] = { 0x1.82a3bep-1f, 0x1.472f1ep-1f, 0x1.81192cp-1f, 0x1.17162p-3f, @@ -435,43 +456,49 @@ namespace tut ll_rand_seed(42); for (S32 ii = 0; ii < 8; ++ii) { - ensure_equals(stringize("drand ", ii), ll_drand(), drand[ii]); + UNSCOPED_INFO(stringize("drand ", ii)); + REQUIRE(ll_drand() == drand[ii]); } ll_rand_seed(42); for (S32 ii = 0; ii < 8; ++ii) { - ensure_equals(stringize("frand ", ii), ll_frand(), frand[ii]); + UNSCOPED_INFO(stringize("frand ", ii)); + REQUIRE(ll_frand() == frand[ii]); } ll_rand_seed(42); for (S32 ii = 0; ii < 8; ++ii) { - ensure_equals(stringize("rand(100) ", ii), ll_rand(100), rand100[ii]); + UNSCOPED_INFO(stringize("rand(100) ", ii)); + REQUIRE(ll_rand(100) == rand100[ii]); } ll_rand_seed(42); for (S32 ii = 0; ii < 8; ++ii) { - ensure_equals(stringize("rand_u32 ", ii), ll_rand_u32(), u32[ii]); + UNSCOPED_INFO(stringize("rand_u32 ", ii)); + REQUIRE(ll_rand_u32() == u32[ii]); } } - template<> template<> - void random_object_t::test<18>() + TEST_CASE_METHOD(random, "LLSeedRand: drand() draws to 53 bits and frand() to 24 and each uses all of them", "[LLSeedRand]") { - set_test_name("drand() draws to 53 bits and frand() to 24, and each uses all of its bits"); ll_rand_seed(18); bool drand_last_bit = false; bool frand_last_bit = false; for (S32 ii = 0; ii < 100000; ++ii) { const F64 drawn = std::ldexp(ll_drand(), 53); - ensure(stringize("drand ", ii, " is a multiple of 2^-53"), drawn == std::floor(drawn)); + UNSCOPED_INFO(stringize("drand ", ii, " is a multiple of 2^-53")); + REQUIRE(drawn == std::floor(drawn)); drand_last_bit = drand_last_bit || std::fmod(drawn, 2.0) != 0.0; const F64 drawn_f = std::ldexp(F64(ll_frand()), 24); - ensure(stringize("frand ", ii, " is a multiple of 2^-24"), drawn_f == std::floor(drawn_f)); + UNSCOPED_INFO(stringize("frand ", ii, " is a multiple of 2^-24")); + REQUIRE(drawn_f == std::floor(drawn_f)); frand_last_bit = frand_last_bit || std::fmod(drawn_f, 2.0) != 0.0; } - ensure("some drand sets the 53rd bit", drand_last_bit); - ensure("some frand sets the 24th bit", frand_last_bit); + UNSCOPED_INFO("some drand sets the 53rd bit"); + CHECK(drand_last_bit); + UNSCOPED_INFO("some frand sets the 24th bit"); + CHECK(frand_last_bit); } } diff --git a/indra/llcommon/tests/llsd_test.cpp b/indra/llcommon/tests/llsd_test.cpp index 900ba8c9d0..95c0741022 100644 --- a/indra/llcommon/tests/llsd_test.cpp +++ b/indra/llcommon/tests/llsd_test.cpp @@ -27,7 +27,7 @@ #define LLSD_DEBUG_INFO #include "linden_common.h" -#include "lltut.h" +#include #include "llsd.h" #include "llsdtraits.h" @@ -48,8 +48,8 @@ namespace tut { const U32 outstanding = llsd::outstandingCount(); body(); - ensure_equals("SDCleanupCheck", - llsd::outstandingCount(), outstanding); + UNSCOPED_INFO("SDCleanupCheck"); + REQUIRE(llsd::outstandingCount() == outstanding); } // ...and the body allocated exactly as many LLSD values as expected. @@ -59,11 +59,11 @@ namespace tut const U32 outstanding = llsd::outstandingCount(); const U32 allocations = llsd::allocationCount(); body(); - ensure_equals(message + " SDAllocationCheck", - llsd::allocationCount() - allocations, + UNSCOPED_INFO(message + " SDAllocationCheck"); + REQUIRE(llsd::allocationCount() - allocations == expectedAllocations); - ensure_equals("SDCleanupCheck", - llsd::outstandingCount(), outstanding); + UNSCOPED_INFO("SDCleanupCheck"); + REQUIRE(llsd::outstandingCount() == outstanding); } } // anonymous namespace @@ -80,8 +80,10 @@ namespace tut std::string s(msg); - ensure( s + " type", traits.checkType(actual)); - ensure_equals( s + " value", traits.get(actual), expectedValue); + UNSCOPED_INFO(s + " type"); + REQUIRE(traits.checkType(actual)); + UNSCOPED_INFO(s + " value"); + REQUIRE(traits.get(actual) == expectedValue); } template @@ -92,19 +94,12 @@ namespace tut std::string s(msg); - ensure( s + " type", traits.checkType(actual)); - ensure_equals( s + " value", traits.get(actual), expectedValue); + UNSCOPED_INFO(s + " type"); + REQUIRE(traits.checkType(actual)); + UNSCOPED_INFO(s + " value"); + REQUIRE(traits.get(actual) == expectedValue); } - struct SDTestData - { - }; - - typedef test_group SDTestGroup; - typedef SDTestGroup::object SDTestObject; - - SDTestGroup sdTestGroup("LLSD(new)"); - // template<> template<> // void SDTestObject::test<1>() // // construction and test of undefined @@ -115,8 +110,7 @@ namespace tut // ensure("is undefined", u.isUndefined()); // } - template<> template<> - void SDTestObject::test<2>() + TEST_CASE("LLSD(new): setting and fetching scalar types", "[LLSD_new]") // setting and fetching scalar types { ensure_cleaned_up([&] @@ -134,7 +128,8 @@ namespace tut v = 3.14159265359; ensureTypeAndValue("set to pi", v, 3.14159265359); - ensure_not_equals("isn't float", v.asReal(), + UNSCOPED_INFO("isn't float"); + REQUIRE(v.asReal() != (float)3.14159265359); v = 6.7e256; ensureTypeAndValue("set to big", v, 6.7e256); @@ -170,12 +165,12 @@ namespace tut v = data; ensureTypeAndRefValue("set to data", v, data); v.clear(); - ensure("reset to undefined", v.type() == LLSD::TypeUndefined); + UNSCOPED_INFO("reset to undefined"); + REQUIRE(v.type() == LLSD::TypeUndefined); }); } - template<> template<> - void SDTestObject::test<3>() + TEST_CASE("LLSD(new): construction and initialization from scalar values", "[LLSD_new]") // construction via scalar values // tests both constructor and initialize forms { @@ -230,19 +225,24 @@ namespace tut { std::string s(msg); - ensure_equals(s+" to bool", v.asBoolean(), eBoolean); - ensure_equals(s+" to int", v.asInteger(), eInteger); + UNSCOPED_INFO(s+" to bool"); + REQUIRE(v.asBoolean() == eBoolean); + UNSCOPED_INFO(s+" to int"); + REQUIRE(v.asInteger() == eInteger); if (eReal == eReal) { - ensure_equals(s+" to real", v.asReal(), eReal); - ensure_equals(s+" to string", v.asString(), eString); + UNSCOPED_INFO(s+" to real"); + REQUIRE(v.asReal() == eReal); + UNSCOPED_INFO(s+" to string"); + REQUIRE(v.asString() == eString); } else { int left = fpclassify(v.asReal()); int right = fpclassify(eReal); - ensure_equals(s+" to real", left, right); + UNSCOPED_INFO(s+" to real"); + REQUIRE(left == right); // ensure_equals(s+" to string", v.asString(), eString); // I've commented this check out, since there doesn't // seem to be uniform string representation for NaN on @@ -252,8 +252,7 @@ namespace tut } } - template<> template<> - void SDTestObject::test<4>() + TEST_CASE("LLSD(new): conversion of undefined and scalars to the basic scalar types", "[LLSD_new]") // conversion between undefined and basic scalar types: // boolean, integer, real and string { @@ -303,18 +302,19 @@ namespace tut std::string str = actual.asString(); if (sExpected) { - ensure_equals(msg + " string", str, sExpected); + UNSCOPED_INFO(msg + " string"); + REQUIRE(str == sExpected); } LLSD u(str); LLSDTraits traits; - ensure_equals(msg + " value", traits.get(u), vExpected); + UNSCOPED_INFO(msg + " value"); + REQUIRE(traits.get(u) == vExpected); } - template<> template<> - void SDTestObject::test<5>() + TEST_CASE("LLSD(new): UUID and Date and URI convert to and from String", "[LLSD_new]") // conversion of String to and from UUID, Date and URI. { ensure_cleaned_up([&] @@ -352,8 +352,7 @@ namespace tut }); } - template<> template<> - void SDTestObject::test<6>() + TEST_CASE("LLSD(new): copies made by construction or assignment change independently", "[LLSD_new]") // copy construction and assignment // checking for shared values after constr. or assignment // checking in both the same type and change of type case @@ -430,8 +429,7 @@ namespace tut } - template<> template<> - void SDTestObject::test<7>() + TEST_CASE("LLSD(new): assigning and casting LLSD to scalar types converts", "[LLSD_new]") // Test assignment and casting to various scalar types. These // assignments should invoke the right conversion without it being // mentioned explicitly. The few exceptions are marked SAD. @@ -441,26 +439,27 @@ namespace tut LLSD v(" 42.375"); bool b = false; - b = v; ensure_equals("assign to bool", b, true); - b = (bool)v; ensure_equals("cast to bool", b, true); + b = v; UNSCOPED_INFO("assign to bool"); REQUIRE(b == true); + b = (bool)v; UNSCOPED_INFO("cast to bool"); REQUIRE(b == true); int i = 99; - i = v; ensure_equals("assign to int", i, 42); - i = (int)v; ensure_equals("cast to int", i, 42); + i = v; UNSCOPED_INFO("assign to int"); REQUIRE(i == 42); + i = (int)v; UNSCOPED_INFO("cast to int"); REQUIRE(i == 42); double d = 3.14159; - d = v; ensure_equals("assign to double", d, 42.375); - d = (double)v; ensure_equals("cast to double", d, 42.375); + d = v; UNSCOPED_INFO("assign to double"); REQUIRE(d == 42.375); + d = (double)v; UNSCOPED_INFO("cast to double"); REQUIRE(d == 42.375); std::string s = "yo"; // SAD s = v; ensure_equals("assign to string", s, " 42.375"); - s = (std::string)v; ensure_equals("cast to string", s, " 42.375"); + s = (std::string)v; UNSCOPED_INFO("cast to string"); REQUIRE(s == " 42.375"); std::string uuidStr = "b1e50c2b-b627-4d23-8a86-a65d97b6319b"; v = uuidStr; LLUUID u; u = v; - ensure_equals("assign to LLUUID", u, LLUUID(uuidStr)); + UNSCOPED_INFO("assign to LLUUID"); + REQUIRE(u == LLUUID(uuidStr)); // SAD u = (LLUUID)v; // ensure_equals("cast to LLUUID", u, LLUUID(uuidStr)); @@ -468,7 +467,8 @@ namespace tut v = dateStr; LLDate date; date = v; - ensure_equals("assign to LLDate", date.asString(), dateStr); + UNSCOPED_INFO("assign to LLDate"); + REQUIRE(date.asString() == dateStr); // SAD date = (LLDate)v; // ensure_equals("cast to LLDate", date.asString(), dateStr); @@ -476,14 +476,14 @@ namespace tut v = uriStr; LLURI uri; uri = v; - ensure_equals("assign to LLURI", uri.asString(), uriStr); + UNSCOPED_INFO("assign to LLURI"); + REQUIRE(uri.asString() == uriStr); // SAD uri = (LLURI)v; // ensure_equals("cast to LLURI", uri.asString(), uriStr); }); } - template<> template<> - void SDTestObject::test<8>() + TEST_CASE("LLSD(new): constructing scalar types from LLSD converts", "[LLSD_new]") // Test construction of various scalar types from LLSD. // Test both construction and initialization forms. // These should invoke the right conversion without it being @@ -493,51 +493,60 @@ namespace tut { LLSD v(" 42.375"); - bool b1(v); ensure_equals("contruct bool", b1, true); - bool b2 = v; ensure_equals("initialize bool", b2, true); + bool b1(v); UNSCOPED_INFO("contruct bool"); REQUIRE(b1 == true); + bool b2 = v; UNSCOPED_INFO("initialize bool"); REQUIRE(b2 == true); - int i1(v); ensure_equals("contruct int", i1, 42); - int i2 = v; ensure_equals("initialize int", i2, 42); + int i1(v); UNSCOPED_INFO("contruct int"); REQUIRE(i1 == 42); + int i2 = v; UNSCOPED_INFO("initialize int"); REQUIRE(i2 == 42); - double d1(v); ensure_equals("contruct double", d1, 42.375); - double d2 = v; ensure_equals("initialize double", d2, 42.375); + double d1(v); UNSCOPED_INFO("contruct double"); REQUIRE(d1 == 42.375); + double d2 = v; UNSCOPED_INFO("initialize double"); REQUIRE(d2 == 42.375); std::string s1(v); std::string s2 = v; - ensure_equals("contruct string", s1, " 42.375"); - ensure_equals("initialize string", s2, " 42.375"); + UNSCOPED_INFO("contruct string"); + REQUIRE(s1 == " 42.375"); + UNSCOPED_INFO("initialize string"); + REQUIRE(s2 == " 42.375"); std::string t1(v); std::string t2 = v.asString(); // SAD - ensure_equals("contruct std::string", t1, " 42.375"); - ensure_equals("initialize std::string", t2, " 42.375"); + UNSCOPED_INFO("contruct std::string"); + REQUIRE(t1 == " 42.375"); + UNSCOPED_INFO("initialize std::string"); + REQUIRE(t2 == " 42.375"); std::string uuidStr = "b1e50c2b-b627-4d23-8a86-a65d97b6319b"; v = uuidStr; LLUUID uuid1(v.asUUID()); // SAD LLUUID uuid2 = v; - ensure_equals("contruct LLUUID", uuid1, LLUUID(uuidStr)); - ensure_equals("initialize LLUUID", uuid2, LLUUID(uuidStr)); + UNSCOPED_INFO("contruct LLUUID"); + REQUIRE(uuid1 == LLUUID(uuidStr)); + UNSCOPED_INFO("initialize LLUUID"); + REQUIRE(uuid2 == LLUUID(uuidStr)); std::string dateStr = "2005-10-24T15:00:00Z"; v = dateStr; LLDate date1(v.asDate()); // SAD LLDate date2 = v; - ensure_equals("contruct LLDate", date1.asString(), dateStr); - ensure_equals("initialize LLDate", date2.asString(), dateStr); + UNSCOPED_INFO("contruct LLDate"); + REQUIRE(date1.asString() == dateStr); + UNSCOPED_INFO("initialize LLDate"); + REQUIRE(date2.asString() == dateStr); std::string uriStr = "http://secondlife.com"; v = uriStr; LLURI uri1(v.asURI()); // SAD LLURI uri2 = v; - ensure_equals("contruct LLURI", uri1.asString(), uriStr); - ensure_equals("initialize LLURI", uri2.asString(), uriStr); + UNSCOPED_INFO("contruct LLURI"); + REQUIRE(uri1.asString() == uriStr); + UNSCOPED_INFO("initialize LLURI"); + REQUIRE(uri2.asString() == uriStr); }); } - template<> template<> - void SDTestObject::test<9>() + TEST_CASE("LLSD(new): the string 0 is true as a bool but 0 as an int", "[LLSD_new]") // test to make sure v is interpreted as a bool in a various // scenarios. { @@ -546,107 +555,134 @@ namespace tut LLSD v = "0"; // magic value that is interpreted as boolean true, but integer false! - ensure_equals("trinary operator bool", (v ? true : false), true); - ensure_equals("convert to int, then bool", - ((int)v ? true : false), false); + UNSCOPED_INFO("trinary operator bool"); + REQUIRE((v ? true : false) == true); + UNSCOPED_INFO("convert to int, then bool"); + REQUIRE(((int)v ? true : false) == false); if(v) { - ensure("if converted to bool", true); + UNSCOPED_INFO("if converted to bool"); + REQUIRE(true); } else { - fail("bool did not convert to a bool in if statement."); + FAIL("bool did not convert to a bool in if statement."); } if(!v) { - fail("bool did not convert to a bool in negated if statement."); + FAIL("bool did not convert to a bool in negated if statement."); } }); } - template<> template<> - void SDTestObject::test<10>() + TEST_CASE("LLSD(new): map has/get/insert/erase and copy", "[LLSD_new]") // map operations { ensure_cleaned_up([&] { LLSD v; - ensure("undefined has no members", !v.has("amy")); - ensure("undefined get() is undefined", v.get("bob").isUndefined()); + UNSCOPED_INFO("undefined has no members"); + REQUIRE_FALSE(v.has("amy")); + UNSCOPED_INFO("undefined get() is undefined"); + REQUIRE(v.get("bob").isUndefined()); v = LLSD::emptyMap(); - ensure("empty map is a map", v.isMap()); - ensure("empty map has no members", !v.has("cam")); - ensure("empty map get() is undefined", v.get("don").isUndefined()); + UNSCOPED_INFO("empty map is a map"); + REQUIRE(v.isMap()); + UNSCOPED_INFO("empty map has no members"); + REQUIRE_FALSE(v.has("cam")); + UNSCOPED_INFO("empty map get() is undefined"); + REQUIRE(v.get("don").isUndefined()); v.clear(); v.insert("eli", 43); - ensure("insert converts to map", v.isMap()); - ensure("inserted key is present", v.has("eli")); + UNSCOPED_INFO("insert converts to map"); + REQUIRE(v.isMap()); + UNSCOPED_INFO("inserted key is present"); + REQUIRE(v.has("eli")); ensureTypeAndValue("inserted value", v.get("eli"), 43); v.insert("fra", false); - ensure("first key still present", v.has("eli")); - ensure("second key is present", v.has("fra")); + UNSCOPED_INFO("first key still present"); + REQUIRE(v.has("eli")); + UNSCOPED_INFO("second key is present"); + REQUIRE(v.has("fra")); ensureTypeAndValue("first value", v.get("eli"), 43); ensureTypeAndValue("second value", v.get("fra"), false); v.erase("eli"); - ensure("first key now gone", !v.has("eli")); - ensure("second key still present", v.has("fra")); - ensure("first value gone", v.get("eli").isUndefined()); + UNSCOPED_INFO("first key now gone"); + REQUIRE_FALSE(v.has("eli")); + UNSCOPED_INFO("second key still present"); + REQUIRE(v.has("fra")); + UNSCOPED_INFO("first value gone"); + REQUIRE(v.get("eli").isUndefined()); ensureTypeAndValue("second value sill there", v.get("fra"), false); v.erase("fra"); - ensure("second key now gone", !v.has("fra")); - ensure("second value gone", v.get("fra").isUndefined()); + UNSCOPED_INFO("second key now gone"); + REQUIRE_FALSE(v.has("fra")); + UNSCOPED_INFO("second value gone"); + REQUIRE(v.get("fra").isUndefined()); v["gil"] = (std::string)"good morning"; - ensure("third key present", v.has("gil")); + UNSCOPED_INFO("third key present"); + REQUIRE(v.has("gil")); ensureTypeAndValue("third key value", v.get("gil"), "good morning"); const LLSD& cv = v; // FIX ME IF POSSIBLE - ensure("missing key", cv["ham"].isUndefined()); - ensure("key not present", !v.has("ham")); + UNSCOPED_INFO("missing key"); + REQUIRE(cv["ham"].isUndefined()); + UNSCOPED_INFO("key not present"); + REQUIRE_FALSE(v.has("ham")); LLSD w = 43; const LLSD& cw = w; // FIX ME IF POSSIBLE int i = cw["ian"]; ensureTypeAndValue("other missing value", i, 0); - ensure("other missing key", !w.has("ian")); - ensure("no conversion", w.isInteger()); + UNSCOPED_INFO("other missing key"); + REQUIRE_FALSE(w.has("ian")); + UNSCOPED_INFO("no conversion"); + REQUIRE(w.isInteger()); LLSD x; x = v; - ensure("copy map type", x.isMap()); + UNSCOPED_INFO("copy map type"); + REQUIRE(x.isMap()); ensureTypeAndValue("copy map value gil", x.get("gil"), "good morning"); }); } - template<> template<> - void SDTestObject::test<11>() + TEST_CASE("LLSD(new): array append/insert/set/erase and copy", "[LLSD_new]") // array operations { ensure_cleaned_up([&] { LLSD v; - ensure_equals("undefined has no size", v.size(), 0); - ensure("undefined get() is undefined", v.get(0).isUndefined()); + UNSCOPED_INFO("undefined has no size"); + REQUIRE(v.size() == 0); + UNSCOPED_INFO("undefined get() is undefined"); + REQUIRE(v.get(0).isUndefined()); v = LLSD::emptyArray(); - ensure("empty array is an array", v.isArray()); - ensure_equals("empty array has no size", v.size(), 0); - ensure("empty map get() is undefined", v.get(0).isUndefined()); + UNSCOPED_INFO("empty array is an array"); + REQUIRE(v.isArray()); + UNSCOPED_INFO("empty array has no size"); + REQUIRE(v.size() == 0); + UNSCOPED_INFO("empty map get() is undefined"); + REQUIRE(v.get(0).isUndefined()); v.clear(); v.append(88); v.append("noodle"); v.append(true); - ensure_equals("appened array size", v.size(), 3); - ensure("append array is an array", v.isArray()); + UNSCOPED_INFO("appened array size"); + REQUIRE(v.size() == 3); + UNSCOPED_INFO("append array is an array"); + REQUIRE(v.isArray()); ensureTypeAndValue("append 0", v[0], 88); ensureTypeAndValue("append 1", v[1], "noodle"); ensureTypeAndValue("append 2", v[2], true); @@ -654,7 +690,8 @@ namespace tut v.insert(0, 77); v.insert(2, "soba"); v.insert(4, false); - ensure_equals("inserted array size", v.size(), 6); + UNSCOPED_INFO("inserted array size"); + REQUIRE(v.size() == 6); ensureTypeAndValue("post insert 0", v[0], 77); ensureTypeAndValue("post insert 1", v[1], 88); ensureTypeAndValue("post insert 2", v[2], "soba"); @@ -664,9 +701,12 @@ namespace tut LLSD beyond; beyond.insert(2, "far"); - ensure_equals("beyond-end insert size", beyond.size(), 3); - ensure("beyond-end insert pad 0", beyond[0].isUndefined()); - ensure("beyond-end insert pad 1", beyond[1].isUndefined()); + UNSCOPED_INFO("beyond-end insert size"); + REQUIRE(beyond.size() == 3); + UNSCOPED_INFO("beyond-end insert pad 0"); + REQUIRE(beyond[0].isUndefined()); + UNSCOPED_INFO("beyond-end insert pad 1"); + REQUIRE(beyond[1].isUndefined()); ensureTypeAndValue("beyond-end insert value", beyond[2], "far"); ensureTypeAndValue("get 1", v.get(1), 88); @@ -674,7 +714,8 @@ namespace tut ensureTypeAndValue("set 1", v.get(1), "hot"); v.erase(3); - ensure_equals("post erase array size", v.size(), 5); + UNSCOPED_INFO("post erase array size"); + REQUIRE(v.size() == 5); ensureTypeAndValue("post erase 0", v[0], 77); ensureTypeAndValue("post erase 1", v[1], "hot"); ensureTypeAndValue("post erase 2", v[2], "soba"); @@ -682,13 +723,16 @@ namespace tut ensureTypeAndValue("post erase 4", v[4], true); v.append(34); - ensure_equals("size after append", v.size(), 6); + UNSCOPED_INFO("size after append"); + REQUIRE(v.size() == 6); ensureTypeAndValue("post append 5", v[5], 34); LLSD w; w = v; - ensure("copy array type", w.isArray()); - ensure_equals("copy array size", w.size(), 6); + UNSCOPED_INFO("copy array type"); + REQUIRE(w.isArray()); + UNSCOPED_INFO("copy array size"); + REQUIRE(w.size() == 6); ensureTypeAndValue("copy array 0", w[0], 77); ensureTypeAndValue("copy array 1", w[1], "hot"); ensureTypeAndValue("copy array 2", w[2], "soba"); @@ -699,8 +743,7 @@ namespace tut } - template<> template<> - void SDTestObject::test<12>() + TEST_CASE("LLSD(new): a copy shares no values with its original", "[LLSD_new]") // no sharing { ensure_cleaned_up([&] @@ -741,8 +784,7 @@ namespace tut }); } - template<> template<> - void SDTestObject::test<13>() + TEST_CASE("LLSD(new): copies share their implementation and scalars never allocate", "[LLSD_new]") // sharing implementation { ensure_cleaned_up([&] @@ -826,36 +868,41 @@ namespace tut }); } - template<> template<> - void SDTestObject::test<14>() + TEST_CASE("LLSD(new): assigning a null char pointer makes a string without crashing", "[LLSD_new]") // make sure that assignment of char* NULL in a string does not crash. { LLSD v; v = (const char*)NULL; - ensure("type is a string", v.isString()); + UNSCOPED_INFO("type is a string"); + CHECK(v.isString()); } - template<> template<> - void SDTestObject::test<15>() + TEST_CASE("LLSD(new): asXMLRPCValue serializes each type", "[LLSD_new]") // XMLRPC value serialization { ensure_cleaned_up([&] { - ensure_equals("xmlrpc undef", LLSD().asXMLRPCValue(), + UNSCOPED_INFO("xmlrpc undef"); + REQUIRE(LLSD().asXMLRPCValue() == ""); - ensure_equals("xmlrpc bool", LLSD(true).asXMLRPCValue(), + UNSCOPED_INFO("xmlrpc bool"); + REQUIRE(LLSD(true).asXMLRPCValue() == "1"); - ensure_equals("xmlrpc int", LLSD(3).asXMLRPCValue(), + UNSCOPED_INFO("xmlrpc int"); + REQUIRE(LLSD(3).asXMLRPCValue() == "3"); - ensure_equals("xmlrpc real", LLSD(1.5).asXMLRPCValue(), + UNSCOPED_INFO("xmlrpc real"); + REQUIRE(LLSD(1.5).asXMLRPCValue() == "1.5"); - ensure_equals("xmlrpc string", LLSD("aa<b"); LLSD array = LLSD::emptyArray(); array.append(1); array.append("two"); - ensure_equals("xmlrpc array", array.asXMLRPCValue(), + UNSCOPED_INFO("xmlrpc array"); + REQUIRE(array.asXMLRPCValue() == "" "1" "two" @@ -863,7 +910,8 @@ namespace tut LLSD map = LLSD::emptyMap(); map["a"] = 1; - ensure_equals("xmlrpc map", map.asXMLRPCValue(), + UNSCOPED_INFO("xmlrpc map"); + REQUIRE(map.asXMLRPCValue() == "" "a1" ""); diff --git a/indra/llcommon/tests/llsdjson_test.cpp b/indra/llcommon/tests/llsdjson_test.cpp index 027d0c36bc..13a0ae30c8 100644 --- a/indra/llcommon/tests/llsdjson_test.cpp +++ b/indra/llcommon/tests/llsdjson_test.cpp @@ -25,7 +25,7 @@ */ #include "linden_common.h" -#include "lltut.h" +#include #include "llsdjson.h" #include "llsdutil.h" @@ -36,7 +36,7 @@ #include #include -namespace tut +namespace { struct llsdjson_data { @@ -46,168 +46,213 @@ namespace tut LLSD result; std::string errmsg; bool ok = LlsdFromJsonString(json, result, &errmsg); - ensure("parse failed for " + json + ": " + errmsg, ok); + UNSCOPED_INFO("parse failed for " + json + ": " + errmsg); + REQUIRE(ok); return result; } }; - typedef test_group llsdjson_test; - typedef llsdjson_test::object llsdjson_object; - tut::llsdjson_test tllsdjson("LlsdJson"); // scalar parsing: types and values - template<> template<> - void llsdjson_object::test<1>() + TEST_CASE_METHOD(llsdjson_data, "LlsdJson: scalars parse to the right type and value", "[LlsdJson]") { LLSD val = parse("null"); - ensure_equals("null type", val.type(), LLSD::TypeUndefined); + UNSCOPED_INFO("null type"); + CHECK(val.type() == LLSD::TypeUndefined); val = parse("true"); - ensure_equals("bool type", val.type(), LLSD::TypeBoolean); - ensure("bool value", val.asBoolean()); + UNSCOPED_INFO("bool type"); + CHECK(val.type() == LLSD::TypeBoolean); + UNSCOPED_INFO("bool value"); + CHECK(val.asBoolean()); val = parse("false"); - ensure("false value", !val.asBoolean()); + UNSCOPED_INFO("false value"); + CHECK_FALSE(val.asBoolean()); val = parse("42"); - ensure_equals("int type", val.type(), LLSD::TypeInteger); - ensure_equals("int value", val.asInteger(), 42); + UNSCOPED_INFO("int type"); + CHECK(val.type() == LLSD::TypeInteger); + UNSCOPED_INFO("int value"); + CHECK(val.asInteger() == 42); val = parse("-7"); - ensure_equals("negative int", val.asInteger(), -7); + UNSCOPED_INFO("negative int"); + CHECK(val.asInteger() == -7); val = parse("3.25"); - ensure_equals("real type", val.type(), LLSD::TypeReal); - ensure_equals("real value", val.asReal(), 3.25); + UNSCOPED_INFO("real type"); + CHECK(val.type() == LLSD::TypeReal); + UNSCOPED_INFO("real value"); + CHECK(val.asReal() == 3.25); val = parse("\"hello world\""); - ensure_equals("string type", val.type(), LLSD::TypeString); - ensure_equals("string value", val.asString(), "hello world"); + UNSCOPED_INFO("string type"); + CHECK(val.type() == LLSD::TypeString); + UNSCOPED_INFO("string value"); + CHECK(val.asString() == "hello world"); } // integer vs real distinction is preserved by the parse - template<> template<> - void llsdjson_object::test<2>() + TEST_CASE_METHOD(llsdjson_data, "LlsdJson: integer vs real distinction is preserved by the parse", "[LlsdJson]") { - ensure_equals("1 is integer", parse("1").type(), LLSD::TypeInteger); - ensure_equals("1.0 is real", parse("1.0").type(), LLSD::TypeReal); - ensure_equals("1e0 is real", parse("1e0").type(), LLSD::TypeReal); - ensure_equals("0 is integer", parse("0").type(), LLSD::TypeInteger); + UNSCOPED_INFO("1 is integer"); + CHECK(parse("1").type() == LLSD::TypeInteger); + UNSCOPED_INFO("1.0 is real"); + CHECK(parse("1.0").type() == LLSD::TypeReal); + UNSCOPED_INFO("1e0 is real"); + CHECK(parse("1e0").type() == LLSD::TypeReal); + UNSCOPED_INFO("0 is integer"); + CHECK(parse("0").type() == LLSD::TypeInteger); } // arrays: order, nesting, empties - template<> template<> - void llsdjson_object::test<3>() + TEST_CASE_METHOD(llsdjson_data, "LlsdJson: arrays parse in order with nesting and empty arrays", "[LlsdJson]") { LLSD val = parse("[1, \"two\", 3.0, null, [true]]"); - ensure_equals("array type", val.type(), LLSD::TypeArray); - ensure_equals("array size", val.size(), 5); - ensure_equals("elt 0", val[0].asInteger(), 1); - ensure_equals("elt 1", val[1].asString(), "two"); - ensure_equals("elt 2", val[2].asReal(), 3.0); - ensure_equals("elt 3 undefined", val[3].type(), LLSD::TypeUndefined); - ensure_equals("nested array", val[4][0].asBoolean(), true); + UNSCOPED_INFO("array type"); + CHECK(val.type() == LLSD::TypeArray); + UNSCOPED_INFO("array size"); + REQUIRE(val.size() == 5); + UNSCOPED_INFO("elt 0"); + CHECK(val[0].asInteger() == 1); + UNSCOPED_INFO("elt 1"); + CHECK(val[1].asString() == "two"); + UNSCOPED_INFO("elt 2"); + CHECK(val[2].asReal() == 3.0); + UNSCOPED_INFO("elt 3 undefined"); + CHECK(val[3].type() == LLSD::TypeUndefined); + UNSCOPED_INFO("nested array"); + CHECK(val[4][0].asBoolean() == true); val = parse("[]"); - ensure_equals("empty array type", val.type(), LLSD::TypeArray); - ensure_equals("empty array size", val.size(), 0); + UNSCOPED_INFO("empty array type"); + CHECK(val.type() == LLSD::TypeArray); + UNSCOPED_INFO("empty array size"); + REQUIRE(val.size() == 0); } // objects: keys, nesting, empties - template<> template<> - void llsdjson_object::test<4>() + TEST_CASE_METHOD(llsdjson_data, "LlsdJson: objects parse by key with nesting and empty objects", "[LlsdJson]") { LLSD val = parse("{\"a\": 1, \"b\": {\"c\": [2, 3]}}"); - ensure_equals("map type", val.type(), LLSD::TypeMap); - ensure_equals("map size", val.size(), 2); - ensure_equals("member a", val["a"].asInteger(), 1); - ensure_equals("nested member", val["b"]["c"][1].asInteger(), 3); + UNSCOPED_INFO("map type"); + CHECK(val.type() == LLSD::TypeMap); + UNSCOPED_INFO("map size"); + REQUIRE(val.size() == 2); + UNSCOPED_INFO("member a"); + CHECK(val["a"].asInteger() == 1); + UNSCOPED_INFO("nested member"); + CHECK(val["b"]["c"][1].asInteger() == 3); val = parse("{}"); - ensure_equals("empty map type", val.type(), LLSD::TypeMap); - ensure_equals("empty map size", val.size(), 0); + UNSCOPED_INFO("empty map type"); + CHECK(val.type() == LLSD::TypeMap); + UNSCOPED_INFO("empty map size"); + REQUIRE(val.size() == 0); } // serialization golden strings - template<> template<> - void llsdjson_object::test<5>() + TEST_CASE_METHOD(llsdjson_data, "LlsdJson: serialization writes the golden strings", "[LlsdJson]") { - ensure_equals("undefined", LlsdToJson(LLSD()), "null"); - ensure_equals("true", LlsdToJson(LLSD(true)), "true"); - ensure_equals("false", LlsdToJson(LLSD(false)), "false"); - ensure_equals("integer", LlsdToJson(LLSD(17)), "17"); - ensure_equals("negative", LlsdToJson(LLSD(-17)), "-17"); - ensure_equals("real", LlsdToJson(LLSD(2.5)), "2.5"); - ensure_equals("string", LlsdToJson(LLSD("simple")), "\"simple\""); - ensure_equals("empty string", LlsdToJson(LLSD("")), "\"\""); - ensure_equals("empty map", LlsdToJson(LLSD::emptyMap()), "{}"); - ensure_equals("empty array", LlsdToJson(LLSD::emptyArray()), "[]"); + UNSCOPED_INFO("undefined"); + CHECK(LlsdToJson(LLSD()) == "null"); + UNSCOPED_INFO("true"); + CHECK(LlsdToJson(LLSD(true)) == "true"); + UNSCOPED_INFO("false"); + CHECK(LlsdToJson(LLSD(false)) == "false"); + UNSCOPED_INFO("integer"); + CHECK(LlsdToJson(LLSD(17)) == "17"); + UNSCOPED_INFO("negative"); + CHECK(LlsdToJson(LLSD(-17)) == "-17"); + UNSCOPED_INFO("real"); + CHECK(LlsdToJson(LLSD(2.5)) == "2.5"); + UNSCOPED_INFO("string"); + CHECK(LlsdToJson(LLSD("simple")) == "\"simple\""); + UNSCOPED_INFO("empty string"); + CHECK(LlsdToJson(LLSD("")) == "\"\""); + UNSCOPED_INFO("empty map"); + CHECK(LlsdToJson(LLSD::emptyMap()) == "{}"); + UNSCOPED_INFO("empty array"); + CHECK(LlsdToJson(LLSD::emptyArray()) == "[]"); LLSD map; map["b"] = 2; map["a"] = 1; // LLSD maps iterate in sorted key order - ensure_equals("map", LlsdToJson(map), "{\"a\":1,\"b\":2}"); + UNSCOPED_INFO("map"); + CHECK(LlsdToJson(map) == "{\"a\":1,\"b\":2}"); LLSD array; array.append(1); array.append("x"); - ensure_equals("array", LlsdToJson(array), "[1,\"x\"]"); + UNSCOPED_INFO("array"); + CHECK(LlsdToJson(array) == "[1,\"x\"]"); } // string escaping in serialization - template<> template<> - void llsdjson_object::test<6>() + TEST_CASE_METHOD(llsdjson_data, "LlsdJson: string escaping in serialization", "[LlsdJson]") { - ensure_equals("quote", LlsdToJson(LLSD("a\"b")), "\"a\\\"b\""); - ensure_equals("backslash", LlsdToJson(LLSD("a\\b")), "\"a\\\\b\""); - ensure_equals("newline", LlsdToJson(LLSD("a\nb")), "\"a\\nb\""); - ensure_equals("tab", LlsdToJson(LLSD("a\tb")), "\"a\\tb\""); + UNSCOPED_INFO("quote"); + CHECK(LlsdToJson(LLSD("a\"b")) == "\"a\\\"b\""); + UNSCOPED_INFO("backslash"); + CHECK(LlsdToJson(LLSD("a\\b")) == "\"a\\\\b\""); + UNSCOPED_INFO("newline"); + CHECK(LlsdToJson(LLSD("a\nb")) == "\"a\\nb\""); + UNSCOPED_INFO("tab"); + CHECK(LlsdToJson(LLSD("a\tb")) == "\"a\\tb\""); // control character must be escaped one way or another; round-trip it LLSD ctrl; - ensure("control char parses", - LlsdFromJsonString(LlsdToJson(LLSD(std::string("a\x01z"))), ctrl)); - ensure_equals("control char round-trip", ctrl.asString(), std::string("a\x01z")); + UNSCOPED_INFO("control char parses"); + REQUIRE(LlsdFromJsonString(LlsdToJson(LLSD(std::string("a\x01z"))), ctrl)); + UNSCOPED_INFO("control char round-trip"); + CHECK(ctrl.asString() == std::string("a\x01z")); // UTF-8 passes through unescaped or escaped, but must round-trip std::string utf8("\xE3\x81\x93\xE3\x82\x93 caf\xC3\xA9"); LLSD uni; - ensure("utf8 parses", LlsdFromJsonString(LlsdToJson(LLSD(utf8)), uni)); - ensure_equals("utf8 round-trip", uni.asString(), utf8); + UNSCOPED_INFO("utf8 parses"); + REQUIRE(LlsdFromJsonString(LlsdToJson(LLSD(utf8)), uni)); + UNSCOPED_INFO("utf8 round-trip"); + CHECK(uni.asString() == utf8); } // special scalar types serialize as strings - template<> template<> - void llsdjson_object::test<7>() + TEST_CASE_METHOD(llsdjson_data, "LlsdJson: special scalar types serialize as strings", "[LlsdJson]") { LLUUID id("c96f9b8e-f5ad-4b72-b6f7-a2b30d1444d2"); - ensure_equals("uuid", LlsdToJson(LLSD(id)), + UNSCOPED_INFO("uuid"); + CHECK(LlsdToJson(LLSD(id)) == "\"c96f9b8e-f5ad-4b72-b6f7-a2b30d1444d2\""); LLDate date("2026-06-11T12:34:56Z"); - ensure_equals("date", LlsdToJson(LLSD(date)), "\"2026-06-11T12:34:56Z\""); + UNSCOPED_INFO("date"); + CHECK(LlsdToJson(LLSD(date)) == "\"2026-06-11T12:34:56Z\""); LLURI uri("http://example.com/path"); - ensure_equals("uri", LlsdToJson(LLSD(uri)), "\"http://example.com/path\""); + UNSCOPED_INFO("uri"); + CHECK(LlsdToJson(LLSD(uri)) == "\"http://example.com/path\""); } // non-finite reals degrade to null (JSON has no NaN/Infinity) - template<> template<> - void llsdjson_object::test<8>() + TEST_CASE_METHOD(llsdjson_data, "LlsdJson: non-finite reals degrade to null (JSON has no NaN/Infinity)", "[LlsdJson]") { - ensure_equals("nan", LlsdToJson(LLSD(std::numeric_limits::quiet_NaN())), "null"); - ensure_equals("inf", LlsdToJson(LLSD(std::numeric_limits::infinity())), "null"); - ensure_equals("-inf", LlsdToJson(LLSD(-std::numeric_limits::infinity())), "null"); + UNSCOPED_INFO("nan"); + CHECK(LlsdToJson(LLSD(std::numeric_limits::quiet_NaN())) == "null"); + UNSCOPED_INFO("inf"); + CHECK(LlsdToJson(LLSD(std::numeric_limits::infinity())) == "null"); + UNSCOPED_INFO("-inf"); + CHECK(LlsdToJson(LLSD(-std::numeric_limits::infinity())) == "null"); // and stay valid inside containers LLSD map; map["bad"] = std::numeric_limits::quiet_NaN(); - ensure_equals("nan in map", LlsdToJson(map), "{\"bad\":null}"); + UNSCOPED_INFO("nan in map"); + CHECK(LlsdToJson(map) == "{\"bad\":null}"); } // structured round trip preserves everything - template<> template<> - void llsdjson_object::test<9>() + TEST_CASE_METHOD(llsdjson_data, "LlsdJson: structured round trip preserves everything", "[LlsdJson]") { LLSD src; src["int"] = 123; @@ -221,26 +266,28 @@ namespace tut LLSD dst; std::string json = LlsdToJson(src); - ensure("round-trip parses: " + json, LlsdFromJsonString(json, dst)); - ensure("round-trip equality: " + json, llsd_equals(src, dst)); + UNSCOPED_INFO("round-trip parses: " + json); + REQUIRE(LlsdFromJsonString(json, dst)); + UNSCOPED_INFO("round-trip equality: " + json); + CHECK(llsd_equals(src, dst)); } // full-precision reals survive the round trip - template<> template<> - void llsdjson_object::test<10>() + TEST_CASE_METHOD(llsdjson_data, "LlsdJson: full-precision reals survive the round trip", "[LlsdJson]") { F64 vals[] = { 0.1, 1.0/3.0, 2.718281828459045, 1e-300, 1.7976931348623157e308 }; for (F64 v : vals) { LLSD parsed; - ensure("real parses", LlsdFromJsonString(LlsdToJson(LLSD(v)), parsed)); - ensure_equals("real precision", parsed.asReal(), v); + UNSCOPED_INFO("real parses"); + REQUIRE(LlsdFromJsonString(LlsdToJson(LLSD(v)), parsed)); + UNSCOPED_INFO("real precision"); + REQUIRE(parsed.asReal() == v); } } // parse failures report errors and clear the output - template<> template<> - void llsdjson_object::test<11>() + TEST_CASE_METHOD(llsdjson_data, "LlsdJson: parse failures report errors and clear the output", "[LlsdJson]") { const char* bad[] = { @@ -262,42 +309,49 @@ namespace tut LLSD out = LLSD::emptyMap(); std::string errmsg; bool ok = LlsdFromJsonString(json, out, &errmsg); - ensure(std::string("rejects: ") + json, !ok); - ensure(std::string("errmsg set for: ") + json, !errmsg.empty()); - ensure_equals(std::string("output cleared for: ") + json, - out.type(), LLSD::TypeUndefined); + UNSCOPED_INFO(std::string("rejects: ") + json); + REQUIRE_FALSE(ok); + UNSCOPED_INFO(std::string("errmsg set for: ") + json); + REQUIRE_FALSE(errmsg.empty()); + UNSCOPED_INFO(std::string("output cleared for: ") + json); + REQUIRE(out.type() == LLSD::TypeUndefined); } // errmsg pointer is optional LLSD out; - ensure("no errmsg crash", !LlsdFromJsonString("{", out)); + UNSCOPED_INFO("no errmsg crash"); + CHECK_FALSE(LlsdFromJsonString("{", out)); } // unicode object keys - template<> template<> - void llsdjson_object::test<12>() + TEST_CASE_METHOD(llsdjson_data, "LlsdJson: unicode object keys round-trip", "[LlsdJson]") { LLSD src; src["caf\xC3\xA9"] = 1; LLSD dst; - ensure("unicode key parses", LlsdFromJsonString(LlsdToJson(src), dst)); - ensure_equals("unicode key value", dst["caf\xC3\xA9"].asInteger(), 1); + UNSCOPED_INFO("unicode key parses"); + REQUIRE(LlsdFromJsonString(LlsdToJson(src), dst)); + UNSCOPED_INFO("unicode key value"); + CHECK(dst["caf\xC3\xA9"].asInteger() == 1); } // padded_string overload parses in place - template<> template<> - void llsdjson_object::test<13>() + TEST_CASE_METHOD(llsdjson_data, "LlsdJson: padded_string overload parses in place", "[LlsdJson]") { LLSD out; - ensure("padded parse", - LlsdFromJsonString(simdjson::padded_string(std::string("{\"a\": [1, 2.5, \"three\"]}")), out)); - ensure_equals("padded int", out["a"][0].asInteger(), 1); - ensure_equals("padded real", out["a"][1].asReal(), 2.5); - ensure_equals("padded string", out["a"][2].asString(), "three"); + UNSCOPED_INFO("padded parse"); + REQUIRE(LlsdFromJsonString(simdjson::padded_string(std::string("{\"a\": [1, 2.5, \"three\"]}")), out)); + UNSCOPED_INFO("padded int"); + CHECK(out["a"][0].asInteger() == 1); + UNSCOPED_INFO("padded real"); + CHECK(out["a"][1].asReal() == 2.5); + UNSCOPED_INFO("padded string"); + CHECK(out["a"][2].asString() == "three"); std::string errmsg; - ensure("padded parse fails", - !LlsdFromJsonString(simdjson::padded_string(std::string("{")), out, &errmsg)); - ensure("padded errmsg set", !errmsg.empty()); + UNSCOPED_INFO("padded parse fails"); + REQUIRE_FALSE(LlsdFromJsonString(simdjson::padded_string(std::string("{")), out, &errmsg)); + UNSCOPED_INFO("padded errmsg set"); + REQUIRE_FALSE(errmsg.empty()); } } diff --git a/indra/llcommon/tests/llsdserialize_python_test.cpp b/indra/llcommon/tests/llsdserialize_python_test.cpp index ece3cc7b30..4b86c4e607 100644 --- a/indra/llcommon/tests/llsdserialize_python_test.cpp +++ b/indra/llcommon/tests/llsdserialize_python_test.cpp @@ -46,7 +46,9 @@ #include "llstring.h" #include "hexdump.h" -#include "../test/lltut.h" +#include +#include "altestfailure.h" +#include "altestmatchers.h" #include "../test/namedtempfile.h" #include "stringize.h" #include "StringVec.h" @@ -68,7 +70,8 @@ namespace tut ARGS&&... args) { auto PYTHON(LLStringUtil::getenv("PYTHON")); - ensure("Set $PYTHON to the Python interpreter", !PYTHON.empty()); + UNSCOPED_INFO("Set $PYTHON to the Python interpreter"); + REQUIRE_FALSE(PYTHON.empty()); NamedTempFile scriptfile("py", script); @@ -82,11 +85,13 @@ namespace tut { char buffer[256]; strerror_s(buffer, errno); // C++ can infer the buffer size! :-O - ensure(STRINGIZE("Couldn't run Python " << desc << "script: " << buffer), false); + UNSCOPED_INFO((STRINGIZE("Couldn't run Python " << desc << "script: " << buffer))); + REQUIRE(false); } else { - ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc), rc, expect); + UNSCOPED_INFO(STRINGIZE(desc << " script terminated with rc " << rc)); + REQUIRE(rc == expect); } #else // LL_DARWIN, LL_LINUX @@ -98,34 +103,35 @@ namespace tut params.args.add(arg); } LLProcessPtr py(LLProcess::create(params)); - ensure(STRINGIZE("Couldn't launch " << desc << " script"), bool(py)); + UNSCOPED_INFO((STRINGIZE("Couldn't launch " << desc << " script"))); + REQUIRE(bool(py)); // Implementing timeout would mean messing with alarm() and // catching SIGALRM... later maybe... int status(0); if (waitpid(py->getProcessID(), &status, 0) == -1) { int waitpid_errno(errno); - ensure_equals(STRINGIZE("Couldn't retrieve rc from " << desc << " script: " - "waitpid() errno " << waitpid_errno), - waitpid_errno, ECHILD); + UNSCOPED_INFO((STRINGIZE("Couldn't retrieve rc from " << desc << " script: " + "waitpid() errno " << waitpid_errno))); + REQUIRE(waitpid_errno == ECHILD); } else { if (WIFEXITED(status)) { int rc(WEXITSTATUS(status)); - ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc), - rc, expect); + UNSCOPED_INFO((STRINGIZE(desc << " script terminated with rc " << rc))); + REQUIRE(rc == expect); } else if (WIFSIGNALED(status)) { - ensure(STRINGIZE(desc << " script terminated by signal " << WTERMSIG(status)), - false); + UNSCOPED_INFO((STRINGIZE(desc << " script terminated by signal " << WTERMSIG(status)))); + REQUIRE(false); } else { - ensure(STRINGIZE(desc << " script produced impossible status " << status), - false); + UNSCOPED_INFO((STRINGIZE(desc << " script produced impossible status " << status))); + REQUIRE(false); } } #endif @@ -145,24 +151,16 @@ namespace tut ~TestPythonCompatible() {} }; - typedef tut::test_group TestPythonCompatibleGroup; - typedef TestPythonCompatibleGroup::object TestPythonCompatibleObject; - TestPythonCompatibleGroup pycompat("LLSD serialize Python compatibility"); - - template<> template<> - void TestPythonCompatibleObject::test<1>() + TEST_CASE_METHOD(TestPythonCompatible, "LLSD serialize Python compatibility: python_expect() sees a nonzero exit code", "[LLSD_serialize_Python_compatibility]") { - set_test_name("verify python()"); python_expect("hello", "import sys\n" "sys.exit(17)\n", 17); // expect nonzero rc } - template<> template<> - void TestPythonCompatibleObject::test<2>() + TEST_CASE_METHOD(TestPythonCompatible, "LLSD serialize Python compatibility: python() runs a script in a NamedTempFile", "[LLSD_serialize_Python_compatibility]") { - set_test_name("verify NamedTempFile"); python("platform", "import sys\n" "print('Running on', sys.platform)\n"); @@ -272,7 +270,7 @@ namespace tut "debug = open(r'" << debug.getPath().string() << "', 'w')\n" "verify(parse_each(open(r'" << file.getPath().string() << "', 'rb')))\n";}); } - catch (const failure&) + catch (const ll_test::Failure&) { LL_DEBUGS() << "Script debug output:" << LL_ENDL; debug.peep_log(); @@ -280,44 +278,34 @@ namespace tut } } - template<> template<> - void TestPythonCompatibleObject::test<3>() + TEST_CASE_METHOD(TestPythonCompatible, "LLSD serialize Python compatibility: to Python using LLSDSerialize::serialize(LLSD_XML)", "[LLSD_serialize_Python_compatibility]") { - set_test_name("to Python using LLSDSerialize::serialize(LLSD_XML)"); toPythonUsing("LLSD_XML", [](const LLSD& sd, std::ostream& out) { LLSDSerialize::serialize(sd, out, LLSDSerialize::LLSD_XML); }); } - template<> template<> - void TestPythonCompatibleObject::test<4>() + TEST_CASE_METHOD(TestPythonCompatible, "LLSD serialize Python compatibility: to Python using LLSDSerialize::serialize(LLSD_NOTATION)", "[LLSD_serialize_Python_compatibility]") { - set_test_name("to Python using LLSDSerialize::serialize(LLSD_NOTATION)"); toPythonUsing("LLSD_NOTATION", [](const LLSD& sd, std::ostream& out) { LLSDSerialize::serialize(sd, out, LLSDSerialize::LLSD_NOTATION); }); } - template<> template<> - void TestPythonCompatibleObject::test<5>() + TEST_CASE_METHOD(TestPythonCompatible, "LLSD serialize Python compatibility: to Python using LLSDSerialize::serialize(LLSD_BINARY)", "[LLSD_serialize_Python_compatibility]") { - set_test_name("to Python using LLSDSerialize::serialize(LLSD_BINARY)"); toPythonUsing("LLSD_BINARY", [](const LLSD& sd, std::ostream& out) { LLSDSerialize::serialize(sd, out, LLSDSerialize::LLSD_BINARY); }); } - template<> template<> - void TestPythonCompatibleObject::test<6>() + TEST_CASE_METHOD(TestPythonCompatible, "LLSD serialize Python compatibility: to Python using LLSDSerialize::toXML()", "[LLSD_serialize_Python_compatibility]") { - set_test_name("to Python using LLSDSerialize::toXML()"); toPythonUsing("toXML()", LLSDSerialize::toXML); } - template<> template<> - void TestPythonCompatibleObject::test<7>() + TEST_CASE_METHOD(TestPythonCompatible, "LLSD serialize Python compatibility: to Python using LLSDSerialize::toNotation()", "[LLSD_serialize_Python_compatibility]") { - set_test_name("to Python using LLSDSerialize::toNotation()"); toPythonUsing("toNotation()", LLSDSerialize::toNotation); } @@ -388,62 +376,52 @@ namespace tut LLSD item; try { - ensure("Failed to read LLSD::Integer from Python", - itemFromStream(inf, item, parse)); - ensure_equals(item.asInteger(), 17); - ensure("Failed to read LLSD::Real from Python", - itemFromStream(inf, item, parse)); - ensure_approximately_equals("Bad LLSD::Real value from Python", - item.asReal(), 3.14, 7); // 7 bits ~= 0.01 - ensure("Failed to read LLSD::String from Python", - itemFromStream(inf, item, parse)); - ensure_equals(item.asString(), + UNSCOPED_INFO("Failed to read LLSD::Integer from Python"); + REQUIRE(itemFromStream(inf, item, parse)); + REQUIRE(item.asInteger() == 17); + UNSCOPED_INFO("Failed to read LLSD::Real from Python"); + REQUIRE(itemFromStream(inf, item, parse)); + UNSCOPED_INFO("Bad LLSD::Real value from Python"); + REQUIRE_THAT(item.asReal(), ll_test::WithinBits(3.14, 7)); // 7 bits ~= 0.01 + UNSCOPED_INFO("Failed to read LLSD::String from Python"); + REQUIRE(itemFromStream(inf, item, parse)); + REQUIRE(item.asString() == "This string\n" "has several\n" "lines."); } - catch (const tut::failure& err) + catch (const ll_test::Failure&) { - std::cout << "for " << err.what() << ", item = " << item << std::endl; + std::cout << "for item = " << item << std::endl; throw; } } - template<> template<> - void TestPythonCompatibleObject::test<8>() + TEST_CASE_METHOD(TestPythonCompatible, "LLSD serialize Python compatibility: from Python XML using LLSDSerialize::deserialize()", "[LLSD_serialize_Python_compatibility]") { - set_test_name("from Python XML using LLSDSerialize::deserialize()"); fromPythonUsing("format_xml"); } - template<> template<> - void TestPythonCompatibleObject::test<9>() + TEST_CASE_METHOD(TestPythonCompatible, "LLSD serialize Python compatibility: from Python notation using LLSDSerialize::deserialize()", "[LLSD_serialize_Python_compatibility]") { - set_test_name("from Python notation using LLSDSerialize::deserialize()"); fromPythonUsing("format_notation"); } - template<> template<> - void TestPythonCompatibleObject::test<10>() + TEST_CASE_METHOD(TestPythonCompatible, "LLSD serialize Python compatibility: from Python binary using LLSDSerialize::deserialize()", "[LLSD_serialize_Python_compatibility]") { - set_test_name("from Python binary using LLSDSerialize::deserialize()"); fromPythonUsing("format_binary"); } - template<> template<> - void TestPythonCompatibleObject::test<11>() + TEST_CASE_METHOD(TestPythonCompatible, "LLSD serialize Python compatibility: from Python XML using fromXML()", "[LLSD_serialize_Python_compatibility]") { - set_test_name("from Python XML using fromXML()"); // fromXML()'s optional 3rd param isn't max_bytes, it's emit_errors fromPythonUsing("format_xml", [](std::istream& istr, LLSD& data, llssize) { return LLSDSerialize::fromXML(data, istr) > 0; }); } - template<> template<> - void TestPythonCompatibleObject::test<12>() + TEST_CASE_METHOD(TestPythonCompatible, "LLSD serialize Python compatibility: from Python notation using fromNotation()", "[LLSD_serialize_Python_compatibility]") { - set_test_name("from Python notation using fromNotation()"); fromPythonUsing("format_notation", [](std::istream& istr, LLSD& data, llssize max_bytes) { return LLSDSerialize::fromNotation(data, istr, max_bytes) > 0; }); diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp index 840674f7d2..5ab32a7e53 100644 --- a/indra/llcommon/tests/llsdserialize_test.cpp +++ b/indra/llcommon/tests/llsdserialize_test.cpp @@ -45,7 +45,8 @@ typedef U32 uint32_t; #include "llformat.h" #include "llmemorystream.h" -#include "../test/lltut.h" +#include +#include "altestmatchers.h" #include "stringize.h" #include "wrapllerrs.h" #include @@ -58,7 +59,7 @@ static std::vector string_to_vector(const std::string& str) return std::vector(str.begin(), str.end()); } -namespace tut +namespace { struct sd_xml_data { @@ -72,16 +73,12 @@ namespace tut { std::ostringstream ostr; mFormatter->format(mSD, ostr); - ensure_equals(name, ostr.str(), expected); + UNSCOPED_INFO(name); + REQUIRE(ostr.str() == expected); } }; - typedef test_group sd_xml_test; - typedef sd_xml_test::object sd_xml_object; - tut::sd_xml_test sd_xml_stream("LLSDXMLFormatter"); - - template<> template<> - void sd_xml_object::test<1>() + TEST_CASE_METHOD(sd_xml_data, "LLSDXMLFormatter: formats each scalar type", "[LLSDXMLFormatter]") { // random atomic tests std::string expected; @@ -129,8 +126,7 @@ namespace tut xml_test("binary", expected); } - template<> template<> - void sd_xml_object::test<2>() + TEST_CASE_METHOD(sd_xml_data, "LLSDXMLFormatter: booleans format as words or digits by boolalpha", "[LLSDXMLFormatter]") { // tests with boolean values. std::string expected; @@ -153,8 +149,7 @@ namespace tut } - template<> template<> - void sd_xml_object::test<3>() + TEST_CASE_METHOD(sd_xml_data, "LLSDXMLFormatter: reals format shortest by default or by realFormat", "[LLSDXMLFormatter]") { // tests with real values. std::string expected; @@ -188,8 +183,7 @@ namespace tut xml_test("no decimal real number", expected); } - template<> template<> - void sd_xml_object::test<4>() + TEST_CASE_METHOD(sd_xml_data, "LLSDXMLFormatter: arrays format empty and with elements", "[LLSDXMLFormatter]") { // tests with arrays std::string expected; @@ -207,8 +201,7 @@ namespace tut xml_test("2 element array", expected); } - template<> template<> - void sd_xml_object::test<5>() + TEST_CASE_METHOD(sd_xml_data, "LLSDXMLFormatter: maps format empty and with their keys sorted", "[LLSDXMLFormatter]") { // tests with arrays std::string expected; @@ -226,8 +219,7 @@ namespace tut xml_test("2 element map", expected); } - template<> template<> - void sd_xml_object::test<6>() + TEST_CASE_METHOD(sd_xml_data, "LLSDXMLFormatter: binary formats as base64", "[LLSDXMLFormatter]") { // tests with binary std::string expected; @@ -242,8 +234,7 @@ namespace tut xml_test("binary", expected); } - template<> template<> - void sd_xml_object::test<7>() + TEST_CASE_METHOD(sd_xml_data, "LLSDXMLFormatter: a single-line document deserializes without a spurious error", "[LLSDXMLFormatter]") { // A complete, single-line legacy document (no embedded '\n') is // read entirely into the header buffer by LLSDSerialize::deserialize and @@ -258,18 +249,19 @@ namespace tut std::istringstream input(xml); LLSD parsed; bool ok = LLSDSerialize::deserialize(parsed, input, xml.size()); - ensure(STRINGIZE("deserialize " << xml << " succeeded"), ok); - ensure_equals(STRINGIZE("deserialize " << xml << " value"), parsed, expected); - ensure(STRINGIZE("no spurious parse error for " << xml), - capture.messageWith("Unexpected XML parsing error", false).empty()); + UNSCOPED_INFO((STRINGIZE("deserialize " << xml << " succeeded"))); + REQUIRE(ok); + UNSCOPED_INFO((STRINGIZE("deserialize " << xml << " value"))); + REQUIRE_THAT(parsed, ll_test::LLSDEquals(expected)); + UNSCOPED_INFO((STRINGIZE("no spurious parse error for " << xml))); + REQUIRE(capture.messageWith("Unexpected XML parsing error", false).empty()); }; deserialize_no_spurious_error("\n", LLSD::emptyMap()); deserialize_no_spurious_error("\n", LLSD::emptyArray()); } - template<> template<> - void sd_xml_object::test<8>() + TEST_CASE_METHOD(sd_xml_data, "LLSDXMLFormatter: format restores the stream's precision and exception bits", "[LLSDXMLFormatter]") { // Test that stream state (precision and exceptions) is correctly restored after format() { @@ -287,15 +279,16 @@ namespace tut S32 result = mFormatter->format(mSD, ostr); // Verify formatting succeeded - ensure("format should succeed", result >= 0); + UNSCOPED_INFO("format should succeed"); + CHECK(result >= 0); // Verify precision was restored - ensure_equals("precision should be restored", - ostr.precision(), 10); + UNSCOPED_INFO("precision should be restored"); + CHECK(ostr.precision() == 10); // Verify exceptions were restored - ensure_equals("exception bits should be restored", - ostr.exceptions(), original_exceptions); + UNSCOPED_INFO("exception bits should be restored"); + CHECK(ostr.exceptions() == original_exceptions); } // Test with no bits set @@ -308,15 +301,16 @@ namespace tut S32 result = mFormatter->format(mSD, ostr); // Verify formatting succeeded - ensure("format should succeed", result >= 0); + UNSCOPED_INFO("format should succeed"); + CHECK(result >= 0); // Verify exceptions were removed - ensure_equals("exception bits should remain unchanged", - ostr.exceptions(), original_exceptions); + UNSCOPED_INFO("exception bits should remain unchanged"); + CHECK(ostr.exceptions() == original_exceptions); // Verify precision was still restored even on failure - ensure_equals("precision should be restored even on stream failure", - ostr.precision(), (std::streamsize)5); + UNSCOPED_INFO("precision should be restored even on stream failure"); + CHECK(ostr.precision() == (std::streamsize)5); } } @@ -376,7 +370,8 @@ namespace tut try { - ensure_equals(msg, w, v); + UNSCOPED_INFO(msg); + REQUIRE_THAT(w, ll_test::LLSDEquals(v)); } catch (...) { @@ -582,97 +577,6 @@ namespace tut checkRoundTrip(msg + " many nested maps", v); } - typedef tut::test_group TestLLSDSerializeGroup; - typedef TestLLSDSerializeGroup::object TestLLSDSerializeObject; - TestLLSDSerializeGroup gTestLLSDSerializeGroup("llsd serialization"); - - template<> template<> - void TestLLSDSerializeObject::test<1>() - { - setFormatterParser(new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_PRETTY_BINARY), - new LLSDNotationParser()); - doRoundTripTests("pretty binary notation serialization"); - } - - template<> template<> - void TestLLSDSerializeObject::test<2>() - { - setFormatterParser(new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_NONE), - new LLSDNotationParser()); - doRoundTripTests("raw binary notation serialization"); - } - - template<> template<> - void TestLLSDSerializeObject::test<3>() - { - setFormatterParser(new LLSDXMLFormatter(), new LLSDXMLParser()); - doRoundTripTests("xml serialization"); - } - - template<> template<> - void TestLLSDSerializeObject::test<4>() - { - setFormatterParser(new LLSDBinaryFormatter(), new LLSDBinaryParser()); - doRoundTripTests("binary serialization"); - } - - template<> template<> - void TestLLSDSerializeObject::test<5>() - { - mFormatter = [](const LLSD& sd, std::ostream& str) - { - LLSDSerialize::serialize(sd, str, LLSDSerialize::LLSD_BINARY); - }; - setParser(LLSDSerialize::deserialize); - doRoundTripTests("serialize(LLSD_BINARY)"); - }; - - template<> template<> - void TestLLSDSerializeObject::test<6>() - { - mFormatter = [](const LLSD& sd, std::ostream& str) - { - LLSDSerialize::serialize(sd, str, LLSDSerialize::LLSD_XML); - }; - setParser(LLSDSerialize::deserialize); - doRoundTripTests("serialize(LLSD_XML)"); - }; - - template<> template<> - void TestLLSDSerializeObject::test<7>() - { - mFormatter = [](const LLSD& sd, std::ostream& str) - { - LLSDSerialize::serialize(sd, str, LLSDSerialize::LLSD_NOTATION); - }; - setParser(LLSDSerialize::deserialize); - // In this test, serialize(LLSD_NOTATION) emits a header recognized by - // deserialize(). - doRoundTripTests("serialize(LLSD_NOTATION)"); - }; - - template<> template<> - void TestLLSDSerializeObject::test<8>() - { - setFormatterParser(new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_NONE), - new LLSDNotationParser()); - setParser(LLSDSerialize::deserialize); - // This is an interesting test because LLSDNotationFormatter does not - // emit an llsd/notation header. - doRoundTripTests("LLSDNotationFormatter -> deserialize"); - }; - - template<> template<> - void TestLLSDSerializeObject::test<9>() - { - setFormatterParser(new LLSDXMLFormatter(false, "", LLSDFormatter::OPTIONS_NONE), - new LLSDXMLParser()); - setParser(LLSDSerialize::deserialize); - // This is an interesting test because LLSDXMLFormatter does not - // emit an LLSD/XML header. - doRoundTripTests("LLSDXMLFormatter -> deserialize"); - }; - /*==========================================================================*| // We do not expect this test to succeed. Without a header, neither // notation LLSD nor binary LLSD reliably start with a distinct character, @@ -690,174 +594,6 @@ namespace tut }; |*==========================================================================*/ - template<> template<> - void TestLLSDSerializeObject::test<10>() - { - // deserialize() with SIZE_UNLIMITED must not turn the unlimited - // byte budget into a bogus negative one when a header line is - // present; sized payloads (binary strings) used to fail the - // resulting negative budget check. - LLSD sd; - sd["foo"] = "bar"; - sd["bin"] = LLSD::Binary{1, 2, 3}; - - for (auto fmt : {LLSDSerialize::LLSD_BINARY, - LLSDSerialize::LLSD_XML, - LLSDSerialize::LLSD_NOTATION}) - { - std::stringstream stream; - LLSDSerialize::serialize(sd, stream, fmt); - LLSD parsed; - ensure(STRINGIZE("deserialize unlimited fmt " << int(fmt)), - LLSDSerialize::deserialize(parsed, stream, LLSDSerialize::SIZE_UNLIMITED)); - ensure_equals(STRINGIZE("deserialize unlimited fmt " << int(fmt) << " value"), - parsed, sd); - } - } - - template<> template<> - void TestLLSDSerializeObject::test<11>() - { - // strip_deprecated_header() must consume the newline following the - // header (the binary parser does not tolerate leading whitespace) - // and report the actual number of bytes skipped. - LLSD sd; - sd["int"] = 17; - sd["str"] = "hello"; - - std::stringstream binstream; - LLSDSerialize::toBinary(sd, binstream); - const std::string body = binstream.str(); - - // header with trailing newline, as LLSDSerialize::serialize() writes it - { - std::string payload = "\n" + body; - std::vector buf(payload.begin(), payload.end()); - llssize cur_size = (llssize)buf.size(); - llssize header_size = 0; - char* p = strip_deprecated_header(buf.data(), cur_size, &header_size); - ensure_equals("stripped header+newline", header_size, llssize(18)); - ensure_equals("stripped size", cur_size, llssize(body.size())); - LLMemoryStream mstr((U8*)p, (S32)cur_size); - LLSD parsed; - ensure("parse after strip", - LLSDSerialize::fromBinary(parsed, mstr, cur_size) > 0); - ensure_equals("value after strip", parsed, sd); - } - - // bare header with no newline - { - std::string payload = "" + body; - std::vector buf(payload.begin(), payload.end()); - llssize cur_size = (llssize)buf.size(); - llssize header_size = 0; - char* p = strip_deprecated_header(buf.data(), cur_size, &header_size); - ensure_equals("stripped bare header", header_size, llssize(17)); - ensure_equals("stripped bare size", cur_size, llssize(body.size())); - LLMemoryStream mstr((U8*)p, (S32)cur_size); - LLSD parsed; - ensure("parse after bare strip", - LLSDSerialize::fromBinary(parsed, mstr, cur_size) > 0); - ensure_equals("value after bare strip", parsed, sd); - } - } - - template<> template<> - void TestLLSDSerializeObject::test<12>() - { - // zip_llsd/unzip_llsd round trip, and clean rejection of a - // corrupted compressed stream - LLSD sd; - sd["message"] = "destination unknown"; - sd["mode"] = 7; - LLSD arr = LLSD::emptyArray(); - for (S32 i = 0; i < 256; ++i) - { - arr.append(i * 0.25); - } - sd["samples"] = arr; - - std::string compressed = zip_llsd(sd); - ensure("zip_llsd produced data", !compressed.empty()); - - LLSD out; - ensure_equals("unzip_llsd ok", - LLUZipHelper::unzip_llsd(out, - (const U8*)compressed.data(), - (S32)compressed.size()), - LLUZipHelper::ZR_OK); - ensure_equals("unzip_llsd round trip", out, sd); - - std::string corrupt(compressed); - for (size_t i = corrupt.size() / 2; i < corrupt.size(); ++i) - { - corrupt[i] ^= 0x5A; - } - LLSD out2; - ensure("corrupt unzip_llsd rejected", - LLUZipHelper::unzip_llsd(out2, - (const U8*)corrupt.data(), - (S32)corrupt.size()) != LLUZipHelper::ZR_OK); - } - - template<> template<> - void TestLLSDSerializeObject::test<13>() - { - // unzip_llsdNavMesh: gzip round trip, and clean failure on a - // corrupted stream after partial output has been produced (this - // path used to realloc a freed buffer and double-free on error) - std::string payload; - payload.reserve(96 * 1024); - while (payload.size() < 96 * 1024) - { - payload += "navmesh payload block "; - } - - z_stream strm{}; - int ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, - 15 + 16, // gzip wrapper - 8, Z_DEFAULT_STRATEGY); - ensure_equals("deflateInit2", ret, Z_OK); - std::string compressed(deflateBound(&strm, (uLong)payload.size()) + 32, '\0'); - strm.next_in = (Bytef*)payload.data(); - strm.avail_in = (uInt)payload.size(); - strm.next_out = (Bytef*)&compressed[0]; - strm.avail_out = (uInt)compressed.size(); - ret = deflate(&strm, Z_FINISH); - ensure_equals("deflate finish", ret, Z_STREAM_END); - compressed.resize(compressed.size() - strm.avail_out); - deflateEnd(&strm); - - { - std::istringstream in(compressed); - bool valid = false; - size_t outsize = 0; - U8* out = unzip_llsdNavMesh(valid, outsize, in, (S32)compressed.size()); - ensure("navmesh unzip valid", valid); - ensure("navmesh unzip buffer", out != NULL); - ensure_equals("navmesh unzip size", outsize, payload.size()); - ensure("navmesh unzip content", - payload.compare(0, payload.size(), - (const char*)out, outsize) == 0); - free(out); - } - - { - // corrupt the deflate tail and gzip trailer - std::string corrupt(compressed); - for (size_t i = corrupt.size() - 16; i < corrupt.size(); ++i) - { - corrupt[i] ^= 0x5A; - } - std::istringstream in(corrupt); - bool valid = true; - size_t outsize = 0; - U8* out = unzip_llsdNavMesh(valid, outsize, in, (S32)corrupt.size()); - ensure("corrupt navmesh rejected", !valid); - ensure("corrupt navmesh returns null", out == NULL); - } - } - /** * @class TestLLSDParsing * @brief Base class for of a parse tester. @@ -884,14 +620,16 @@ namespace tut LLSD parsed_result; mParser->reset(); // reset() call is needed since test code re-uses mParser S32 parsed_count = mParser->parse(input, parsed_result, in.size(), depth_limit); - ensure_equals(msg.c_str(), parsed_result, expected_value); + UNSCOPED_INFO(msg.c_str()); + REQUIRE_THAT(parsed_result, ll_test::LLSDEquals(expected_value)); // This count check is really only useful for expected // parse failures, since the ensures equal will already // require equality. std::string count_msg(msg); count_msg += " (count)"; - ensure_equals(count_msg, parsed_count, expected_count); + UNSCOPED_INFO(count_msg); + REQUIRE(parsed_count == expected_count); } LLPointer mParser; @@ -908,12 +646,7 @@ namespace tut TestLLSDXMLParsing() {} }; - typedef tut::test_group TestLLSDXMLParsingGroup; - typedef TestLLSDXMLParsingGroup::object TestLLSDXMLParsingObject; - TestLLSDXMLParsingGroup gTestLLSDXMLParsingGroup("llsd XML parsing"); - - template<> template<> - void TestLLSDXMLParsingObject::test<1>() + TEST_CASE_METHOD(TestLLSDXMLParsing, "llsd XML parsing: xml that is not llsd fails to parse", "[llsd_XML_parsing]") { // test handling of xml not recognized as llsd results in an // LLSD Undefined @@ -940,8 +673,7 @@ namespace tut } - template<> template<> - void TestLLSDXMLParsingObject::test<2>() + TEST_CASE_METHOD(TestLLSDXMLParsing, "llsd XML parsing: a value of unknown type parses as undefined", "[llsd_XML_parsing]") { // test handling of unrecognized or unparseable llsd values LLSD v; @@ -960,8 +692,7 @@ namespace tut static_cast(v.size()) + 1); } - template<> template<> - void TestLLSDXMLParsingObject::test<3>() + TEST_CASE_METHOD(TestLLSDXMLParsing, "llsd XML parsing: html inside maps and arrays is skipped", "[llsd_XML_parsing]") { // test handling of nested bad data @@ -1041,8 +772,7 @@ namespace tut static_cast(v.size()) + 1); } - template<> template<> - void TestLLSDXMLParsingObject::test<4>() + TEST_CASE_METHOD(TestLLSDXMLParsing, "llsd XML parsing: base64 binary parses on one line or many", "[llsd_XML_parsing]") { // test handling of binary object in XML std::string xml; @@ -1078,8 +808,7 @@ namespace tut 1); } - template<> template<> - void TestLLSDXMLParsingObject::test<5>() + TEST_CASE_METHOD(TestLLSDXMLParsing, "llsd XML parsing: deeply nested maps parse", "[llsd_XML_parsing]") { // test deeper nested levels LLSD level_5 = LLSD::emptyMap(); level_5["level_5"] = 42.f; @@ -1114,8 +843,7 @@ namespace tut } - template<> template<> - void TestLLSDXMLParsingObject::test<6>() + TEST_CASE_METHOD(TestLLSDXMLParsing, "llsd XML parsing: empty binary parses as empty Binary not undefined", "[llsd_XML_parsing]") { // empty binary must parse to an empty LLSD::Binary, not undef ensureParse( @@ -1154,891 +882,1101 @@ namespace tut TestLLSDNotationParsing() {} }; - typedef tut::test_group TestLLSDNotationParsingGroup; - typedef TestLLSDNotationParsingGroup::object TestLLSDNotationParsingObject; - TestLLSDNotationParsingGroup gTestLLSDNotationParsingGroup( - "llsd notation parsing"); + /** + * @class TestLLSDBinaryParsing + * @brief Concrete instance of a parse tester. + */ + class TestLLSDBinaryParsing : public TestLLSDParsing + { + public: + TestLLSDBinaryParsing() {} + }; - template<> template<> - void TestLLSDNotationParsingObject::test<1>() + TEST_CASE_METHOD(TestLLSDBinaryParsing, "llsd binary parsing: a string parses only with the right size", "[llsd_binary_parsing]") { - // test handling of xml not recognized as llsd results in an - // LLSD Undefined - ensureParse( - "malformed notation map", - "{'ha ha'", - LLSD(), - LLSDParser::PARSE_FAILURE); - ensureParse( - "malformed notation array", - "['ha ha'", - LLSD(), - LLSDParser::PARSE_FAILURE); - ensureParse( - "malformed notation string", - "'ha ha", - LLSD(), - LLSDParser::PARSE_FAILURE); + std::vector vec; + vec.resize(6); + vec[0] = 'a'; vec[1] = 'b'; vec[2] = 'c'; + vec[3] = '3'; vec[4] = '2'; vec[5] = '1'; + std::string string_expected((char*)&vec[0], vec.size()); + LLSD value = string_expected; + + vec.resize(11); + vec[0] = 's'; // for string + vec[5] = 'a'; vec[6] = 'b'; vec[7] = 'c'; + vec[8] = '3'; vec[9] = '2'; vec[10] = '1'; + + uint32_t size = htonl(6); + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_good((char*)&vec[0], vec.size()); + ensureParse("correct string parse", str_good, value, 1); + + size = htonl(7); + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_bad_1((char*)&vec[0], vec.size()); ensureParse( - "bad notation noise", - "g48ejlnfr", + "incorrect size string parse", + str_bad_1, LLSD(), LLSDParser::PARSE_FAILURE); - } - template<> template<> - void TestLLSDNotationParsingObject::test<2>() - { - ensureParse("valid undef", "!", LLSD(), 1); + size = htonl(100000); + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_bad_2((char*)&vec[0], vec.size()); + ensureParse( + "incorrect size string parse", + str_bad_2, + LLSD(), + LLSDParser::PARSE_FAILURE); } - template<> template<> - void TestLLSDNotationParsingObject::test<3>() + TEST_CASE_METHOD(TestLLSDBinaryParsing, "llsd binary parsing: binary parses only with the right size", "[llsd_binary_parsing]") { - LLSD val = false; - ensureParse("valid boolean false 0", "false", val, 1); - ensureParse("valid boolean false 1", "f", val, 1); - ensureParse("valid boolean false 2", "0", val, 1); - ensureParse("valid boolean false 3", "F", val, 1); - ensureParse("valid boolean false 4", "FALSE", val, 1); - val = true; - ensureParse("valid boolean true 0", "true", val, 1); - ensureParse("valid boolean true 1", "t", val, 1); - ensureParse("valid boolean true 2", "1", val, 1); - ensureParse("valid boolean true 3", "T", val, 1); - ensureParse("valid boolean true 4", "TRUE", val, 1); - - val.clear(); - ensureParse("invalid true", "TR", val, LLSDParser::PARSE_FAILURE); - ensureParse("invalid false", "FAL", val, LLSDParser::PARSE_FAILURE); - } + std::vector vec; + vec.resize(6); + vec[0] = 'a'; vec[1] = 'b'; vec[2] = 'c'; + vec[3] = '3'; vec[4] = '2'; vec[5] = '1'; + LLSD value = vec; - template<> template<> - void TestLLSDNotationParsingObject::test<4>() - { - LLSD val = 123; - ensureParse("valid integer", "i123", val, 1); - ensureParse("explicit plus integer", "i+123", val, 1); - val = -123; - ensureParse("negative integer", "i-123", val, 1); - val.clear(); - ensureParse("invalid integer", "421", val, LLSDParser::PARSE_FAILURE); - ensureParse("empty integer", "i,", val, LLSDParser::PARSE_FAILURE); - // an oversized token must fail whole, not parse as a buffered prefix - ensureParse("oversized integer token", - "i" + std::string(100, '1'), val, LLSDParser::PARSE_FAILURE); - } + vec.resize(11); + vec[0] = 'b'; // for binary + vec[5] = 'a'; vec[6] = 'b'; vec[7] = 'c'; + vec[8] = '3'; vec[9] = '2'; vec[10] = '1'; - template<> template<> - void TestLLSDNotationParsingObject::test<5>() - { - LLSD val = 456.7; - ensureParse("valid real", "r456.7", val, 1); - ensureParse("explicit plus real", "r+456.7", val, 1); - val = -456.7; - ensureParse("negative real", "r-456.7", val, 1); - val = 1500.0; - ensureParse("scientific real", "r1.5e3", val, 1); - ensureParse("scientific real explicit plus exponent", "r1.5E+3", val, 1); - val = std::numeric_limits::infinity(); - ensureParse("infinity real", "rinf", val, 1); - val = -std::numeric_limits::infinity(); - ensureParse("negative infinity real", "r-inf", val, 1); - val.clear(); - ensureParse("invalid real", "456.7", val, LLSDParser::PARSE_FAILURE); - ensureParse("empty real", "r,", val, LLSDParser::PARSE_FAILURE); - // an oversized token must fail whole, not parse as a buffered prefix - ensureParse("oversized real token", - "r0." + std::string(600, '3'), val, LLSDParser::PARSE_FAILURE); - } + uint32_t size = htonl(6); + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_good((char*)&vec[0], vec.size()); + ensureParse("correct binary parse", str_good, value, 1); - template<> template<> - void TestLLSDNotationParsingObject::test<6>() - { - LLUUID id; - LLSD val = id; + size = htonl(7); + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_bad_1((char*)&vec[0], vec.size()); ensureParse( - "unparseable uuid", - "u123", + "incorrect size binary parse 1", + str_bad_1, LLSD(), LLSDParser::PARSE_FAILURE); - id.generate(); - val = id; - std::string uuid_str("u"); - uuid_str += id.asString(); - ensureParse("valid uuid", uuid_str.c_str(), val, 1); - } - template<> template<> - void TestLLSDNotationParsingObject::test<7>() - { - LLSD val = std::string("foolish"); - ensureParse("valid string 1", "\"foolish\"", val, 1); - val = std::string("g'day"); - ensureParse("valid string 2", "\"g'day\"", val, 1); - val = std::string("have a \"nice\" day"); - ensureParse("valid string 3", "'have a \"nice\" day'", val, 1); - val = std::string("whatever"); - ensureParse("valid string 4", "s(8)\"whatever\"", val, 1); + size = htonl(100000); + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_bad_2((char*)&vec[0], vec.size()); + ensureParse( + "incorrect size binary parse 2", + str_bad_2, + LLSD(), + LLSDParser::PARSE_FAILURE); } - template<> template<> - void TestLLSDNotationParsingObject::test<8>() + TEST_CASE_METHOD(TestLLSDBinaryParsing, "llsd binary parsing: malformed input fails to parse", "[llsd_binary_parsing]") { + // test handling of xml not recognized as llsd results in an + // LLSD Undefined ensureParse( - "invalid string 1", - "s(7)\"whatever\"", + "malformed binary map", + "{'ha ha'", LLSD(), LLSDParser::PARSE_FAILURE); ensureParse( - "invalid string 2", - "s(9)\"whatever\"", + "malformed binary array", + "['ha ha'", + LLSD(), + LLSDParser::PARSE_FAILURE); + ensureParse( + "malformed binary string", + "'ha ha", + LLSD(), + LLSDParser::PARSE_FAILURE); + ensureParse( + "bad noise", + "g48ejlnfr", LLSD(), LLSDParser::PARSE_FAILURE); } - - template<> template<> - void TestLLSDNotationParsingObject::test<9>() + TEST_CASE_METHOD(TestLLSDBinaryParsing, "llsd binary parsing: undef parses", "[llsd_binary_parsing]") { - LLSD val = LLURI("http://www.google.com"); - ensureParse("valid uri", "l\"http://www.google.com\"", val, 1); + ensureParse("valid undef", "!", LLSD(), 1); } - template<> template<> - void TestLLSDNotationParsingObject::test<10>() + TEST_CASE_METHOD(TestLLSDBinaryParsing, "llsd binary parsing: booleans parse from 0 and 1 only", "[llsd_binary_parsing]") { - LLSD val = LLDate("2007-12-28T09:22:53.10Z"); - ensureParse("valid date", "d\"2007-12-28T09:22:53.10Z\"", val, 1); - } + LLSD val = false; + ensureParse("valid boolean false 2", "0", val, 1); + val = true; + ensureParse("valid boolean true 2", "1", val, 1); - template<> template<> - void TestLLSDNotationParsingObject::test<11>() - { - std::vector vec; - vec.push_back((U8)'a'); vec.push_back((U8)'b'); vec.push_back((U8)'c'); - vec.push_back((U8)'3'); vec.push_back((U8)'2'); vec.push_back((U8)'1'); - LLSD val = vec; - ensureParse("valid binary b64", "b64\"YWJjMzIx\"", val, 1); - ensureParse("valid bainry b16", "b16\"616263333231\"", val, 1); - ensureParse("valid bainry raw", "b(6)\"abc321\"", val, 1); + val.clear(); + ensureParse("invalid true", "t", val, LLSDParser::PARSE_FAILURE); + ensureParse("invalid false", "f", val, LLSDParser::PARSE_FAILURE); } - template<> template<> - void TestLLSDNotationParsingObject::test<12>() + TEST_CASE_METHOD(TestLLSDBinaryParsing, "llsd binary parsing: a map parses only with the right sizes and terminator", "[llsd_binary_parsing]") { + std::vector vec; + vec.push_back('{'); + vec.resize(vec.size() + 4); + uint32_t size = htonl(1); + memcpy(&vec[1], &size, sizeof(uint32_t)); + vec.push_back('k'); + auto key_size_loc = vec.size(); + size = htonl(1); // 1 too short + vec.resize(vec.size() + 4); + memcpy(&vec[key_size_loc], &size, sizeof(uint32_t)); + vec.push_back('a'); vec.push_back('m'); vec.push_back('y'); + vec.push_back('i'); + auto integer_loc = vec.size(); + vec.resize(vec.size() + 4); + uint32_t val_int = htonl(23); + memcpy(&vec[integer_loc], &val_int, sizeof(uint32_t)); + std::string str_bad_1((char*)&vec[0], vec.size()); ensureParse( - "invalid -- binary length specified too long", - "b(7)\"abc321\"", + "invalid key size", + str_bad_1, LLSD(), LLSDParser::PARSE_FAILURE); + + // check with correct size, but unterminated map (missing '}') + size = htonl(3); // correct size + memcpy(&vec[key_size_loc], &size, sizeof(uint32_t)); + std::string str_bad_2((char*)&vec[0], vec.size()); ensureParse( - "invalid -- binary length specified way too long", - "b(1000000)\"abc321\"", + "valid key size, unterminated map", + str_bad_2, LLSD(), LLSDParser::PARSE_FAILURE); - } - template<> template<> - void TestLLSDNotationParsingObject::test<13>() - { + // check w/ correct size and correct map termination LLSD val; val["amy"] = 23; - val["bob"] = LLSD(); - val["cam"] = 1.23; - ensureParse("simple map", "{'amy':i23,'bob':!,'cam':r1.23}", val, 4); - - val["bob"] = LLSD::emptyMap(); - val["bob"]["vehicle"] = std::string("bicycle"); + vec.push_back('}'); + std::string str_good((char*)&vec[0], vec.size()); ensureParse( - "nested map", - "{'amy':i23,'bob':{'vehicle':'bicycle'},'cam':r1.23}", + "valid map", + str_good, val, - 5); + 2); + + // check w/ incorrect sizes and correct map termination + size = htonl(0); // 1 too few (for the map entry) + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_bad_3((char*)&vec[0], vec.size()); + ensureParse( + "invalid map too long", + str_bad_3, + LLSD(), + LLSDParser::PARSE_FAILURE); + + size = htonl(2); // 1 too many + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_bad_4((char*)&vec[0], vec.size()); + ensureParse( + "invalid map too short", + str_bad_4, + LLSD(), + LLSDParser::PARSE_FAILURE); } - template<> template<> - void TestLLSDNotationParsingObject::test<14>() + TEST_CASE_METHOD(TestLLSDBinaryParsing, "llsd binary parsing: an array parses only with the right size and terminator", "[llsd_binary_parsing]") { - LLSD val; - val.append(23); - val.append(LLSD()); - val.append(1.23); - ensureParse("simple array", "[i23,!,r1.23]", val, 4); - val[1] = LLSD::emptyArray(); - val[1].append("bicycle"); - ensureParse("nested array", "[i23,['bicycle'],r1.23]", val, 5); - } - - template<> template<> - void TestLLSDNotationParsingObject::test<15>() - { - LLSD val; - val["amy"] = 23; - val["bob"]["dogs"] = LLSD::emptyArray(); - val["bob"]["dogs"].append(LLSD::emptyMap()); - val["bob"]["dogs"][0]["name"] = std::string("groove"); - val["bob"]["dogs"][0]["breed"] = std::string("samoyed"); - val["bob"]["dogs"].append(LLSD::emptyMap()); - val["bob"]["dogs"][1]["name"] = std::string("greyley"); - val["bob"]["dogs"][1]["breed"] = std::string("chow/husky"); - val["cam"] = 1.23; - ensureParse( - "nested notation", - "{'amy':i23," - " 'bob':{'dogs':[" - "{'name':'groove', 'breed':'samoyed'}," - "{'name':'greyley', 'breed':'chow/husky'}]}," - " 'cam':r1.23}", - val, - 11); - } + std::vector vec; + vec.push_back('['); + vec.resize(vec.size() + 4); + uint32_t size = htonl(1); // 1 too short + memcpy(&vec[1], &size, sizeof(uint32_t)); + vec.push_back('"'); vec.push_back('a'); vec.push_back('m'); + vec.push_back('y'); vec.push_back('"'); vec.push_back('i'); + auto integer_loc = vec.size(); + vec.resize(vec.size() + 4); + uint32_t val_int = htonl(23); + memcpy(&vec[integer_loc], &val_int, sizeof(uint32_t)); - template<> template<> - void TestLLSDNotationParsingObject::test<16>() - { - // text to make sure that incorrect sizes bail because - std::string bad_str("s(5)\"hi\""); + std::string str_bad_1((char*)&vec[0], vec.size()); ensureParse( - "size longer than bytes left", - bad_str, + "invalid array size", + str_bad_1, LLSD(), LLSDParser::PARSE_FAILURE); - } - template<> template<> - void TestLLSDNotationParsingObject::test<17>() - { - // text to make sure that incorrect sizes bail because - std::string bad_bin("b(5)\"hi\""); + // check with correct size, but unterminated map (missing ']') + size = htonl(2); // correct size + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_bad_2((char*)&vec[0], vec.size()); ensureParse( - "size longer than bytes left", - bad_bin, + "unterminated array", + str_bad_2, LLSD(), LLSDParser::PARSE_FAILURE); - } - - template<> template<> - void TestLLSDNotationParsingObject::test<18>() - { - LLSD level_1 = LLSD::emptyMap(); level_1["level_2"] = 99; - LLSD level_0 = LLSD::emptyMap(); level_0["level_1"] = level_1; - - LLSD deep = LLSD::emptyMap(); - deep["level_0"] = level_0; - - LLSD root = LLSD::emptyMap(); - root["deep"] = deep; + // check w/ correct size and correct map termination + LLSD val; + val.append("amy"); + val.append(23); + vec.push_back(']'); + std::string str_good((char*)&vec[0], vec.size()); ensureParse( - "nested notation 3 deep", - "{'deep' : {'level_0':{'level_1':{'level_2': i99} } } }", - root, - 5, - 5); // 4 '{' plus i99 also counts as llsd, so real depth is 5 - } - - template<> template<> - void TestLLSDNotationParsingObject::test<19>() - { - LLSD level_9 = LLSD::emptyMap(); level_9["level_9"] = (S32)99; - LLSD level_8 = LLSD::emptyMap(); level_8["level_8"] = level_9; - LLSD level_7 = LLSD::emptyMap(); level_7["level_7"] = level_8; - LLSD level_6 = LLSD::emptyMap(); level_6["level_6"] = level_7; - LLSD level_5 = LLSD::emptyMap(); level_5["level_5"] = level_6; - LLSD level_4 = LLSD::emptyMap(); level_4["level_4"] = level_5; - LLSD level_3 = LLSD::emptyMap(); level_3["level_3"] = level_4; - LLSD level_2 = LLSD::emptyMap(); level_2["level_2"] = level_3; - LLSD level_1 = LLSD::emptyMap(); level_1["level_1"] = level_2; - LLSD level_0 = LLSD::emptyMap(); level_0["level_0"] = level_1; - - LLSD deep = LLSD::emptyMap(); - deep["deep"] = level_0; + "valid array", + str_good, + val, + 3); + // check with too many elements + size = htonl(3); // 1 too long + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_bad_3((char*)&vec[0], vec.size()); ensureParse( - "nested notation 10 deep", - "{'deep' : {'level_0':{'level_1':{'level_2':{'level_3':{'level_4':{'level_5':{'level_6':{'level_7':{'level_8':{'level_9':i99}" - "} } } } } } } } } }", - deep, - 12, - 15); + "array too short", + str_bad_3, + LLSD(), + LLSDParser::PARSE_FAILURE); } - template<> template<> - void TestLLSDNotationParsingObject::test<20>() + TEST_CASE_METHOD(TestLLSDBinaryParsing, "llsd binary parsing: an empty map parses", "[llsd_binary_parsing]") { - LLSD end = LLSD::emptyMap(); end["end"] = (S32)99; - - LLSD level_49 = LLSD::emptyMap(); level_49["level_49"] = end; - LLSD level_48 = LLSD::emptyMap(); level_48["level_48"] = level_49; - LLSD level_47 = LLSD::emptyMap(); level_47["level_47"] = level_48; - LLSD level_46 = LLSD::emptyMap(); level_46["level_46"] = level_47; - LLSD level_45 = LLSD::emptyMap(); level_45["level_45"] = level_46; - LLSD level_44 = LLSD::emptyMap(); level_44["level_44"] = level_45; - LLSD level_43 = LLSD::emptyMap(); level_43["level_43"] = level_44; - LLSD level_42 = LLSD::emptyMap(); level_42["level_42"] = level_43; - LLSD level_41 = LLSD::emptyMap(); level_41["level_41"] = level_42; - LLSD level_40 = LLSD::emptyMap(); level_40["level_40"] = level_41; - - LLSD level_39 = LLSD::emptyMap(); level_39["level_39"] = level_40; - LLSD level_38 = LLSD::emptyMap(); level_38["level_38"] = level_39; - LLSD level_37 = LLSD::emptyMap(); level_37["level_37"] = level_38; - LLSD level_36 = LLSD::emptyMap(); level_36["level_36"] = level_37; - LLSD level_35 = LLSD::emptyMap(); level_35["level_35"] = level_36; - LLSD level_34 = LLSD::emptyMap(); level_34["level_34"] = level_35; - LLSD level_33 = LLSD::emptyMap(); level_33["level_33"] = level_34; - LLSD level_32 = LLSD::emptyMap(); level_32["level_32"] = level_33; - LLSD level_31 = LLSD::emptyMap(); level_31["level_31"] = level_32; - LLSD level_30 = LLSD::emptyMap(); level_30["level_30"] = level_31; - - LLSD level_29 = LLSD::emptyMap(); level_29["level_29"] = level_30; - LLSD level_28 = LLSD::emptyMap(); level_28["level_28"] = level_29; - LLSD level_27 = LLSD::emptyMap(); level_27["level_27"] = level_28; - LLSD level_26 = LLSD::emptyMap(); level_26["level_26"] = level_27; - LLSD level_25 = LLSD::emptyMap(); level_25["level_25"] = level_26; - LLSD level_24 = LLSD::emptyMap(); level_24["level_24"] = level_25; - LLSD level_23 = LLSD::emptyMap(); level_23["level_23"] = level_24; - LLSD level_22 = LLSD::emptyMap(); level_22["level_22"] = level_23; - LLSD level_21 = LLSD::emptyMap(); level_21["level_21"] = level_22; - LLSD level_20 = LLSD::emptyMap(); level_20["level_20"] = level_21; - - LLSD level_19 = LLSD::emptyMap(); level_19["level_19"] = level_20; - LLSD level_18 = LLSD::emptyMap(); level_18["level_18"] = level_19; - LLSD level_17 = LLSD::emptyMap(); level_17["level_17"] = level_18; - LLSD level_16 = LLSD::emptyMap(); level_16["level_16"] = level_17; - LLSD level_15 = LLSD::emptyMap(); level_15["level_15"] = level_16; - LLSD level_14 = LLSD::emptyMap(); level_14["level_14"] = level_15; - LLSD level_13 = LLSD::emptyMap(); level_13["level_13"] = level_14; - LLSD level_12 = LLSD::emptyMap(); level_12["level_12"] = level_13; - LLSD level_11 = LLSD::emptyMap(); level_11["level_11"] = level_12; - LLSD level_10 = LLSD::emptyMap(); level_10["level_10"] = level_11; - - LLSD level_9 = LLSD::emptyMap(); level_9["level_9"] = level_10; - LLSD level_8 = LLSD::emptyMap(); level_8["level_8"] = level_9; - LLSD level_7 = LLSD::emptyMap(); level_7["level_7"] = level_8; - LLSD level_6 = LLSD::emptyMap(); level_6["level_6"] = level_7; - LLSD level_5 = LLSD::emptyMap(); level_5["level_5"] = level_6; - LLSD level_4 = LLSD::emptyMap(); level_4["level_4"] = level_5; - LLSD level_3 = LLSD::emptyMap(); level_3["level_3"] = level_4; - LLSD level_2 = LLSD::emptyMap(); level_2["level_2"] = level_3; - LLSD level_1 = LLSD::emptyMap(); level_1["level_1"] = level_2; - LLSD level_0 = LLSD::emptyMap(); level_0["level_0"] = level_1; - - LLSD deep = LLSD::emptyMap(); - deep["deep"] = level_0; - + std::vector vec; + vec.push_back('{'); + vec.resize(vec.size() + 4); + memset(&vec[1], 0, 4); + vec.push_back('}'); + std::string str_good((char*)&vec[0], vec.size()); + LLSD val = LLSD::emptyMap(); ensureParse( - "nested notation deep", - "{'deep':" - "{'level_0' :{'level_1' :{'level_2' :{'level_3' :{'level_4' :{'level_5' :{'level_6' :{'level_7' :{'level_8' :{'level_9' :" - "{'level_10':{'level_11':{'level_12':{'level_13':{'level_14':{'level_15':{'level_16':{'level_17':{'level_18':{'level_19':" - "{'level_20':{'level_21':{'level_22':{'level_23':{'level_24':{'level_25':{'level_26':{'level_27':{'level_28':{'level_29':" - "{'level_30':{'level_31':{'level_32':{'level_33':{'level_34':{'level_35':{'level_36':{'level_37':{'level_38':{'level_39':" - "{'level_40':{'level_41':{'level_42':{'level_43':{'level_44':{'level_45':{'level_46':{'level_47':{'level_48':{'level_49':" - "{'end':i99}" - "} } } } } } } } } }" - "} } } } } } } } } }" - "} } } } } } } } } }" - "} } } } } } } } } }" - "} } } } } } } } } }" - "}", - deep, - 53); + "empty map", + str_good, + val, + 1); } - template<> template<> - void TestLLSDNotationParsingObject::test<21>() + TEST_CASE_METHOD(TestLLSDBinaryParsing, "llsd binary parsing: an empty array parses", "[llsd_binary_parsing]") { + std::vector vec; + vec.push_back('['); + vec.resize(vec.size() + 4); + memset(&vec[1], 0, 4); + vec.push_back(']'); + std::string str_good((char*)&vec[0], vec.size()); + LLSD val = LLSD::emptyArray(); ensureParse( - "nested notation 10 deep", - "{'deep' : {'level_0':{'level_1':{'level_2':{'level_3':{'level_4':{'level_5':{'level_6':{'level_7':{'level_8':{'level_9':i99}" - "} } } } } } } } } }", - LLSD(), - LLSDParser::PARSE_FAILURE, - 9); + "empty array", + str_good, + val, + 1); } - template<> template<> - void TestLLSDNotationParsingObject::test<22>() + TEST_CASE_METHOD(TestLLSDBinaryParsing, "llsd binary parsing: a uri parses only with the right length", "[llsd_binary_parsing]") { - // b16 edge cases - ensureParse("empty b16", "b16\"\"", LLSD(LLSD::Binary()), 1); - // an odd number of hex digits cannot form whole bytes + std::vector vec; + vec.push_back('l'); + vec.resize(vec.size() + 4); + uint32_t size = htonl(14); // 1 too long + memcpy(&vec[1], &size, sizeof(uint32_t)); + vec.push_back('h'); vec.push_back('t'); vec.push_back('t'); + vec.push_back('p'); vec.push_back(':'); vec.push_back('/'); + vec.push_back('/'); vec.push_back('s'); vec.push_back('l'); + vec.push_back('.'); vec.push_back('c'); vec.push_back('o'); + vec.push_back('m'); + std::string str_bad((char*)&vec[0], vec.size()); ensureParse( - "odd-length b16", - "b16\"616\"", + "invalid uri length size", + str_bad, LLSD(), LLSDParser::PARSE_FAILURE); - // unterminated b16 data must fail rather than spin forever + + LLSD val; + val = LLURI("http://sl.com"); + size = htonl(13); // correct length + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_good((char*)&vec[0], vec.size()); ensureParse( - "unterminated b16", - "b16\"6162", - LLSD(), - LLSDParser::PARSE_FAILURE); + "valid key size", + str_good, + val, + 1); } - template<> template<> - void TestLLSDNotationParsingObject::test<23>() + TEST_CASE_METHOD(TestLLSDBinaryParsing, "llsd binary parsing: hostile and negative sizes fail", "[llsd_binary_parsing]") { - // b64 edge cases - ensureParse("empty b64", "b64\"\"", LLSD(LLSD::Binary()), 1); - + // hostile size fields must not be trusted std::vector vec; - vec.push_back((U8)'a'); vec.push_back((U8)'b'); vec.push_back((U8)'c'); - vec.push_back((U8)'3'); vec.push_back((U8)'2'); vec.push_back((U8)'1'); - LLSD val = vec; - // forgiving base64: ASCII whitespace inside the payload is ignored - ensureParse("b64 with whitespace", "b64\"YWJj\nMzIx\"", val, 1); - ensureParse( - "invalid b64 data", - "b64\"Y!WJjMzIx\"", - LLSD(), - LLSDParser::PARSE_FAILURE); + vec.push_back('['); + vec.resize(vec.size() + 4); + uint32_t size = htonl(0x7FFFFFFF); + memcpy(&vec[1], &size, sizeof(uint32_t)); + vec.push_back('i'); + auto integer_loc = vec.size(); + vec.resize(vec.size() + 4); + uint32_t val_int = htonl(23); + memcpy(&vec[integer_loc], &val_int, sizeof(uint32_t)); + vec.push_back(']'); + std::string str_huge((char*)&vec[0], vec.size()); ensureParse( - "unterminated b64", - "b64\"YWJj", + "array size lies huge", + str_huge, LLSD(), LLSDParser::PARSE_FAILURE); - } - template<> template<> - void TestLLSDNotationParsingObject::test<24>() - { - // negative sizes must not be fed to resize() - ensureParse( - "negative raw binary size", - "b(-5)\"hi\"", - LLSD(), - LLSDParser::PARSE_FAILURE); + // negative (unrepresentable) sizes used to sign-extend into a + // multi-gigabyte reserve() request + size = htonl(0xFFFFFFFF); + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_neg_array((char*)&vec[0], vec.size()); ensureParse( - "negative raw string size", - "s(-5)\"hi\"", + "negative array size", + str_neg_array, LLSD(), LLSDParser::PARSE_FAILURE); - } - - /** - * @class TestLLSDBinaryParsing - * @brief Concrete instance of a parse tester. - */ - class TestLLSDBinaryParsing : public TestLLSDParsing - { - public: - TestLLSDBinaryParsing() {} - }; - - typedef tut::test_group TestLLSDBinaryParsingGroup; - typedef TestLLSDBinaryParsingGroup::object TestLLSDBinaryParsingObject; - TestLLSDBinaryParsingGroup gTestLLSDBinaryParsingGroup( - "llsd binary parsing"); - - template<> template<> - void TestLLSDBinaryParsingObject::test<1>() - { - std::vector vec; - vec.resize(6); - vec[0] = 'a'; vec[1] = 'b'; vec[2] = 'c'; - vec[3] = '3'; vec[4] = '2'; vec[5] = '1'; - std::string string_expected((char*)&vec[0], vec.size()); - LLSD value = string_expected; - vec.resize(11); - vec[0] = 's'; // for string - vec[5] = 'a'; vec[6] = 'b'; vec[7] = 'c'; - vec[8] = '3'; vec[9] = '2'; vec[10] = '1'; - - uint32_t size = htonl(6); - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_good((char*)&vec[0], vec.size()); - ensureParse("correct string parse", str_good, value, 1); - - size = htonl(7); - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_bad_1((char*)&vec[0], vec.size()); + vec[0] = '{'; + vec.back() = '}'; + std::string str_neg_map((char*)&vec[0], vec.size()); ensureParse( - "incorrect size string parse", - str_bad_1, + "negative map size", + str_neg_map, LLSD(), LLSDParser::PARSE_FAILURE); - size = htonl(100000); - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_bad_2((char*)&vec[0], vec.size()); + std::vector bin; + bin.push_back('b'); + bin.resize(bin.size() + 4); + memcpy(&bin[1], &size, sizeof(uint32_t)); + std::string str_neg_bin((char*)&bin[0], bin.size()); ensureParse( - "incorrect size string parse", - str_bad_2, + "negative binary size", + str_neg_bin, LLSD(), LLSDParser::PARSE_FAILURE); } - template<> template<> - void TestLLSDBinaryParsingObject::test<2>() - { - std::vector vec; - vec.resize(6); - vec[0] = 'a'; vec[1] = 'b'; vec[2] = 'c'; - vec[3] = '3'; vec[4] = '2'; vec[5] = '1'; - LLSD value = vec; - - vec.resize(11); - vec[0] = 'b'; // for binary - vec[5] = 'a'; vec[6] = 'b'; vec[7] = 'c'; - vec[8] = '3'; vec[9] = '2'; vec[10] = '1'; - - uint32_t size = htonl(6); - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_good((char*)&vec[0], vec.size()); - ensureParse("correct binary parse", str_good, value, 1); - - size = htonl(7); - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_bad_1((char*)&vec[0], vec.size()); + TEST_CASE_METHOD(TestLLSDBinaryParsing, "llsd binary parsing: truncated fixed-width payloads fail", "[llsd_binary_parsing]") + { + // truncated fixed-width payloads must fail, not parse as zero ensureParse( - "incorrect size binary parse 1", - str_bad_1, + "truncated integer", + std::string("i\x00\x01", 3), + LLSD(), + LLSDParser::PARSE_FAILURE); + ensureParse( + "truncated real", + std::string("r\x3f\xf0\x00\x00", 5), + LLSD(), + LLSDParser::PARSE_FAILURE); + ensureParse( + "truncated uuid", + std::string("u\x01\x02\x03\x04\x05\x06\x07\x08", 9), LLSD(), LLSDParser::PARSE_FAILURE); + } - size = htonl(100000); + TEST_CASE_METHOD(TestLLSDBinaryParsing, "llsd binary parsing: a bogus map key marker fails", "[llsd_binary_parsing]") + { + // a map key must be marked 'k' or quoted; anything else used to be + // swallowed silently as an empty key, desynchronizing the stream + std::vector vec; + vec.push_back('{'); + vec.resize(vec.size() + 4); + uint32_t size = htonl(1); memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_bad_2((char*)&vec[0], vec.size()); + vec.push_back('x'); // bogus key marker + vec.push_back('i'); + auto integer_loc = vec.size(); + vec.resize(vec.size() + 4); + uint32_t val_int = htonl(23); + memcpy(&vec[integer_loc], &val_int, sizeof(uint32_t)); + vec.push_back('}'); + std::string str_bad((char*)&vec[0], vec.size()); ensureParse( - "incorrect size binary parse 2", - str_bad_2, + "bogus map key marker", + str_bad, LLSD(), LLSDParser::PARSE_FAILURE); } - template<> template<> - void TestLLSDBinaryParsingObject::test<3>() + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: malformed input fails to parse", "[llsd_notation_parsing]") { // test handling of xml not recognized as llsd results in an // LLSD Undefined ensureParse( - "malformed binary map", + "malformed notation map", "{'ha ha'", LLSD(), LLSDParser::PARSE_FAILURE); ensureParse( - "malformed binary array", + "malformed notation array", "['ha ha'", LLSD(), LLSDParser::PARSE_FAILURE); ensureParse( - "malformed binary string", + "malformed notation string", "'ha ha", LLSD(), LLSDParser::PARSE_FAILURE); ensureParse( - "bad noise", + "bad notation noise", "g48ejlnfr", LLSD(), LLSDParser::PARSE_FAILURE); } - template<> template<> - void TestLLSDBinaryParsingObject::test<4>() + + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: undef parses", "[llsd_notation_parsing]") { ensureParse("valid undef", "!", LLSD(), 1); } - template<> template<> - void TestLLSDBinaryParsingObject::test<5>() + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: booleans parse in every spelling and fail when cut short", "[llsd_notation_parsing]") { LLSD val = false; + ensureParse("valid boolean false 0", "false", val, 1); + ensureParse("valid boolean false 1", "f", val, 1); ensureParse("valid boolean false 2", "0", val, 1); + ensureParse("valid boolean false 3", "F", val, 1); + ensureParse("valid boolean false 4", "FALSE", val, 1); val = true; + ensureParse("valid boolean true 0", "true", val, 1); + ensureParse("valid boolean true 1", "t", val, 1); ensureParse("valid boolean true 2", "1", val, 1); + ensureParse("valid boolean true 3", "T", val, 1); + ensureParse("valid boolean true 4", "TRUE", val, 1); val.clear(); - ensureParse("invalid true", "t", val, LLSDParser::PARSE_FAILURE); - ensureParse("invalid false", "f", val, LLSDParser::PARSE_FAILURE); + ensureParse("invalid true", "TR", val, LLSDParser::PARSE_FAILURE); + ensureParse("invalid false", "FAL", val, LLSDParser::PARSE_FAILURE); } - template<> template<> - void TestLLSDBinaryParsingObject::test<6>() + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: integers parse with a sign and fail when malformed", "[llsd_notation_parsing]") { - std::vector vec; - vec.push_back('{'); - vec.resize(vec.size() + 4); - uint32_t size = htonl(1); - memcpy(&vec[1], &size, sizeof(uint32_t)); - vec.push_back('k'); - auto key_size_loc = vec.size(); - size = htonl(1); // 1 too short - vec.resize(vec.size() + 4); - memcpy(&vec[key_size_loc], &size, sizeof(uint32_t)); - vec.push_back('a'); vec.push_back('m'); vec.push_back('y'); - vec.push_back('i'); - auto integer_loc = vec.size(); - vec.resize(vec.size() + 4); - uint32_t val_int = htonl(23); - memcpy(&vec[integer_loc], &val_int, sizeof(uint32_t)); - std::string str_bad_1((char*)&vec[0], vec.size()); + LLSD val = 123; + ensureParse("valid integer", "i123", val, 1); + ensureParse("explicit plus integer", "i+123", val, 1); + val = -123; + ensureParse("negative integer", "i-123", val, 1); + val.clear(); + ensureParse("invalid integer", "421", val, LLSDParser::PARSE_FAILURE); + ensureParse("empty integer", "i,", val, LLSDParser::PARSE_FAILURE); + // an oversized token must fail whole, not parse as a buffered prefix + ensureParse("oversized integer token", + "i" + std::string(100, '1'), val, LLSDParser::PARSE_FAILURE); + } + + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: reals parse signed and scientific and infinite", "[llsd_notation_parsing]") + { + LLSD val = 456.7; + ensureParse("valid real", "r456.7", val, 1); + ensureParse("explicit plus real", "r+456.7", val, 1); + val = -456.7; + ensureParse("negative real", "r-456.7", val, 1); + val = 1500.0; + ensureParse("scientific real", "r1.5e3", val, 1); + ensureParse("scientific real explicit plus exponent", "r1.5E+3", val, 1); + val = std::numeric_limits::infinity(); + ensureParse("infinity real", "rinf", val, 1); + val = -std::numeric_limits::infinity(); + ensureParse("negative infinity real", "r-inf", val, 1); + val.clear(); + ensureParse("invalid real", "456.7", val, LLSDParser::PARSE_FAILURE); + ensureParse("empty real", "r,", val, LLSDParser::PARSE_FAILURE); + // an oversized token must fail whole, not parse as a buffered prefix + ensureParse("oversized real token", + "r0." + std::string(600, '3'), val, LLSDParser::PARSE_FAILURE); + } + + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: a uuid parses only when well-formed", "[llsd_notation_parsing]") + { + LLUUID id; + LLSD val = id; ensureParse( - "invalid key size", - str_bad_1, + "unparseable uuid", + "u123", LLSD(), LLSDParser::PARSE_FAILURE); + id.generate(); + val = id; + std::string uuid_str("u"); + uuid_str += id.asString(); + ensureParse("valid uuid", uuid_str.c_str(), val, 1); + } - // check with correct size, but unterminated map (missing '}') - size = htonl(3); // correct size - memcpy(&vec[key_size_loc], &size, sizeof(uint32_t)); - std::string str_bad_2((char*)&vec[0], vec.size()); + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: strings parse quoted or sized", "[llsd_notation_parsing]") + { + LLSD val = std::string("foolish"); + ensureParse("valid string 1", "\"foolish\"", val, 1); + val = std::string("g'day"); + ensureParse("valid string 2", "\"g'day\"", val, 1); + val = std::string("have a \"nice\" day"); + ensureParse("valid string 3", "'have a \"nice\" day'", val, 1); + val = std::string("whatever"); + ensureParse("valid string 4", "s(8)\"whatever\"", val, 1); + } + + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: a sized string with the wrong size fails", "[llsd_notation_parsing]") + { ensureParse( - "valid key size, unterminated map", - str_bad_2, + "invalid string 1", + "s(7)\"whatever\"", LLSD(), LLSDParser::PARSE_FAILURE); - - // check w/ correct size and correct map termination - LLSD val; - val["amy"] = 23; - vec.push_back('}'); - std::string str_good((char*)&vec[0], vec.size()); ensureParse( - "valid map", - str_good, - val, - 2); + "invalid string 2", + "s(9)\"whatever\"", + LLSD(), + LLSDParser::PARSE_FAILURE); + } - // check w/ incorrect sizes and correct map termination - size = htonl(0); // 1 too few (for the map entry) - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_bad_3((char*)&vec[0], vec.size()); + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: a uri parses", "[llsd_notation_parsing]") + { + LLSD val = LLURI("http://www.google.com"); + ensureParse("valid uri", "l\"http://www.google.com\"", val, 1); + } + + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: a date parses", "[llsd_notation_parsing]") + { + LLSD val = LLDate("2007-12-28T09:22:53.10Z"); + ensureParse("valid date", "d\"2007-12-28T09:22:53.10Z\"", val, 1); + } + + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: binary parses as b64 or b16 or raw", "[llsd_notation_parsing]") + { + std::vector vec; + vec.push_back((U8)'a'); vec.push_back((U8)'b'); vec.push_back((U8)'c'); + vec.push_back((U8)'3'); vec.push_back((U8)'2'); vec.push_back((U8)'1'); + LLSD val = vec; + ensureParse("valid binary b64", "b64\"YWJjMzIx\"", val, 1); + ensureParse("valid bainry b16", "b16\"616263333231\"", val, 1); + ensureParse("valid bainry raw", "b(6)\"abc321\"", val, 1); + } + + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: binary with too long a length fails", "[llsd_notation_parsing]") + { ensureParse( - "invalid map too long", - str_bad_3, + "invalid -- binary length specified too long", + "b(7)\"abc321\"", LLSD(), LLSDParser::PARSE_FAILURE); - - size = htonl(2); // 1 too many - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_bad_4((char*)&vec[0], vec.size()); ensureParse( - "invalid map too short", - str_bad_4, + "invalid -- binary length specified way too long", + "b(1000000)\"abc321\"", LLSD(), LLSDParser::PARSE_FAILURE); } - template<> template<> - void TestLLSDBinaryParsingObject::test<7>() + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: maps parse flat and nested", "[llsd_notation_parsing]") { - std::vector vec; - vec.push_back('['); - vec.resize(vec.size() + 4); - uint32_t size = htonl(1); // 1 too short - memcpy(&vec[1], &size, sizeof(uint32_t)); - vec.push_back('"'); vec.push_back('a'); vec.push_back('m'); - vec.push_back('y'); vec.push_back('"'); vec.push_back('i'); - auto integer_loc = vec.size(); - vec.resize(vec.size() + 4); - uint32_t val_int = htonl(23); - memcpy(&vec[integer_loc], &val_int, sizeof(uint32_t)); + LLSD val; + val["amy"] = 23; + val["bob"] = LLSD(); + val["cam"] = 1.23; + ensureParse("simple map", "{'amy':i23,'bob':!,'cam':r1.23}", val, 4); + + val["bob"] = LLSD::emptyMap(); + val["bob"]["vehicle"] = std::string("bicycle"); + ensureParse( + "nested map", + "{'amy':i23,'bob':{'vehicle':'bicycle'},'cam':r1.23}", + val, + 5); + } + + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: arrays parse flat and nested", "[llsd_notation_parsing]") + { + LLSD val; + val.append(23); + val.append(LLSD()); + val.append(1.23); + ensureParse("simple array", "[i23,!,r1.23]", val, 4); + val[1] = LLSD::emptyArray(); + val[1].append("bicycle"); + ensureParse("nested array", "[i23,['bicycle'],r1.23]", val, 5); + } + + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: maps and arrays nest in each other", "[llsd_notation_parsing]") + { + LLSD val; + val["amy"] = 23; + val["bob"]["dogs"] = LLSD::emptyArray(); + val["bob"]["dogs"].append(LLSD::emptyMap()); + val["bob"]["dogs"][0]["name"] = std::string("groove"); + val["bob"]["dogs"][0]["breed"] = std::string("samoyed"); + val["bob"]["dogs"].append(LLSD::emptyMap()); + val["bob"]["dogs"][1]["name"] = std::string("greyley"); + val["bob"]["dogs"][1]["breed"] = std::string("chow/husky"); + val["cam"] = 1.23; + ensureParse( + "nested notation", + "{'amy':i23," + " 'bob':{'dogs':[" + "{'name':'groove', 'breed':'samoyed'}," + "{'name':'greyley', 'breed':'chow/husky'}]}," + " 'cam':r1.23}", + val, + 11); + } - std::string str_bad_1((char*)&vec[0], vec.size()); + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: a string size past the end of input fails", "[llsd_notation_parsing]") + { + // text to make sure that incorrect sizes bail because + std::string bad_str("s(5)\"hi\""); ensureParse( - "invalid array size", - str_bad_1, + "size longer than bytes left", + bad_str, LLSD(), LLSDParser::PARSE_FAILURE); + } - // check with correct size, but unterminated map (missing ']') - size = htonl(2); // correct size - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_bad_2((char*)&vec[0], vec.size()); + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: a binary size past the end of input fails", "[llsd_notation_parsing]") + { + // text to make sure that incorrect sizes bail because + std::string bad_bin("b(5)\"hi\""); ensureParse( - "unterminated array", - str_bad_2, + "size longer than bytes left", + bad_bin, LLSD(), LLSDParser::PARSE_FAILURE); + } - // check w/ correct size and correct map termination - LLSD val; - val.append("amy"); - val.append(23); - vec.push_back(']'); - std::string str_good((char*)&vec[0], vec.size()); - ensureParse( - "valid array", - str_good, - val, - 3); + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: nesting 5 deep parses within a depth limit of 5", "[llsd_notation_parsing]") + { + LLSD level_1 = LLSD::emptyMap(); level_1["level_2"] = 99; + LLSD level_0 = LLSD::emptyMap(); level_0["level_1"] = level_1; + + LLSD deep = LLSD::emptyMap(); + deep["level_0"] = level_0; + + LLSD root = LLSD::emptyMap(); + root["deep"] = deep; - // check with too many elements - size = htonl(3); // 1 too long - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_bad_3((char*)&vec[0], vec.size()); ensureParse( - "array too short", - str_bad_3, - LLSD(), - LLSDParser::PARSE_FAILURE); + "nested notation 3 deep", + "{'deep' : {'level_0':{'level_1':{'level_2': i99} } } }", + root, + 5, + 5); // 4 '{' plus i99 also counts as llsd, so real depth is 5 } - template<> template<> - void TestLLSDBinaryParsingObject::test<8>() + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: nesting 12 deep parses within a depth limit of 15", "[llsd_notation_parsing]") { - std::vector vec; - vec.push_back('{'); - vec.resize(vec.size() + 4); - memset(&vec[1], 0, 4); - vec.push_back('}'); - std::string str_good((char*)&vec[0], vec.size()); - LLSD val = LLSD::emptyMap(); + LLSD level_9 = LLSD::emptyMap(); level_9["level_9"] = (S32)99; + LLSD level_8 = LLSD::emptyMap(); level_8["level_8"] = level_9; + LLSD level_7 = LLSD::emptyMap(); level_7["level_7"] = level_8; + LLSD level_6 = LLSD::emptyMap(); level_6["level_6"] = level_7; + LLSD level_5 = LLSD::emptyMap(); level_5["level_5"] = level_6; + LLSD level_4 = LLSD::emptyMap(); level_4["level_4"] = level_5; + LLSD level_3 = LLSD::emptyMap(); level_3["level_3"] = level_4; + LLSD level_2 = LLSD::emptyMap(); level_2["level_2"] = level_3; + LLSD level_1 = LLSD::emptyMap(); level_1["level_1"] = level_2; + LLSD level_0 = LLSD::emptyMap(); level_0["level_0"] = level_1; + + LLSD deep = LLSD::emptyMap(); + deep["deep"] = level_0; + ensureParse( - "empty map", - str_good, - val, - 1); + "nested notation 10 deep", + "{'deep' : {'level_0':{'level_1':{'level_2':{'level_3':{'level_4':{'level_5':{'level_6':{'level_7':{'level_8':{'level_9':i99}" + "} } } } } } } } } }", + deep, + 12, + 15); } - template<> template<> - void TestLLSDBinaryParsingObject::test<9>() + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: nesting over 50 deep parses with no depth limit", "[llsd_notation_parsing]") { - std::vector vec; - vec.push_back('['); - vec.resize(vec.size() + 4); - memset(&vec[1], 0, 4); - vec.push_back(']'); - std::string str_good((char*)&vec[0], vec.size()); - LLSD val = LLSD::emptyArray(); + LLSD end = LLSD::emptyMap(); end["end"] = (S32)99; + + LLSD level_49 = LLSD::emptyMap(); level_49["level_49"] = end; + LLSD level_48 = LLSD::emptyMap(); level_48["level_48"] = level_49; + LLSD level_47 = LLSD::emptyMap(); level_47["level_47"] = level_48; + LLSD level_46 = LLSD::emptyMap(); level_46["level_46"] = level_47; + LLSD level_45 = LLSD::emptyMap(); level_45["level_45"] = level_46; + LLSD level_44 = LLSD::emptyMap(); level_44["level_44"] = level_45; + LLSD level_43 = LLSD::emptyMap(); level_43["level_43"] = level_44; + LLSD level_42 = LLSD::emptyMap(); level_42["level_42"] = level_43; + LLSD level_41 = LLSD::emptyMap(); level_41["level_41"] = level_42; + LLSD level_40 = LLSD::emptyMap(); level_40["level_40"] = level_41; + + LLSD level_39 = LLSD::emptyMap(); level_39["level_39"] = level_40; + LLSD level_38 = LLSD::emptyMap(); level_38["level_38"] = level_39; + LLSD level_37 = LLSD::emptyMap(); level_37["level_37"] = level_38; + LLSD level_36 = LLSD::emptyMap(); level_36["level_36"] = level_37; + LLSD level_35 = LLSD::emptyMap(); level_35["level_35"] = level_36; + LLSD level_34 = LLSD::emptyMap(); level_34["level_34"] = level_35; + LLSD level_33 = LLSD::emptyMap(); level_33["level_33"] = level_34; + LLSD level_32 = LLSD::emptyMap(); level_32["level_32"] = level_33; + LLSD level_31 = LLSD::emptyMap(); level_31["level_31"] = level_32; + LLSD level_30 = LLSD::emptyMap(); level_30["level_30"] = level_31; + + LLSD level_29 = LLSD::emptyMap(); level_29["level_29"] = level_30; + LLSD level_28 = LLSD::emptyMap(); level_28["level_28"] = level_29; + LLSD level_27 = LLSD::emptyMap(); level_27["level_27"] = level_28; + LLSD level_26 = LLSD::emptyMap(); level_26["level_26"] = level_27; + LLSD level_25 = LLSD::emptyMap(); level_25["level_25"] = level_26; + LLSD level_24 = LLSD::emptyMap(); level_24["level_24"] = level_25; + LLSD level_23 = LLSD::emptyMap(); level_23["level_23"] = level_24; + LLSD level_22 = LLSD::emptyMap(); level_22["level_22"] = level_23; + LLSD level_21 = LLSD::emptyMap(); level_21["level_21"] = level_22; + LLSD level_20 = LLSD::emptyMap(); level_20["level_20"] = level_21; + + LLSD level_19 = LLSD::emptyMap(); level_19["level_19"] = level_20; + LLSD level_18 = LLSD::emptyMap(); level_18["level_18"] = level_19; + LLSD level_17 = LLSD::emptyMap(); level_17["level_17"] = level_18; + LLSD level_16 = LLSD::emptyMap(); level_16["level_16"] = level_17; + LLSD level_15 = LLSD::emptyMap(); level_15["level_15"] = level_16; + LLSD level_14 = LLSD::emptyMap(); level_14["level_14"] = level_15; + LLSD level_13 = LLSD::emptyMap(); level_13["level_13"] = level_14; + LLSD level_12 = LLSD::emptyMap(); level_12["level_12"] = level_13; + LLSD level_11 = LLSD::emptyMap(); level_11["level_11"] = level_12; + LLSD level_10 = LLSD::emptyMap(); level_10["level_10"] = level_11; + + LLSD level_9 = LLSD::emptyMap(); level_9["level_9"] = level_10; + LLSD level_8 = LLSD::emptyMap(); level_8["level_8"] = level_9; + LLSD level_7 = LLSD::emptyMap(); level_7["level_7"] = level_8; + LLSD level_6 = LLSD::emptyMap(); level_6["level_6"] = level_7; + LLSD level_5 = LLSD::emptyMap(); level_5["level_5"] = level_6; + LLSD level_4 = LLSD::emptyMap(); level_4["level_4"] = level_5; + LLSD level_3 = LLSD::emptyMap(); level_3["level_3"] = level_4; + LLSD level_2 = LLSD::emptyMap(); level_2["level_2"] = level_3; + LLSD level_1 = LLSD::emptyMap(); level_1["level_1"] = level_2; + LLSD level_0 = LLSD::emptyMap(); level_0["level_0"] = level_1; + + LLSD deep = LLSD::emptyMap(); + deep["deep"] = level_0; + ensureParse( - "empty array", - str_good, - val, - 1); + "nested notation deep", + "{'deep':" + "{'level_0' :{'level_1' :{'level_2' :{'level_3' :{'level_4' :{'level_5' :{'level_6' :{'level_7' :{'level_8' :{'level_9' :" + "{'level_10':{'level_11':{'level_12':{'level_13':{'level_14':{'level_15':{'level_16':{'level_17':{'level_18':{'level_19':" + "{'level_20':{'level_21':{'level_22':{'level_23':{'level_24':{'level_25':{'level_26':{'level_27':{'level_28':{'level_29':" + "{'level_30':{'level_31':{'level_32':{'level_33':{'level_34':{'level_35':{'level_36':{'level_37':{'level_38':{'level_39':" + "{'level_40':{'level_41':{'level_42':{'level_43':{'level_44':{'level_45':{'level_46':{'level_47':{'level_48':{'level_49':" + "{'end':i99}" + "} } } } } } } } } }" + "} } } } } } } } } }" + "} } } } } } } } } }" + "} } } } } } } } } }" + "} } } } } } } } } }" + "}", + deep, + 53); } - template<> template<> - void TestLLSDBinaryParsingObject::test<10>() + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: nesting past the depth limit fails", "[llsd_notation_parsing]") { - std::vector vec; - vec.push_back('l'); - vec.resize(vec.size() + 4); - uint32_t size = htonl(14); // 1 too long - memcpy(&vec[1], &size, sizeof(uint32_t)); - vec.push_back('h'); vec.push_back('t'); vec.push_back('t'); - vec.push_back('p'); vec.push_back(':'); vec.push_back('/'); - vec.push_back('/'); vec.push_back('s'); vec.push_back('l'); - vec.push_back('.'); vec.push_back('c'); vec.push_back('o'); - vec.push_back('m'); - std::string str_bad((char*)&vec[0], vec.size()); ensureParse( - "invalid uri length size", - str_bad, + "nested notation 10 deep", + "{'deep' : {'level_0':{'level_1':{'level_2':{'level_3':{'level_4':{'level_5':{'level_6':{'level_7':{'level_8':{'level_9':i99}" + "} } } } } } } } } }", LLSD(), - LLSDParser::PARSE_FAILURE); - - LLSD val; - val = LLURI("http://sl.com"); - size = htonl(13); // correct length - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_good((char*)&vec[0], vec.size()); - ensureParse( - "valid key size", - str_good, - val, - 1); + LLSDParser::PARSE_FAILURE, + 9); } - template<> template<> - void TestLLSDBinaryParsingObject::test<11>() + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: b16 that is empty parses and odd or unterminated fails", "[llsd_notation_parsing]") { - // hostile size fields must not be trusted - std::vector vec; - vec.push_back('['); - vec.resize(vec.size() + 4); - uint32_t size = htonl(0x7FFFFFFF); - memcpy(&vec[1], &size, sizeof(uint32_t)); - vec.push_back('i'); - auto integer_loc = vec.size(); - vec.resize(vec.size() + 4); - uint32_t val_int = htonl(23); - memcpy(&vec[integer_loc], &val_int, sizeof(uint32_t)); - vec.push_back(']'); - std::string str_huge((char*)&vec[0], vec.size()); + // b16 edge cases + ensureParse("empty b16", "b16\"\"", LLSD(LLSD::Binary()), 1); + // an odd number of hex digits cannot form whole bytes ensureParse( - "array size lies huge", - str_huge, + "odd-length b16", + "b16\"616\"", LLSD(), LLSDParser::PARSE_FAILURE); - - // negative (unrepresentable) sizes used to sign-extend into a - // multi-gigabyte reserve() request - size = htonl(0xFFFFFFFF); - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_neg_array((char*)&vec[0], vec.size()); + // unterminated b16 data must fail rather than spin forever ensureParse( - "negative array size", - str_neg_array, + "unterminated b16", + "b16\"6162", LLSD(), LLSDParser::PARSE_FAILURE); + } - vec[0] = '{'; - vec.back() = '}'; - std::string str_neg_map((char*)&vec[0], vec.size()); - ensureParse( - "negative map size", - str_neg_map, + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: b64 empty or with whitespace parses and invalid or unterminated fails", "[llsd_notation_parsing]") + { + // b64 edge cases + ensureParse("empty b64", "b64\"\"", LLSD(LLSD::Binary()), 1); + + std::vector vec; + vec.push_back((U8)'a'); vec.push_back((U8)'b'); vec.push_back((U8)'c'); + vec.push_back((U8)'3'); vec.push_back((U8)'2'); vec.push_back((U8)'1'); + LLSD val = vec; + // forgiving base64: ASCII whitespace inside the payload is ignored + ensureParse("b64 with whitespace", "b64\"YWJj\nMzIx\"", val, 1); + ensureParse( + "invalid b64 data", + "b64\"Y!WJjMzIx\"", LLSD(), LLSDParser::PARSE_FAILURE); - - std::vector bin; - bin.push_back('b'); - bin.resize(bin.size() + 4); - memcpy(&bin[1], &size, sizeof(uint32_t)); - std::string str_neg_bin((char*)&bin[0], bin.size()); ensureParse( - "negative binary size", - str_neg_bin, + "unterminated b64", + "b64\"YWJj", LLSD(), LLSDParser::PARSE_FAILURE); } - template<> template<> - void TestLLSDBinaryParsingObject::test<12>() + TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: negative raw sizes fail", "[llsd_notation_parsing]") { - // truncated fixed-width payloads must fail, not parse as zero - ensureParse( - "truncated integer", - std::string("i\x00\x01", 3), - LLSD(), - LLSDParser::PARSE_FAILURE); + // negative sizes must not be fed to resize() ensureParse( - "truncated real", - std::string("r\x3f\xf0\x00\x00", 5), + "negative raw binary size", + "b(-5)\"hi\"", LLSD(), LLSDParser::PARSE_FAILURE); ensureParse( - "truncated uuid", - std::string("u\x01\x02\x03\x04\x05\x06\x07\x08", 9), + "negative raw string size", + "s(-5)\"hi\"", LLSD(), LLSDParser::PARSE_FAILURE); } - template<> template<> - void TestLLSDBinaryParsingObject::test<13>() + TEST_CASE_METHOD(TestLLSDSerializeData, "llsd serialization: pretty binary notation round-trips", "[llsd_serialization]") { - // a map key must be marked 'k' or quoted; anything else used to be - // swallowed silently as an empty key, desynchronizing the stream - std::vector vec; - vec.push_back('{'); - vec.resize(vec.size() + 4); - uint32_t size = htonl(1); - memcpy(&vec[1], &size, sizeof(uint32_t)); - vec.push_back('x'); // bogus key marker - vec.push_back('i'); - auto integer_loc = vec.size(); - vec.resize(vec.size() + 4); - uint32_t val_int = htonl(23); - memcpy(&vec[integer_loc], &val_int, sizeof(uint32_t)); - vec.push_back('}'); - std::string str_bad((char*)&vec[0], vec.size()); - ensureParse( - "bogus map key marker", - str_bad, - LLSD(), - LLSDParser::PARSE_FAILURE); + setFormatterParser(new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_PRETTY_BINARY), + new LLSDNotationParser()); + doRoundTripTests("pretty binary notation serialization"); + } + + TEST_CASE_METHOD(TestLLSDSerializeData, "llsd serialization: raw binary notation round-trips", "[llsd_serialization]") + { + setFormatterParser(new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_NONE), + new LLSDNotationParser()); + doRoundTripTests("raw binary notation serialization"); + } + + TEST_CASE_METHOD(TestLLSDSerializeData, "llsd serialization: xml round-trips", "[llsd_serialization]") + { + setFormatterParser(new LLSDXMLFormatter(), new LLSDXMLParser()); + doRoundTripTests("xml serialization"); + } + + TEST_CASE_METHOD(TestLLSDSerializeData, "llsd serialization: binary round-trips", "[llsd_serialization]") + { + setFormatterParser(new LLSDBinaryFormatter(), new LLSDBinaryParser()); + doRoundTripTests("binary serialization"); + } + + TEST_CASE_METHOD(TestLLSDSerializeData, "llsd serialization: serialize(LLSD_BINARY) round-trips through deserialize", "[llsd_serialization]") + { + mFormatter = [](const LLSD& sd, std::ostream& str) + { + LLSDSerialize::serialize(sd, str, LLSDSerialize::LLSD_BINARY); + }; + setParser(LLSDSerialize::deserialize); + doRoundTripTests("serialize(LLSD_BINARY)"); + }; + + TEST_CASE_METHOD(TestLLSDSerializeData, "llsd serialization: serialize(LLSD_XML) round-trips through deserialize", "[llsd_serialization]") + { + mFormatter = [](const LLSD& sd, std::ostream& str) + { + LLSDSerialize::serialize(sd, str, LLSDSerialize::LLSD_XML); + }; + setParser(LLSDSerialize::deserialize); + doRoundTripTests("serialize(LLSD_XML)"); + }; + + TEST_CASE_METHOD(TestLLSDSerializeData, "llsd serialization: serialize(LLSD_NOTATION) round-trips through deserialize", "[llsd_serialization]") + { + mFormatter = [](const LLSD& sd, std::ostream& str) + { + LLSDSerialize::serialize(sd, str, LLSDSerialize::LLSD_NOTATION); + }; + setParser(LLSDSerialize::deserialize); + // In this test, serialize(LLSD_NOTATION) emits a header recognized by + // deserialize(). + doRoundTripTests("serialize(LLSD_NOTATION)"); + }; + + TEST_CASE_METHOD(TestLLSDSerializeData, "llsd serialization: LLSDNotationFormatter output without a header deserializes", "[llsd_serialization]") + { + setFormatterParser(new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_NONE), + new LLSDNotationParser()); + setParser(LLSDSerialize::deserialize); + // This is an interesting test because LLSDNotationFormatter does not + // emit an llsd/notation header. + doRoundTripTests("LLSDNotationFormatter -> deserialize"); + }; + + TEST_CASE_METHOD(TestLLSDSerializeData, "llsd serialization: LLSDXMLFormatter output without a header deserializes", "[llsd_serialization]") + { + setFormatterParser(new LLSDXMLFormatter(false, "", LLSDFormatter::OPTIONS_NONE), + new LLSDXMLParser()); + setParser(LLSDSerialize::deserialize); + // This is an interesting test because LLSDXMLFormatter does not + // emit an LLSD/XML header. + doRoundTripTests("LLSDXMLFormatter -> deserialize"); + }; + + TEST_CASE_METHOD(TestLLSDSerializeData, "llsd serialization: deserialize with SIZE_UNLIMITED reads every format", "[llsd_serialization]") + { + // deserialize() with SIZE_UNLIMITED must not turn the unlimited + // byte budget into a bogus negative one when a header line is + // present; sized payloads (binary strings) used to fail the + // resulting negative budget check. + LLSD sd; + sd["foo"] = "bar"; + sd["bin"] = LLSD::Binary{1, 2, 3}; + + for (auto fmt : {LLSDSerialize::LLSD_BINARY, + LLSDSerialize::LLSD_XML, + LLSDSerialize::LLSD_NOTATION}) + { + std::stringstream stream; + LLSDSerialize::serialize(sd, stream, fmt); + LLSD parsed; + UNSCOPED_INFO((STRINGIZE("deserialize unlimited fmt " << int(fmt)))); + REQUIRE(LLSDSerialize::deserialize(parsed, stream, LLSDSerialize::SIZE_UNLIMITED)); + UNSCOPED_INFO((STRINGIZE("deserialize unlimited fmt " << int(fmt) << " value"))); + REQUIRE_THAT(parsed, ll_test::LLSDEquals(sd)); + } + } + + TEST_CASE_METHOD(TestLLSDSerializeData, "llsd serialization: strip_deprecated_header skips a header with or without a newline", "[llsd_serialization]") + { + // strip_deprecated_header() must consume the newline following the + // header (the binary parser does not tolerate leading whitespace) + // and report the actual number of bytes skipped. + LLSD sd; + sd["int"] = 17; + sd["str"] = "hello"; + + std::stringstream binstream; + LLSDSerialize::toBinary(sd, binstream); + const std::string body = binstream.str(); + + // header with trailing newline, as LLSDSerialize::serialize() writes it + { + std::string payload = "\n" + body; + std::vector buf(payload.begin(), payload.end()); + llssize cur_size = (llssize)buf.size(); + llssize header_size = 0; + char* p = strip_deprecated_header(buf.data(), cur_size, &header_size); + UNSCOPED_INFO("stripped header+newline"); + CHECK(header_size == llssize(18)); + UNSCOPED_INFO("stripped size"); + REQUIRE(cur_size == llssize(body.size())); + LLMemoryStream mstr((U8*)p, (S32)cur_size); + LLSD parsed; + UNSCOPED_INFO("parse after strip"); + CHECK(LLSDSerialize::fromBinary(parsed, mstr, cur_size) > 0); + UNSCOPED_INFO("value after strip"); + CHECK_THAT(parsed, ll_test::LLSDEquals(sd)); + } + + // bare header with no newline + { + std::string payload = "" + body; + std::vector buf(payload.begin(), payload.end()); + llssize cur_size = (llssize)buf.size(); + llssize header_size = 0; + char* p = strip_deprecated_header(buf.data(), cur_size, &header_size); + UNSCOPED_INFO("stripped bare header"); + CHECK(header_size == llssize(17)); + UNSCOPED_INFO("stripped bare size"); + REQUIRE(cur_size == llssize(body.size())); + LLMemoryStream mstr((U8*)p, (S32)cur_size); + LLSD parsed; + UNSCOPED_INFO("parse after bare strip"); + CHECK(LLSDSerialize::fromBinary(parsed, mstr, cur_size) > 0); + UNSCOPED_INFO("value after bare strip"); + CHECK_THAT(parsed, ll_test::LLSDEquals(sd)); + } + } + + TEST_CASE_METHOD(TestLLSDSerializeData, "llsd serialization: zip_llsd round-trips and unzip_llsd rejects corruption", "[llsd_serialization]") + { + // zip_llsd/unzip_llsd round trip, and clean rejection of a + // corrupted compressed stream + LLSD sd; + sd["message"] = "destination unknown"; + sd["mode"] = 7; + LLSD arr = LLSD::emptyArray(); + for (S32 i = 0; i < 256; ++i) + { + arr.append(i * 0.25); + } + sd["samples"] = arr; + + std::string compressed = zip_llsd(sd); + UNSCOPED_INFO("zip_llsd produced data"); + REQUIRE_FALSE(compressed.empty()); + + LLSD out; + UNSCOPED_INFO("unzip_llsd ok"); + REQUIRE(LLUZipHelper::unzip_llsd(out, + (const U8*)compressed.data(), + (S32)compressed.size()) == + LLUZipHelper::ZR_OK); + UNSCOPED_INFO("unzip_llsd round trip"); + CHECK_THAT(out, ll_test::LLSDEquals(sd)); + + std::string corrupt(compressed); + for (size_t i = corrupt.size() / 2; i < corrupt.size(); ++i) + { + corrupt[i] ^= 0x5A; + } + LLSD out2; + UNSCOPED_INFO("corrupt unzip_llsd rejected"); + REQUIRE(LLUZipHelper::unzip_llsd(out2, + (const U8*)corrupt.data(), + (S32)corrupt.size()) != LLUZipHelper::ZR_OK); + } + + TEST_CASE_METHOD(TestLLSDSerializeData, "llsd serialization: unzip_llsdNavMesh round-trips and rejects corruption", "[llsd_serialization]") + { + // unzip_llsdNavMesh: gzip round trip, and clean failure on a + // corrupted stream after partial output has been produced (this + // path used to realloc a freed buffer and double-free on error) + std::string payload; + payload.reserve(96 * 1024); + while (payload.size() < 96 * 1024) + { + payload += "navmesh payload block "; + } + + z_stream strm{}; + int ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, + 15 + 16, // gzip wrapper + 8, Z_DEFAULT_STRATEGY); + UNSCOPED_INFO("deflateInit2"); + CHECK(ret == Z_OK); + std::string compressed(deflateBound(&strm, (uLong)payload.size()) + 32, '\0'); + strm.next_in = (Bytef*)payload.data(); + strm.avail_in = (uInt)payload.size(); + strm.next_out = (Bytef*)&compressed[0]; + strm.avail_out = (uInt)compressed.size(); + ret = deflate(&strm, Z_FINISH); + UNSCOPED_INFO("deflate finish"); + CHECK(ret == Z_STREAM_END); + compressed.resize(compressed.size() - strm.avail_out); + deflateEnd(&strm); + + { + std::istringstream in(compressed); + bool valid = false; + size_t outsize = 0; + U8* out = unzip_llsdNavMesh(valid, outsize, in, (S32)compressed.size()); + UNSCOPED_INFO("navmesh unzip valid"); + CHECK(valid); + UNSCOPED_INFO("navmesh unzip buffer"); + REQUIRE(out != NULL); + UNSCOPED_INFO("navmesh unzip size"); + REQUIRE(outsize == payload.size()); + UNSCOPED_INFO("navmesh unzip content"); + REQUIRE(payload.compare(0, payload.size(), + (const char*)out, outsize) == 0); + free(out); + } + + { + // corrupt the deflate tail and gzip trailer + std::string corrupt(compressed); + for (size_t i = corrupt.size() - 16; i < corrupt.size(); ++i) + { + corrupt[i] ^= 0x5A; + } + std::istringstream in(corrupt); + bool valid = true; + size_t outsize = 0; + U8* out = unzip_llsdNavMesh(valid, outsize, in, (S32)corrupt.size()); + UNSCOPED_INFO("corrupt navmesh rejected"); + CHECK_FALSE(valid); + UNSCOPED_INFO("corrupt navmesh returns null"); + REQUIRE(out == NULL); + } } /** @@ -2062,31 +2000,27 @@ namespace tut actual_value_bin, str1, LLSDSerialize::SIZE_UNLIMITED); - ensure_equals( - "ensureBinaryAndNotation binary count", - count2, + UNSCOPED_INFO("ensureBinaryAndNotation binary count"); + REQUIRE(count2 == count1); // to notation and back again std::stringstream str2; S32 count3 = LLSDSerialize::toNotation(actual_value_bin, str2); - ensure_equals( - "ensureBinaryAndNotation notation count1", - count3, + UNSCOPED_INFO("ensureBinaryAndNotation notation count1"); + REQUIRE(count3 == count2); LLSD actual_value_notation; S32 count4 = LLSDSerialize::fromNotation( actual_value_notation, str2, LLSDSerialize::SIZE_UNLIMITED); - ensure_equals( - "ensureBinaryAndNotation notation count2", - count4, + UNSCOPED_INFO("ensureBinaryAndNotation notation count2"); + REQUIRE(count4 == count3); - ensure_equals( - (msg + " (binaryandnotation)").c_str(), - actual_value_notation, - input); + UNSCOPED_INFO((msg + " (binaryandnotation)").c_str()); + REQUIRE_THAT(actual_value_notation, + ll_test::LLSDEquals(input)); } void ensureBinaryAndXML( @@ -2101,35 +2035,27 @@ namespace tut actual_value_bin, str1, LLSDSerialize::SIZE_UNLIMITED); - ensure_equals( - "ensureBinaryAndXML binary count", - count2, + UNSCOPED_INFO("ensureBinaryAndXML binary count"); + REQUIRE(count2 == count1); // to xml and back again std::stringstream str2; S32 count3 = LLSDSerialize::toXML(actual_value_bin, str2); - ensure_equals( - "ensureBinaryAndXML xml count1", - count3, + UNSCOPED_INFO("ensureBinaryAndXML xml count1"); + REQUIRE(count3 == count2); LLSD actual_value_xml; S32 count4 = LLSDSerialize::fromXML(actual_value_xml, str2); - ensure_equals( - "ensureBinaryAndXML xml count2", - count4, + UNSCOPED_INFO("ensureBinaryAndXML xml count2"); + REQUIRE(count4 == count3); - ensure_equals((msg + " (binaryandxml)").c_str(), actual_value_xml, input); + UNSCOPED_INFO((msg + " (binaryandxml)").c_str()); + REQUIRE_THAT(actual_value_xml, ll_test::LLSDEquals(input)); } }; - typedef tut::test_group TestLLSDCompatibleGroup; - typedef TestLLSDCompatibleGroup::object TestLLSDCompatibleObject; - TestLLSDCompatibleGroup gTestLLSDCompatibleGroup( - "llsd serialize compatible"); - - template<> template<> - void TestLLSDCompatibleObject::test<1>() + TEST_CASE_METHOD(TestLLSDCrossCompatible, "llsd serialize compatible: scalars round-trip via binary into notation and XML", "[llsd_serialize_compatible]") { LLSD test; ensureBinaryAndNotation("undef", test); @@ -2160,8 +2086,7 @@ namespace tut ensureBinaryAndXML("real negative", test); } - template<> template<> - void TestLLSDCompatibleObject::test<2>() + TEST_CASE_METHOD(TestLLSDCrossCompatible, "llsd serialize compatible: a string round-trips via binary into notation and XML", "[llsd_serialize_compatible]") { LLSD test; test = "foobar"; @@ -2169,8 +2094,7 @@ namespace tut ensureBinaryAndXML("string", test); } - template<> template<> - void TestLLSDCompatibleObject::test<3>() + TEST_CASE_METHOD(TestLLSDCrossCompatible, "llsd serialize compatible: a uuid round-trips via binary into notation and XML", "[llsd_serialize_compatible]") { LLSD test; LLUUID id; @@ -2180,8 +2104,7 @@ namespace tut ensureBinaryAndXML("uuid", test); } - template<> template<> - void TestLLSDCompatibleObject::test<4>() + TEST_CASE_METHOD(TestLLSDCrossCompatible, "llsd serialize compatible: a date round-trips via binary into notation and XML", "[llsd_serialize_compatible]") { LLSD test; test = LLDate(12345.0); @@ -2189,8 +2112,7 @@ namespace tut ensureBinaryAndXML("date", test); } - template<> template<> - void TestLLSDCompatibleObject::test<5>() + TEST_CASE_METHOD(TestLLSDCrossCompatible, "llsd serialize compatible: a uri round-trips via binary into notation and XML", "[llsd_serialize_compatible]") { LLSD test; test = LLURI("http://www.secondlife.com/"); @@ -2198,8 +2120,7 @@ namespace tut ensureBinaryAndXML("uri", test); } - template<> template<> - void TestLLSDCompatibleObject::test<6>() + TEST_CASE_METHOD(TestLLSDCrossCompatible, "llsd serialize compatible: random binary round-trips via binary into notation and XML", "[llsd_serialize_compatible]") { LLSD test; typedef std::vector buf_t; @@ -2218,8 +2139,7 @@ namespace tut ensureBinaryAndXML("binary", test); } - template<> template<> - void TestLLSDCompatibleObject::test<7>() + TEST_CASE_METHOD(TestLLSDCrossCompatible, "llsd serialize compatible: an array round-trips via binary into notation and XML", "[llsd_serialize_compatible]") { LLSD test; test = LLSD::emptyArray(); @@ -2229,8 +2149,7 @@ namespace tut ensureBinaryAndXML("array", test); } - template<> template<> - void TestLLSDCompatibleObject::test<8>() + TEST_CASE_METHOD(TestLLSDCrossCompatible, "llsd serialize compatible: a map round-trips via binary into notation and XML", "[llsd_serialize_compatible]") { LLSD test; test = LLSD::emptyArray(); diff --git a/indra/llcommon/tests/llsdutil_test.cpp b/indra/llcommon/tests/llsdutil_test.cpp index 1209bb3807..ee78f07763 100644 --- a/indra/llcommon/tests/llsdutil_test.cpp +++ b/indra/llcommon/tests/llsdutil_test.cpp @@ -27,7 +27,8 @@ */ #include "linden_common.h" -#include "lltut.h" +#include +#include #include "m4math.h" #include "v2math.h" #include "v2math.h" @@ -43,7 +44,7 @@ #include #include -namespace tut +namespace { struct llsdutil_data { @@ -62,7 +63,8 @@ namespace tut // This test is supposed to succeed. Comparing to the // empty string ensures that if the test fails, it will // display the string received so we can tell what failed. - ensure_equals("match", match, ""); + UNSCOPED_INFO("match"); + REQUIRE(match == ""); } else { @@ -70,53 +72,49 @@ namespace tut // the string 'match' will be empty, which doesn't tell us // much about which case went awry. So construct a more // detailed description string. - ensure(proto_key + " shouldn't match " + pi->first, ! match.empty()); + UNSCOPED_INFO(proto_key + " shouldn't match " + pi->first); + REQUIRE_FALSE(match.empty()); } } } }; - typedef test_group llsdutil_test;; - typedef llsdutil_test::object llsdutil_object; - tut::llsdutil_test tut_llsdutil("llsdutil"); + ; - template<> template<> - void llsdutil_object::test<1>() + TEST_CASE_METHOD(llsdutil_data, "llsdutil: ll_sd_from_U64 round-trips a U64", "[llsdutil]") { LLSD sd; U64 valueIn , valueOut; valueIn = U64L(0xFEDCBA9876543210); sd = ll_sd_from_U64(valueIn); valueOut = ll_U64_from_sd(sd); - ensure_equals("U64 valueIn->sd->valueOut", valueIn, valueOut); + UNSCOPED_INFO("U64 valueIn->sd->valueOut"); + CHECK(valueIn == valueOut); } - template<> template<> - void llsdutil_object::test<2>() + TEST_CASE_METHOD(llsdutil_data, "llsdutil: ll_sd_from_U32 round-trips a U32", "[llsdutil]") { LLSD sd; U32 valueIn, valueOut; valueIn = 0x87654321; sd = ll_sd_from_U32(valueIn); valueOut = ll_U32_from_sd(sd); - ensure_equals("U32 valueIn->sd->valueOut", valueIn, valueOut); + UNSCOPED_INFO("U32 valueIn->sd->valueOut"); + CHECK(valueIn == valueOut); } - template<> template<> - void llsdutil_object::test<3>() + TEST_CASE_METHOD(llsdutil_data, "llsdutil: ll_sd_from_ipaddr round-trips an address", "[llsdutil]") { U32 valueIn, valueOut; valueIn = 0x87654321; LLSD sd; sd = ll_sd_from_ipaddr(valueIn); valueOut = ll_ipaddr_from_sd(sd); - ensure_equals("valueIn->sd->valueOut", valueIn, valueOut); + UNSCOPED_INFO("valueIn->sd->valueOut"); + CHECK(valueIn == valueOut); } - template<> template<> - void llsdutil_object::test<4>() + TEST_CASE_METHOD(llsdutil_data, "llsdutil: llsd_matches and llsd_equals across every LLSD type", "[llsdutil]") { - set_test_name("llsd_matches"); - // for this test, construct a map of all possible LLSD types LLSD map; map.insert("empty", LLSD()); @@ -144,7 +142,8 @@ namespace tut // empty prototype matches anything for (mi = map.beginMap(); mi != mend; ++mi) { - ensure_equals(std::string("empty matches ") + mi->first, llsd_matches(LLSD(), mi->second), ""); + UNSCOPED_INFO(std::string("empty matches ") + mi->first); + REQUIRE(llsd_matches(LLSD(), mi->second) == ""); } LLSD proto_array, data_array; @@ -157,27 +156,33 @@ namespace tut // prototype array matches only array for (mi = map.beginMap(); mi != mend; ++mi) { - ensure(std::string("array doesn't match ") + mi->first, - ! llsd_matches(proto_array, mi->second).empty()); + UNSCOPED_INFO(std::string("array doesn't match ") + mi->first); + REQUIRE_FALSE(llsd_matches(proto_array, mi->second).empty()); } // data array must be at least as long as prototype array proto_array.append(LLSD()); - ensure_equals("data array too short", llsd_matches(proto_array, data_array), + UNSCOPED_INFO("data array too short"); + CHECK(llsd_matches(proto_array, data_array) == "Array size 4 required instead of Array size 3"); data_array.append(LLSD()); - ensure_equals("data array just right", llsd_matches(proto_array, data_array), ""); + UNSCOPED_INFO("data array just right"); + CHECK(llsd_matches(proto_array, data_array) == ""); data_array.append(LLSD()); - ensure_equals("data array longer", llsd_matches(proto_array, data_array), ""); + UNSCOPED_INFO("data array longer"); + CHECK(llsd_matches(proto_array, data_array) == ""); // array element matching data_array[0] = LLSD::String(); - ensure_equals("undefined prototype array entry", llsd_matches(proto_array, data_array), ""); + UNSCOPED_INFO("undefined prototype array entry"); + CHECK(llsd_matches(proto_array, data_array) == ""); proto_array[0] = LLSD::Binary(); - ensure_equals("scalar prototype array entry", llsd_matches(proto_array, data_array), + UNSCOPED_INFO("scalar prototype array entry"); + CHECK(llsd_matches(proto_array, data_array) == "[0]: Binary required instead of String"); data_array[0] = LLSD::Binary(); - ensure_equals("matching prototype array entry", llsd_matches(proto_array, data_array), ""); + UNSCOPED_INFO("matching prototype array entry"); + CHECK(llsd_matches(proto_array, data_array) == ""); // build a coupla maps LLSD proto_map, data_map; @@ -194,34 +199,46 @@ namespace tut // prototype map matches only map for (mi = map.beginMap(); mi != mend; ++mi) { - ensure(std::string("map doesn't match ") + mi->first, - ! llsd_matches(proto_map, mi->second).empty()); + UNSCOPED_INFO(std::string("map doesn't match ") + mi->first); + REQUIRE_FALSE(llsd_matches(proto_map, mi->second).empty()); } // data map must contain all keys in prototype map std::string error(llsd_matches(proto_map, data_map)); - ensure_contains("missing keys", error, "missing keys"); - ensure_contains("missing foo", error, "foo"); - ensure_contains("missing bar", error, "bar"); - ensure_does_not_contain("found found", error, "found"); - ensure_does_not_contain("got got", error, "got"); + UNSCOPED_INFO("missing keys"); + CHECK_THAT(error, Catch::Matchers::ContainsSubstring("missing keys")); + UNSCOPED_INFO("missing foo"); + CHECK_THAT(error, Catch::Matchers::ContainsSubstring("foo")); + UNSCOPED_INFO("missing bar"); + CHECK_THAT(error, Catch::Matchers::ContainsSubstring("bar")); + UNSCOPED_INFO("found found"); + CHECK_THAT(error, !Catch::Matchers::ContainsSubstring("found")); + UNSCOPED_INFO("got got"); + CHECK_THAT(error, !Catch::Matchers::ContainsSubstring("got")); data_map["bar"] = LLSD(); error = llsd_matches(proto_map, data_map); - ensure_contains("missing foo", error, "foo"); - ensure_does_not_contain("got bar", error, "bar"); + UNSCOPED_INFO("missing foo"); + CHECK_THAT(error, Catch::Matchers::ContainsSubstring("foo")); + UNSCOPED_INFO("got bar"); + CHECK_THAT(error, !Catch::Matchers::ContainsSubstring("bar")); data_map["foo"] = LLSD(); - ensure_equals("data map just right", llsd_matches(proto_map, data_map), ""); + UNSCOPED_INFO("data map just right"); + CHECK(llsd_matches(proto_map, data_map) == ""); data_map["extra"] = LLSD(); - ensure_equals("data map with extra", llsd_matches(proto_map, data_map), ""); + UNSCOPED_INFO("data map with extra"); + CHECK(llsd_matches(proto_map, data_map) == ""); // map element matching data_map["foo"] = LLSD::String(); - ensure_equals("undefined prototype map entry", llsd_matches(proto_map, data_map), ""); + UNSCOPED_INFO("undefined prototype map entry"); + CHECK(llsd_matches(proto_map, data_map) == ""); proto_map["foo"] = LLSD::Binary(); - ensure_equals("scalar prototype map entry", llsd_matches(proto_map, data_map), + UNSCOPED_INFO("scalar prototype map entry"); + CHECK(llsd_matches(proto_map, data_map) == "['foo']: Binary required instead of String"); data_map["foo"] = LLSD::Binary(); - ensure_equals("matching prototype map entry", llsd_matches(proto_map, data_map), ""); + UNSCOPED_INFO("matching prototype map entry"); + CHECK(llsd_matches(proto_map, data_map) == ""); // String { @@ -276,8 +293,8 @@ namespace tut // interest, e.g "String::Integer". // We expect the values (xmi->second) to be equal if and only // if the type names (xmi->first) are equal. - ensure(STRINGIZE(lmi->first << "::" << rmi->first), - bool(lmi->first == rmi->first) == + UNSCOPED_INFO((STRINGIZE(lmi->first << "::" << rmi->first))); + REQUIRE(bool(lmi->first == rmi->first) == bool(llsd_equals(lmi->second, rmi->second))); } } @@ -288,15 +305,19 @@ namespace tut rarray.append(2); rarray.append("3"); LLSD larray(rarray); - ensure("llsd_equals(equal arrays)", llsd_equals(larray, rarray)); + UNSCOPED_INFO("llsd_equals(equal arrays)"); + CHECK(llsd_equals(larray, rarray)); rarray[2] = "4"; - ensure("llsd_equals(different [2])", ! llsd_equals(larray, rarray)); + UNSCOPED_INFO("llsd_equals(different [2])"); + CHECK_FALSE(llsd_equals(larray, rarray)); rarray = larray; rarray.append(LLSD::Date()); - ensure("llsd_equals(longer right array)", ! llsd_equals(larray, rarray)); + UNSCOPED_INFO("llsd_equals(longer right array)"); + CHECK_FALSE(llsd_equals(larray, rarray)); rarray = larray; rarray.erase(2); - ensure("llsd_equals(shorter right array)", ! llsd_equals(larray, rarray)); + UNSCOPED_INFO("llsd_equals(shorter right array)"); + CHECK_FALSE(llsd_equals(larray, rarray)); // Map cases LLSD rmap; @@ -304,57 +325,66 @@ namespace tut rmap["Phoenix"] = 92; rmap["Boston"] = 77; LLSD lmap(rmap); - ensure("llsd_equals(equal maps)", llsd_equals(lmap, rmap)); + UNSCOPED_INFO("llsd_equals(equal maps)"); + CHECK(llsd_equals(lmap, rmap)); rmap["Boston"] = 80; - ensure("llsd_equals(different [\"Boston\"])", ! llsd_equals(lmap, rmap)); + UNSCOPED_INFO("llsd_equals(different [\"Boston\"])"); + CHECK_FALSE(llsd_equals(lmap, rmap)); rmap = lmap; rmap["Atlanta"] = 95; - ensure("llsd_equals(superset right map)", ! llsd_equals(lmap, rmap)); + UNSCOPED_INFO("llsd_equals(superset right map)"); + CHECK_FALSE(llsd_equals(lmap, rmap)); rmap = lmap; lmap["Seattle"] = 72; - ensure("llsd_equals(superset left map)", ! llsd_equals(lmap, rmap)); + UNSCOPED_INFO("llsd_equals(superset left map)"); + CHECK_FALSE(llsd_equals(lmap, rmap)); } - template<> template<> - void llsdutil_object::test<5>() + TEST_CASE_METHOD(llsdutil_data, "llsdutil: hashing LLSD follows its type and contents", "[llsdutil]") { - set_test_name("llsd_hashing"); - { LLSD data_s1 = LLSD::String("The quick brown aardvark jumped over the lazy lemming."); LLSD data_s2 = LLSD::String("The quick brown aardvark jumped over the lazy lemming."); - ensure("hash: Identical string hashes match.", boost::hash{}(data_s1) == boost::hash{}(data_s2)); + UNSCOPED_INFO("hash: Identical string hashes match."); + CHECK(boost::hash{}(data_s1) == boost::hash{}(data_s2)); } { LLSD data_r1 = LLSD::Real(3.0f); LLSD data_i1 = LLSD::Integer(3); - ensure("hash: equivalent values but different types do not match.", boost::hash{}(data_r1) != boost::hash{}(data_i1)); + UNSCOPED_INFO("hash: equivalent values but different types do not match."); + CHECK(boost::hash{}(data_r1) != boost::hash{}(data_i1)); } { LLSD data_a1 = llsd::array("A", "B", "C"); LLSD data_a2 = llsd::array("A", "B", "C"); - ensure("hash: identical arrays produce identical results", boost::hash{}(data_a1) == boost::hash{}(data_a2)); + UNSCOPED_INFO("hash: identical arrays produce identical results"); + CHECK(boost::hash{}(data_a1) == boost::hash{}(data_a2)); data_a2.append(llsd::array(1, 2)); - ensure("hash: changing the array changes the hash.", boost::hash{}(data_a1) != boost::hash{}(data_a2)); + UNSCOPED_INFO("hash: changing the array changes the hash."); + CHECK(boost::hash{}(data_a1) != boost::hash{}(data_a2)); data_a1.append(llsd::array(1, 2)); - ensure("hash: identical arrays produce identical results with nested arrays", boost::hash{}(data_a1) == boost::hash{}(data_a2)); + UNSCOPED_INFO("hash: identical arrays produce identical results with nested arrays"); + CHECK(boost::hash{}(data_a1) == boost::hash{}(data_a2)); } { LLSD data_m1 = LLSDMap("key1", LLSD::Real(3.0))("key2", "value2")("key3", llsd::array(1, 2, 3)); LLSD data_m2 = LLSDMap("key1", LLSD::Real(3.0))("key2", "value2")("key3", llsd::array(1, 2, 3)); - ensure("hash: identical maps produce identical results", boost::hash{}(data_m1) == boost::hash{}(data_m2)); + UNSCOPED_INFO("hash: identical maps produce identical results"); + CHECK(boost::hash{}(data_m1) == boost::hash{}(data_m2)); LLSD data_m3 = LLSDMap("key1", LLSD::Real(5.0))("key2", "value2")("key3", llsd::array(1, 2, 3)); - ensure("hash: Different values in the map produce different hashes.", boost::hash{}(data_m1) != boost::hash{}(data_m3)); + UNSCOPED_INFO("hash: Different values in the map produce different hashes."); + CHECK(boost::hash{}(data_m1) != boost::hash{}(data_m3)); LLSD data_m4 = LLSDMap("keyA", LLSD::Real(3.0))("key2", "value2")("key3", llsd::array(1, 2, 3)); - ensure("hash: Different keys in the map produce different hashes.", boost::hash{}(data_m1) != boost::hash{}(data_m4)); + UNSCOPED_INFO("hash: Different keys in the map produce different hashes."); + CHECK(boost::hash{}(data_m1) != boost::hash{}(data_m4)); } } diff --git a/indra/llcommon/tests/llsingleton_test.cpp b/indra/llcommon/tests/llsingleton_test.cpp index 08c3c1cb27..d73ab0d6e5 100644 --- a/indra/llcommon/tests/llsingleton_test.cpp +++ b/indra/llcommon/tests/llsingleton_test.cpp @@ -28,7 +28,8 @@ #include "linden_common.h" #include "llsingleton.h" -#include "../test/lltut.h" +#include +#include #include "wrapllerrs.h" #include "llsd.h" @@ -93,7 +94,7 @@ DEFINE_MEMBERS(B, A) } // anonymous namespace -namespace tut +namespace { struct singleton { @@ -104,37 +105,30 @@ namespace tut }; }; - typedef test_group singleton_t; - typedef singleton_t::object singleton_object_t; - tut::singleton_t tut_llsingleton("LLSingleton"); - - template<> template<> - void singleton_object_t::test<1>() + TEST_CASE_METHOD(singleton, "LLSingleton: an empty case that checks nothing", "[LLSingleton]") { } - template<> template<> - void singleton_object_t::test<2>() + TEST_CASE_METHOD(singleton, "LLSingleton: getInstance() makes an instance", "[LLSingleton]") { LLSingletonTest* singleton_test = LLSingletonTest::getInstance(); - ensure(singleton_test); + REQUIRE(singleton_test); } - template<> template<> - void singleton_object_t::test<3>() + TEST_CASE_METHOD(singleton, "LLSingleton: deleteSingleton() removes the instance and getInstance() remakes it", "[LLSingleton]") { //Construct the instance LLSingletonTest::getInstance(); - ensure(LLSingletonTest::instanceExists()); + CHECK(LLSingletonTest::instanceExists()); //Delete the instance LLSingletonTest::deleteSingleton(); - ensure(!LLSingletonTest::instanceExists()); + CHECK_FALSE(LLSingletonTest::instanceExists()); //Construct it again. LLSingletonTest* singleton_test = LLSingletonTest::getInstance(); - ensure(singleton_test); - ensure(LLSingletonTest::instanceExists()); + REQUIRE(singleton_test); + CHECK(LLSingletonTest::instanceExists()); } // Makes CLS, CLS and OTHER depending on each other as the flags say, and @@ -149,64 +143,48 @@ namespace tut sLog.clear(); (void)CLS::instance(); - ensure_equals(sLog, made); + REQUIRE(sLog == made); LLSingletonBase::deleteAll(); - ensure_equals(sLog, made + deleted); + REQUIRE(sLog == made + deleted); } - template<> template<> - void singleton_object_t::test<4>() + TEST_CASE_METHOD(singleton, "LLSingleton: A alone is made and deleted in order", "[LLSingleton]") { - set_test_name("just A"); check_order(A::DEP_NONE, B::DEP_NONE, "AiA", "xA~A"); } - template<> template<> - void singleton_object_t::test<5>() + TEST_CASE_METHOD(singleton, "LLSingleton: A needing B in its constructor is deleted before B", "[LLSingleton]") { - set_test_name("A ctor depends B"); check_order(A::DEP_CTOR, B::DEP_NONE, "ABiBiA", "xA~AxB~B"); } - template<> template<> - void singleton_object_t::test<6>() + TEST_CASE_METHOD(singleton, "LLSingleton: A needing B in initSingleton is deleted before B", "[LLSingleton]") { - set_test_name("A init depends B"); check_order(A::DEP_INIT, B::DEP_NONE, "AiABiB", "xA~AxB~B"); } - template<> template<> - void singleton_object_t::test<7>() + TEST_CASE_METHOD(singleton, "LLSingleton: A needing B in initSingleton and B needing A is deleted before B", "[LLSingleton]") { - set_test_name("A circular init"); check_order(A::DEP_INIT, B::DEP_CTOR, "AiABiB", "xA~AxB~B"); } - template<> template<> - void singleton_object_t::test<8>() + TEST_CASE_METHOD(singleton, "LLSingleton: B alone is made and deleted in order", "[LLSingleton]") { - set_test_name("just B"); check_order(B::DEP_NONE, A::DEP_NONE, "BiB", "xB~B"); } - template<> template<> - void singleton_object_t::test<9>() + TEST_CASE_METHOD(singleton, "LLSingleton: B needing A in its constructor is deleted before A", "[LLSingleton]") { - set_test_name("B ctor depends A"); check_order(B::DEP_CTOR, A::DEP_NONE, "BAiAiB", "xB~BxA~A"); } - template<> template<> - void singleton_object_t::test<10>() + TEST_CASE_METHOD(singleton, "LLSingleton: B needing A in initSingleton is deleted before A", "[LLSingleton]") { - set_test_name("B init depends A"); check_order(B::DEP_INIT, A::DEP_NONE, "BiBAiA", "xB~BxA~A"); } - template<> template<> - void singleton_object_t::test<11>() + TEST_CASE_METHOD(singleton, "LLSingleton: B needing A in initSingleton and A needing B is deleted before A", "[LLSingleton]") { - set_test_name("B circular init"); check_order(B::DEP_INIT, A::DEP_CTOR, "BiBAiA", "xB~BxA~A"); } @@ -227,78 +205,78 @@ namespace tut // validly initialize each using two different constructors. If we tried // to test that with a single LLParamSingleton class within the same test // program, we'd get 'trying to use deleted LLParamSingleton' errors. - namespace - { PARAMSINGLETON(PSing1); PARAMSINGLETON(PSing2); - } // anonymous namespace - template<> template<> - void singleton_object_t::test<12>() + TEST_CASE_METHOD(singleton, "LLSingleton: LLParamSingleton must be initialized once and not used after deletion", "[LLSingleton]") { - set_test_name("LLParamSingleton"); - WrapLLErrs catcherr; // query methods - ensure("false positive on instanceExists()", ! PSing1::instanceExists()); - ensure("false positive on wasDeleted()", ! PSing1::wasDeleted()); + UNSCOPED_INFO("false positive on instanceExists()"); + CHECK_FALSE(PSing1::instanceExists()); + UNSCOPED_INFO("false positive on wasDeleted()"); + CHECK_FALSE(PSing1::wasDeleted()); // try to reference before initializing std::string threw = catcherr.catch_llerrs([](){ (void)PSing1::instance(); }); - ensure_contains("too-early instance() didn't throw", threw, "Uninitialized"); + UNSCOPED_INFO("too-early instance() didn't throw"); + CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("Uninitialized")); // getInstance() behaves the same as instance() threw = catcherr.catch_llerrs([](){ (void)PSing1::getInstance(); }); - ensure_contains("too-early getInstance() didn't throw", threw, "Uninitialized"); + UNSCOPED_INFO("too-early getInstance() didn't throw"); + CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("Uninitialized")); // initialize using LLSD::String constructor PSing1::initParamSingleton("string"); - ensure_equals(PSing1::instance().desc(), "string"); - ensure("false negative on instanceExists()", PSing1::instanceExists()); + CHECK(PSing1::instance().desc() == "string"); + UNSCOPED_INFO("false negative on instanceExists()"); + CHECK(PSing1::instanceExists()); // try to initialize again threw = catcherr.catch_llerrs([](){ PSing1::initParamSingleton("again"); }); - ensure_contains("second ctor(string) didn't throw", threw, "twice"); + UNSCOPED_INFO("second ctor(string) didn't throw"); + CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("twice")); // try to initialize using the other constructor -- should be // well-formed, but illegal at runtime threw = catcherr.catch_llerrs([](){ PSing1::initParamSingleton(17); }); - ensure_contains("other ctor(int) didn't throw", threw, "twice"); + UNSCOPED_INFO("other ctor(int) didn't throw"); + CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("twice")); PSing1::deleteSingleton(); - ensure("false negative on wasDeleted()", PSing1::wasDeleted()); + UNSCOPED_INFO("false negative on wasDeleted()"); + CHECK(PSing1::wasDeleted()); threw = catcherr.catch_llerrs([](){ (void)PSing1::instance(); }); - ensure_contains("accessed deleted LLParamSingleton", threw, "deleted"); + UNSCOPED_INFO("accessed deleted LLParamSingleton"); + CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("deleted")); } - template<> template<> - void singleton_object_t::test<13>() + TEST_CASE_METHOD(singleton, "LLSingleton: LLParamSingleton by its alternate constructor initializes only once", "[LLSingleton]") { - set_test_name("LLParamSingleton alternate ctor"); - WrapLLErrs catcherr; // We don't have to restate all the tests for PSing1. Only test validly // using the other constructor. PSing2::initParamSingleton(17); - ensure_equals(PSing2::instance().desc(), "17"); + CHECK(PSing2::instance().desc() == "17"); // can't do it twice std::string threw = catcherr.catch_llerrs([](){ PSing2::initParamSingleton(34); }); - ensure_contains("second ctor(int) didn't throw", threw, "twice"); + UNSCOPED_INFO("second ctor(int) didn't throw"); + CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("twice")); // can't use the other constructor either threw = catcherr.catch_llerrs([](){ PSing2::initParamSingleton("string"); }); - ensure_contains("other ctor(string) didn't throw", threw, "twice"); + UNSCOPED_INFO("other ctor(string) didn't throw"); + CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("twice")); } - namespace - { class CircularPCtor: public LLParamSingleton { LLSINGLETON(CircularPCtor) @@ -307,21 +285,17 @@ namespace tut (void)instance(); } }; - } // anonymous namespace - template<> template<> - void singleton_object_t::test<14>() + TEST_CASE_METHOD(singleton, "LLSingleton: a circular LLParamSingleton constructor errs", "[LLSingleton]") { - set_test_name("Circular LLParamSingleton constructor"); WrapLLErrs catcherr; std::string threw = catcherr.catch_llerrs([](){ CircularPCtor::initParamSingleton(); }); - ensure_contains("constructor circularity didn't throw", threw, "constructor"); + UNSCOPED_INFO("constructor circularity didn't throw"); + CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("constructor")); } - namespace - { class CircularPInit: public LLParamSingleton { LLSINGLETON_EMPTY_CTOR(CircularPInit); @@ -336,16 +310,14 @@ namespace tut } } }; - } // anonymous namespace - template<> template<> - void singleton_object_t::test<15>() + TEST_CASE_METHOD(singleton, "LLSingleton: a circular LLParamSingleton initSingleton() is allowed", "[LLSingleton]") { - set_test_name("Circular LLParamSingleton initSingleton()"); WrapLLErrs catcherr; std::string threw = catcherr.catch_llerrs([](){ CircularPInit::initParamSingleton(); }); - ensure("initSingleton() circularity threw", threw.empty()); + UNSCOPED_INFO("initSingleton() circularity threw"); + REQUIRE(threw.empty()); } } diff --git a/indra/llcommon/tests/llstreamtools_test.cpp b/indra/llcommon/tests/llstreamtools_test.cpp index 970afb3b02..b2d6c422bc 100644 --- a/indra/llcommon/tests/llstreamtools_test.cpp +++ b/indra/llcommon/tests/llstreamtools_test.cpp @@ -26,25 +26,17 @@ * $/LicenseInfo$ */ -#include +#include +#include "altestmatchers.h" #include "linden_common.h" #include "llstreamtools.h" -#include "lltut.h" -namespace tut +namespace { - struct streamtools_data - { - }; - typedef test_group streamtools_test; - typedef streamtools_test::object streamtools_object; - tut::streamtools_test streamtools_testcase("streamtools"); - //test cases for skip_whitespace() - template<> template<> - void streamtools_object::test<1>() + TEST_CASE("streamtools: skip_whitespace skips spaces and tabs but not line ends", "[streamtools]") { char arr[255]; std::string str; @@ -53,40 +45,44 @@ namespace tut std::istringstream is; is.str(str = ""); - ensure("skip_whitespace: empty string", (false == skip_whitespace(is))); + UNSCOPED_INFO("skip_whitespace: empty string"); + CHECK_FALSE(skip_whitespace(is)); is.clear(); is.str(str = " SecondLife is a 3D World"); skip_whitespace(is); is.get(arr, 255, '\0'); expected_result = "SecondLife is a 3D World"; - ensure_equals("skip_whitespace: space", arr, expected_result); + UNSCOPED_INFO("skip_whitespace: space"); + CHECK(arr == expected_result); is.clear(); is.str(str = "\t \tSecondLife is a 3D World"); skip_whitespace(is); is.get(arr, 255, '\0'); expected_result = "SecondLife is a 3D World"; - ensure_equals("skip_whitespace: space and tabs", arr, expected_result); + UNSCOPED_INFO("skip_whitespace: space and tabs"); + CHECK(arr == expected_result); is.clear(); is.str(str = "\t \tSecondLife is a 3D World "); skip_whitespace(is); is.get(arr, 255, '\0'); expected_result = "SecondLife is a 3D World "; - ensure_equals("skip_whitespace: space at end", arr, expected_result); + UNSCOPED_INFO("skip_whitespace: space at end"); + CHECK(arr == expected_result); is.clear(); is.str(str = "\t \r\nSecondLife is a 3D World"); skip_whitespace(is); is.get(arr, 255, '\0'); expected_result = "\r\nSecondLife is a 3D World"; - ensure_equals("skip_whitespace: space at end", arr, expected_result); + UNSCOPED_INFO("skip_whitespace: space at end"); + CHECK(arr == expected_result); } //testcases for skip_emptyspaces() - template<> template<> - void streamtools_object::test<2>() + TEST_CASE("streamtools: skip_emptyspace skips whitespace and line ends", "[streamtools]") { char arr[255]; std::string str; @@ -100,31 +96,34 @@ namespace tut skip_emptyspace(is); is.get(arr, 255, '\0'); expected_result = "SecondLife is a 3D World.\n"; - ensure_equals("skip_emptyspace: space and tabs", arr, expected_result); + UNSCOPED_INFO("skip_emptyspace: space and tabs"); + CHECK(arr == expected_result); is.clear(); is.str(str = " \t\r\n \r SecondLife is a 3D World.\n"); skip_emptyspace(is); is.get(arr, 255, '\0'); expected_result = "SecondLife is a 3D World.\n"; - ensure_equals("skip_emptyspace: space, tabs, carriage return, newline", arr, expected_result); + UNSCOPED_INFO("skip_emptyspace: space, tabs, carriage return, newline"); + CHECK(arr == expected_result); is.clear(); is.str(str = ""); ret = skip_emptyspace(is); is.get(arr, 255, '\0'); - ensure("skip_emptyspace: empty string", ret == false); + UNSCOPED_INFO("skip_emptyspace: empty string"); + CHECK_FALSE(ret); is.clear(); is.str(str = " \r\n \t "); ret = skip_emptyspace(is); is.get(arr, 255, '\0'); - ensure("skip_emptyspace: space newline empty", ret == false); + UNSCOPED_INFO("skip_emptyspace: space newline empty"); + CHECK_FALSE(ret); } //testcases for skip_comments_and_emptyspace() - template<> template<> - void streamtools_object::test<3>() + TEST_CASE("streamtools: skip_comments_and_emptyspace skips comment lines and empty space", "[streamtools]") { char arr[255]; std::string str; @@ -138,19 +137,23 @@ namespace tut skip_comments_and_emptyspace(is); is.get(arr, 255, '\0'); expected_result = "SecondLife is a 3D World.\n"; - ensure_equals("skip_comments_and_emptyspace: space, tabs, carriage return, newline", arr, expected_result); + UNSCOPED_INFO("skip_comments_and_emptyspace: space, tabs, carriage return, newline"); + CHECK(arr == expected_result); is.clear(); is.str(str = "# \r\n SecondLife is a 3D World."); skip_comments_and_emptyspace(is); is.get(arr, 255, '\0'); expected_result = "SecondLife is a 3D World."; - ensure_equals("skip_comments_and_emptyspace: skip comment - 1", arr, expected_result); + UNSCOPED_INFO("skip_comments_and_emptyspace: skip comment - 1"); + CHECK(arr == expected_result); is.clear(); is.str(str = "# \r\n # SecondLife is a 3D World. ##"); - ensure("should not be good()", ! skip_comments_and_emptyspace(is)); - ensure("should be at eof()", is.eof()); + UNSCOPED_INFO("should not be good()"); + CHECK_FALSE(skip_comments_and_emptyspace(is)); + UNSCOPED_INFO("should be at eof()"); + CHECK(is.eof()); // don't get(): given bad state, we can't rely on results is.clear(); @@ -158,22 +161,24 @@ namespace tut skip_comments_and_emptyspace(is); is.get(arr, 255, '\0'); expected_result = "SecondLife is a 3D World. ##"; - ensure_equals("skip_comments_and_emptyspace: skip comment - 3", arr, expected_result); + UNSCOPED_INFO("skip_comments_and_emptyspace: skip comment - 3"); + CHECK(arr == expected_result); is.clear(); is.str(str = ""); ret = skip_comments_and_emptyspace(is); - ensure("skip_comments_and_emptyspace: empty string", ! ret); + UNSCOPED_INFO("skip_comments_and_emptyspace: empty string"); + CHECK_FALSE(ret); is.clear(); is.str(str = " \r\n \t # SecondLife is a 3D World"); ret = skip_comments_and_emptyspace(is); - ensure("skip_comments_and_emptyspace: space newline comment empty", ! ret); + UNSCOPED_INFO("skip_comments_and_emptyspace: space newline comment empty"); + CHECK_FALSE(ret); } //testcases for skip_line() - template<> template<> - void streamtools_object::test<4>() + TEST_CASE("streamtools: skip_line skips past the next newline", "[streamtools]") { char arr[255]; std::string str; @@ -187,32 +192,35 @@ namespace tut skip_line(is); is.get(arr, 255, '\0'); expected_result = "\n It provides an opportunity to the site \nuser to perform real life activities in virtual world."; - ensure_equals("skip_line: 1 newline", arr, expected_result); + UNSCOPED_INFO("skip_line: 1 newline"); + CHECK(arr == expected_result); is.clear(); is.str(expected_result); skip_line(is); is.get(arr, 255, '\0'); expected_result = " It provides an opportunity to the site \nuser to perform real life activities in virtual world."; - ensure_equals("skip_line: 2 newline", arr, expected_result); + UNSCOPED_INFO("skip_line: 2 newline"); + CHECK(arr == expected_result); is.clear(); is.str(expected_result); skip_line(is); is.get(arr, 255, '\0'); expected_result = "user to perform real life activities in virtual world."; - ensure_equals("skip_line: 3 newline", arr, expected_result); + UNSCOPED_INFO("skip_line: 3 newline"); + CHECK(arr == expected_result); is.clear(); is.str(str = ""); ret = skip_line(is); - ensure("skip_line: empty string", ret == false); + UNSCOPED_INFO("skip_line: empty string"); + CHECK_FALSE(ret); } // testcases for skip_to_next_word() - template<> template<> - void streamtools_object::test<5>() + TEST_CASE("streamtools: skip_to_next_word skips the word and the delimiters after it", "[streamtools]") { char arr[255]; std::string str; @@ -226,7 +234,8 @@ namespace tut skip_to_next_word(is); // get past SecondLife is.get(arr, 255, '\0'); expected_result = "is a 3D_World.\n\n It-provides an opportunity to the site \nuser to perform real life activities in virtual world."; - ensure_equals("skip_to_next_word: 1", arr, expected_result); + UNSCOPED_INFO("skip_to_next_word: 1"); + CHECK(arr == expected_result); is.clear(); is.str(expected_result); @@ -235,30 +244,33 @@ namespace tut skip_to_next_word(is); // get past 3D_World.\n\n is.get(arr, 255, '\0'); expected_result = "It-provides an opportunity to the site \nuser to perform real life activities in virtual world."; - ensure_equals("skip_to_next_word: get past .\n\n 2", arr, expected_result); + UNSCOPED_INFO("skip_to_next_word: get past .\n\n 2"); + CHECK(arr == expected_result); is.clear(); is.str(expected_result); skip_to_next_word(is); // get past It- expected_result = "provides an opportunity to the site \nuser to perform real life activities in virtual world."; is.get(arr, 255, '\0'); - ensure_equals("skip_to_next_word: get past -", arr, expected_result); + UNSCOPED_INFO("skip_to_next_word: get past -"); + CHECK(arr == expected_result); is.clear(); is.str(str = ""); ret = skip_to_next_word(is); - ensure("skip_line: empty string", ret == false); + UNSCOPED_INFO("skip_line: empty string"); + CHECK_FALSE(ret); is.clear(); is.str(str = " \r\n\r\n"); ret = skip_to_next_word(is); - ensure("skip_line: space new lines", ret == false); + UNSCOPED_INFO("skip_line: space new lines"); + CHECK_FALSE(ret); } //testcases for skip_to_end_of_next_keyword() - template<> template<> - void streamtools_object::test<6>() + TEST_CASE("streamtools: skip_to_end_of_next_keyword finds a keyword that starts a line", "[streamtools]") { char arr[255]; std::string str; @@ -272,55 +284,62 @@ namespace tut ret = skip_to_end_of_next_keyword("FIRSTKEY", is); is.get(arr, 255, '\0'); expected_result = " followed by second delimiter\nSECONDKEY\t SecondValue followed by third delimiter \nSECONDKEY\nFOURTHKEY FOURTHVALUEis a 3DWorld."; - ensure_equals("skip_to_end_of_next_keyword: 1", arr, expected_result); + UNSCOPED_INFO("skip_to_end_of_next_keyword: 1"); + CHECK(arr == expected_result); is.clear(); is.str(expected_result); ret = skip_to_end_of_next_keyword("SECONDKEY", is); is.get(arr, 255, '\0'); expected_result = "\t SecondValue followed by third delimiter \nSECONDKEY\nFOURTHKEY FOURTHVALUEis a 3DWorld."; - ensure_equals("skip_to_end_of_next_keyword: 2", arr, expected_result); + UNSCOPED_INFO("skip_to_end_of_next_keyword: 2"); + CHECK(arr == expected_result); is.clear(); is.str(expected_result); ret = skip_to_end_of_next_keyword("SECONDKEY", is); is.get(arr, 255, '\0'); expected_result = "\nFOURTHKEY FOURTHVALUEis a 3DWorld."; - ensure_equals("skip_to_end_of_next_keyword: 3", arr, expected_result); + UNSCOPED_INFO("skip_to_end_of_next_keyword: 3"); + CHECK(arr == expected_result); is.clear(); is.str(expected_result); ret = skip_to_end_of_next_keyword("FOURTHKEY", is); is.get(arr, 255, '\0'); expected_result = " FOURTHVALUEis a 3DWorld."; - ensure_equals("skip_to_end_of_next_keyword: 4", arr, expected_result); + UNSCOPED_INFO("skip_to_end_of_next_keyword: 4"); + CHECK(arr == expected_result); is.clear(); is.str(str = "{should be skipped as newline/space/tab does not follow but this one should be picked\n { Does it?\n"); ret = skip_to_end_of_next_keyword("{", is); is.get(arr, 255, '\0'); expected_result = " Does it?\n"; - ensure_equals("skip_to_end_of_next_keyword: multiple delim matches on same line", arr, expected_result); + UNSCOPED_INFO("skip_to_end_of_next_keyword: multiple delim matches on same line"); + CHECK(arr == expected_result); is.clear(); is.str(str = "Delim { could not be found at start"); ret = skip_to_end_of_next_keyword("{", is); - ensure("skip_to_end_of_next_keyword: delim should not be present", ret == false); + UNSCOPED_INFO("skip_to_end_of_next_keyword: delim should not be present"); + CHECK_FALSE(ret); is.clear(); is.str(str = "Empty Delim"); ret = skip_to_end_of_next_keyword("", is); - ensure("skip_to_end_of_next_keyword: empty delim should not be valid", ret == false); + UNSCOPED_INFO("skip_to_end_of_next_keyword: empty delim should not be valid"); + CHECK_FALSE(ret); is.clear(); is.str(str = ""); ret = skip_to_end_of_next_keyword("}", is); - ensure("skip_to_end_of_next_keyword: empty string", ret == false); + UNSCOPED_INFO("skip_to_end_of_next_keyword: empty string"); + CHECK_FALSE(ret); } //testcases for get_word(std::string& output_string, std::istream& input_stream) - template<> template<> - void streamtools_object::test<7>() + TEST_CASE("streamtools: get_word reads words split by whitespace", "[streamtools]") { std::string str; std::string expected_result; @@ -333,17 +352,20 @@ namespace tut actual_result = ""; ret = get_word(actual_result, is); expected_result = "First"; - ensure_equals("get_word: 1", actual_result, expected_result); + UNSCOPED_INFO("get_word: 1"); + CHECK(actual_result == expected_result); actual_result = ""; ret = get_word(actual_result, is); expected_result = "Second"; - ensure_equals("get_word: 2", actual_result, expected_result); + UNSCOPED_INFO("get_word: 2"); + CHECK(actual_result == expected_result); actual_result = ""; ret = get_word(actual_result, is); expected_result = "Third"; - ensure_equals("get_word: 3", actual_result, expected_result); + UNSCOPED_INFO("get_word: 3"); + CHECK(actual_result == expected_result); // the current implementation of get_word seems inconsistent with // skip_to_next_word. skip_to_next_word treats any character other @@ -354,29 +376,32 @@ namespace tut actual_result = ""; ret = get_word(actual_result, is); // will copy Fourth-ShouldThisBePartOfFourth expected_result = "Fourth-ShouldThisBePartOfFourth"; // as per current impl. - ensure_equals("get_word: 4", actual_result, expected_result); + UNSCOPED_INFO("get_word: 4"); + CHECK(actual_result == expected_result); actual_result = ""; ret = get_word(actual_result, is); // will copy Fifth expected_result = "Fifth"; // as per current impl. - ensure_equals("get_word: 5", actual_result, expected_result); + UNSCOPED_INFO("get_word: 5"); + CHECK(actual_result == expected_result); is.clear(); is.str(str = " \t \r \n "); actual_result = ""; ret = get_word(actual_result, is); - ensure("get_word: empty all spaces, newline tabs", ret == false); + UNSCOPED_INFO("get_word: empty all spaces, newline tabs"); + CHECK_FALSE(ret); is.clear(); is.str(str = ""); actual_result = ""; ret = get_word(actual_result, is); - ensure("get_word: empty string", ret == false); + UNSCOPED_INFO("get_word: empty string"); + CHECK_FALSE(ret); } // testcase for get_word and skip_to_next_word compatibility - template<> template<> - void streamtools_object::test<8>() + TEST_CASE("streamtools: get_word and skip_to_next_word reach the same fifth word", "[streamtools]") { std::string str; std::string expected_result; @@ -415,12 +440,12 @@ namespace tut // will copy ShouldThisBePartOfFourth, the fifth word, // while using get_word above five times result in getting "Fifth" get_word(expected_result, is); - ensure_equals("get_word: skip_to_next_word compatibility", actual_result, expected_result); + UNSCOPED_INFO("get_word: skip_to_next_word compatibility"); + CHECK(actual_result == expected_result); } //testcases for get_word(std::string& output_string, std::istream& input_stream, int n) - template<> template<> - void streamtools_object::test<9>() + TEST_CASE("streamtools: get_word with a limit stops after n characters", "[streamtools]") { std::string str; std::string expected_result; @@ -433,44 +458,50 @@ namespace tut actual_result = ""; ret = get_word(actual_result, is, 255); expected_result = "First"; - ensure_equals("get_word: 1", actual_result, expected_result); + UNSCOPED_INFO("get_word: 1"); + CHECK(actual_result == expected_result); actual_result = ""; ret = get_word(actual_result, is, 4); expected_result = "Seco"; // should be cut short - ensure_equals("get_word: 2", actual_result, expected_result); + UNSCOPED_INFO("get_word: 2"); + CHECK(actual_result == expected_result); actual_result = ""; ret = get_word(actual_result, is, 255); expected_result = "nd"; // get remainder of Second - ensure_equals("get_word: 3", actual_result, expected_result); + UNSCOPED_INFO("get_word: 3"); + CHECK(actual_result == expected_result); actual_result = ""; ret = get_word(actual_result, is, 0); // 0 size string expected_result = ""; // get remainder of Second - ensure_equals("get_word: 0 sized output", actual_result, expected_result); + UNSCOPED_INFO("get_word: 0 sized output"); + CHECK(actual_result == expected_result); actual_result = ""; ret = get_word(actual_result, is, 255); expected_result = "Third"; - ensure_equals("get_word: 4", actual_result, expected_result); + UNSCOPED_INFO("get_word: 4"); + CHECK(actual_result == expected_result); is.clear(); is.str(str = " \t \r \n "); actual_result = ""; ret = get_word(actual_result, is, 255); - ensure("get_word: empty all spaces, newline tabs", ret == false); + UNSCOPED_INFO("get_word: empty all spaces, newline tabs"); + CHECK_FALSE(ret); is.clear(); is.str(str = ""); actual_result = ""; ret = get_word(actual_result, is, 255); - ensure("get_word: empty string", ret == false); + UNSCOPED_INFO("get_word: empty string"); + CHECK_FALSE(ret); } //test cases for get_line(std::string& output_string, std::istream& input_stream) - template<> template<> - void streamtools_object::test<10>() + TEST_CASE("streamtools: get_line reads up to and including each newline", "[streamtools]") { std::string str; std::string expected_result; @@ -482,39 +513,44 @@ namespace tut actual_result = ""; get_line(actual_result, is); expected_result = "First Second \t \r\n"; - ensure_equals("get_line: 1", actual_result, expected_result); + UNSCOPED_INFO("get_line: 1"); + CHECK(actual_result == expected_result); actual_result = ""; get_line(actual_result, is); expected_result = " Third Fourth-ShouldThisBePartOfFourth IsThisFifth\n"; - ensure_equals("get_line: 2", actual_result, expected_result); + UNSCOPED_INFO("get_line: 2"); + CHECK(actual_result == expected_result); is.clear(); is.str(str = "\nFirst Line.\n\nSecond Line.\n"); actual_result = ""; get_line(actual_result, is); expected_result = "\n"; - ensure_equals("get_line: First char as newline", actual_result, expected_result); + UNSCOPED_INFO("get_line: First char as newline"); + CHECK(actual_result == expected_result); actual_result = ""; get_line(actual_result, is); expected_result = "First Line.\n"; - ensure_equals("get_line: 3", actual_result, expected_result); + UNSCOPED_INFO("get_line: 3"); + CHECK(actual_result == expected_result); actual_result = ""; get_line(actual_result, is); expected_result = "\n"; - ensure_equals("get_line: 4", actual_result, expected_result); + UNSCOPED_INFO("get_line: 4"); + CHECK(actual_result == expected_result); actual_result = ""; get_line(actual_result, is); expected_result = "Second Line.\n"; - ensure_equals("get_line: 5", actual_result, expected_result); + UNSCOPED_INFO("get_line: 5"); + CHECK(actual_result == expected_result); } //test cases for get_line(std::string& output_string, std::istream& input_stream) - template<> template<> - void streamtools_object::test<11>() + TEST_CASE("streamtools: get_line reads a line with no newline and returns good()", "[streamtools]") { std::string str; std::string expected_result; @@ -527,13 +563,14 @@ namespace tut actual_result = ""; ret = get_line(actual_result, is); expected_result = "One Line only with no newline"; - ensure_equals("get_line: No newline", actual_result, expected_result); - ensure_equals("return value is good state of stream", ret, is.good()); + UNSCOPED_INFO("get_line: No newline"); + CHECK(actual_result == expected_result); + UNSCOPED_INFO("return value is good state of stream"); + CHECK(ret == is.good()); } //test cases for get_line(std::string& output_string, std::istream& input_stream) - template<> template<> - void streamtools_object::test<12>() + TEST_CASE("streamtools: get_line keeps a lone carriage return", "[streamtools]") { std::string str; std::string expected_result; @@ -546,12 +583,12 @@ namespace tut actual_result = ""; get_line(actual_result, is); expected_result = "Should not skip lone \r.\r\n"; - ensure_equals("get_line: carriage return skipped even though not followed by newline", actual_result, expected_result); + UNSCOPED_INFO("get_line: carriage return skipped even though not followed by newline"); + CHECK(actual_result == expected_result); } //test cases for get_line(std::string& output_string, std::istream& input_stream) - template<> template<> - void streamtools_object::test<13>() + TEST_CASE("streamtools: get_line reads a line that is just a newline", "[streamtools]") { std::string str; std::string expected_result; @@ -563,13 +600,13 @@ namespace tut actual_result = ""; get_line(actual_result, is); expected_result = "\n"; - ensure_equals("get_line: Just newline", actual_result, expected_result); + UNSCOPED_INFO("get_line: Just newline"); + CHECK(actual_result == expected_result); } //testcases for get_line(std::string& output_string, std::istream& input_stream, int n) - template<> template<> - void streamtools_object::test<14>() + TEST_CASE("streamtools: get_line with a limit cuts the line and adds a newline", "[streamtools]") { std::string str; std::string expected_result; @@ -581,36 +618,40 @@ namespace tut actual_result = ""; get_line(actual_result, is, 255); expected_result = "First Line.\n"; - ensure_equals("get_line: Basic Operation", actual_result, expected_result); + UNSCOPED_INFO("get_line: Basic Operation"); + CHECK(actual_result == expected_result); actual_result = ""; get_line(actual_result, is, sizeof("Second")-1); expected_result = "Second\n"; - ensure_equals("get_line: Insufficient length 1", actual_result, expected_result); + UNSCOPED_INFO("get_line: Insufficient length 1"); + CHECK(actual_result == expected_result); actual_result = ""; get_line(actual_result, is, 255); expected_result = " Line.\n"; - ensure_equals("get_line: Remainder after earlier insufficient length", actual_result, expected_result); + UNSCOPED_INFO("get_line: Remainder after earlier insufficient length"); + CHECK(actual_result == expected_result); is.clear(); is.str(str = "One Line only with no newline with limited length"); actual_result = ""; get_line(actual_result, is, sizeof("One Line only with no newline with limited length")-1); expected_result = "One Line only with no newline with limited length\n"; - ensure_equals("get_line: No newline with limited length", actual_result, expected_result); + UNSCOPED_INFO("get_line: No newline with limited length"); + CHECK(actual_result == expected_result); is.clear(); is.str(str = "One Line only with no newline"); actual_result = ""; get_line(actual_result, is, 255); expected_result = "One Line only with no newline"; - ensure_equals("get_line: No newline", actual_result, expected_result); + UNSCOPED_INFO("get_line: No newline"); + CHECK(actual_result == expected_result); } //testcases for get_line(std::string& output_string, std::istream& input_stream, int n) - template<> template<> - void streamtools_object::test<15>() + TEST_CASE("streamtools: get_line with a limit reads a line with no newline and returns good()", "[streamtools]") { std::string str; std::string expected_result; @@ -623,13 +664,14 @@ namespace tut actual_result = ""; ret = get_line(actual_result, is, 255); expected_result = "One Line only with no newline"; - ensure_equals("get_line: No newline", actual_result, expected_result); - ensure_equals("return value is good state of stream", ret, is.good()); + UNSCOPED_INFO("get_line: No newline"); + CHECK(actual_result == expected_result); + UNSCOPED_INFO("return value is good state of stream"); + CHECK(ret == is.good()); } //testcases for remove_last_char() - template<> template<> - void streamtools_object::test<16>() + TEST_CASE("streamtools: remove_last_char removes only a matching last character", "[streamtools]") { std::string str; std::string expected_result; @@ -639,25 +681,29 @@ namespace tut ret = remove_last_char('d',str); expected_result = "SecondLife is a 3D Worl"; - ensure_equals("remove_last_char: should remove last char", str, expected_result); + UNSCOPED_INFO("remove_last_char: should remove last char"); + CHECK(str == expected_result); str = "SecondLife is a 3D World"; ret = remove_last_char('W',str); expected_result = "SecondLife is a 3D World"; - ensure_equals("remove_last_char: should not remove as it is not last char", str, expected_result); - ensure("remove_last_char: should return false", ret == false); + UNSCOPED_INFO("remove_last_char: should not remove as it is not last char"); + CHECK(str == expected_result); + UNSCOPED_INFO("remove_last_char: should return false"); + CHECK_FALSE(ret); str = "SecondLife is a 3D World\n"; ret = remove_last_char('\n',str); expected_result = "SecondLife is a 3D World"; - ensure_equals("remove_last_char: should remove last newline", str, expected_result); - ensure("remove_last_char: should remove newline and return true", ret == true); + UNSCOPED_INFO("remove_last_char: should remove last newline"); + CHECK(str == expected_result); + UNSCOPED_INFO("remove_last_char: should remove newline and return true"); + CHECK(ret); } //testcases for unescaped_string() - template<> template<> - void streamtools_object::test<17>() + TEST_CASE("streamtools: unescape_string unescapes newlines and backslashes only", "[streamtools]") { std::string str; std::string expected_result; @@ -665,37 +711,42 @@ namespace tut str = "SecondLife is a 3D world \\n"; unescape_string(str); expected_result = "SecondLife is a 3D world \n"; - ensure_equals("unescape_string: newline", str, expected_result); + UNSCOPED_INFO("unescape_string: newline"); + CHECK(str == expected_result); str = "SecondLife is a 3D world \\\\t \\n"; unescape_string(str); expected_result = "SecondLife is a 3D world \\t \n"; - ensure_equals("unescape_string: backslash and newline", str, expected_result); + UNSCOPED_INFO("unescape_string: backslash and newline"); + CHECK(str == expected_result); str = "SecondLife is a 3D world \\ "; unescape_string(str); expected_result = "SecondLife is a 3D world \\ "; - ensure_equals("unescape_string: insufficient to unescape", str, expected_result); + UNSCOPED_INFO("unescape_string: insufficient to unescape"); + CHECK(str == expected_result); str = "SecondLife is a 3D world \\n \\n \\n \\\\\\n"; unescape_string(str); expected_result = "SecondLife is a 3D world \n \n \n \\\n"; - ensure_equals("unescape_string: multipel newline and backslash", str, expected_result); + UNSCOPED_INFO("unescape_string: multipel newline and backslash"); + CHECK(str == expected_result); str = "SecondLife is a 3D world \\t"; unescape_string(str); expected_result = "SecondLife is a 3D world \\t"; - ensure_equals("unescape_string: leaves tab as is", str, expected_result); + UNSCOPED_INFO("unescape_string: leaves tab as is"); + CHECK(str == expected_result); str = "\\n"; unescape_string(str); expected_result = "\n"; - ensure_equals("unescape_string: only a newline", str, expected_result); + UNSCOPED_INFO("unescape_string: only a newline"); + CHECK(str == expected_result); } //testcases for escape_string() - template<> template<> - void streamtools_object::test<18>() + TEST_CASE("streamtools: escape_string escapes newlines and backslashes and round-trips", "[streamtools]") { std::string str; std::string expected_result; @@ -703,46 +754,52 @@ namespace tut str = "SecondLife is a 3D world \n"; escape_string(str); expected_result = "SecondLife is a 3D world \\n"; - ensure_equals("escape_string: newline", str, expected_result); + UNSCOPED_INFO("escape_string: newline"); + CHECK(str == expected_result); str = "SecondLife is a 3D world \\t \n"; escape_string(str); expected_result = "SecondLife is a 3D world \\\\t \\n"; - ensure_equals("escape_string: backslash and newline", str, expected_result); + UNSCOPED_INFO("escape_string: backslash and newline"); + CHECK(str == expected_result); str = "SecondLife is a 3D world \n \n \n \\\n"; escape_string(str); expected_result = "SecondLife is a 3D world \\n \\n \\n \\\\\\n"; - ensure_equals("escape_string: multipel newline and backslash", str, expected_result); + UNSCOPED_INFO("escape_string: multipel newline and backslash"); + CHECK(str == expected_result); str = "SecondLife is a 3D world \t"; escape_string(str); expected_result = "SecondLife is a 3D world \t"; - ensure_equals("unescape_string: leaves tab as is", str, expected_result); + UNSCOPED_INFO("unescape_string: leaves tab as is"); + CHECK(str == expected_result); str = "\n"; escape_string(str); expected_result = "\\n"; - ensure_equals("unescape_string: only a newline", str, expected_result); + UNSCOPED_INFO("unescape_string: only a newline"); + CHECK(str == expected_result); // serialization/deserialization escape->unescape str = "SecondLife is a 3D world \n \n \n \\\n"; escape_string(str); unescape_string(str); expected_result = "SecondLife is a 3D world \n \n \n \\\n"; - ensure_equals("escape_string: should preserve with escape/unescape", str, expected_result); + UNSCOPED_INFO("escape_string: should preserve with escape/unescape"); + CHECK(str == expected_result); // serialization/deserialization unescape->escape str = "SecondLife is a 3D world \\n \\n \\n \\\\"; unescape_string(str); escape_string(str); expected_result = "SecondLife is a 3D world \\n \\n \\n \\\\"; - ensure_equals("escape_string: should preserve with unescape/escape", str, expected_result); + UNSCOPED_INFO("escape_string: should preserve with unescape/escape"); + CHECK(str == expected_result); } // testcases for replace_newlines_with_whitespace() - template<> template<> - void streamtools_object::test<19>() + TEST_CASE("streamtools: replace_newlines_with_whitespace replaces only newlines", "[streamtools]") { std::string str; std::string expected_result; @@ -750,27 +807,30 @@ namespace tut str = "SecondLife is a 3D \n\nworld\n"; replace_newlines_with_whitespace(str); expected_result = "SecondLife is a 3D world "; - ensure_equals("replace_newlines_with_whitespace: replace all newline", str, expected_result); + UNSCOPED_INFO("replace_newlines_with_whitespace: replace all newline"); + CHECK(str == expected_result); str = "\nSecondLife is a 3D world\n"; replace_newlines_with_whitespace(str); expected_result = " SecondLife is a 3D world "; - ensure_equals("replace_newlines_with_whitespace: begin and newline", str, expected_result); + UNSCOPED_INFO("replace_newlines_with_whitespace: begin and newline"); + CHECK(str == expected_result); str = "SecondLife is a 3D world\r\t"; replace_newlines_with_whitespace(str); expected_result = "SecondLife is a 3D world\r\t"; - ensure_equals("replace_newlines_with_whitespace: should only replace newline", str, expected_result); + UNSCOPED_INFO("replace_newlines_with_whitespace: should only replace newline"); + CHECK(str == expected_result); str = ""; replace_newlines_with_whitespace(str); expected_result = ""; - ensure_equals("replace_newlines_with_whitespace: empty string", str, expected_result); + UNSCOPED_INFO("replace_newlines_with_whitespace: empty string"); + CHECK(str == expected_result); } //testcases for remove_double_quotes() - template<> template<> - void streamtools_object::test<20>() + TEST_CASE("streamtools: remove_double_quotes removes paired and lone double quotes", "[streamtools]") { std::string str; std::string expected_result; @@ -778,42 +838,53 @@ namespace tut str = "SecondLife is a \"\"3D world"; remove_double_quotes(str); expected_result = "SecondLife is a 3D world"; - ensure_equals("remove_double_quotes: replace empty doube quotes", str, expected_result); + UNSCOPED_INFO("remove_double_quotes: replace empty doube quotes"); + CHECK(str == expected_result); str = "SecondLife is a \"3D world"; remove_double_quotes(str); expected_result = "SecondLife is a 3D world"; - ensure_equals("remove_double_quotes: keep as is it matching quote not found", str, expected_result); + UNSCOPED_INFO("remove_double_quotes: keep as is it matching quote not found"); + CHECK(str == expected_result); } // testcases for get_brace_count() - template<> template<> - void streamtools_object::test<21>() + TEST_CASE("streamtools: get_brace_count has no checks yet", "[streamtools]") { } //testcases for get_keyword_and_value() - template<> template<> - void streamtools_object::test<22>() + TEST_CASE("streamtools: get_keyword_and_value splits off the first word as the keyword", "[streamtools]") { std::string s = "SecondLife is a 3D World"; std::string keyword; std::string value; get_keyword_and_value(keyword, value, s); - ensure("get_keyword_and_value: Unable to get Keyword and Value", ((keyword == "SecondLife") && (value == "is a 3D World"))); + { + INFO("get_keyword_and_value: Unable to get Keyword and Value"); + CHECK(keyword == "SecondLife"); + CHECK(value == "is a 3D World"); + } s = "SecondLife"; get_keyword_and_value(keyword, value, s); - ensure("get_keyword_and_value: value should be empty", ((keyword == "SecondLife") && (value == ""))); + { + INFO("get_keyword_and_value: value should be empty"); + CHECK(keyword == "SecondLife"); + CHECK(value == ""); + } s = "SecondLife \t is cool! \n"; get_keyword_and_value(keyword, value, s); - ensure("get_keyword_and_value: remove space before value but not after", ((keyword == "SecondLife") && (value == "is cool! "))); + { + INFO("get_keyword_and_value: remove space before value but not after"); + CHECK(keyword == "SecondLife"); + CHECK(value == "is cool! "); + } } //testcases for get_keyword_and_value() - template<> template<> - void streamtools_object::test<23>() + TEST_CASE("streamtools: get_keyword_and_value gives no value after a keyword and newline", "[streamtools]") { std::string s; std::string keyword = "SOME PRIOR KEYWORD"; @@ -821,12 +892,15 @@ namespace tut s = "SecondLife\n"; get_keyword_and_value(keyword, value, s); - ensure("get_keyword_and_value: terminated with newline. value should be empty", ((keyword == "SecondLife") && (value == ""))); + { + INFO("get_keyword_and_value: terminated with newline. value should be empty"); + CHECK(keyword == "SecondLife"); + CHECK(value == ""); + } } //testcases for get_keyword_and_value() - template<> template<> - void streamtools_object::test<24>() + TEST_CASE("streamtools: get_keyword_and_value of an empty string clears both", "[streamtools]") { std::string s; std::string keyword = "SOME PRIOR KEYWORD"; @@ -834,19 +908,23 @@ namespace tut s = ""; get_keyword_and_value(keyword, value, s); - ensure("get_keyword_and_value: empty string. keyword value should empty", ((keyword == "") && (value == ""))); + { + INFO("get_keyword_and_value: empty string. keyword value should empty"); + CHECK(keyword == ""); + CHECK(value == ""); + } } //testcase for fullread() - template<> template<> - void streamtools_object::test<25>() + TEST_CASE("streamtools: fullread reads across newlines up to the requested count", "[streamtools]") { std::string str = "First Line.\nSecond Line\n"; std::istringstream is(str); char buf[255] = {0}; fullread(is, buf, 255); - ensure_memory_matches("fullread: read with newlines", (void*) buf, static_cast(str.size())-1, (void*) str.c_str(), static_cast(str.size())-1); + UNSCOPED_INFO("fullread: read with newlines"); + REQUIRE_THAT(ll_test::Memory((void*) buf, static_cast(str.size())-1), ll_test::MemoryMatches((void*) str.c_str(), static_cast(str.size())-1)); is.clear(); is.str(str = "First Line.\nSecond Line\n"); @@ -855,14 +933,14 @@ namespace tut char expected_string[] = "First Line.\nSecond"; int len = sizeof(expected_string)-1; fullread(is, buf, len); - ensure_memory_matches("fullread: read with newlines", (void*) buf, len, (void*) &expected_string, len); + UNSCOPED_INFO("fullread: read with newlines"); + REQUIRE_THAT(ll_test::Memory((void*) buf, len), ll_test::MemoryMatches((void*) &expected_string, len)); } // testcases for operator >> - template<> template<> - void streamtools_object::test<26>() + TEST_CASE("streamtools: operator>> consumes a matching C string and fails on a mismatch", "[streamtools]") { char arr[255]; std::string str; @@ -872,12 +950,14 @@ namespace tut stream >> toCheck.c_str(); stream.get(arr, 255, '\0'); expected_result = " is a 3D World"; - ensure_equals("istream << operator", arr, expected_result); + UNSCOPED_INFO("istream << operator"); + CHECK(arr == expected_result); stream.clear(); stream.str(str = "SecondLife is a 3D World"); toCheck = "is"; stream >> toCheck.c_str(); - ensure("istream << operator should have failed", stream.good() == false); + UNSCOPED_INFO("istream << operator should have failed"); + CHECK_FALSE(stream.good()); } } diff --git a/indra/llcommon/tests/llstring_test.cpp b/indra/llcommon/tests/llstring_test.cpp index 44a1d3f087..f3ba3cc1cf 100644 --- a/indra/llcommon/tests/llstring_test.cpp +++ b/indra/llcommon/tests/llstring_test.cpp @@ -31,433 +31,603 @@ #include "../llstring.h" #include "../llsd.h" #include "StringVec.h" // must come BEFORE lltut.h -#include "../test/lltut.h" +#include +#include -namespace tut +namespace { - struct string_index - { - }; - typedef test_group string_index_t; - typedef string_index_t::object string_index_object_t; - tut::string_index_t tut_string_index("LLString"); - - template<> template<> - void string_index_object_t::test<1>() + TEST_CASE("LLString: std::string constructors give the expected text and size", "[LLString]") { std::string llstr1; - ensure("Empty std::string", (llstr1.size() == 0) && llstr1.empty()); + { + INFO("Empty std::string"); + REQUIRE(llstr1.size() == 0); + REQUIRE(llstr1.empty()); + } std::string llstr2("Hello"); - ensure("std::string = Hello", (!strcmp(llstr2.c_str(), "Hello")) && (llstr2.size() == 5) && !llstr2.empty()); + { + INFO("std::string = Hello"); + CHECK(std::string_view(llstr2.c_str()) == "Hello"); + REQUIRE(llstr2.size() == 5); + REQUIRE_FALSE(llstr2.empty()); + } std::string llstr3(llstr2); - ensure("std::string = std::string(std::string)", (!strcmp(llstr3.c_str(), "Hello")) && (llstr3.size() == 5) && !llstr3.empty()); + { + INFO("std::string = std::string(std::string)"); + CHECK(std::string_view(llstr3.c_str()) == "Hello"); + REQUIRE(llstr3.size() == 5); + REQUIRE_FALSE(llstr3.empty()); + } std::string str("Hello World"); std::string llstr4(str, 6); - ensure("std::string = std::string(s, size_type pos, size_type n = npos)", (!strcmp(llstr4.c_str(), "World")) && (llstr4.size() == 5) && !llstr4.empty()); + { + INFO("std::string = std::string(s, size_type pos, size_type n = npos)"); + CHECK(std::string_view(llstr4.c_str()) == "World"); + REQUIRE(llstr4.size() == 5); + REQUIRE_FALSE(llstr4.empty()); + } std::string llstr5(str, str.size()); - ensure("std::string = std::string(s, size_type pos, size_type n = npos)", (llstr5.size() == 0) && llstr5.empty()); + { + INFO("std::string = std::string(s, size_type pos, size_type n = npos)"); + REQUIRE(llstr5.size() == 0); + REQUIRE(llstr5.empty()); + } std::string llstr6(5, 'A'); - ensure("std::string = std::string(count, c)", (!strcmp(llstr6.c_str(), "AAAAA")) && (llstr6.size() == 5) && !llstr6.empty()); + { + INFO("std::string = std::string(count, c)"); + CHECK(std::string_view(llstr6.c_str()) == "AAAAA"); + REQUIRE(llstr6.size() == 5); + REQUIRE_FALSE(llstr6.empty()); + } std::string llstr7("Hello World", 5); - ensure("std::string(s, n)", (!strcmp(llstr7.c_str(), "Hello")) && (llstr7.size() == 5) && !llstr7.empty()); + { + INFO("std::string(s, n)"); + CHECK(std::string_view(llstr7.c_str()) == "Hello"); + REQUIRE(llstr7.size() == 5); + REQUIRE_FALSE(llstr7.empty()); + } std::string llstr8("Hello World", 6, 5); - ensure("std::string(s, n, count)", (!strcmp(llstr8.c_str(), "World")) && (llstr8.size() == 5) && !llstr8.empty()); + { + INFO("std::string(s, n, count)"); + CHECK(std::string_view(llstr8.c_str()) == "World"); + REQUIRE(llstr8.size() == 5); + REQUIRE_FALSE(llstr8.empty()); + } std::string llstr9("Hello World", sizeof("Hello World")-1, 5); // go past end - ensure("std::string(s, n, count) goes past end", (llstr9.size() == 0) && llstr9.empty()); + { + INFO("std::string(s, n, count) goes past end"); + REQUIRE(llstr9.size() == 0); + REQUIRE(llstr9.empty()); + } } - template<> template<> - void string_index_object_t::test<3>() + TEST_CASE("LLString: isValidIndex accepts indexes up to and including the length", "[LLString]") { std::string str("Len=5"); - ensure("isValidIndex failed", LLStringUtil::isValidIndex(str, 0) == true && - LLStringUtil::isValidIndex(str, 5) == true && - LLStringUtil::isValidIndex(str, 6) == false); + { + INFO("isValidIndex failed"); + CHECK(LLStringUtil::isValidIndex(str, 0)); + CHECK(LLStringUtil::isValidIndex(str, 5)); + CHECK_FALSE(LLStringUtil::isValidIndex(str, 6)); + } std::string str1; - ensure("isValidIndex failed fo rempty string", LLStringUtil::isValidIndex(str1, 0) == false); + UNSCOPED_INFO("isValidIndex failed fo rempty string"); + CHECK_FALSE(LLStringUtil::isValidIndex(str1, 0)); } - template<> template<> - void string_index_object_t::test<4>() + TEST_CASE("LLString: trimHead removes leading spaces and line ends", "[LLString]") { std::string str_val(" Testing the extra whitespaces "); LLStringUtil::trimHead(str_val); - ensure_equals("1: trimHead failed", str_val, "Testing the extra whitespaces "); + UNSCOPED_INFO("1: trimHead failed"); + CHECK(str_val == "Testing the extra whitespaces "); std::string str_val1("\n\t\r\n Testing the extra whitespaces "); LLStringUtil::trimHead(str_val1); - ensure_equals("2: trimHead failed", str_val1, "Testing the extra whitespaces "); + UNSCOPED_INFO("2: trimHead failed"); + CHECK(str_val1 == "Testing the extra whitespaces "); } - template<> template<> - void string_index_object_t::test<5>() + TEST_CASE("LLString: trimTail removes trailing spaces and line ends", "[LLString]") { std::string str_val(" Testing the extra whitespaces "); LLStringUtil::trimTail(str_val); - ensure_equals("1: trimTail failed", str_val, " Testing the extra whitespaces"); + UNSCOPED_INFO("1: trimTail failed"); + CHECK(str_val == " Testing the extra whitespaces"); std::string str_val1("\n Testing the extra whitespaces \n\t\r\n "); LLStringUtil::trimTail(str_val1); - ensure_equals("2: trimTail failed", str_val1, "\n Testing the extra whitespaces"); + UNSCOPED_INFO("2: trimTail failed"); + CHECK(str_val1 == "\n Testing the extra whitespaces"); } - template<> template<> - void string_index_object_t::test<6>() + TEST_CASE("LLString: trim removes whitespace at both ends only", "[LLString]") { std::string str_val(" \t \r Testing the extra \r\n whitespaces \n \t "); LLStringUtil::trim(str_val); - ensure_equals("1: trim failed", str_val, "Testing the extra \r\n whitespaces"); + UNSCOPED_INFO("1: trim failed"); + CHECK(str_val == "Testing the extra \r\n whitespaces"); } - template<> template<> - void string_index_object_t::test<7>() + TEST_CASE("LLString: truncate shortens to a length and to nothing", "[LLString]") { std::string str("Second LindenLabs"); LLStringUtil::truncate(str, 6); - ensure_equals("1: truncate", str, "Second"); + UNSCOPED_INFO("1: truncate"); + CHECK(str == "Second"); // further truncate more than the length LLStringUtil::truncate(str, 0); - ensure_equals("2: truncate", str, ""); + UNSCOPED_INFO("2: truncate"); + CHECK(str == ""); } - template<> template<> - void string_index_object_t::test<8>() + TEST_CASE("LLString: toUpper uppercases ASCII", "[LLString]") { std::string str_val("SecondLife Source"); LLStringUtil::toUpper(str_val); - ensure_equals("toUpper failed", str_val, "SECONDLIFE SOURCE"); + UNSCOPED_INFO("toUpper failed"); + CHECK(str_val == "SECONDLIFE SOURCE"); } - template<> template<> - void string_index_object_t::test<9>() + TEST_CASE("LLString: toLower lowercases ASCII", "[LLString]") { std::string str_val("SecondLife Source"); LLStringUtil::toLower(str_val); - ensure_equals("toLower failed", str_val, "secondlife source"); + UNSCOPED_INFO("toLower failed"); + CHECK(str_val == "secondlife source"); } - template<> template<> - void string_index_object_t::test<10>() + TEST_CASE("LLString: isHead matches a prefix and never an empty string", "[LLString]") { std::string str_val("Second"); - ensure("1. isHead failed", LLStringUtil::isHead(str_val, "SecondLife Source") == true); - ensure("2. isHead failed", LLStringUtil::isHead(str_val, " SecondLife Source") == false); + UNSCOPED_INFO("1. isHead failed"); + CHECK(LLStringUtil::isHead(str_val, "SecondLife Source")); + UNSCOPED_INFO("2. isHead failed"); + CHECK_FALSE(LLStringUtil::isHead(str_val, " SecondLife Source")); std::string str_val2(""); - ensure("3. isHead failed", LLStringUtil::isHead(str_val2, "") == false); + UNSCOPED_INFO("3. isHead failed"); + CHECK_FALSE(LLStringUtil::isHead(str_val2, "")); } - template<> template<> - void string_index_object_t::test<11>() + TEST_CASE("LLString: addCRLF and removeCRLF convert line ends both ways", "[LLString]") { std::string str_val("Hello.\n\n Lindenlabs. \n This is \na simple test.\n"); std::string orig_str_val(str_val); LLStringUtil::addCRLF(str_val); - ensure_equals("addCRLF failed", str_val, "Hello.\r\n\r\n Lindenlabs. \r\n This is \r\na simple test.\r\n"); + UNSCOPED_INFO("addCRLF failed"); + CHECK(str_val == "Hello.\r\n\r\n Lindenlabs. \r\n This is \r\na simple test.\r\n"); LLStringUtil::removeCRLF(str_val); - ensure_equals("removeCRLF failed", str_val, orig_str_val); + UNSCOPED_INFO("removeCRLF failed"); + CHECK(str_val == orig_str_val); } - template<> template<> - void string_index_object_t::test<12>() + TEST_CASE("LLString: replaceTabsWithSpaces puts n spaces for each tab", "[LLString]") { std::string str_val("Hello.\n\n\t \t Lindenlabs. \t\t"); std::string orig_str_val(str_val); LLStringUtil::replaceTabsWithSpaces(str_val, 1); - ensure_equals("replaceTabsWithSpaces failed", str_val, "Hello.\n\n Lindenlabs. "); + UNSCOPED_INFO("replaceTabsWithSpaces failed"); + CHECK(str_val == "Hello.\n\n Lindenlabs. "); LLStringUtil::replaceTabsWithSpaces(orig_str_val, 0); - ensure_equals("replaceTabsWithSpaces failed for 0", orig_str_val, "Hello.\n\n Lindenlabs. "); + UNSCOPED_INFO("replaceTabsWithSpaces failed for 0"); + CHECK(orig_str_val == "Hello.\n\n Lindenlabs. "); str_val = "\t\t\t\t"; LLStringUtil::replaceTabsWithSpaces(str_val, 0); - ensure_equals("replaceTabsWithSpaces failed for all tabs", str_val, ""); + UNSCOPED_INFO("replaceTabsWithSpaces failed for all tabs"); + CHECK(str_val == ""); } - template<> template<> - void string_index_object_t::test<13>() + TEST_CASE("LLString: replaceNonstandardASCII replaces controls other than newline", "[LLString]") { std::string str_val("Hello.\n\n\t\t\r\nLindenlabsX."); LLStringUtil::replaceNonstandardASCII(str_val, 'X'); - ensure_equals("replaceNonstandardASCII failed", str_val, "Hello.\n\nXXX\nLindenlabsX."); + UNSCOPED_INFO("replaceNonstandardASCII failed"); + CHECK(str_val == "Hello.\n\nXXX\nLindenlabsX."); } - template<> template<> - void string_index_object_t::test<14>() + TEST_CASE("LLString: replaceChar replaces every instance of a character", "[LLString]") { std::string str_val("Hello.\n\t\r\nABCDEFGHIABABAB"); LLStringUtil::replaceChar(str_val, 'A', 'X'); - ensure_equals("1: replaceChar failed", str_val, "Hello.\n\t\r\nXBCDEFGHIXBXBXB"); + UNSCOPED_INFO("1: replaceChar failed"); + CHECK(str_val == "Hello.\n\t\r\nXBCDEFGHIXBXBXB"); std::string str_val1("Hello.\n\t\r\nABCDEFGHIABABAB"); } - template<> template<> - void string_index_object_t::test<15>() + TEST_CASE("LLString: containsNonprintable finds controls and separators but not text", "[LLString]") { std::string str_val("Hello.\n\r\t"); - ensure("containsNonprintable failed", LLStringUtil::containsNonprintable(str_val) == true); + UNSCOPED_INFO("containsNonprintable failed"); + CHECK(LLStringUtil::containsNonprintable(str_val)); str_val = "ABC "; - ensure("containsNonprintable failed", LLStringUtil::containsNonprintable(str_val) == false); + UNSCOPED_INFO("containsNonprintable failed"); + CHECK_FALSE(LLStringUtil::containsNonprintable(str_val)); // Controls above the ASCII range are two bytes, and neither of them // looks like a control on its own. U+0085 NEXT LINE. str_val = "AB\xC2\x85" "C"; - ensure("C1 control found", LLStringUtil::containsNonprintable(str_val) == true); + UNSCOPED_INFO("C1 control found"); + CHECK(LLStringUtil::containsNonprintable(str_val)); // U+2028 LINE SEPARATOR. str_val = "AB\xE2\x80\xA8" "C"; - ensure("line separator found", LLStringUtil::containsNonprintable(str_val) == true); + UNSCOPED_INFO("line separator found"); + CHECK(LLStringUtil::containsNonprintable(str_val)); // DEL, which a test for "below 0x20" never saw. str_val = "AB\x7F" "C"; - ensure("delete found", LLStringUtil::containsNonprintable(str_val) == true); + UNSCOPED_INFO("delete found"); + CHECK(LLStringUtil::containsNonprintable(str_val)); // Non-ASCII text is printable, and so are the format characters that // scripts depend on -- ZWJ here, which Indic conjuncts need. str_val = "\xE6\x97\xA5" "\xC3\xA9" "ok"; - ensure("ordinary text is printable", LLStringUtil::containsNonprintable(str_val) == false); + UNSCOPED_INFO("ordinary text is printable"); + CHECK_FALSE(LLStringUtil::containsNonprintable(str_val)); str_val = "\xE0\xA4\x95\xE2\x80\x8D\xE0\xA4\xB7"; - ensure("ZWJ is printable", LLStringUtil::containsNonprintable(str_val) == false); + UNSCOPED_INFO("ZWJ is printable"); + CHECK_FALSE(LLStringUtil::containsNonprintable(str_val)); } - template<> template<> - void string_index_object_t::test<16>() + TEST_CASE("LLString: stripNonprintable removes controls and separators whole", "[LLString]") { std::string str_val("Hello.\n\r\t Again!"); LLStringUtil::stripNonprintable(str_val); - ensure_equals("stripNonprintable failed", str_val, "Hello. Again!"); + UNSCOPED_INFO("stripNonprintable failed"); + CHECK(str_val == "Hello. Again!"); str_val = "\r\n\t\t"; LLStringUtil::stripNonprintable(str_val); - ensure_equals("stripNonprintable resulting in empty string failed", str_val, ""); + UNSCOPED_INFO("stripNonprintable resulting in empty string failed"); + CHECK(str_val == ""); str_val = ""; LLStringUtil::stripNonprintable(str_val); - ensure_equals("stripNonprintable of empty string resulting in empty string failed", str_val, ""); + UNSCOPED_INFO("stripNonprintable of empty string resulting in empty string failed"); + CHECK(str_val == ""); // A control above the ASCII range goes whole, taking both its bytes. str_val = "AB\xC2\x85" "C"; LLStringUtil::stripNonprintable(str_val); - ensure_equals("C1 control stripped whole", str_val, "ABC"); + UNSCOPED_INFO("C1 control stripped whole"); + CHECK(str_val == "ABC"); // Text either side of it is left alone, bytes and all. str_val = "\xE6\x97\xA5" "\xE2\x80\xA8" "\xC3\xA9"; LLStringUtil::stripNonprintable(str_val); - ensure_equals("only the separator goes", str_val, "\xE6\x97\xA5" "\xC3\xA9"); - } - - // Capitalising a byte can only ever reach ASCII, so the narrow form works - // in codepoints. - template<> template<> - void string_index_object_t::test<45>() - { - std::string str_val("hello world-again_now"); - LLStringUtil::capitalize(str_val); - ensure_equals("ascii words", str_val, "Hello World-Again_Now"); - - // U+00E9 -> U+00C9, which uppercasing one byte at a time could not do. - str_val = "\xC3\xA9" "cole"; - LLStringUtil::capitalize(str_val); - ensure_equals("accented first letter", str_val, "\xC3\x89" "cole"); - - // The word rule is the caller's and still only sees these three. - str_val = "a b-c_d.e"; - LLStringUtil::capitalize(str_val); - ensure_equals("dot is not a separator", str_val, "A B-C_D.e"); - - str_val = ""; - LLStringUtil::capitalize(str_val); - ensure_equals("empty", str_val, ""); + UNSCOPED_INFO("only the separator goes"); + CHECK(str_val == "\xE6\x97\xA5" "\xC3\xA9"); } - template<> template<> - void string_index_object_t::test<17>() + TEST_CASE("LLString: convertToBOOL reads the true and false spellings", "[LLString]") { bool value; std::string str_val("1"); - ensure("convertToBOOL 1 failed", LLStringUtil::convertToBOOL(str_val, value) && value); + { + INFO("convertToBOOL 1 failed"); + REQUIRE(LLStringUtil::convertToBOOL(str_val, value)); + CHECK(value); + } str_val = "T"; - ensure("convertToBOOL T failed", LLStringUtil::convertToBOOL(str_val, value) && value); + { + INFO("convertToBOOL T failed"); + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK(value); + } str_val = "t"; - ensure("convertToBOOL t failed", LLStringUtil::convertToBOOL(str_val, value) && value); + { + INFO("convertToBOOL t failed"); + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK(value); + } str_val = "TRUE"; - ensure("convertToBOOL TRUE failed", LLStringUtil::convertToBOOL(str_val, value) && value); + { + INFO("convertToBOOL TRUE failed"); + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK(value); + } str_val = "True"; - ensure("convertToBOOL True failed", LLStringUtil::convertToBOOL(str_val, value) && value); + { + INFO("convertToBOOL True failed"); + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK(value); + } str_val = "true"; - ensure("convertToBOOL true failed", LLStringUtil::convertToBOOL(str_val, value) && value); + { + INFO("convertToBOOL true failed"); + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK(value); + } str_val = "0"; - ensure("convertToBOOL 0 failed", LLStringUtil::convertToBOOL(str_val, value) && !value); + { + INFO("convertToBOOL 0 failed"); + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK_FALSE(value); + } str_val = "F"; - ensure("convertToBOOL F failed", LLStringUtil::convertToBOOL(str_val, value) && !value); + { + INFO("convertToBOOL F failed"); + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK_FALSE(value); + } str_val = "f"; - ensure("convertToBOOL f failed", LLStringUtil::convertToBOOL(str_val, value) && !value); + { + INFO("convertToBOOL f failed"); + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK_FALSE(value); + } str_val = "FALSE"; - ensure("convertToBOOL FASLE failed", LLStringUtil::convertToBOOL(str_val, value) && !value); + { + INFO("convertToBOOL FASLE failed"); + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK_FALSE(value); + } str_val = "False"; - ensure("convertToBOOL False failed", LLStringUtil::convertToBOOL(str_val, value) && !value); + { + INFO("convertToBOOL False failed"); + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK_FALSE(value); + } str_val = "false"; - ensure("convertToBOOL false failed", LLStringUtil::convertToBOOL(str_val, value) && !value); + { + INFO("convertToBOOL false failed"); + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK_FALSE(value); + } str_val = "Tblah"; - ensure("convertToBOOL false failed", !LLStringUtil::convertToBOOL(str_val, value)); + UNSCOPED_INFO("convertToBOOL false failed"); + CHECK_FALSE(LLStringUtil::convertToBOOL(str_val, value)); } - template<> template<> - void string_index_object_t::test<18>() + TEST_CASE("LLString: convertToU8 accepts 0 to 255 only", "[LLString]") { U8 value; std::string str_val("255"); - ensure("1: convertToU8 failed", LLStringUtil::convertToU8(str_val, value) && value == 255); + { + INFO("1: convertToU8 failed"); + REQUIRE(LLStringUtil::convertToU8(str_val, value)); + CHECK(value == 255); + } str_val = "0"; - ensure("2: convertToU8 failed", LLStringUtil::convertToU8(str_val, value) && value == 0); + { + INFO("2: convertToU8 failed"); + CHECK(LLStringUtil::convertToU8(str_val, value)); + CHECK(value == 0); + } str_val = "-1"; - ensure("3: convertToU8 failed", !LLStringUtil::convertToU8(str_val, value)); + UNSCOPED_INFO("3: convertToU8 failed"); + CHECK_FALSE(LLStringUtil::convertToU8(str_val, value)); str_val = "256"; // bigger than MAX_U8 - ensure("4: convertToU8 failed", !LLStringUtil::convertToU8(str_val, value)); + UNSCOPED_INFO("4: convertToU8 failed"); + CHECK_FALSE(LLStringUtil::convertToU8(str_val, value)); } - template<> template<> - void string_index_object_t::test<19>() + TEST_CASE("LLString: convertToS8 accepts -128 to 127 only", "[LLString]") { S8 value; std::string str_val("127"); - ensure("1: convertToS8 failed", LLStringUtil::convertToS8(str_val, value) && value == 127); + { + INFO("1: convertToS8 failed"); + REQUIRE(LLStringUtil::convertToS8(str_val, value)); + CHECK(value == 127); + } str_val = "0"; - ensure("2: convertToS8 failed", LLStringUtil::convertToS8(str_val, value) && value == 0); + { + INFO("2: convertToS8 failed"); + CHECK(LLStringUtil::convertToS8(str_val, value)); + CHECK(value == 0); + } str_val = "-128"; - ensure("3: convertToS8 failed", LLStringUtil::convertToS8(str_val, value) && value == -128); + { + INFO("3: convertToS8 failed"); + CHECK(LLStringUtil::convertToS8(str_val, value)); + CHECK(value == -128); + } str_val = "128"; // bigger than MAX_S8 - ensure("4: convertToS8 failed", !LLStringUtil::convertToS8(str_val, value)); + UNSCOPED_INFO("4: convertToS8 failed"); + CHECK_FALSE(LLStringUtil::convertToS8(str_val, value)); str_val = "-129"; - ensure("5: convertToS8 failed", !LLStringUtil::convertToS8(str_val, value)); + UNSCOPED_INFO("5: convertToS8 failed"); + CHECK_FALSE(LLStringUtil::convertToS8(str_val, value)); } - template<> template<> - void string_index_object_t::test<20>() + TEST_CASE("LLString: convertToS16 accepts -32768 to 32767 only", "[LLString]") { S16 value; std::string str_val("32767"); - ensure("1: convertToS16 failed", LLStringUtil::convertToS16(str_val, value) && value == 32767); + { + INFO("1: convertToS16 failed"); + REQUIRE(LLStringUtil::convertToS16(str_val, value)); + CHECK(value == 32767); + } str_val = "0"; - ensure("2: convertToS16 failed", LLStringUtil::convertToS16(str_val, value) && value == 0); + { + INFO("2: convertToS16 failed"); + CHECK(LLStringUtil::convertToS16(str_val, value)); + CHECK(value == 0); + } str_val = "-32768"; - ensure("3: convertToS16 failed", LLStringUtil::convertToS16(str_val, value) && value == -32768); + { + INFO("3: convertToS16 failed"); + CHECK(LLStringUtil::convertToS16(str_val, value)); + CHECK(value == -32768); + } str_val = "32768"; - ensure("4: convertToS16 failed", !LLStringUtil::convertToS16(str_val, value)); + UNSCOPED_INFO("4: convertToS16 failed"); + CHECK_FALSE(LLStringUtil::convertToS16(str_val, value)); str_val = "-32769"; - ensure("5: convertToS16 failed", !LLStringUtil::convertToS16(str_val, value)); + UNSCOPED_INFO("5: convertToS16 failed"); + CHECK_FALSE(LLStringUtil::convertToS16(str_val, value)); } - template<> template<> - void string_index_object_t::test<21>() + TEST_CASE("LLString: convertToU16 accepts 0 to 65535 only", "[LLString]") { U16 value; std::string str_val("65535"); //0xFFFF - ensure("1: convertToU16 failed", LLStringUtil::convertToU16(str_val, value) && value == 65535); + { + INFO("1: convertToU16 failed"); + REQUIRE(LLStringUtil::convertToU16(str_val, value)); + CHECK(value == 65535); + } str_val = "0"; - ensure("2: convertToU16 failed", LLStringUtil::convertToU16(str_val, value) && value == 0); + { + INFO("2: convertToU16 failed"); + CHECK(LLStringUtil::convertToU16(str_val, value)); + CHECK(value == 0); + } str_val = "-1"; - ensure("3: convertToU16 failed", !LLStringUtil::convertToU16(str_val, value)); + UNSCOPED_INFO("3: convertToU16 failed"); + CHECK_FALSE(LLStringUtil::convertToU16(str_val, value)); str_val = "65536"; - ensure("4: convertToU16 failed", !LLStringUtil::convertToU16(str_val, value)); + UNSCOPED_INFO("4: convertToU16 failed"); + CHECK_FALSE(LLStringUtil::convertToU16(str_val, value)); } - template<> template<> - void string_index_object_t::test<22>() + TEST_CASE("LLString: convertToU32 accepts 0 to 4294967295 and a leading plus", "[LLString]") { U32 value; std::string str_val("4294967295"); //0xFFFFFFFF - ensure("1: convertToU32 failed", LLStringUtil::convertToU32(str_val, value) && value == 4294967295UL); + { + INFO("1: convertToU32 failed"); + REQUIRE(LLStringUtil::convertToU32(str_val, value)); + CHECK(value == 4294967295UL); + } str_val = "0"; - ensure("2: convertToU32 failed", LLStringUtil::convertToU32(str_val, value) && value == 0); + { + INFO("2: convertToU32 failed"); + CHECK(LLStringUtil::convertToU32(str_val, value)); + CHECK(value == 0); + } str_val = "4294967296"; - ensure("3: convertToU32 failed", !LLStringUtil::convertToU32(str_val, value)); + UNSCOPED_INFO("3: convertToU32 failed"); + CHECK_FALSE(LLStringUtil::convertToU32(str_val, value)); // A negative is rejected rather than wrapped. convertToU8 and // convertToU16 above already answer false for "-1" -- they range-check // after parsing as signed -- so this is the unsigned form agreeing with // its own siblings. Stream extraction let num_get negate it instead. str_val = "-1"; - ensure("4: convertToU32 failed", !LLStringUtil::convertToU32(str_val, value)); + UNSCOPED_INFO("4: convertToU32 failed"); + CHECK_FALSE(LLStringUtil::convertToU32(str_val, value)); // A leading '+' is still accepted, as it was through the stream. str_val = "+7"; - ensure("5: convertToU32 failed", LLStringUtil::convertToU32(str_val, value) && value == 7); + { + INFO("5: convertToU32 failed"); + CHECK(LLStringUtil::convertToU32(str_val, value)); + CHECK(value == 7); + } } - template<> template<> - void string_index_object_t::test<23>() + TEST_CASE("LLString: convertToS32 reads the S32 range with a sign or spaces or trailing text", "[LLString]") { S32 value; std::string str_val("2147483647"); //0x7FFFFFFF - ensure("1: convertToS32 failed", LLStringUtil::convertToS32(str_val, value) && value == 2147483647); + { + INFO("1: convertToS32 failed"); + REQUIRE(LLStringUtil::convertToS32(str_val, value)); + CHECK(value == 2147483647); + } str_val = "0"; - ensure("2: convertToS32 failed", LLStringUtil::convertToS32(str_val, value) && value == 0); + { + INFO("2: convertToS32 failed"); + CHECK(LLStringUtil::convertToS32(str_val, value)); + CHECK(value == 0); + } // Avoid "unary minus operator applied to unsigned type" warning on VC++. JC S32 min_val = -2147483647 - 1; str_val = "-2147483648"; - ensure("3: convertToS32 failed", LLStringUtil::convertToS32(str_val, value) && value == min_val); + { + INFO("3: convertToS32 failed"); + CHECK(LLStringUtil::convertToS32(str_val, value)); + CHECK(value == min_val); + } str_val = "2147483648"; - ensure("4: convertToS32 failed", !LLStringUtil::convertToS32(str_val, value)); + UNSCOPED_INFO("4: convertToS32 failed"); + CHECK_FALSE(LLStringUtil::convertToS32(str_val, value)); str_val = "-2147483649"; - ensure("5: convertToS32 failed", !LLStringUtil::convertToS32(str_val, value)); + UNSCOPED_INFO("5: convertToS32 failed"); + CHECK_FALSE(LLStringUtil::convertToS32(str_val, value)); // from_chars does not take a leading '+' on its own; the conversions // strip it so they keep accepting what stream extraction accepted. str_val = "+42"; - ensure("6: convertToS32 failed", LLStringUtil::convertToS32(str_val, value) && value == 42); + { + INFO("6: convertToS32 failed"); + CHECK(LLStringUtil::convertToS32(str_val, value)); + CHECK(value == 42); + } // Surrounding space is trimmed, and a parse that stops early still // reports what it read -- both as the stream did. str_val = " 13 "; - ensure("7: convertToS32 failed", LLStringUtil::convertToS32(str_val, value) && value == 13); + { + INFO("7: convertToS32 failed"); + CHECK(LLStringUtil::convertToS32(str_val, value)); + CHECK(value == 13); + } str_val = "12abc"; - ensure("8: convertToS32 failed", LLStringUtil::convertToS32(str_val, value) && value == 12); + { + INFO("8: convertToS32 failed"); + CHECK(LLStringUtil::convertToS32(str_val, value)); + CHECK(value == 12); + } str_val = "abc"; - ensure("9: convertToS32 failed", !LLStringUtil::convertToS32(str_val, value)); + UNSCOPED_INFO("9: convertToS32 failed"); + CHECK_FALSE(LLStringUtil::convertToS32(str_val, value)); } - template<> template<> - void string_index_object_t::test<24>() + TEST_CASE("LLString: convertToF32 reads decimals and exponents and signs", "[LLString]") { F32 value; std::string str_val("2147483647"); //0x7FFFFFFF - ensure("1: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 2147483647.f); + { + INFO("1: convertToF32 failed"); + REQUIRE(LLStringUtil::convertToF32(str_val, value)); + CHECK(value == 2147483647.f); + } str_val = "0"; - ensure("2: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 0); + { + INFO("2: convertToF32 failed"); + CHECK(LLStringUtil::convertToF32(str_val, value)); + CHECK(value == 0); + } /* Need to find max/min F32 values str_val = "-2147483648"; @@ -474,33 +644,61 @@ namespace tut // a leading '+' all read the same as they did through the stream, and // the decimal point stays a point whatever the C locale is set to. str_val = "1.5"; - ensure("6: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 1.5f); + { + INFO("6: convertToF32 failed"); + CHECK(LLStringUtil::convertToF32(str_val, value)); + CHECK(value == 1.5f); + } str_val = "-2.25"; - ensure("7: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == -2.25f); + { + INFO("7: convertToF32 failed"); + CHECK(LLStringUtil::convertToF32(str_val, value)); + CHECK(value == -2.25f); + } str_val = "+0.5"; - ensure("8: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 0.5f); + { + INFO("8: convertToF32 failed"); + CHECK(LLStringUtil::convertToF32(str_val, value)); + CHECK(value == 0.5f); + } str_val = "1.5e2"; - ensure("9: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 150.f); + { + INFO("9: convertToF32 failed"); + CHECK(LLStringUtil::convertToF32(str_val, value)); + CHECK(value == 150.f); + } str_val = " 0.25 "; - ensure("10: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 0.25f); + { + INFO("10: convertToF32 failed"); + CHECK(LLStringUtil::convertToF32(str_val, value)); + CHECK(value == 0.25f); + } str_val = "nonsense"; - ensure("11: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value)); + UNSCOPED_INFO("11: convertToF32 failed"); + CHECK_FALSE(LLStringUtil::convertToF32(str_val, value)); } - template<> template<> - void string_index_object_t::test<25>() + TEST_CASE("LLString: convertToF64 reads a large integer and zero", "[LLString]") { F64 value; std::string str_val("9223372036854775807"); //0x7FFFFFFFFFFFFFFF - ensure("1: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 9223372036854775807.0); + { + INFO("1: convertToF64 failed"); + REQUIRE(LLStringUtil::convertToF64(str_val, value)); + CHECK(value == 9223372036854775807.0); + } str_val = "0"; - ensure("2: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 0.0F); + { + INFO("2: convertToF64 failed"); + CHECK(LLStringUtil::convertToF64(str_val, value)); + CHECK(value == 0.0F); + } /* Need to find max/min F64 values str_val = "-2147483648"; @@ -514,118 +712,133 @@ namespace tut */ } - template<> template<> - void string_index_object_t::test<26>() + TEST_CASE("LLString: compareStrings puts null after text and compares text", "[LLString]") { const char* str1 = NULL; const char* str2 = NULL; - ensure("1: compareStrings failed", LLStringUtil::compareStrings(str1, str2) == 0); + UNSCOPED_INFO("1: compareStrings failed"); + CHECK(LLStringUtil::compareStrings(str1, str2) == 0); str2 = "A"; - ensure("2: compareStrings failed", LLStringUtil::compareStrings(str1, str2) > 0); - ensure("3: compareStrings failed", LLStringUtil::compareStrings(str2, str1) < 0); + UNSCOPED_INFO("2: compareStrings failed"); + CHECK(LLStringUtil::compareStrings(str1, str2) > 0); + UNSCOPED_INFO("3: compareStrings failed"); + CHECK(LLStringUtil::compareStrings(str2, str1) < 0); str1 = "A is smaller than B"; str2 = "B is greater than A"; - ensure("4: compareStrings failed", LLStringUtil::compareStrings(str1, str2) < 0); + UNSCOPED_INFO("4: compareStrings failed"); + CHECK(LLStringUtil::compareStrings(str1, str2) < 0); str2 = "A is smaller than B"; - ensure("5: compareStrings failed", LLStringUtil::compareStrings(str1, str2) == 0); + UNSCOPED_INFO("5: compareStrings failed"); + CHECK(LLStringUtil::compareStrings(str1, str2) == 0); } - template<> template<> - void string_index_object_t::test<27>() + TEST_CASE("LLString: compareInsensitive ignores case but not accents or sharp s", "[LLString]") { const char* str1 = NULL; const char* str2 = NULL; - ensure("1: compareInsensitive failed", LLStringUtil::compareInsensitive(str1, str2) == 0); + UNSCOPED_INFO("1: compareInsensitive failed"); + CHECK(LLStringUtil::compareInsensitive(str1, str2) == 0); str2 = "A"; - ensure("2: compareInsensitive failed", LLStringUtil::compareInsensitive(str1, str2) > 0); - ensure("3: compareInsensitive failed", LLStringUtil::compareInsensitive(str2, str1) < 0); + UNSCOPED_INFO("2: compareInsensitive failed"); + CHECK(LLStringUtil::compareInsensitive(str1, str2) > 0); + UNSCOPED_INFO("3: compareInsensitive failed"); + CHECK(LLStringUtil::compareInsensitive(str2, str1) < 0); str1 = "A is equal to a"; str2 = "a is EQUAL to A"; - ensure("4: compareInsensitive failed", LLStringUtil::compareInsensitive(str1, str2) == 0); + UNSCOPED_INFO("4: compareInsensitive failed"); + CHECK(LLStringUtil::compareInsensitive(str1, str2) == 0); // Ignoring case is not the same as uppercasing both sides, which is // what this used to do: that turns sharp s into SS and so calls two // different words the same word. - ensure("sharp s is not ss", - LLStringUtil::compareInsensitive(std::string("stra\xC3\x9F""e"), + UNSCOPED_INFO("sharp s is not ss"); + CHECK(LLStringUtil::compareInsensitive(std::string("stra\xC3\x9F""e"), std::string("strasse")) != 0); - ensure("sharp s is not SS", - LLStringUtil::compareInsensitive(std::string("stra\xC3\x9F""e"), + UNSCOPED_INFO("sharp s is not SS"); + CHECK(LLStringUtil::compareInsensitive(std::string("stra\xC3\x9F""e"), std::string("STRASSE")) != 0); // Accents are a secondary difference and survive; case is tertiary // and does not. - ensure("accents survive", - LLStringUtil::compareInsensitive(std::string("ab"), + UNSCOPED_INFO("accents survive"); + CHECK(LLStringUtil::compareInsensitive(std::string("ab"), std::string("\xC3\xA4""b")) < 0); - ensure("case does not", - LLStringUtil::compareInsensitive(std::string("\xC3\x84""B"), + UNSCOPED_INFO("case does not"); + CHECK(LLStringUtil::compareInsensitive(std::string("\xC3\x84""B"), std::string("\xC3\xA4""b")) == 0); } - template<> template<> - void string_index_object_t::test<28>() + TEST_CASE("LLString: compareDict and precedesDict order dictionary style", "[LLString]") { std::string lhs_str("PROgraM12files"); std::string rhs_str("PROgram12Files"); - ensure("compareDict 1 failed", LLStringUtil::compareDict(lhs_str, rhs_str) < 0); - ensure("precedesDict 1 failed", LLStringUtil::precedesDict(lhs_str, rhs_str) == true); + UNSCOPED_INFO("compareDict 1 failed"); + CHECK(LLStringUtil::compareDict(lhs_str, rhs_str) < 0); + UNSCOPED_INFO("precedesDict 1 failed"); + CHECK(LLStringUtil::precedesDict(lhs_str, rhs_str)); lhs_str = "PROgram12Files"; rhs_str = "PROgram12Files"; - ensure("compareDict 2 failed", LLStringUtil::compareDict(lhs_str, rhs_str) == 0); - ensure("precedesDict 2 failed", LLStringUtil::precedesDict(lhs_str, rhs_str) == false); + UNSCOPED_INFO("compareDict 2 failed"); + CHECK(LLStringUtil::compareDict(lhs_str, rhs_str) == 0); + UNSCOPED_INFO("precedesDict 2 failed"); + CHECK_FALSE(LLStringUtil::precedesDict(lhs_str, rhs_str)); lhs_str = "PROgram12Files"; rhs_str = "PROgRAM12FILES"; - ensure("compareDict 3 failed", LLStringUtil::compareDict(lhs_str, rhs_str) > 0); - ensure("precedesDict 3 failed", LLStringUtil::precedesDict(lhs_str, rhs_str) == false); + UNSCOPED_INFO("compareDict 3 failed"); + CHECK(LLStringUtil::compareDict(lhs_str, rhs_str) > 0); + UNSCOPED_INFO("precedesDict 3 failed"); + CHECK_FALSE(LLStringUtil::precedesDict(lhs_str, rhs_str)); // Uppercase sorts first, which is a tertiary-level ordering. The // caseless form drops that level and so sees no difference at all. - ensure("compareDictInsensitive case", LLStringUtil::compareDictInsensitive(lhs_str, rhs_str) == 0); - ensure("compareDict case", LLStringUtil::compareDict(std::string("abc"), std::string("ABC")) > 0); - ensure("compareDict case 2", LLStringUtil::compareDict(std::string("Apple"), std::string("apple")) < 0); + UNSCOPED_INFO("compareDictInsensitive case"); + CHECK(LLStringUtil::compareDictInsensitive(lhs_str, rhs_str) == 0); + UNSCOPED_INFO("compareDict case"); + CHECK(LLStringUtil::compareDict(std::string("abc"), std::string("ABC")) > 0); + UNSCOPED_INFO("compareDict case 2"); + CHECK(LLStringUtil::compareDict(std::string("Apple"), std::string("apple")) < 0); // Digit runs compare as the numbers they spell. - ensure("natural 2 before 10", - LLStringUtil::compareDict(std::string("item2"), std::string("item10")) < 0); - ensure("natural 10 after 9", - LLStringUtil::compareDict(std::string("item10"), std::string("item9")) > 0); + UNSCOPED_INFO("natural 2 before 10"); + CHECK(LLStringUtil::compareDict(std::string("item2"), std::string("item10")) < 0); + UNSCOPED_INFO("natural 10 after 9"); + CHECK(LLStringUtil::compareDict(std::string("item10"), std::string("item9")) > 0); // An accented letter sorts beside the letter it decorates rather than // past the end of the alphabet: a < a-diaeresis < b, not merely // somewhere before z. Codepoint order put it after z. - ensure("accent after plain", - LLStringUtil::compareDict(std::string("ab"), std::string("\xC3\xA4" "b")) < 0); - ensure("accent before next letter", - LLStringUtil::compareDict(std::string("\xC3\xA4" "b"), std::string("bb")) < 0); - ensure("accent before z", - LLStringUtil::compareDict(std::string("\xC3\xA4" "b"), std::string("zb")) < 0); + UNSCOPED_INFO("accent after plain"); + CHECK(LLStringUtil::compareDict(std::string("ab"), std::string("\xC3\xA4" "b")) < 0); + UNSCOPED_INFO("accent before next letter"); + CHECK(LLStringUtil::compareDict(std::string("\xC3\xA4" "b"), std::string("bb")) < 0); + UNSCOPED_INFO("accent before z"); + CHECK(LLStringUtil::compareDict(std::string("\xC3\xA4" "b"), std::string("zb")) < 0); // ... and case-insensitive still keeps accents apart. - ensure("caseless keeps accents", - LLStringUtil::compareDictInsensitive(std::string("ab"), std::string("\xC3\xA4" "b")) < 0); + UNSCOPED_INFO("caseless keeps accents"); + CHECK(LLStringUtil::compareDictInsensitive(std::string("ab"), std::string("\xC3\xA4" "b")) < 0); } - template<> template<> - void string_index_object_t::test<29>() + TEST_CASE("LLString: copy copies no more than the destination size", "[LLString]") { char str1[] = "First String..."; char str2[100]; LLStringUtil::copy(str2, str1, 100); - ensure("LLStringUtil::copy with enough dest length failed", strcmp(str2, str1) == 0); + UNSCOPED_INFO("LLStringUtil::copy with enough dest length failed"); + CHECK(std::string_view(str2) == std::string_view(str1)); LLStringUtil::copy(str2, str1, sizeof("First")); - ensure("LLStringUtil::copy with less dest length failed", strcmp(str2, "First") == 0); + UNSCOPED_INFO("LLStringUtil::copy with less dest length failed"); + CHECK(std::string_view(str2) == "First"); } - template<> template<> - void string_index_object_t::test<30>() + TEST_CASE("LLString: copyInto inserts at an offset and appends at the end", "[LLString]") { std::string str1 = "This is the sentence..."; std::string str2 = "This is the "; @@ -636,15 +849,16 @@ namespace tut dest = str1; LLStringUtil::copyInto(dest, str3, str2.length()); - ensure("LLStringUtil::copyInto insert failed", dest == str4); + UNSCOPED_INFO("LLStringUtil::copyInto insert failed"); + CHECK(dest == str4); dest = str1; LLStringUtil::copyInto(dest, str3, dest.length()); - ensure("LLStringUtil::copyInto append failed", dest == str5); + UNSCOPED_INFO("LLStringUtil::copyInto append failed"); + CHECK(dest == str5); } - template<> template<> - void string_index_object_t::test<31>() + TEST_CASE("LLString: strip_invalid_xml removes controls other than tab and line ends", "[LLString]") { std::string stripped; @@ -652,7 +866,8 @@ namespace tut // should not be altered. std::string simple_text = "Hello, world!"; stripped = LLStringFn::strip_invalid_xml(simple_text); - ensure("Simple text passed unchanged", stripped == simple_text); + UNSCOPED_INFO("Simple text passed unchanged"); + CHECK(stripped == simple_text); // Control characters should be removed // except for 0x09, 0x0a, 0x0d @@ -667,8 +882,8 @@ namespace tut allowed_control_chars.push_back( (char)0x0d ); stripped = LLStringFn::strip_invalid_xml(control_chars); - ensure("Only tab, LF, CR control characters allowed", - stripped == allowed_control_chars); + UNSCOPED_INFO("Only tab, LF, CR control characters allowed"); + CHECK(stripped == allowed_control_chars); // UTF-8 should be passed intact, including high byte // characters. Try Francais (with C squiggle cedilla) @@ -677,11 +892,11 @@ namespace tut french.push_back( (char)0xA7 ); french += "ais"; stripped = LLStringFn::strip_invalid_xml( french ); - ensure("UTF-8 high byte text is allowed", french == stripped ); + UNSCOPED_INFO("UTF-8 high byte text is allowed"); + CHECK(french == stripped); } - template<> template<> - void string_index_object_t::test<32>() + TEST_CASE("LLString: format substitutes keys and counts the substitutions", "[LLString]") { // Test LLStringUtil::format() string interpolation LLStringUtil::format_map_t fmt_map; @@ -707,14 +922,17 @@ namespace tut std::string s1 = (std::string)iter->first; std::string s2 = (std::string)iter->second; subcount = LLStringUtil::format(s1, fmt_map); - ensure_equals("LLStringUtil::format: Raw interpolation result", s1, s2); + UNSCOPED_INFO("LLStringUtil::format: Raw interpolation result"); + REQUIRE(s1 == s2); if (s1 == "?" || s1 == "[]") // no interp expected { - ensure_equals("LLStringUtil::format: Raw interpolation result count", 0, subcount); + UNSCOPED_INFO("LLStringUtil::format: Raw interpolation result count"); + REQUIRE(0 == subcount); } else { - ensure_equals("LLStringUtil::format: Raw interpolation result count", 1, subcount); + UNSCOPED_INFO("LLStringUtil::format: Raw interpolation result count"); + REQUIRE(1 == subcount); } } @@ -725,14 +943,17 @@ namespace tut std::string s2 = (std::string)iter->second; s = s1 + s1 + s1 + s1; subcount = LLStringUtil::format(s, fmt_map); - ensure_equals("LLStringUtil::format: Rawx4 interpolation result", s, s2 + s2 + s2 + s2); + UNSCOPED_INFO("LLStringUtil::format: Rawx4 interpolation result"); + REQUIRE(s == s2 + s2 + s2 + s2); if (s1 == "?" || s1 == "[]") // no interp expected { - ensure_equals("LLStringUtil::format: Rawx4 interpolation result count", 0, subcount); + UNSCOPED_INFO("LLStringUtil::format: Rawx4 interpolation result count"); + REQUIRE(0 == subcount); } else { - ensure_equals("LLStringUtil::format: Rawx4 interpolation result count", 4, subcount); + UNSCOPED_INFO("LLStringUtil::format: Rawx4 interpolation result count"); + REQUIRE(4 == subcount); } } @@ -740,55 +961,68 @@ namespace tut std::string srcs = "!!!!!!!!!!!!!!!!"; s = srcs; subcount = LLStringUtil::format(s, fmt_map); - ensure_equals("LLStringUtil::format: No key test result", s, srcs); - ensure_equals("LLStringUtil::format: No key test result count", 0, subcount); + UNSCOPED_INFO("LLStringUtil::format: No key test result"); + CHECK(s == srcs); + UNSCOPED_INFO("LLStringUtil::format: No key test result count"); + CHECK(0 == subcount); // Test when source string has no keys and is empty std::string srcs3; s = srcs3; subcount = LLStringUtil::format(s, fmt_map); - ensure("LLStringUtil::format: No key test3 result", s.empty()); - ensure_equals("LLStringUtil::format: No key test3 result count", 0, subcount); + UNSCOPED_INFO("LLStringUtil::format: No key test3 result"); + REQUIRE(s.empty()); + UNSCOPED_INFO("LLStringUtil::format: No key test3 result count"); + CHECK(0 == subcount); // Test a substitution where a key is substituted with blankness std::string srcs2 = "[DELETE]"; s = srcs2; subcount = LLStringUtil::format(s, fmt_map); - ensure("LLStringUtil::format: Delete key test2 result", s.empty()); - ensure_equals("LLStringUtil::format: Delete key test2 result count", 1, subcount); + UNSCOPED_INFO("LLStringUtil::format: Delete key test2 result"); + REQUIRE(s.empty()); + UNSCOPED_INFO("LLStringUtil::format: Delete key test2 result count"); + CHECK(1 == subcount); // Test an assorted substitution std::string srcs4 = "[TRICK1][A][B][AAA][BBB][TRICK2][KEYLONGER][KEYSHORTER]?[DELETE]"; s = srcs4; subcount = LLStringUtil::format(s, fmt_map); - ensure_equals("LLStringUtil::format: Assorted Test1 result", s, "[A]abaaabbb[A]shortAm I not a long string??"); - ensure_equals("LLStringUtil::format: Assorted Test1 result count", 9, subcount); + UNSCOPED_INFO("LLStringUtil::format: Assorted Test1 result"); + CHECK(s == "[A]abaaabbb[A]shortAm I not a long string??"); + UNSCOPED_INFO("LLStringUtil::format: Assorted Test1 result count"); + CHECK(9 == subcount); // Test an assorted substitution std::string srcs5 = "[DELETE]?[KEYSHORTER][KEYLONGER][TRICK2][BBB][AAA][B][A][TRICK1]"; s = srcs5; subcount = LLStringUtil::format(s, fmt_map); - ensure_equals("LLStringUtil::format: Assorted Test2 result", s, "?Am I not a long string?short[A]bbbaaaba[A]"); - ensure_equals("LLStringUtil::format: Assorted Test2 result count", 9, subcount); + UNSCOPED_INFO("LLStringUtil::format: Assorted Test2 result"); + CHECK(s == "?Am I not a long string?short[A]bbbaaaba[A]"); + UNSCOPED_INFO("LLStringUtil::format: Assorted Test2 result count"); + CHECK(9 == subcount); // Test on nested brackets std::string srcs6 = "[[TRICK1]][[A]][[B]][[AAA]][[BBB]][[TRICK2]][[KEYLONGER]][[KEYSHORTER]]?[[DELETE]]"; s = srcs6; subcount = LLStringUtil::format(s, fmt_map); - ensure_equals("LLStringUtil::format: Assorted Test2 result", s, "[[A]][a][b][aaa][bbb][[A]][short][Am I not a long string?]?[]"); - ensure_equals("LLStringUtil::format: Assorted Test2 result count", 9, subcount); + UNSCOPED_INFO("LLStringUtil::format: Assorted Test2 result"); + CHECK(s == "[[A]][a][b][aaa][bbb][[A]][short][Am I not a long string?]?[]"); + UNSCOPED_INFO("LLStringUtil::format: Assorted Test2 result count"); + CHECK(9 == subcount); // Test an assorted substitution std::string srcs8 = "foo[DELETE]bar?"; s = srcs8; subcount = LLStringUtil::format(s, fmt_map); - ensure_equals("LLStringUtil::format: Assorted Test3 result", s, "foobar?"); - ensure_equals("LLStringUtil::format: Assorted Test3 result count", 1, subcount); + UNSCOPED_INFO("LLStringUtil::format: Assorted Test3 result"); + CHECK(s == "foobar?"); + UNSCOPED_INFO("LLStringUtil::format: Assorted Test3 result count"); + CHECK(1 == subcount); } - template<> template<> - void string_index_object_t::test<33>() + TEST_CASE("LLString: format with an empty map changes nothing", "[LLString]") { // Test LLStringUtil::format() string interpolation LLStringUtil::format_map_t blank_fmt_map; @@ -799,19 +1033,22 @@ namespace tut std::string srcs6 = "12345"; s = srcs6; subcount = LLStringUtil::format(s, blank_fmt_map); - ensure_equals("LLStringUtil::format: Blankfmt Test1 result", s, "12345"); - ensure_equals("LLStringUtil::format: Blankfmt Test1 result count", 0, subcount); + UNSCOPED_INFO("LLStringUtil::format: Blankfmt Test1 result"); + CHECK(s == "12345"); + UNSCOPED_INFO("LLStringUtil::format: Blankfmt Test1 result count"); + CHECK(0 == subcount); // Test substituting a blank string out of a blank format_map std::string srcs7; s = srcs7; subcount = LLStringUtil::format(s, blank_fmt_map); - ensure("LLStringUtil::format: Blankfmt Test2 result", s.empty()); - ensure_equals("LLStringUtil::format: Blankfmt Test2 result count", 0, subcount); + UNSCOPED_INFO("LLStringUtil::format: Blankfmt Test2 result"); + REQUIRE(s.empty()); + UNSCOPED_INFO("LLStringUtil::format: Blankfmt Test2 result count"); + CHECK(0 == subcount); } - template<> template<> - void string_index_object_t::test<34>() + TEST_CASE("LLString: format with an empty key in the map changes nothing", "[LLString]") { // Test that incorrect LLStringUtil::format() use does not explode. LLStringUtil::format_map_t nasty_fmt_map; @@ -824,95 +1061,105 @@ namespace tut std::string srcs6 = "12345"; s = srcs6; subcount = LLStringUtil::format(s, nasty_fmt_map); - ensure_equals("LLStringUtil::format: Nastyfmt Test1 result", s, "12345"); - ensure_equals("LLStringUtil::format: Nastyfmt Test1 result count", 0, subcount); + UNSCOPED_INFO("LLStringUtil::format: Nastyfmt Test1 result"); + CHECK(s == "12345"); + UNSCOPED_INFO("LLStringUtil::format: Nastyfmt Test1 result count"); + CHECK(0 == subcount); // Test substituting a blank string out of a nasty format_map std::string srcs7; s = srcs7; subcount = LLStringUtil::format(s, nasty_fmt_map); - ensure("LLStringUtil::format: Nastyfmt Test2 result", s.empty()); - ensure_equals("LLStringUtil::format: Nastyfmt Test2 result count", 0, subcount); + UNSCOPED_INFO("LLStringUtil::format: Nastyfmt Test2 result"); + REQUIRE(s.empty()); + UNSCOPED_INFO("LLStringUtil::format: Nastyfmt Test2 result count"); + CHECK(0 == subcount); } - template<> template<> - void string_index_object_t::test<35>() + TEST_CASE("LLString: startsWith finds a prefix", "[LLString]") { // Make sure startsWith works std::string string("anybody in there?"); std::string substr("anybody"); - ensure("startsWith works.", LLStringUtil::startsWith(string, substr)); + UNSCOPED_INFO("startsWith works."); + CHECK(LLStringUtil::startsWith(string, substr)); } - template<> template<> - void string_index_object_t::test<36>() + TEST_CASE("LLString: startsWith rejects a word that is not the prefix", "[LLString]") { // Make sure startsWith correctly fails std::string string("anybody in there?"); std::string substr("there"); - ensure("startsWith fails.", !LLStringUtil::startsWith(string, substr)); + UNSCOPED_INFO("startsWith fails."); + CHECK_FALSE(LLStringUtil::startsWith(string, substr)); } - template<> template<> - void string_index_object_t::test<37>() + TEST_CASE("LLString: startsWith is false where either string is empty", "[LLString]") { // startsWith fails on empty strings std::string value("anybody in there?"); std::string empty; - ensure("empty string.", !LLStringUtil::startsWith(value, empty)); - ensure("empty substr.", !LLStringUtil::startsWith(empty, value)); - ensure("empty everything.", !LLStringUtil::startsWith(empty, empty)); + UNSCOPED_INFO("empty string."); + CHECK_FALSE(LLStringUtil::startsWith(value, empty)); + UNSCOPED_INFO("empty substr."); + CHECK_FALSE(LLStringUtil::startsWith(empty, value)); + UNSCOPED_INFO("empty everything."); + CHECK_FALSE(LLStringUtil::startsWith(empty, empty)); } - template<> template<> - void string_index_object_t::test<38>() + TEST_CASE("LLString: endsWith finds a suffix", "[LLString]") { // Make sure endsWith works correctly std::string string("anybody in there?"); std::string substr("there?"); - ensure("endsWith works.", LLStringUtil::endsWith(string, substr)); + UNSCOPED_INFO("endsWith works."); + CHECK(LLStringUtil::endsWith(string, substr)); } - template<> template<> - void string_index_object_t::test<39>() + TEST_CASE("LLString: endsWith rejects what is not the suffix", "[LLString]") { // Make sure endsWith correctly fails std::string string("anybody in there?"); std::string substr("anybody"); - ensure("endsWith fails.", !LLStringUtil::endsWith(string, substr)); + UNSCOPED_INFO("endsWith fails."); + CHECK_FALSE(LLStringUtil::endsWith(string, substr)); substr = "there"; - ensure("endsWith fails.", !LLStringUtil::endsWith(string, substr)); + UNSCOPED_INFO("endsWith fails."); + CHECK_FALSE(LLStringUtil::endsWith(string, substr)); substr = "ther?"; - ensure("endsWith fails.", !LLStringUtil::endsWith(string, substr)); + UNSCOPED_INFO("endsWith fails."); + CHECK_FALSE(LLStringUtil::endsWith(string, substr)); } - template<> template<> - void string_index_object_t::test<40>() + TEST_CASE("LLString: endsWith is false where either string is empty", "[LLString]") { // endsWith fails on empty strings std::string value("anybody in there?"); std::string empty; - ensure("empty string.", !LLStringUtil::endsWith(value, empty)); - ensure("empty substr.", !LLStringUtil::endsWith(empty, value)); - ensure("empty everything.", !LLStringUtil::endsWith(empty, empty)); + UNSCOPED_INFO("empty string."); + CHECK_FALSE(LLStringUtil::endsWith(value, empty)); + UNSCOPED_INFO("empty substr."); + CHECK_FALSE(LLStringUtil::endsWith(empty, value)); + UNSCOPED_INFO("empty everything."); + CHECK_FALSE(LLStringUtil::endsWith(empty, empty)); } - template<> template<> - void string_index_object_t::test<41>() + TEST_CASE("LLString: getTokens splits on delimiters and drops them", "[LLString]") { - set_test_name("getTokens(\"delims\")"); - ensure_equals("empty string", LLStringUtil::getTokens("", " "), StringVec()); - ensure_equals("only delims", - LLStringUtil::getTokens(" \r\n ", " \r\n"), StringVec()); - ensure_equals("sequence of delims", - LLStringUtil::getTokens(",,, one ,,,", ","), StringVec{"one"}); + UNSCOPED_INFO("empty string"); + CHECK(LLStringUtil::getTokens("", " ") == StringVec()); + UNSCOPED_INFO("only delims"); + CHECK(LLStringUtil::getTokens(" \r\n ", " \r\n") == StringVec()); + UNSCOPED_INFO("sequence of delims"); + CHECK(LLStringUtil::getTokens(",,, one ,,,", ",") == StringVec{"one"}); // nat considers this a dubious implementation side effect, but I'd // hate to change it now... - ensure_equals("noncontiguous tokens", - LLStringUtil::getTokens(", ,, , one ,,,", ","), StringVec{ "", "", "one" }); - ensure_equals("space-padded tokens", - LLStringUtil::getTokens(", one , two ,", ","), StringVec{"one", "two"}); - ensure_equals("no delims", LLStringUtil::getTokens("one", ","), StringVec{ "one" }); + UNSCOPED_INFO("noncontiguous tokens"); + CHECK(LLStringUtil::getTokens(", ,, , one ,,,", ",") == StringVec{ "", "", "one" }); + UNSCOPED_INFO("space-padded tokens"); + CHECK(LLStringUtil::getTokens(", one , two ,", ",") == StringVec{"one", "two"}); + UNSCOPED_INFO("no delims"); + CHECK(LLStringUtil::getTokens("one", ",") == StringVec{ "one" }); } // Shorthand for verifying that getTokens() behaves the same when you @@ -926,14 +1173,14 @@ namespace tut const std::string& quotes, const std::vector& expect) { - ensure_equals(desc + " - no esc", - LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes), + UNSCOPED_INFO(desc + " - no esc"); + REQUIRE(LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes) == expect); - ensure_equals(desc + " - empty esc", - LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes, ""), + UNSCOPED_INFO(desc + " - empty esc"); + REQUIRE(LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes, "") == expect); - ensure_equals(desc + " - unused esc", - LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes, "!"), + UNSCOPED_INFO(desc + " - unused esc"); + REQUIRE(LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes, "!") == expect); } @@ -946,10 +1193,8 @@ namespace tut ensure_getTokens(desc, string, drop_delims, keep_delims, "", expect); } - template<> template<> - void string_index_object_t::test<42>() + TEST_CASE("LLString: getTokens keeps delimiters and handles quotes and escapes", "[LLString]") { - set_test_name("getTokens(\"delims\", etc.)"); // Signatures to test in this method: // getTokens(string, drop_delims, keep_delims [, quotes [, escapes]]) // If you omit keep_delims, you get the older function (test above). @@ -991,55 +1236,51 @@ namespace tut // Don't use backslash as an escape for these tests -- you'll go nuts // between the C++ string scanner and getTokens() escapes. Test with // something else! - ensure_equals("escaped delims", - LLStringUtil::getTokens("^ a - dog^-gone^ phrase", " ", "-", "", "^"), + UNSCOPED_INFO("escaped delims"); + CHECK(LLStringUtil::getTokens("^ a - dog^-gone^ phrase", " ", "-", "", "^") == StringVec{ " a", "-", "dog-gone phrase" }); - ensure_equals("escaped quotes", - LLStringUtil::getTokens("say: 'this isn^'t w^orking'.", " ", "", "'", "^"), + UNSCOPED_INFO("escaped quotes"); + CHECK(LLStringUtil::getTokens("say: 'this isn^'t w^orking'.", " ", "", "'", "^") == StringVec{ "say:", "this isn't working." }); - ensure_equals("escaped escape", - LLStringUtil::getTokens("want x^^2", " ", "", "", "^"), StringVec{ "want", "x^2" }); - ensure_equals("escape at end", LLStringUtil::getTokens("it's^ up there^", " ", "", "'", "^"), StringVec{ "it's up", "there^" }); + UNSCOPED_INFO("escaped escape"); + CHECK(LLStringUtil::getTokens("want x^^2", " ", "", "", "^") == StringVec{ "want", "x^2" }); + UNSCOPED_INFO("escape at end"); + CHECK(LLStringUtil::getTokens("it's^ up there^", " ", "", "'", "^") == StringVec{ "it's up", "there^" }); } - template<> template<> - void string_index_object_t::test<43>() + TEST_CASE("LLString: xml_encode and xml_decode escape element and attribute specials", "[LLString]") { - set_test_name("xml_encode/xml_decode"); - - ensure_equals("encode passthrough", - LLStringFn::xml_encode("plain text"), "plain text"); - ensure_equals("encode element specials", - LLStringFn::xml_encode("ac&d\"e'f"), + UNSCOPED_INFO("encode passthrough"); + CHECK(LLStringFn::xml_encode("plain text") == "plain text"); + UNSCOPED_INFO("encode element specials"); + CHECK(LLStringFn::xml_encode("ac&d\"e'f") == "a<b>c&d\"e'f"); - ensure_equals("encode attribute specials", - LLStringFn::xml_encode("ac&d\"e'f", true), + UNSCOPED_INFO("encode attribute specials"); + CHECK(LLStringFn::xml_encode("ac&d\"e'f", true) == "a<b>c&d"e'f"); - ensure_equals("encode empty", LLStringFn::xml_encode(""), ""); + UNSCOPED_INFO("encode empty"); + CHECK(LLStringFn::xml_encode("") == ""); - ensure_equals("decode element specials", - LLStringFn::xml_decode("a<b>c&d"e'f"), + UNSCOPED_INFO("decode element specials"); + CHECK(LLStringFn::xml_decode("a<b>c&d"e'f") == "ac&d"e'f"); - ensure_equals("decode attribute specials", - LLStringFn::xml_decode("a<b>c&d"e'f", true), + UNSCOPED_INFO("decode attribute specials"); + CHECK(LLStringFn::xml_decode("a<b>c&d"e'f", true) == "ac&d\"e'f"); - ensure_equals("decode unknown entity untouched", - LLStringFn::xml_decode("a&foo;b&"), "a&foo;b&"); + UNSCOPED_INFO("decode unknown entity untouched"); + CHECK(LLStringFn::xml_decode("a&foo;b&") == "a&foo;b&"); // each literal decodes exactly once: &lt; is the text "<", // not a doubly-encoded "<" - ensure_equals("decode does not cascade", - LLStringFn::xml_decode("a&lt;b"), "a<b"); - ensure_equals("round trip nested entity text", - LLStringFn::xml_decode(LLStringFn::xml_encode("a<b")), + UNSCOPED_INFO("decode does not cascade"); + CHECK(LLStringFn::xml_decode("a&lt;b") == "a<b"); + UNSCOPED_INFO("round trip nested entity text"); + CHECK(LLStringFn::xml_decode(LLStringFn::xml_encode("a<b")) == "a<b"); } - template<> template<> - void string_index_object_t::test<44>() + TEST_CASE("LLString: format leaves unsubstitutable strings alone", "[LLString]") { - set_test_name("format leaves unsubstitutable strings alone"); - LLStringUtil::format_map_t fmt_map; fmt_map["FOO"] = "bar"; @@ -1061,79 +1302,53 @@ namespace tut for (const char* src : untouched) { std::string s(src); - ensure_equals(std::string("format_map count: ") + src, - 0, LLStringUtil::format(s, fmt_map)); - ensure_equals(std::string("format_map result: ") + src, s, std::string(src)); + UNSCOPED_INFO(std::string("format_map count: ") + src); + REQUIRE(0 == LLStringUtil::format(s, fmt_map)); + UNSCOPED_INFO(std::string("format_map result: ") + src); + REQUIRE(s == std::string(src)); std::string s2(src); - ensure_equals(std::string("LLSD count: ") + src, - 0, LLStringUtil::format(s2, sd_map)); - ensure_equals(std::string("LLSD result: ") + src, s2, std::string(src)); + UNSCOPED_INFO(std::string("LLSD count: ") + src); + REQUIRE(0 == LLStringUtil::format(s2, sd_map)); + UNSCOPED_INFO(std::string("LLSD result: ") + src); + REQUIRE(s2 == std::string(src)); } // A real substitution still fires, and the surrounding text — including // brackets that are not a key — survives it. std::string s("no [FOO] here [MISSING] -=[Stylized Name]=-"); - ensure_equals("mixed count", 1, LLStringUtil::format(s, fmt_map)); - ensure_equals("mixed result", s, + UNSCOPED_INFO("mixed count"); + CHECK(1 == LLStringUtil::format(s, fmt_map)); + UNSCOPED_INFO("mixed result"); + CHECK(s == std::string("no bar here [MISSING] -=[Stylized Name]=-")); } - template<> template<> - void string_index_object_t::test<48>() + // Capitalising a byte can only ever reach ASCII, so the narrow form works + // in codepoints. + TEST_CASE("LLString: capitalize works in codepoints and not bytes", "[LLString]") { - set_test_name("format over two maps reads the first's value where both have a key, as a merge with the first kept would"); - - LLStringUtil::format_map_t first; - first["[APP]"] = "Alchemy"; - first["[BOTH]"] = "first"; - first["[BARE]"] = "bracketed in the first"; - first["datetime"] = "0"; - LLStringUtil::format_map_t second; - second["[NAME]"] = "Rye"; - second["[BOTH]"] = "second"; - second["BARE"] = "bare in the second"; - second["[COUNT]"] = "3.14159"; - - // A bare key is found before a bracketed one whichever map has it, - // as the merge's one lookup order had it. - const char* const text = "[APP] for [NAME]: [BOTH], [BARE], [COUNT,number,2], [MISSING]"; - std::string s(text); - ensure_equals("five put in", 5, LLStringUtil::format(s, first, second)); - ensure_equals("from either, the first first, a number formatted, what neither has left", s, - std::string("Alchemy for Rye: first, bare in the second, 3.14, [MISSING]")); - - // The same as the merge would give. - LLStringUtil::format_map_t merged = first; - merged.insert(second.begin(), second.end()); - std::string m(text); - LLStringUtil::format(m, merged); - ensure_equals("as the merge", s, m); + std::string str_val("hello world-again_now"); + LLStringUtil::capitalize(str_val); + UNSCOPED_INFO("ascii words"); + CHECK(str_val == "Hello World-Again_Now"); - // The maps the other way about: the second's value where both have - // the key. - std::string r("[BOTH]"); - ensure_equals("one put in", 1, LLStringUtil::format(r, second, first)); - ensure_equals("the other first", r, std::string("second")); + // U+00E9 -> U+00C9, which uppercasing one byte at a time could not do. + str_val = "\xC3\xA9" "cole"; + LLStringUtil::capitalize(str_val); + UNSCOPED_INFO("accented first letter"); + CHECK(str_val == "\xC3\x89" "cole"); - std::string plain("nothing bracketed"); - ensure_equals("nothing to do", 0, LLStringUtil::format(plain, first, second)); - ensure_equals("and nothing done", plain, std::string("nothing bracketed")); + // The word rule is the caller's and still only sees these three. + str_val = "a b-c_d.e"; + LLStringUtil::capitalize(str_val); + UNSCOPED_INFO("dot is not a separator"); + CHECK(str_val == "A B-C_D.e"); - // A map over an LLSD map, as a notification's substitutions under - // the defaults: the map first, the LLSD where the map has nothing, - // and a datetime the LLSD lacks counted from the epoch as the LLSD - // form has always had it. - LLSD sd; - sd["NAME"] = "Rye"; - sd["[BOTH]"] = "the notification's"; - std::string over("[APP] for [NAME]: [BOTH]"); - ensure_equals("three put in", 3, LLStringUtil::format(over, first, sd)); - ensure_equals("the map first, the LLSD after", over, std::string("Alchemy for Rye: first")); - LLSD not_a_map; - std::string alone("[APP] [NAME]"); - ensure_equals("with no LLSD map, the map alone", 1, LLStringUtil::format(alone, first, not_a_map)); - ensure_equals("what the map had", alone, std::string("Alchemy [NAME]")); + str_val = ""; + LLStringUtil::capitalize(str_val); + UNSCOPED_INFO("empty"); + CHECK(str_val == ""); } // Both views replaceString takes are allowed to point into the string it is @@ -1141,16 +1356,15 @@ namespace tut // buffer under them. Taking them by value used to make that safe without // saying so; a view does not, so the guard is the only thing holding it up // and nothing was watching the guard. - template<> template<> - void string_index_object_t::test<46>() + TEST_CASE("LLString: replaceString takes views into the string it replaces in", "[LLString]") { // replacement views the subject: the classic case, and the one the // original guard covered. { std::string s("abcabc"); LLStringUtil::replaceString(s, "b", std::string_view(s).substr(0, 3)); - ensure_equals("replacement viewing the subject", - s, std::string("aabccaabcc")); + UNSCOPED_INFO("replacement viewing the subject"); + CHECK(s == std::string("aabccaabcc")); } // target views the subject. This one is read on every turn of the loop, @@ -1158,7 +1372,8 @@ namespace tut { std::string s("xyxyxy"); LLStringUtil::replaceString(s, std::string_view(s).substr(0, 2), "Q"); - ensure_equals("target viewing the subject", s, std::string("QQQ")); + UNSCOPED_INFO("target viewing the subject"); + CHECK(s == std::string("QQQ")); } // Both at once, and a replacement long enough to force the subject to @@ -1168,34 +1383,35 @@ namespace tut LLStringUtil::replaceString(s, std::string_view(s).substr(1, 1), std::string_view(s).substr(0, 1)); - ensure_equals("both viewing the subject", s, std::string("aabac")); + UNSCOPED_INFO("both viewing the subject"); + CHECK(s == std::string("aabac")); } // And the ordinary path still works. { std::string s("one two one"); LLStringUtil::replaceString(s, "one", "three"); - ensure_equals("unrelated arguments", s, std::string("three two three")); + UNSCOPED_INFO("unrelated arguments"); + CHECK(s == std::string("three two three")); } { std::string s("aaa"); LLStringUtil::replaceString(s, "a", "aa"); - ensure_equals("replacement containing the target terminates", - s, std::string("aaaaaa")); + UNSCOPED_INFO("replacement containing the target terminates"); + CHECK(s == std::string("aaaaaa")); } } // copyInto has the same shape: src may view dst, the append path appends // from it, and the insert path assigns dst out from under it first. - template<> template<> - void string_index_object_t::test<47>() + TEST_CASE("LLString: copyInto takes a source that views the destination", "[LLString]") { // Append from a view of the destination. { std::string s("abcd"); LLStringUtil::copyInto(s, std::string_view(s).substr(0, 2), s.length()); - ensure_equals("append from a view of the destination", - s, std::string("abcdab")); + UNSCOPED_INFO("append from a view of the destination"); + CHECK(s == std::string("abcdab")); } // Insert from a view of the destination, at the front, so the assign @@ -1203,16 +1419,16 @@ namespace tut { std::string s("abcd"); LLStringUtil::copyInto(s, std::string_view(s).substr(2, 2), 0); - ensure_equals("insert from a view of the destination", - s, std::string("cdabcd")); + UNSCOPED_INFO("insert from a view of the destination"); + CHECK(s == std::string("cdabcd")); } // A view of the whole of it, long enough that the growth reallocates. { std::string s("0123456789"); LLStringUtil::copyInto(s, std::string_view(s), 5); - ensure_equals("insert the whole of the destination into itself", - s, std::string("01234012345678956789")); + UNSCOPED_INFO("insert the whole of the destination into itself"); + CHECK(s == std::string("01234012345678956789")); } // Offset at the end is the append fast path; offset past it is not a @@ -1220,7 +1436,73 @@ namespace tut { std::string s("head"); LLStringUtil::copyInto(s, std::string("-tail"), s.length()); - ensure_equals("unrelated source appends", s, std::string("head-tail")); + UNSCOPED_INFO("unrelated source appends"); + CHECK(s == std::string("head-tail")); } } + + TEST_CASE("LLString: format over two maps reads the first's value as a merge would", "[LLString]") + { + LLStringUtil::format_map_t first; + first["[APP]"] = "Alchemy"; + first["[BOTH]"] = "first"; + first["[BARE]"] = "bracketed in the first"; + first["datetime"] = "0"; + LLStringUtil::format_map_t second; + second["[NAME]"] = "Rye"; + second["[BOTH]"] = "second"; + second["BARE"] = "bare in the second"; + second["[COUNT]"] = "3.14159"; + + // A bare key is found before a bracketed one whichever map has it, + // as the merge's one lookup order had it. + const char* const text = "[APP] for [NAME]: [BOTH], [BARE], [COUNT,number,2], [MISSING]"; + std::string s(text); + UNSCOPED_INFO("five put in"); + CHECK(5 == LLStringUtil::format(s, first, second)); + UNSCOPED_INFO("from either, the first first, a number formatted, what neither has left"); + CHECK(s == + std::string("Alchemy for Rye: first, bare in the second, 3.14, [MISSING]")); + + // The same as the merge would give. + LLStringUtil::format_map_t merged = first; + merged.insert(second.begin(), second.end()); + std::string m(text); + LLStringUtil::format(m, merged); + UNSCOPED_INFO("as the merge"); + CHECK(s == m); + + // The maps the other way about: the second's value where both have + // the key. + std::string r("[BOTH]"); + UNSCOPED_INFO("one put in"); + CHECK(1 == LLStringUtil::format(r, second, first)); + UNSCOPED_INFO("the other first"); + CHECK(r == std::string("second")); + + std::string plain("nothing bracketed"); + UNSCOPED_INFO("nothing to do"); + CHECK(0 == LLStringUtil::format(plain, first, second)); + UNSCOPED_INFO("and nothing done"); + CHECK(plain == std::string("nothing bracketed")); + + // A map over an LLSD map, as a notification's substitutions under + // the defaults: the map first, the LLSD where the map has nothing, + // and a datetime the LLSD lacks counted from the epoch as the LLSD + // form has always had it. + LLSD sd; + sd["NAME"] = "Rye"; + sd["[BOTH]"] = "the notification's"; + std::string over("[APP] for [NAME]: [BOTH]"); + UNSCOPED_INFO("three put in"); + CHECK(3 == LLStringUtil::format(over, first, sd)); + UNSCOPED_INFO("the map first, the LLSD after"); + CHECK(over == std::string("Alchemy for Rye: first")); + LLSD not_a_map; + std::string alone("[APP] [NAME]"); + UNSCOPED_INFO("with no LLSD map, the map alone"); + CHECK(1 == LLStringUtil::format(alone, first, not_a_map)); + UNSCOPED_INFO("what the map had"); + CHECK(alone == std::string("Alchemy [NAME]")); + } } diff --git a/indra/llcommon/tests/llstring_utf_test.cpp b/indra/llcommon/tests/llstring_utf_test.cpp index c53c7d0d4f..3f2e9b73b7 100644 --- a/indra/llcommon/tests/llstring_utf_test.cpp +++ b/indra/llcommon/tests/llstring_utf_test.cpp @@ -37,7 +37,7 @@ #include "linden_common.h" #include "../llstring.h" -#include "../test/lltut.h" +#include #include #include @@ -197,7 +197,7 @@ namespace for (llwchar c : actual) oss << "U+" << (U32)c << ' '; oss << "\n expect: "; for (llwchar c : expected) oss << "U+" << (U32)c << ' '; - throw tut::failure(oss.str()); + FAIL(oss.str()); } } @@ -222,106 +222,111 @@ namespace tut // The TUT default registers only test<1>..test<50>, but the explicit // test_group<..., 150> below raises that ceiling. Keep this index in // sync with categories used below. - typedef test_group llstring_utf_t; - typedef llstring_utf_t::object llstring_utf_object_t; - tut::llstring_utf_t tut_llstring_utf("LLStringUTF"); // --------------------------------------------------------------- // round-trip coverage // --------------------------------------------------------------- // Pure ASCII: all six conversion directions must round-trip byte-for-byte. - template<> template<> - void llstring_utf_object_t::test<1>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: ASCII converts in every direction", "[LLStringUTF]") { const std::string ascii = "Hello, World!"; const std::u32string wascii (ascii.begin(), ascii.end()); const llutf16string u16ascii(ascii.begin(), ascii.end()); ensure_wstring_equals ("utf8->wstring ASCII", to_u32 (ascii), wascii); - ensure_equals ("wstring->utf8 ASCII", to_utf8 (wascii), ascii); - ensure_equals ("utf16->utf8 ASCII", utf16str_to_utf8str(u16ascii), ascii); + UNSCOPED_INFO("wstring->utf8 ASCII"); + CHECK(to_utf8 (wascii) == ascii); + UNSCOPED_INFO("utf16->utf8 ASCII"); + CHECK(utf16str_to_utf8str(u16ascii) == ascii); } // Latin-1 BMP: 2-byte UTF-8 (é = U+00E9 → C3 A9). - template<> template<> - void llstring_utf_object_t::test<2>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a two-byte character converts in every direction", "[LLStringUTF]") { const std::string utf8 = "H\xC3\xA9llo"; const std::u32string w = { (llwchar)'H', (llwchar)0x00E9, (llwchar)'l', (llwchar)'l', (llwchar)'o' }; const llutf16string u16 = { (char16_t)'H', (char16_t)0x00E9, (char16_t)'l', (char16_t)'l', (char16_t)'o' }; ensure_wstring_equals ("utf8->wstring 2-byte", to_u32 (utf8), w); - ensure_equals ("wstring->utf8 2-byte", to_utf8 (w), utf8); - ensure_equals ("utf16->utf8 2-byte", utf16str_to_utf8str(u16), utf8); + UNSCOPED_INFO("wstring->utf8 2-byte"); + CHECK(to_utf8 (w) == utf8); + UNSCOPED_INFO("utf16->utf8 2-byte"); + CHECK(utf16str_to_utf8str(u16) == utf8); } // CJK BMP: 3-byte UTF-8 (日 = U+65E5 → E6 97 A5, 本 = U+672C → E6 9C AC). - template<> template<> - void llstring_utf_object_t::test<3>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: three-byte CJK converts in every direction", "[LLStringUTF]") { const std::string utf8 = "\xE6\x97\xA5\xE6\x9C\xAC"; const std::u32string w = { (llwchar)0x65E5, (llwchar)0x672C }; const llutf16string u16 = { (char16_t)0x65E5, (char16_t)0x672C }; ensure_wstring_equals ("utf8->wstring 3-byte", to_u32 (utf8), w); - ensure_equals ("wstring->utf8 3-byte", to_utf8 (w), utf8); - ensure_equals ("utf16->utf8 3-byte", utf16str_to_utf8str(u16), utf8); + UNSCOPED_INFO("wstring->utf8 3-byte"); + CHECK(to_utf8 (w) == utf8); + UNSCOPED_INFO("utf16->utf8 3-byte"); + CHECK(utf16str_to_utf8str(u16) == utf8); } // Astral: 4-byte UTF-8 (U+1F680 rocket → F0 9F 9A 80; UTF-16 surrogate pair D83D DE80). - template<> template<> - void llstring_utf_object_t::test<4>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: an astral character converts in every direction", "[LLStringUTF]") { const std::string utf8 = "\xF0\x9F\x9A\x80"; const std::u32string w = { (llwchar)0x1F680 }; const llutf16string u16 = { (char16_t)0xD83D, (char16_t)0xDE80 }; ensure_wstring_equals ("utf8->wstring astral", to_u32 (utf8), w); - ensure_equals ("wstring->utf8 astral", to_utf8 (w), utf8); - ensure_equals ("utf16->utf8 astral", utf16str_to_utf8str(u16), utf8); + UNSCOPED_INFO("wstring->utf8 astral"); + CHECK(to_utf8 (w) == utf8); + UNSCOPED_INFO("utf16->utf8 astral"); + CHECK(utf16str_to_utf8str(u16) == utf8); } // Mixed: ASCII + 3-byte BMP + 4-byte astral. Uses explicit string-literal // concatenation between astral bytes and the trailing 'B' so \xA5B and // \x80B don't eat an extra hex digit. - template<> template<> - void llstring_utf_object_t::test<5>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: mixed one- three- and four-byte text converts in every direction", "[LLStringUTF]") { const std::string utf8 = "A\xE6\x97\xA5\xF0\x9F\x9A\x80" "B"; const std::u32string w = { (llwchar)'A', (llwchar)0x65E5, (llwchar)0x1F680, (llwchar)'B' }; const llutf16string u16 = { (char16_t)'A', (char16_t)0x65E5, (char16_t)0xD83D, (char16_t)0xDE80, (char16_t)'B' }; ensure_wstring_equals ("utf8->wstring mixed", to_u32 (utf8), w); - ensure_equals ("wstring->utf8 mixed", to_utf8 (w), utf8); - ensure_equals ("utf16->utf8 mixed", utf16str_to_utf8str(u16), utf8); + UNSCOPED_INFO("wstring->utf8 mixed"); + CHECK(to_utf8 (w) == utf8); + UNSCOPED_INFO("utf16->utf8 mixed"); + CHECK(utf16str_to_utf8str(u16) == utf8); } // Empty inputs produce empty outputs in every direction. - template<> template<> - void llstring_utf_object_t::test<6>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: empty input converts to empty output in every direction", "[LLStringUTF]") { - ensure_equals("utf8->wstring empty", to_u32 (std::string()).size(), size_t(0)); - ensure_equals("wstring->utf8 empty", to_utf8 (std::u32string()), std::string()); - ensure_equals("utf16->utf8 empty", utf16str_to_utf8str(llutf16string()), std::string()); + UNSCOPED_INFO("utf8->wstring empty"); + REQUIRE(to_u32 (std::string()).size() == size_t(0)); + UNSCOPED_INFO("wstring->utf8 empty"); + CHECK(to_utf8 (std::u32string()) == std::string()); + UNSCOPED_INFO("utf16->utf8 empty"); + CHECK(utf16str_to_utf8str(llutf16string()) == std::string()); } // Round-trip via rawstr_to_utf8 (utf8 -> std::u32string -> utf8) preserves valid input. - template<> template<> - void llstring_utf_object_t::test<7>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: rawstr_to_utf8 leaves valid UTF-8 as it is", "[LLStringUTF]") { const std::string mixed = "A\xE6\x97\xA5\xF0\x9F\x9A\x80" "B"; - ensure_equals("rawstr_to_utf8 preserves valid utf8", rawstr_to_utf8(mixed), mixed); + UNSCOPED_INFO("rawstr_to_utf8 preserves valid utf8"); + CHECK(rawstr_to_utf8(mixed) == mixed); } // utf8 <-> u8 reinterpret (not conversion) preserves bytes. - template<> template<> - void llstring_utf_object_t::test<8>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: str_to_u8str and u8str_to_str keep the bytes", "[LLStringUTF]") { const std::string bytes = "\xF0\x9F\x9A\x80"; const std::u8string u8 = str_to_u8str(bytes); - ensure_equals("str_to_u8str size", u8.size(), bytes.size()); - ensure_equals("u8 round-trip", u8str_to_str(u8), bytes); + UNSCOPED_INFO("str_to_u8str size"); + REQUIRE(u8.size() == bytes.size()); + UNSCOPED_INFO("u8 round-trip"); + CHECK(u8str_to_str(u8) == bytes); } // --------------------------------------------------------------- @@ -329,67 +334,87 @@ namespace tut // --------------------------------------------------------------- // wchar_to_utf8chars covers 1, 2, 3, 4-byte canonical forms. - template<> template<> - void llstring_utf_object_t::test<10>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: wchar_to_utf8chars writes one- to four-byte canonical forms", "[LLStringUTF]") { char buf[8]; std::memset(buf, 0xAA, sizeof(buf)); - ensure_equals("'A' len", wchar_to_utf8chars(0x41, buf), std::ptrdiff_t(1)); - ensure_equals("'A' b0", (U32)(U8)buf[0], U32(0x41)); - - ensure_equals("é len", wchar_to_utf8chars(0x00E9, buf), std::ptrdiff_t(2)); - ensure_equals("é b0", (U32)(U8)buf[0], U32(0xC3)); - ensure_equals("é b1", (U32)(U8)buf[1], U32(0xA9)); - - ensure_equals("日 len", wchar_to_utf8chars(0x65E5, buf), std::ptrdiff_t(3)); - ensure_equals("日 b0", (U32)(U8)buf[0], U32(0xE6)); - ensure_equals("日 b1", (U32)(U8)buf[1], U32(0x97)); - ensure_equals("日 b2", (U32)(U8)buf[2], U32(0xA5)); - - ensure_equals("🚀 len", wchar_to_utf8chars(0x1F680, buf), std::ptrdiff_t(4)); - ensure_equals("🚀 b0", (U32)(U8)buf[0], U32(0xF0)); - ensure_equals("🚀 b1", (U32)(U8)buf[1], U32(0x9F)); - ensure_equals("🚀 b2", (U32)(U8)buf[2], U32(0x9A)); - ensure_equals("🚀 b3", (U32)(U8)buf[3], U32(0x80)); + UNSCOPED_INFO("'A' len"); + CHECK(wchar_to_utf8chars(0x41, buf) == std::ptrdiff_t(1)); + UNSCOPED_INFO("'A' b0"); + CHECK((U32)(U8)buf[0] == U32(0x41)); + + UNSCOPED_INFO("é len"); + CHECK(wchar_to_utf8chars(0x00E9, buf) == std::ptrdiff_t(2)); + UNSCOPED_INFO("é b0"); + CHECK((U32)(U8)buf[0] == U32(0xC3)); + UNSCOPED_INFO("é b1"); + CHECK((U32)(U8)buf[1] == U32(0xA9)); + + UNSCOPED_INFO("日 len"); + CHECK(wchar_to_utf8chars(0x65E5, buf) == std::ptrdiff_t(3)); + UNSCOPED_INFO("日 b0"); + CHECK((U32)(U8)buf[0] == U32(0xE6)); + UNSCOPED_INFO("日 b1"); + CHECK((U32)(U8)buf[1] == U32(0x97)); + UNSCOPED_INFO("日 b2"); + CHECK((U32)(U8)buf[2] == U32(0xA5)); + + UNSCOPED_INFO("🚀 len"); + CHECK(wchar_to_utf8chars(0x1F680, buf) == std::ptrdiff_t(4)); + UNSCOPED_INFO("🚀 b0"); + CHECK((U32)(U8)buf[0] == U32(0xF0)); + UNSCOPED_INFO("🚀 b1"); + CHECK((U32)(U8)buf[1] == U32(0x9F)); + UNSCOPED_INFO("🚀 b2"); + CHECK((U32)(U8)buf[2] == U32(0x9A)); + UNSCOPED_INFO("🚀 b3"); + CHECK((U32)(U8)buf[3] == U32(0x80)); } // wchar_to_utf8chars current behaviour: emits 5 and 6-byte legacy // encodings for codepoints > U+10FFFF. simdutf rejects these — test // pins today's behaviour so the migration change is visible. - template<> template<> - void llstring_utf_object_t::test<11>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: wchar_to_utf8chars still writes five- and six-byte forms", "[LLStringUTF]") { char buf[8]; std::memset(buf, 0xAA, sizeof(buf)); - ensure_equals("5-byte len", wchar_to_utf8chars(0x00200000, buf), std::ptrdiff_t(5)); - ensure_equals("5-byte lead", (U32)((U8)buf[0] & 0xF8), U32(0xF8)); + UNSCOPED_INFO("5-byte len"); + CHECK(wchar_to_utf8chars(0x00200000, buf) == std::ptrdiff_t(5)); + UNSCOPED_INFO("5-byte lead"); + CHECK((U32)((U8)buf[0] & 0xF8) == U32(0xF8)); std::memset(buf, 0xAA, sizeof(buf)); - ensure_equals("6-byte len", wchar_to_utf8chars(0x04000000, buf), std::ptrdiff_t(6)); - ensure_equals("6-byte lead", (U32)((U8)buf[0] & 0xFC), U32(0xFC)); + UNSCOPED_INFO("6-byte len"); + CHECK(wchar_to_utf8chars(0x04000000, buf) == std::ptrdiff_t(6)); + UNSCOPED_INFO("6-byte lead"); + CHECK((U32)((U8)buf[0] & 0xFC) == U32(0xFC)); } // --------------------------------------------------------------- // length / count helpers // --------------------------------------------------------------- - template<> template<> - void llstring_utf_object_t::test<20>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: wchar_utf8_length counts one to six bytes", "[LLStringUTF]") { - ensure_equals("wchar_utf8_length ASCII", wchar_utf8_length(0x00000041), S32(1)); - ensure_equals("wchar_utf8_length 2-byte", wchar_utf8_length(0x000000E9), S32(2)); - ensure_equals("wchar_utf8_length 3-byte", wchar_utf8_length(0x000065E5), S32(3)); - ensure_equals("wchar_utf8_length 4-byte", wchar_utf8_length(0x0001F680), S32(4)); - ensure_equals("wchar_utf8_length 5-byte", wchar_utf8_length(0x00200000), S32(5)); - ensure_equals("wchar_utf8_length 6-byte", wchar_utf8_length(0x04000000), S32(6)); + UNSCOPED_INFO("wchar_utf8_length ASCII"); + CHECK(wchar_utf8_length(0x00000041) == S32(1)); + UNSCOPED_INFO("wchar_utf8_length 2-byte"); + CHECK(wchar_utf8_length(0x000000E9) == S32(2)); + UNSCOPED_INFO("wchar_utf8_length 3-byte"); + CHECK(wchar_utf8_length(0x000065E5) == S32(3)); + UNSCOPED_INFO("wchar_utf8_length 4-byte"); + CHECK(wchar_utf8_length(0x0001F680) == S32(4)); + UNSCOPED_INFO("wchar_utf8_length 5-byte"); + CHECK(wchar_utf8_length(0x00200000) == S32(5)); + UNSCOPED_INFO("wchar_utf8_length 6-byte"); + CHECK(wchar_utf8_length(0x04000000) == S32(6)); } #if LL_WINDOWS // wide_wstring_length is Windows-only because it assumes std::wstring holds UTF-16. - template<> template<> - void llstring_utf_object_t::test<24>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: wide_wstring_length counts a surrogate pair as one codepoint", "[LLStringUTF]") { std::wstring w; w.push_back(L'A'); @@ -397,8 +422,10 @@ namespace tut w.push_back((wchar_t)0xDE80); w.push_back(L'B'); // 4 UTF-16 units = 3 codepoints. - ensure_equals("wide_wstring_length 4u -> 3cp", wide_wstring_length(w, 4), S32(3)); - ensure_equals("wide_wstring_length 0", wide_wstring_length(w, 0), S32(0)); + UNSCOPED_INFO("wide_wstring_length 4u -> 3cp"); + REQUIRE(wide_wstring_length(w, 4) == S32(3)); + UNSCOPED_INFO("wide_wstring_length 0"); + REQUIRE(wide_wstring_length(w, 0) == S32(0)); } #endif @@ -406,14 +433,17 @@ namespace tut // truncation helpers // --------------------------------------------------------------- - template<> template<> - void llstring_utf_object_t::test<30>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_truncate cuts ASCII at the length given", "[LLStringUTF]") { const std::string s = "Hello"; - ensure_equals("truncate noop", utf8str_truncate(s, 10), s); - ensure_equals("truncate exact", utf8str_truncate(s, 5), s); - ensure_equals("truncate to 3", utf8str_truncate(s, 3), std::string("Hel")); - ensure_equals("truncate to 0", utf8str_truncate(s, 0), std::string()); + UNSCOPED_INFO("truncate noop"); + CHECK(utf8str_truncate(s, 10) == s); + UNSCOPED_INFO("truncate exact"); + CHECK(utf8str_truncate(s, 5) == s); + UNSCOPED_INFO("truncate to 3"); + CHECK(utf8str_truncate(s, 3) == std::string("Hel")); + UNSCOPED_INFO("truncate to 0"); + CHECK(utf8str_truncate(s, 0) == std::string()); } // Truncation walks back only when max_len lands *inside* a multi-byte @@ -421,36 +451,45 @@ namespace tut // max_len=2 → cut at 0x97 (cont) → walk back to 1 → "A" // max_len=3 → cut at 0xA5 (cont) → walk back to 1 → "A" // max_len=4 → cut at 0x42 (ASCII 'B') → no walk-back → "A日" - template<> template<> - void llstring_utf_object_t::test<31>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_truncate backs off only from inside a character", "[LLStringUTF]") { const std::string s = "A\xE6\x97\xA5" "B"; - ensure_equals("trunc 2 inside 日", utf8str_truncate(s, 2), std::string("A")); - ensure_equals("trunc 3 inside 日", utf8str_truncate(s, 3), std::string("A")); - ensure_equals("trunc 4 on 'B'", utf8str_truncate(s, 4), std::string("A\xE6\x97\xA5")); - ensure_equals("trunc 5 whole", utf8str_truncate(s, 5), s); + UNSCOPED_INFO("trunc 2 inside 日"); + CHECK(utf8str_truncate(s, 2) == std::string("A")); + UNSCOPED_INFO("trunc 3 inside 日"); + CHECK(utf8str_truncate(s, 3) == std::string("A")); + UNSCOPED_INFO("trunc 4 on 'B'"); + CHECK(utf8str_truncate(s, 4) == std::string("A\xE6\x97\xA5")); + UNSCOPED_INFO("trunc 5 whole"); + CHECK(utf8str_truncate(s, 5) == s); } - template<> template<> - void llstring_utf_object_t::test<32>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_substr takes a start and a length", "[LLStringUTF]") { const std::string s = "ABCDE"; - ensure_equals("substr 1,3", utf8str_substr(s, 1, 3), std::string("BCD")); - ensure_equals("substr 3,2", utf8str_substr(s, 3, 2), std::string("DE")); - ensure_equals("substr 0,0", utf8str_substr(s, 0, 0), std::string()); + UNSCOPED_INFO("substr 1,3"); + CHECK(utf8str_substr(s, 1, 3) == std::string("BCD")); + UNSCOPED_INFO("substr 3,2"); + CHECK(utf8str_substr(s, 3, 2) == std::string("DE")); + UNSCOPED_INFO("substr 0,0"); + CHECK(utf8str_substr(s, 0, 0) == std::string()); } // utf8str_symbol_truncate returns the first N complete UTF-8 symbols. // "A日🚀B" = 1+3+4+1 = 9 bytes, 4 symbols. - template<> template<> - void llstring_utf_object_t::test<33>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_symbol_truncate returns the first N complete UTF-8 symbols", "[LLStringUTF]") { const std::string s = "A\xE6\x97\xA5\xF0\x9F\x9A\x80" "B"; - ensure_equals("sym 4 whole", utf8str_symbol_truncate(s, 4), s); - ensure_equals("sym 3", utf8str_symbol_truncate(s, 3), std::string("A\xE6\x97\xA5\xF0\x9F\x9A\x80")); - ensure_equals("sym 2", utf8str_symbol_truncate(s, 2), std::string("A\xE6\x97\xA5")); - ensure_equals("sym 1", utf8str_symbol_truncate(s, 1), std::string("A")); - ensure_equals("sym 0", utf8str_symbol_truncate(s, 0), std::string()); + UNSCOPED_INFO("sym 4 whole"); + CHECK(utf8str_symbol_truncate(s, 4) == s); + UNSCOPED_INFO("sym 3"); + CHECK(utf8str_symbol_truncate(s, 3) == std::string("A\xE6\x97\xA5\xF0\x9F\x9A\x80")); + UNSCOPED_INFO("sym 2"); + CHECK(utf8str_symbol_truncate(s, 2) == std::string("A\xE6\x97\xA5")); + UNSCOPED_INFO("sym 1"); + CHECK(utf8str_symbol_truncate(s, 1) == std::string("A")); + UNSCOPED_INFO("sym 0"); + CHECK(utf8str_symbol_truncate(s, 0) == std::string()); } // --------------------------------------------------------------- @@ -459,23 +498,27 @@ namespace tut // Lone continuation byte (0x80) and invalid leader (0xFF) each map to // one LL_UNKNOWN_CHAR. - template<> template<> - void llstring_utf_object_t::test<40>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a lone continuation or invalid lead byte is one bad character", "[LLStringUTF]") { for (const std::string& bad : { std::string("\x80"), std::string("\xFF") }) { - ensure("rejected outright", !utf8str_is_valid(bad)); + UNSCOPED_INFO("rejected outright"); + REQUIRE_FALSE(utf8str_is_valid(bad)); // The decoder gives one replacement character per bad byte, so a // walk driven off it always advances. const std::u32string w = to_u32(bad); - ensure_equals("one bad byte, one character", w.size(), size_t(1)); - ensure_equals("decodes to U+FFFD", (U32)w[0], U32(0xFFFD)); + UNSCOPED_INFO("one bad byte, one character"); + REQUIRE(w.size() == size_t(1)); + UNSCOPED_INFO("decodes to U+FFFD"); + REQUIRE((U32)w[0] == U32(0xFFFD)); // Repair substitutes LL_UNKNOWN_CHAR, and the result is valid. const std::string fixed = utf8str_sanitize(bad); - ensure_equals("repaired to one byte", fixed, std::string(1, LL_UNKNOWN_CHAR)); - ensure("repair is valid", utf8str_is_valid(fixed)); + UNSCOPED_INFO("repaired to one byte"); + REQUIRE(fixed == std::string(1, LL_UNKNOWN_CHAR)); + UNSCOPED_INFO("repair is valid"); + REQUIRE(utf8str_is_valid(fixed)); } } @@ -484,102 +527,111 @@ namespace tut // we get one '?' per rejected byte. The previous hand-rolled decoder // produced a single '?' per malformed sequence; this behavioural change // is intentional for the migration. - template<> template<> - void llstring_utf_object_t::test<41>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: an overlong two-byte NUL is two bad bytes", "[LLStringUTF]") { const std::string overlong("\xC0\x80"); - ensure("overlong NUL rejected", !utf8str_is_valid(overlong)); + UNSCOPED_INFO("overlong NUL rejected"); + CHECK_FALSE(utf8str_is_valid(overlong)); const std::u32string w = to_u32(overlong); - ensure_equals("overlong NUL size", w.size(), size_t(2)); - ensure_equals("overlong NUL[0]", (U32)w[0], U32(0xFFFD)); - ensure_equals("overlong NUL[1]", (U32)w[1], U32(0xFFFD)); + UNSCOPED_INFO("overlong NUL size"); + REQUIRE(w.size() == size_t(2)); + UNSCOPED_INFO("overlong NUL[0]"); + CHECK((U32)w[0] == U32(0xFFFD)); + UNSCOPED_INFO("overlong NUL[1]"); + CHECK((U32)w[1] == U32(0xFFFD)); - ensure_equals("repaired byte for byte", - utf8str_sanitize(overlong), std::string(2, LL_UNKNOWN_CHAR)); + UNSCOPED_INFO("repaired byte for byte"); + CHECK(utf8str_sanitize(overlong) == std::string(2, LL_UNKNOWN_CHAR)); } // Truncated 3-byte sequence "A\xE6\x97" — simdutf rejects both 0xE6 (leader // without a full body) and 0x97 (lone continuation), so the result is // "A??" (A + two replacement chars). Previous hand-rolled decoder // produced "A?". - template<> template<> - void llstring_utf_object_t::test<42>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a truncated three-byte sequence is two bad bytes", "[LLStringUTF]") { const std::string truncated("A\xE6\x97"); - ensure("truncated rejected", !utf8str_is_valid(truncated)); + UNSCOPED_INFO("truncated rejected"); + CHECK_FALSE(utf8str_is_valid(truncated)); const std::u32string w = to_u32(truncated); - ensure_equals("truncated size", w.size(), size_t(3)); - ensure_equals("truncated[0]='A'", (U32)w[0], U32('A')); - ensure_equals("truncated[1]", (U32)w[1], U32(0xFFFD)); - ensure_equals("truncated[2]", (U32)w[2], U32(0xFFFD)); + UNSCOPED_INFO("truncated size"); + REQUIRE(w.size() == size_t(3)); + UNSCOPED_INFO("truncated[0]='A'"); + CHECK((U32)w[0] == U32('A')); + UNSCOPED_INFO("truncated[1]"); + CHECK((U32)w[1] == U32(0xFFFD)); + UNSCOPED_INFO("truncated[2]"); + CHECK((U32)w[2] == U32(0xFFFD)); - ensure_equals("the good byte survives the repair", - utf8str_sanitize(truncated), std::string("A??")); + UNSCOPED_INFO("the good byte survives the repair"); + CHECK(utf8str_sanitize(truncated) == std::string("A??")); } // Legacy 5-byte UTF-8 encoding (U+00200000 → F8 88 80 80 80) is no longer // accepted — RFC-3629 compliance. Each byte is flagged individually, so // the input becomes 5 replacement chars instead of 1 wchar with the legacy // codepoint. - template<> template<> - void llstring_utf_object_t::test<43>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a legacy five-byte sequence is five bad bytes", "[LLStringUTF]") { const std::string legacy("\xF8\x88\x80\x80\x80"); - ensure("5-byte legacy rejected", !utf8str_is_valid(legacy)); + UNSCOPED_INFO("5-byte legacy rejected"); + CHECK_FALSE(utf8str_is_valid(legacy)); const std::u32string w = to_u32(legacy); - ensure_equals("5-byte legacy size", w.size(), size_t(5)); + UNSCOPED_INFO("5-byte legacy size"); + REQUIRE(w.size() == size_t(5)); for (size_t i = 0; i < w.size(); ++i) { - ensure_equals("5-byte legacy[i]", (U32)w[i], U32(0xFFFD)); + UNSCOPED_INFO("5-byte legacy[i]"); + REQUIRE((U32)w[i] == U32(0xFFFD)); } - ensure_equals("every byte repaired", - utf8str_sanitize(legacy), std::string(5, LL_UNKNOWN_CHAR)); + UNSCOPED_INFO("every byte repaired"); + CHECK(utf8str_sanitize(legacy) == std::string(5, LL_UNKNOWN_CHAR)); } // UTF-16 lone high surrogate followed by a non-surrogate: simdutf rejects // the lone 0xD83D (one '?') and converts the following 0x0041 normally. // Previous hand-rolled decoder greedily consumed both units as one garbage // codepoint. - template<> template<> - void llstring_utf_object_t::test<44>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a lone UTF-16 high surrogate becomes one unknown character", "[LLStringUTF]") { const llutf16string bad = { (char16_t)0xD83D, (char16_t)0x0041 }; const std::string out = utf16str_to_utf8str(bad); - ensure_equals("lone surrogate + 'A' size", out.size(), size_t(2)); - ensure_equals("lone surrogate -> '?'", (U32)(U8)out[0], U32((U8)LL_UNKNOWN_CHAR)); - ensure_equals("'A' preserved", (U32)(U8)out[1], U32('A')); + UNSCOPED_INFO("lone surrogate + 'A' size"); + REQUIRE(out.size() == size_t(2)); + UNSCOPED_INFO("lone surrogate -> '?'"); + CHECK((U32)(U8)out[0] == U32((U8)LL_UNKNOWN_CHAR)); + UNSCOPED_INFO("'A' preserved"); + CHECK((U32)(U8)out[1] == U32('A')); } // --------------------------------------------------------------- // round-trip helpers (smoke) // --------------------------------------------------------------- - template<> template<> - void llstring_utf_object_t::test<50>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_trim and utf8str_tolower keep non-ASCII text", "[LLStringUTF]") { // These compose utf8str_to_wstring + wstring_to_utf8str. Covered here // so composition regressions surface before we diagnose them through // callers. const std::string mixed = "H\xC3\xA9LLO \xE6\x97\xA5"; - ensure_equals("utf8str_trim passthrough", - utf8str_trim(mixed), mixed); - ensure_equals("utf8str_tolower preserves BMP non-ASCII", - utf8str_tolower(mixed), std::string("h\xC3\xA9llo \xE6\x97\xA5")); + UNSCOPED_INFO("utf8str_trim passthrough"); + CHECK(utf8str_trim(mixed) == mixed); + UNSCOPED_INFO("utf8str_tolower preserves BMP non-ASCII"); + CHECK(utf8str_tolower(mixed) == std::string("h\xC3\xA9llo \xE6\x97\xA5")); } - template<> template<> - void llstring_utf_object_t::test<51>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_compare_insensitive orders regardless of case", "[LLStringUTF]") { - ensure_equals("compare_insensitive eq", - utf8str_compare_insensitive("Hello", "hello"), S32(0)); - ensure("compare_insensitive lt", - utf8str_compare_insensitive("abc", "abd") < 0); - ensure("compare_insensitive gt", - utf8str_compare_insensitive("abd", "abc") > 0); + UNSCOPED_INFO("compare_insensitive eq"); + CHECK(utf8str_compare_insensitive("Hello", "hello") == S32(0)); + UNSCOPED_INFO("compare_insensitive lt"); + CHECK(utf8str_compare_insensitive("abc", "abd") < 0); + UNSCOPED_INFO("compare_insensitive gt"); + CHECK(utf8str_compare_insensitive("abd", "abc") > 0); } // --------------------------------------------------------------- @@ -590,56 +642,69 @@ namespace tut // in colour on its own. Symbols that only become emoji when a VS-16 asks // them to (©, ®, ❤) are not, and neither is a codepoint that merely sits // in an emoji block. - template<> template<> - void llstring_utf_object_t::test<60>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: LLStringOps::isEmoji is Unicode's Emoji_Presentation", "[LLStringUTF]") { - ensure("isEmoji rocket", LLStringOps::isEmoji((llwchar)0x1F680)); + UNSCOPED_INFO("isEmoji rocket"); + CHECK(LLStringOps::isEmoji((llwchar)0x1F680)); // Both mahjong tiles, one of which Unicode calls an emoji and one of // which it does not. A block-shaped range test cannot tell them apart. - ensure("isEmoji mahjong red dragon", LLStringOps::isEmoji((llwchar)0x1F004)); - ensure("not isEmoji mahjong east wind", !LLStringOps::isEmoji((llwchar)0x1F000)); + UNSCOPED_INFO("isEmoji mahjong red dragon"); + CHECK(LLStringOps::isEmoji((llwchar)0x1F004)); + UNSCOPED_INFO("not isEmoji mahjong east wind"); + CHECK_FALSE(LLStringOps::isEmoji((llwchar)0x1F000)); // A BMP emoji, which the old astral-only range could never see. - ensure("isEmoji watch", LLStringOps::isEmoji((llwchar)0x231A)); - ensure("not isEmoji 'A'", !LLStringOps::isEmoji((llwchar)'A')); - ensure("not isEmoji ©", !LLStringOps::isEmoji((llwchar)0x00A9)); - ensure("not isEmoji ❤", !LLStringOps::isEmoji((llwchar)0x2764)); - ensure("not isEmoji CJK-Ext", !LLStringOps::isEmoji((llwchar)0x20000)); + UNSCOPED_INFO("isEmoji watch"); + CHECK(LLStringOps::isEmoji((llwchar)0x231A)); + UNSCOPED_INFO("not isEmoji 'A'"); + CHECK_FALSE(LLStringOps::isEmoji((llwchar)'A')); + UNSCOPED_INFO("not isEmoji ©"); + CHECK_FALSE(LLStringOps::isEmoji((llwchar)0x00A9)); + UNSCOPED_INFO("not isEmoji ❤"); + CHECK_FALSE(LLStringOps::isEmoji((llwchar)0x2764)); + UNSCOPED_INFO("not isEmoji CJK-Ext"); + CHECK_FALSE(LLStringOps::isEmoji((llwchar)0x20000)); } // wstring_remove_emojis must strip consecutive emojis without skipping // the following code point (the implementation uses i-- after erase to // re-check the new index). - template<> template<> - void llstring_utf_object_t::test<62>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: wstring_remove_emojis strips consecutive emoji and keeps what follows", "[LLStringUTF]") { std::u32string ws = { (llwchar)'H', (llwchar)0x1F680, (llwchar)0x1F681, (llwchar)'i' }; const std::u32string expected = { (llwchar)'H', (llwchar)'i' }; - ensure("remove_emojis returned true", wstring_remove_emojis(ws)); + UNSCOPED_INFO("remove_emojis returned true"); + CHECK(wstring_remove_emojis(ws)); ensure_wstring_equals("consecutive emojis stripped", ws, expected); std::u32string none = { (llwchar)'H', (llwchar)'i' }; - ensure("remove_emojis returned false", !wstring_remove_emojis(none)); + UNSCOPED_INFO("remove_emojis returned false"); + CHECK_FALSE(wstring_remove_emojis(none)); ensure_wstring_equals("unchanged when no emojis", none, expected); } - template<> template<> - void llstring_utf_object_t::test<63>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_remove_emojis strips whole clusters from UTF-8", "[LLStringUTF]") { std::string s = "Hi\xF0\x9F\x9A\x80" "!"; - ensure("utf8str_remove_emojis found", utf8str_remove_emojis(s)); - ensure_equals("utf8 stripped", s, std::string("Hi!")); + UNSCOPED_INFO("utf8str_remove_emojis found"); + CHECK(utf8str_remove_emojis(s)); + UNSCOPED_INFO("utf8 stripped"); + CHECK(s == std::string("Hi!")); std::string clean = "Hello"; - ensure("utf8str_remove_emojis clean", !utf8str_remove_emojis(clean)); - ensure_equals("clean unchanged", clean, std::string("Hello")); + UNSCOPED_INFO("utf8str_remove_emojis clean"); + CHECK_FALSE(utf8str_remove_emojis(clean)); + UNSCOPED_INFO("clean unchanged"); + CHECK(clean == std::string("Hello")); // Heart-on-fire ❤️‍🔥 round-trips through utf8 encoding without // the wstring layer disagreeing with the byte layer about cluster // bounds. UTF-8 byte sequence: U+2764 (E2 9D A4) U+FE0F (EF B8 8F) // U+200D (E2 80 8D) U+1F525 (F0 9F 94 A5). std::string fire = "A" "\xE2\x9D\xA4" "\xEF\xB8\x8F" "\xE2\x80\x8D" "\xF0\x9F\x94\xA5" "B"; - ensure("utf8 heart-on-fire found", utf8str_remove_emojis(fire)); - ensure_equals("utf8 heart-on-fire stripped", fire, std::string("AB")); + UNSCOPED_INFO("utf8 heart-on-fire found"); + CHECK(utf8str_remove_emojis(fire)); + UNSCOPED_INFO("utf8 heart-on-fire stripped"); + CHECK(fire == std::string("AB")); // Subdivision flag 🏴󠁧󠁢󠁥󠁮󠁧󠁿 — base + 5 tag chars + CANCEL TAG, all // astral. UTF-8 bytes: U+1F3F4 (F0 9F 8F B4) followed by 6 four-byte @@ -648,15 +713,16 @@ namespace tut "\xF3\xA0\x81\xA7" "\xF3\xA0\x81\xA2" "\xF3\xA0\x81\xB3" "\xF3\xA0\x81\xA3" "\xF3\xA0\x81\xB4" "\xF3\xA0\x81\xBF" "]"; - ensure("utf8 subdiv flag found", utf8str_remove_emojis(flag)); - ensure_equals("utf8 subdiv flag stripped", flag, std::string("[]")); + UNSCOPED_INFO("utf8 subdiv flag found"); + CHECK(utf8str_remove_emojis(flag)); + UNSCOPED_INFO("utf8 subdiv flag stripped"); + CHECK(flag == std::string("[]")); } // ZWJ family (U+1F468 U+200D U+1F469 U+200D U+1F467) must strip as a // single unit — the old implementation left the two ZWJ code points // behind, producing visible tofu. - template<> template<> - void llstring_utf_object_t::test<64>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a ZWJ family strips as one unit", "[LLStringUTF]") { std::u32string ws = { (llwchar)'H', (llwchar)0x1F468, (llwchar)0x200D, @@ -664,54 +730,55 @@ namespace tut (llwchar)0x1F467, (llwchar)'i' }; const std::u32string expected = { (llwchar)'H', (llwchar)'i' }; - ensure("ZWJ family found", wstring_remove_emojis(ws)); + UNSCOPED_INFO("ZWJ family found"); + CHECK(wstring_remove_emojis(ws)); ensure_wstring_equals("ZWJ family stripped", ws, expected); } // Skin-tone modifier (U+1F3FB..FF) must strip together with its base emoji. - template<> template<> - void llstring_utf_object_t::test<65>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a skin-tone modifier strips with its base emoji", "[LLStringUTF]") { std::u32string ws = { (llwchar)'M', (llwchar)0x1F468, (llwchar)0x1F3FB, (llwchar)'X' }; const std::u32string expected = { (llwchar)'M', (llwchar)'X' }; - ensure("skin tone found", wstring_remove_emojis(ws)); + UNSCOPED_INFO("skin tone found"); + CHECK(wstring_remove_emojis(ws)); ensure_wstring_equals("skin tone stripped", ws, expected); } // Regional indicator flag (🇺🇸 = U+1F1FA U+1F1F8). Both code points are in // the astral emoji range so both strip — the output must not leave a // dangling half-flag indicator. - template<> template<> - void llstring_utf_object_t::test<66>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a flag strips both its regional indicators", "[LLStringUTF]") { std::u32string ws = { (llwchar)0x1F1FA, (llwchar)0x1F1F8, (llwchar)'!' }; const std::u32string expected = { (llwchar)'!' }; - ensure("flag found", wstring_remove_emojis(ws)); + UNSCOPED_INFO("flag found"); + CHECK(wstring_remove_emojis(ws)); ensure_wstring_equals("flag stripped", ws, expected); } // Trailing VS16 (U+FE0F) after an astral emoji must be consumed together // with the base glyph. - template<> template<> - void llstring_utf_object_t::test<67>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a trailing VS16 strips with its astral base", "[LLStringUTF]") { std::u32string ws = { (llwchar)0x1F680, (llwchar)0xFE0F, (llwchar)'!' }; const std::u32string expected = { (llwchar)'!' }; - ensure("VS16 found", wstring_remove_emojis(ws)); + UNSCOPED_INFO("VS16 found"); + CHECK(wstring_remove_emojis(ws)); ensure_wstring_equals("VS16 stripped with base", ws, expected); } // Bare ZWJ or VS16 (no adjacent emoji) are not emoji themselves and must // be left intact — critical because ZWJ is used in Arabic/Indic shaping // outside any emoji context. - template<> template<> - void llstring_utf_object_t::test<68>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a bare ZWJ is not an emoji and stays", "[LLStringUTF]") { std::u32string ws = { (llwchar)'a', (llwchar)0x200D, (llwchar)'b' }; const std::u32string expected = ws; - ensure("bare ZWJ not found", !wstring_remove_emojis(ws)); + UNSCOPED_INFO("bare ZWJ not found"); + CHECK_FALSE(wstring_remove_emojis(ws)); ensure_wstring_equals("bare ZWJ preserved", ws, expected); } @@ -722,11 +789,11 @@ namespace tut // builds left this pair intact under a narrower isEmoji-only contract; // the change unifies the strip behavior with cluster-aware tooltip and // cursor logic so they can't disagree on cluster bounds. - template<> template<> - void llstring_utf_object_t::test<69>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a heart with VS16 strips but a bare heart stays", "[LLStringUTF]") { std::u32string ws = { (llwchar)0x2764, (llwchar)0xFE0F }; - ensure("BMP heart+VS16 stripped", wstring_remove_emojis(ws)); + UNSCOPED_INFO("BMP heart+VS16 stripped"); + CHECK(wstring_remove_emojis(ws)); ensure_wstring_equals("BMP heart+VS16 cleared", ws, std::u32string()); // Bare BMP heart (no VS-16) is not a cluster — single-codepoint @@ -734,7 +801,8 @@ namespace tut // range too. Must pass through. std::u32string bare_heart = { (llwchar)0x2764 }; const std::u32string bare_expected = bare_heart; - ensure("bare heart not stripped", !wstring_remove_emojis(bare_heart)); + UNSCOPED_INFO("bare heart not stripped"); + CHECK_FALSE(wstring_remove_emojis(bare_heart)); ensure_wstring_equals("bare heart preserved", bare_heart, bare_expected); } @@ -747,15 +815,15 @@ namespace tut // heart/VS-16/ZWJ then strip the lone fire, leaving `❤️‍` (heart + // VS-16 + dangling ZWJ) on screen. Cluster-driven removal strips // the whole sequence as one unit. - template<> template<> - void llstring_utf_object_t::test<70>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: heart-on-fire strips whole though its base is in the BMP", "[LLStringUTF]") { std::u32string ws = { (llwchar)'A', (llwchar)0x2764, (llwchar)0xFE0F, (llwchar)0x200D, (llwchar)0x1F525, (llwchar)'B' }; const std::u32string expected = { (llwchar)'A', (llwchar)'B' }; - ensure("heart-on-fire found", wstring_remove_emojis(ws)); + UNSCOPED_INFO("heart-on-fire found"); + CHECK(wstring_remove_emojis(ws)); ensure_wstring_equals("heart-on-fire cleared", ws, expected); } @@ -764,27 +832,30 @@ namespace tut // the old narrow walker left the whole thing intact. The cluster walker // identifies it as a single 3-codepoint cluster, and the broad contract // strips it. - template<> template<> - void llstring_utf_object_t::test<71>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: keycaps strip as clusters and a bare digit stays", "[LLStringUTF]") { std::u32string digit_kc = { (llwchar)'1', (llwchar)0xFE0F, (llwchar)0x20E3, (llwchar)'!' }; const std::u32string expected = { (llwchar)'!' }; - ensure("digit keycap found", wstring_remove_emojis(digit_kc)); + UNSCOPED_INFO("digit keycap found"); + CHECK(wstring_remove_emojis(digit_kc)); ensure_wstring_equals("digit keycap cleared", digit_kc, expected); std::u32string hash_kc = { (llwchar)'#', (llwchar)0xFE0F, (llwchar)0x20E3 }; - ensure("hash keycap found", wstring_remove_emojis(hash_kc)); + UNSCOPED_INFO("hash keycap found"); + CHECK(wstring_remove_emojis(hash_kc)); ensure_wstring_equals("hash keycap cleared", hash_kc, std::u32string()); std::u32string star_kc = { (llwchar)'*', (llwchar)0xFE0F, (llwchar)0x20E3 }; - ensure("star keycap found", wstring_remove_emojis(star_kc)); + UNSCOPED_INFO("star keycap found"); + CHECK(wstring_remove_emojis(star_kc)); ensure_wstring_equals("star keycap cleared", star_kc, std::u32string()); // Bare digit (no VS-16 + combiner) must pass through. The cluster // walker rejects it as a starter; nothing to strip. std::u32string bare_digit = { (llwchar)'9' }; const std::u32string bare_expected = bare_digit; - ensure("bare digit not stripped", !wstring_remove_emojis(bare_digit)); + UNSCOPED_INFO("bare digit not stripped"); + CHECK_FALSE(wstring_remove_emojis(bare_digit)); ensure_wstring_equals("bare digit preserved", bare_digit, bare_expected); } @@ -792,8 +863,7 @@ namespace tut // Pre-rewrite the walker stripped the astral 🏴 base via isEmoji but // left tag chars and the U+E007F terminator behind as garbage code // points. Cluster-driven removal sweeps the whole sequence. - template<> template<> - void llstring_utf_object_t::test<72>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a subdivision flag strips with its tag characters", "[LLStringUTF]") { std::u32string ws = { (llwchar)'<', (llwchar)0x1F3F4, @@ -802,7 +872,8 @@ namespace tut (llwchar)0xE007F, (llwchar)'>' }; const std::u32string expected = { (llwchar)'<', (llwchar)'>' }; - ensure("subdivision flag found", wstring_remove_emojis(ws)); + UNSCOPED_INFO("subdivision flag found"); + CHECK(wstring_remove_emojis(ws)); ensure_wstring_equals("subdivision flag cleared", ws, expected); } @@ -811,25 +882,25 @@ namespace tut // gets stripped together with its base. Pre-rewrite VS-15 was not in // is_emoji_sequence_extender's narrow set and would have been left as // an orphan after the base was stripped. - template<> template<> - void llstring_utf_object_t::test<73>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a VS15 after an astral emoji strips with it", "[LLStringUTF]") { std::u32string ws = { (llwchar)0x1F680, (llwchar)0xFE0E, (llwchar)'!' }; const std::u32string expected = { (llwchar)'!' }; - ensure("VS-15 cluster found", wstring_remove_emojis(ws)); + UNSCOPED_INFO("VS-15 cluster found"); + CHECK(wstring_remove_emojis(ws)); ensure_wstring_equals("VS-15 cluster cleared", ws, expected); } // Two flags back-to-back 🇺🇸🇯🇵 — distinct clusters [0,2) and [2,4). // Both must strip cleanly; no leftover RI codepoints. - template<> template<> - void llstring_utf_object_t::test<74>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: two flags back to back both strip", "[LLStringUTF]") { std::u32string ws = { (llwchar)0x1F1FA, (llwchar)0x1F1F8, (llwchar)0x1F1EF, (llwchar)0x1F1F5, (llwchar)'!' }; const std::u32string expected = { (llwchar)'!' }; - ensure("two flags found", wstring_remove_emojis(ws)); + UNSCOPED_INFO("two flags found"); + CHECK(wstring_remove_emojis(ws)); ensure_wstring_equals("two flags cleared", ws, expected); } @@ -837,12 +908,12 @@ namespace tut // when adjacent to an astral emoji. Pre-rewrite this happened to work // (ZWSP isn't in the extender set), but pin it now that the path uses // the cluster walker, which also treats ZWSP as inert. - template<> template<> - void llstring_utf_object_t::test<75>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a ZWSP next to an emoji is not stripped", "[LLStringUTF]") { std::u32string ws = { (llwchar)0x1F680, (llwchar)0x200B, (llwchar)'X' }; const std::u32string expected = { (llwchar)0x200B, (llwchar)'X' }; - ensure("rocket+ZWSP found", wstring_remove_emojis(ws)); + UNSCOPED_INFO("rocket+ZWSP found"); + CHECK(wstring_remove_emojis(ws)); ensure_wstring_equals("rocket stripped, ZWSP kept", ws, expected); } @@ -850,117 +921,42 @@ namespace tut // 11x utf8str helper smoke pins // --------------------------------------------------------------- - // utf8str_substChar replaces every occurrence of a code point (incl. - // astrals) with another code point, routing through std::u32string so the - // byte-length change is handled for us. - template<> template<> - void llstring_utf_object_t::test<110>() - { - const std::string in = "A\xF0\x9F\x9A\x80" "B\xF0\x9F\x9A\x80" "C"; - const std::string out = utf8str_substChar(in, (llwchar)0x1F680, (llwchar)'X'); - ensure_equals("substChar astral -> ASCII", out, std::string("AXBXC")); - - const std::string in2 = "A\xE6\x97\xA5" "B"; - const std::string out2 = utf8str_substChar(in2, (llwchar)0x65E5, (llwchar)'.'); - ensure_equals("substChar BMP -> ASCII", out2, std::string("A.B")); - } - - // utf8str_makeASCII replaces every non-ASCII llwchar (>0x7F) with '?' - // before serializing back to utf-8. - template<> template<> - void llstring_utf_object_t::test<111>() - { - const std::string mixed = "A\xE6\x97\xA5" "B\xF0\x9F\x9A\x80" "C"; - const std::string expected = "A?B?C"; - ensure_equals("utf8str_makeASCII", utf8str_makeASCII(mixed), expected); - ensure_equals("utf8str_makeASCII ascii passthrough", - utf8str_makeASCII("Hello"), std::string("Hello")); - } - - // mbcsstring_makeASCII is byte-level: every byte >0x7F becomes '?'. So a - // 3-byte UTF-8 character yields three '?' rather than one. - template<> template<> - void llstring_utf_object_t::test<112>() - { - const std::string mixed = "A\xE6\x97\xA5" "B"; - const std::string expected = "A???B"; - ensure_equals("mbcsstring_makeASCII", mbcsstring_makeASCII(mixed), expected); - } - - // utf8str_removeCRLF strips CR (0x0D) only — LF is preserved. - template<> template<> - void llstring_utf_object_t::test<113>() - { - ensure_equals("removeCRLF crlf", utf8str_removeCRLF("a\r\nb"), std::string("a\nb")); - ensure_equals("removeCRLF cr", utf8str_removeCRLF("a\rb"), std::string("ab")); - ensure_equals("removeCRLF lf", utf8str_removeCRLF("a\nb"), std::string("a\nb")); - ensure_equals("removeCRLF empty", utf8str_removeCRLF(""), std::string()); - } - - // utf8str_split splits at the last split-token at or before maxlen bytes, - // falling back to a hard byte cut when no token is in range. Non-final - // chunks retain the trailing split-token (current implementation). - template<> template<> - void llstring_utf_object_t::test<114>() - { - { - std::list out; - utf8str_split(out, "Hello there world", 10, ' '); - ensure_equals("split 3 parts", out.size(), size_t(3)); - auto it = out.begin(); - ensure_equals("split[0]", *it++, std::string("Hello")); - ensure_equals("split[1]", *it++, std::string("there ")); - ensure_equals("split[2]", *it, std::string("world")); - } - { - std::list out; - utf8str_split(out, "short", 10, ' '); - ensure_equals("split single", out.size(), size_t(1)); - ensure_equals("split single val", out.front(), std::string("short")); - } - } - - // wchar_utf8_preview formats the code point in hex, plus (when multi-byte) - // the decoded UTF-8 byte sequence also in hex. - template<> template<> - void llstring_utf_object_t::test<115>() - { - ensure_equals("preview ASCII", wchar_utf8_preview((llwchar)'A'), std::string("41")); - // é = U+00E9 -> 0xC3 0xA9 - ensure_equals("preview 2-byte", wchar_utf8_preview((llwchar)0x00E9), - std::string("E9 [C3, A9]")); - // Rocket = U+1F680 -> 0xF0 0x9F 0x9A 0x80 - ensure_equals("preview astral", wchar_utf8_preview((llwchar)0x1F680), - std::string("1F680 [F0, 9F, 9A, 80]")); - } - // --------------------------------------------------------------- // byte-level helpers // --------------------------------------------------------------- - template<> template<> - void llstring_utf_object_t::test<81>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: is_char_hex accepts hex digits only", "[LLStringUTF]") { for (char c : std::string("0123456789abcdefABCDEF")) { - ensure("is_char_hex yes", is_char_hex(c)); + UNSCOPED_INFO("is_char_hex yes"); + REQUIRE(is_char_hex(c)); } - ensure("is_char_hex 'g'", !is_char_hex('g')); - ensure("is_char_hex 'Z'", !is_char_hex('Z')); - ensure("is_char_hex ' '", !is_char_hex(' ')); - } - - template<> template<> - void llstring_utf_object_t::test<82>() - { - ensure_equals("nybble '0'", (U32)hex_as_nybble('0'), U32(0)); - ensure_equals("nybble '9'", (U32)hex_as_nybble('9'), U32(9)); - ensure_equals("nybble 'a'", (U32)hex_as_nybble('a'), U32(10)); - ensure_equals("nybble 'f'", (U32)hex_as_nybble('f'), U32(15)); - ensure_equals("nybble 'A'", (U32)hex_as_nybble('A'), U32(10)); - ensure_equals("nybble 'F'", (U32)hex_as_nybble('F'), U32(15)); + UNSCOPED_INFO("is_char_hex 'g'"); + CHECK_FALSE(is_char_hex('g')); + UNSCOPED_INFO("is_char_hex 'Z'"); + CHECK_FALSE(is_char_hex('Z')); + UNSCOPED_INFO("is_char_hex ' '"); + CHECK_FALSE(is_char_hex(' ')); + } + + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: hex_as_nybble reads either case and gives 0 otherwise", "[LLStringUTF]") + { + UNSCOPED_INFO("nybble '0'"); + CHECK((U32)hex_as_nybble('0') == U32(0)); + UNSCOPED_INFO("nybble '9'"); + CHECK((U32)hex_as_nybble('9') == U32(9)); + UNSCOPED_INFO("nybble 'a'"); + CHECK((U32)hex_as_nybble('a') == U32(10)); + UNSCOPED_INFO("nybble 'f'"); + CHECK((U32)hex_as_nybble('f') == U32(15)); + UNSCOPED_INFO("nybble 'A'"); + CHECK((U32)hex_as_nybble('A') == U32(10)); + UNSCOPED_INFO("nybble 'F'"); + CHECK((U32)hex_as_nybble('F') == U32(15)); // Non-hex inputs fall through to 0 (undefined-but-documented behavior). - ensure_equals("nybble 'g' -> 0", (U32)hex_as_nybble('g'), U32(0)); + UNSCOPED_INFO("nybble 'g' -> 0"); + CHECK((U32)hex_as_nybble('g') == U32(0)); } // --------------------------------------------------------------- @@ -970,110 +966,127 @@ namespace tut // Inputs that never need shaping — empty, ASCII, CJK, a single isolated // emoji, and two adjacent but unrelated emoji all render correctly via // the 1:1 codepoint path, so the detector must return no runs. - template<> template<> - void llstring_utf_object_t::test<90>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: text that needs no shaping has no emoji clusters", "[LLStringUTF]") { - ensure_equals("empty", wstring_find_emoji_clusters(std::u32string()).size(), size_t(0)); + UNSCOPED_INFO("empty"); + REQUIRE(wstring_find_emoji_clusters(std::u32string()).size() == size_t(0)); std::u32string ascii = { (llwchar)'H', (llwchar)'i', (llwchar)'!' }; - ensure_equals("ascii", wstring_find_emoji_clusters(ascii).size(), size_t(0)); + UNSCOPED_INFO("ascii"); + REQUIRE(wstring_find_emoji_clusters(ascii).size() == size_t(0)); std::u32string cjk = { (llwchar)0x65E5, (llwchar)0x672C }; - ensure_equals("cjk", wstring_find_emoji_clusters(cjk).size(), size_t(0)); + UNSCOPED_INFO("cjk"); + REQUIRE(wstring_find_emoji_clusters(cjk).size() == size_t(0)); std::u32string lone = { (llwchar)0x1F680 }; - ensure_equals("lone emoji", wstring_find_emoji_clusters(lone).size(), size_t(0)); + UNSCOPED_INFO("lone emoji"); + REQUIRE(wstring_find_emoji_clusters(lone).size() == size_t(0)); std::u32string pair = { (llwchar)0x1F680, (llwchar)0x1F681 }; - ensure_equals("two adj em", wstring_find_emoji_clusters(pair).size(), size_t(0)); + UNSCOPED_INFO("two adj em"); + REQUIRE(wstring_find_emoji_clusters(pair).size() == size_t(0)); } // ZWJ family 👨‍👩‍👧 = U+1F468 U+200D U+1F469 U+200D U+1F467 — must emerge // as a single 5-codepoint run. - template<> template<> - void llstring_utf_object_t::test<91>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a ZWJ family is one five-codepoint cluster", "[LLStringUTF]") { std::u32string ws = { (llwchar)0x1F468, (llwchar)0x200D, (llwchar)0x1F469, (llwchar)0x200D, (llwchar)0x1F467 }; auto runs = wstring_find_emoji_clusters(ws); - ensure_equals("one run", runs.size(), size_t(1)); - ensure_equals("run begin", runs[0].first, size_t(0)); - ensure_equals("run end", runs[0].second, size_t(5)); + UNSCOPED_INFO("one run"); + REQUIRE(runs.size() == size_t(1)); + UNSCOPED_INFO("run begin"); + CHECK(runs[0].first == size_t(0)); + UNSCOPED_INFO("run end"); + CHECK(runs[0].second == size_t(5)); } // Skin-tone modifier (U+1F3FB..FF) combines with the preceding emoji. - template<> template<> - void llstring_utf_object_t::test<92>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a skin-tone modifier joins the emoji before it", "[LLStringUTF]") { std::u32string ws = { (llwchar)0x1F468, (llwchar)0x1F3FB }; auto runs = wstring_find_emoji_clusters(ws); - ensure_equals("skintone runs", runs.size(), size_t(1)); - ensure_equals("skintone end", runs[0].second, size_t(2)); + UNSCOPED_INFO("skintone runs"); + REQUIRE(runs.size() == size_t(1)); + UNSCOPED_INFO("skintone end"); + CHECK(runs[0].second == size_t(2)); } // Trailing VS16 (U+FE0F) forces emoji presentation and must be part of // the same shaped run as its base. - template<> template<> - void llstring_utf_object_t::test<93>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a trailing VS16 joins its base's cluster", "[LLStringUTF]") { std::u32string ws = { (llwchar)0x1F680, (llwchar)0xFE0F }; auto runs = wstring_find_emoji_clusters(ws); - ensure_equals("vs16 runs", runs.size(), size_t(1)); - ensure_equals("vs16 end", runs[0].second, size_t(2)); + UNSCOPED_INFO("vs16 runs"); + REQUIRE(runs.size() == size_t(1)); + UNSCOPED_INFO("vs16 end"); + CHECK(runs[0].second == size_t(2)); } // Regional indicator pair — 🇺🇸 = U+1F1FA U+1F1F8 — becomes a flag glyph // only under shaping. Two consecutive RIs form exactly one pair. - template<> template<> - void llstring_utf_object_t::test<94>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: regional indicators pair into flag clusters", "[LLStringUTF]") { std::u32string ws = { (llwchar)0x1F1FA, (llwchar)0x1F1F8 }; auto runs = wstring_find_emoji_clusters(ws); - ensure_equals("flag runs", runs.size(), size_t(1)); - ensure_equals("flag end", runs[0].second, size_t(2)); + UNSCOPED_INFO("flag runs"); + REQUIRE(runs.size() == size_t(1)); + UNSCOPED_INFO("flag end"); + CHECK(runs[0].second == size_t(2)); // Four RIs in a row form two separate flags. std::u32string four = { (llwchar)0x1F1FA, (llwchar)0x1F1F8, (llwchar)0x1F1EB, (llwchar)0x1F1F7 }; auto more = wstring_find_emoji_clusters(four); - ensure_equals("two flags", more.size(), size_t(2)); - ensure_equals("flag0 end", more[0].second, size_t(2)); - ensure_equals("flag1 begin", more[1].first, size_t(2)); - ensure_equals("flag1 end", more[1].second, size_t(4)); + UNSCOPED_INFO("two flags"); + REQUIRE(more.size() == size_t(2)); + UNSCOPED_INFO("flag0 end"); + CHECK(more[0].second == size_t(2)); + UNSCOPED_INFO("flag1 begin"); + CHECK(more[1].first == size_t(2)); + UNSCOPED_INFO("flag1 end"); + CHECK(more[1].second == size_t(4)); } // Keycap: digit/#/* + U+FE0F + U+20E3 is a shaped 3-codepoint sequence. // shapeRun itemises this into per-face sub-runs (the ASCII digit on // the text font, the combining mark on the emoji font), so the // cluster is still detected here for cursor-snapping purposes. - template<> template<> - void llstring_utf_object_t::test<95>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a keycap is a three-codepoint cluster", "[LLStringUTF]") { std::u32string digit_kc = { (llwchar)'9', (llwchar)0xFE0F, (llwchar)0x20E3 }; auto runs = wstring_find_emoji_clusters(digit_kc); - ensure_equals("keycap runs", runs.size(), size_t(1)); - ensure_equals("keycap end", runs[0].second, size_t(3)); + UNSCOPED_INFO("keycap runs"); + REQUIRE(runs.size() == size_t(1)); + UNSCOPED_INFO("keycap end"); + CHECK(runs[0].second == size_t(3)); std::u32string hash_kc = { (llwchar)'#', (llwchar)0xFE0F, (llwchar)0x20E3 }; - ensure_equals("hash keycap", wstring_find_emoji_clusters(hash_kc).size(), size_t(1)); + UNSCOPED_INFO("hash keycap"); + REQUIRE(wstring_find_emoji_clusters(hash_kc).size() == size_t(1)); std::u32string bare = { (llwchar)'5' }; - ensure_equals("bare digit", wstring_find_emoji_clusters(bare).size(), size_t(0)); + UNSCOPED_INFO("bare digit"); + REQUIRE(wstring_find_emoji_clusters(bare).size() == size_t(0)); } // Subdivision flag — base 🏴 (U+1F3F4) + tag characters (U+E0020..U+E007F) // terminated by U+E007F. All tag bytes must be absorbed into the run. - template<> template<> - void llstring_utf_object_t::test<96>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a subdivision flag's cluster takes in its tag characters", "[LLStringUTF]") { std::u32string ws = { (llwchar)0x1F3F4, (llwchar)0xE0067, (llwchar)0xE0062, (llwchar)0xE0073, (llwchar)0xE0063, (llwchar)0xE0074, (llwchar)0xE007F }; auto runs = wstring_find_emoji_clusters(ws); - ensure_equals("tag runs", runs.size(), size_t(1)); - ensure_equals("tag begin", runs[0].first, size_t(0)); - ensure_equals("tag end", runs[0].second, size_t(7)); + UNSCOPED_INFO("tag runs"); + REQUIRE(runs.size() == size_t(1)); + UNSCOPED_INFO("tag begin"); + CHECK(runs[0].first == size_t(0)); + UNSCOPED_INFO("tag end"); + CHECK(runs[0].second == size_t(7)); } // Mixed: ASCII + flag + ASCII + ZWJ family + ASCII. Two disjoint runs // with correct bounds; surrounding ASCII is untouched. - template<> template<> - void llstring_utf_object_t::test<97>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a flag and a ZWJ family among ASCII are two clusters", "[LLStringUTF]") { std::u32string ws = { (llwchar)'H', (llwchar)0x1F1FA, (llwchar)0x1F1F8, // flag @ [1,3) @@ -1083,29 +1096,36 @@ namespace tut (llwchar)0x1F467, (llwchar)'!' }; auto runs = wstring_find_emoji_clusters(ws); - ensure_equals("two runs", runs.size(), size_t(2)); - ensure_equals("run0 begin", runs[0].first, size_t(1)); - ensure_equals("run0 end", runs[0].second, size_t(3)); - ensure_equals("run1 begin", runs[1].first, size_t(4)); - ensure_equals("run1 end", runs[1].second, size_t(9)); + UNSCOPED_INFO("two runs"); + REQUIRE(runs.size() == size_t(2)); + UNSCOPED_INFO("run0 begin"); + CHECK(runs[0].first == size_t(1)); + UNSCOPED_INFO("run0 end"); + CHECK(runs[0].second == size_t(3)); + UNSCOPED_INFO("run1 begin"); + CHECK(runs[1].first == size_t(4)); + UNSCOPED_INFO("run1 end"); + CHECK(runs[1].second == size_t(9)); } // Bare ZWJ/VS16 surrounded by non-emoji (as used in Arabic/Indic shaping // outside any emoji context) must NOT produce a run. There is no emoji // face to shape with and the caller should keep the 1:1 path. - template<> template<> - void llstring_utf_object_t::test<98>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: bare extenders and malformed sequences cluster as pinned", "[LLStringUTF]") { std::u32string zwj = { (llwchar)'a', (llwchar)0x200D, (llwchar)'b' }; - ensure_equals("bare zwj", wstring_find_emoji_clusters(zwj).size(), size_t(0)); + UNSCOPED_INFO("bare zwj"); + REQUIRE(wstring_find_emoji_clusters(zwj).size() == size_t(0)); std::u32string vs16 = { (llwchar)'a', (llwchar)0xFE0F, (llwchar)'b' }; - ensure_equals("bare vs16", wstring_find_emoji_clusters(vs16).size(), size_t(0)); + UNSCOPED_INFO("bare vs16"); + REQUIRE(wstring_find_emoji_clusters(vs16).size() == size_t(0)); // Malformed inputs: leading/trailing extenders, lone modifiers, and // singletons that need a partner. Pin current behavior so future // refactors don't silently change cluster boundaries on broken input. std::u32string lead_zwj = { (llwchar)0x200D, (llwchar)0x1F468 }; - ensure_equals("leading zwj", wstring_find_emoji_clusters(lead_zwj).size(), size_t(0)); + UNSCOPED_INFO("leading zwj"); + REQUIRE(wstring_find_emoji_clusters(lead_zwj).size() == size_t(0)); std::u32string trail_zwj = { (llwchar)0x1F468, (llwchar)0x200D }; // Trailing ZWJ: is_shaping_starter accepts the base because the // next codepoint is a ZWJ, but advance_shaping_run hits the @@ -1113,26 +1133,34 @@ namespace tut // the resulting length-1 "run" so the result matches the doc // contract (isolated single-codepoint emoji are intentionally // absent from the cluster list). - ensure_equals("trailing zwj", wstring_find_emoji_clusters(trail_zwj).size(), size_t(0)); + UNSCOPED_INFO("trailing zwj"); + REQUIRE(wstring_find_emoji_clusters(trail_zwj).size() == size_t(0)); // Same shape but with VS-16 instead of ZWJ: 'is_shaping_starter' // accepts because VS-16 is a valid extender, advance consumes it, // run length is 2 → real 2-codepoint cluster. Confirm we didn't // over-trim and accidentally drop legitimate 2-codepoint clusters. std::u32string base_vs16 = { (llwchar)0x1F680, (llwchar)0xFE0F }; auto vs_runs = wstring_find_emoji_clusters(base_vs16); - ensure_equals("base+VS16 count", vs_runs.size(), size_t(1)); - ensure_equals("base+VS16 begin", vs_runs[0].first, size_t(0)); - ensure_equals("base+VS16 end", vs_runs[0].second, size_t(2)); + UNSCOPED_INFO("base+VS16 count"); + REQUIRE(vs_runs.size() == size_t(1)); + UNSCOPED_INFO("base+VS16 begin"); + CHECK(vs_runs[0].first == size_t(0)); + UNSCOPED_INFO("base+VS16 end"); + CHECK(vs_runs[0].second == size_t(2)); std::u32string lone_skin = { (llwchar)0x1F3FB }; - ensure_equals("lone skintone", wstring_find_emoji_clusters(lone_skin).size(), size_t(0)); + UNSCOPED_INFO("lone skintone"); + REQUIRE(wstring_find_emoji_clusters(lone_skin).size() == size_t(0)); std::u32string lone_ri = { (llwchar)0x1F1FA }; - ensure_equals("lone RI", wstring_find_emoji_clusters(lone_ri).size(), size_t(0)); + UNSCOPED_INFO("lone RI"); + REQUIRE(wstring_find_emoji_clusters(lone_ri).size() == size_t(0)); // Range boundaries on isPictographBase's astral / BMP windows. std::u32string just_below_astral = { (llwchar)0x1FFF }; - ensure_equals("U+1FFF no run", wstring_find_emoji_clusters(just_below_astral).size(), size_t(0)); + UNSCOPED_INFO("U+1FFF no run"); + REQUIRE(wstring_find_emoji_clusters(just_below_astral).size() == size_t(0)); std::u32string just_above_bmp = { (llwchar)0x3300 }; - ensure_equals("U+3300 no run", wstring_find_emoji_clusters(just_above_bmp).size(), size_t(0)); + UNSCOPED_INFO("U+3300 no run"); + REQUIRE(wstring_find_emoji_clusters(just_above_bmp).size() == size_t(0)); // Back-to-back ZWJ families with no separator. Pin current behavior: // each MAN-ZWJ-WOMAN sub-sequence registers as its own cluster, so we @@ -1140,47 +1168,62 @@ namespace tut std::u32string two_fam = { (llwchar)0x1F468, (llwchar)0x200D, (llwchar)0x1F469, (llwchar)0x1F468, (llwchar)0x200D, (llwchar)0x1F469 }; auto two_fam_runs = wstring_find_emoji_clusters(two_fam); - ensure_equals("two_fam count", two_fam_runs.size(), size_t(2)); - ensure_equals("two_fam[0].first", two_fam_runs[0].first, size_t(0)); - ensure_equals("two_fam[0].second", two_fam_runs[0].second, size_t(3)); - ensure_equals("two_fam[1].first", two_fam_runs[1].first, size_t(3)); - ensure_equals("two_fam[1].second", two_fam_runs[1].second, size_t(6)); + UNSCOPED_INFO("two_fam count"); + REQUIRE(two_fam_runs.size() == size_t(2)); + UNSCOPED_INFO("two_fam[0].first"); + CHECK(two_fam_runs[0].first == size_t(0)); + UNSCOPED_INFO("two_fam[0].second"); + CHECK(two_fam_runs[0].second == size_t(3)); + UNSCOPED_INFO("two_fam[1].first"); + CHECK(two_fam_runs[1].first == size_t(3)); + UNSCOPED_INFO("two_fam[1].second"); + CHECK(two_fam_runs[1].second == size_t(6)); // ZWJ followed by a non-pictograph: starter accepts the base // (next is ZWJ), advance_shaping_run reaches the ZWJ, sees the // non-pictograph 'A' afterwards, breaks orphan-ZWJ. Length-1 run // gets discarded by the cluster walker. No cluster emitted. std::u32string zwj_plus_nonemoji = { (llwchar)0x1F468, (llwchar)0x200D, (llwchar)'A' }; - ensure_equals("ZWJ+non-pictograph", wstring_find_emoji_clusters(zwj_plus_nonemoji).size(), size_t(0)); + UNSCOPED_INFO("ZWJ+non-pictograph"); + REQUIRE(wstring_find_emoji_clusters(zwj_plus_nonemoji).size() == size_t(0)); // Double ZWJ (ZWJ + ZWJ between bases): advance hits the first // ZWJ, looks at the next codepoint, finds another ZWJ which is // not a pictograph base. Orphan-ZWJ break. Length 1, discarded. std::u32string double_zwj = { (llwchar)0x1F468, (llwchar)0x200D, (llwchar)0x200D, (llwchar)0x1F469 }; - ensure_equals("double ZWJ", wstring_find_emoji_clusters(double_zwj).size(), size_t(0)); + UNSCOPED_INFO("double ZWJ"); + REQUIRE(wstring_find_emoji_clusters(double_zwj).size() == size_t(0)); // Truncated subdivision flag — base 🏴 + a couple of tag bytes, // no U+E007F terminator. Pin: walker greedily eats every tag // codepoint to end-of-string. Real implementation behavior. std::u32string trunc_tag = { (llwchar)0x1F3F4, (llwchar)0xE0067, (llwchar)0xE0062 }; auto trunc_runs = wstring_find_emoji_clusters(trunc_tag); - ensure_equals("trunc tag count", trunc_runs.size(), size_t(1)); - ensure_equals("trunc tag begin", trunc_runs[0].first, size_t(0)); - ensure_equals("trunc tag end", trunc_runs[0].second, size_t(3)); + UNSCOPED_INFO("trunc tag count"); + REQUIRE(trunc_runs.size() == size_t(1)); + UNSCOPED_INFO("trunc tag begin"); + CHECK(trunc_runs[0].first == size_t(0)); + UNSCOPED_INFO("trunc tag end"); + CHECK(trunc_runs[0].second == size_t(3)); // Three RIs — first two form a flag at [0,2), third RI alone is // not a starter (no partner). Pin: one cluster, length 2. std::u32string three_ri = { (llwchar)0x1F1FA, (llwchar)0x1F1F8, (llwchar)0x1F1F8 }; auto three_ri_runs = wstring_find_emoji_clusters(three_ri); - ensure_equals("three RI count", three_ri_runs.size(), size_t(1)); - ensure_equals("three RI begin", three_ri_runs[0].first, size_t(0)); - ensure_equals("three RI end", three_ri_runs[0].second, size_t(2)); + UNSCOPED_INFO("three RI count"); + REQUIRE(three_ri_runs.size() == size_t(1)); + UNSCOPED_INFO("three RI begin"); + CHECK(three_ri_runs[0].first == size_t(0)); + UNSCOPED_INFO("three RI end"); + CHECK(three_ri_runs[0].second == size_t(2)); // Star keycap '*' + VS-16 + U+20E3. Pin parity with digit/hash. std::u32string star_kc = { (llwchar)'*', (llwchar)0xFE0F, (llwchar)0x20E3 }; auto star_runs = wstring_find_emoji_clusters(star_kc); - ensure_equals("star kc count", star_runs.size(), size_t(1)); - ensure_equals("star kc end", star_runs[0].second, size_t(3)); + UNSCOPED_INFO("star kc count"); + REQUIRE(star_runs.size() == size_t(1)); + UNSCOPED_INFO("star kc end"); + CHECK(star_runs[0].second == size_t(3)); } // BMP pictograph + VS16 + ZWJ + astral emoji (❤️‍🔥 = U+2764 U+FE0F @@ -1188,15 +1231,17 @@ namespace tut // range, but the shaping detector must still pick up the whole sequence // so HarfBuzz gets to compose the "heart on fire" glyph instead of // leaving ❤ and 🔥 as disjoint codepoints. - template<> template<> - void llstring_utf_object_t::test<99>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: heart-on-fire is one cluster though its base is in the BMP", "[LLStringUTF]") { std::u32string ws = { (llwchar)0x2764, (llwchar)0xFE0F, (llwchar)0x200D, (llwchar)0x1F525 }; auto runs = wstring_find_emoji_clusters(ws); - ensure_equals("bmp zwj runs", runs.size(), size_t(1)); - ensure_equals("bmp zwj begin", runs[0].first, size_t(0)); - ensure_equals("bmp zwj end", runs[0].second, size_t(4)); + UNSCOPED_INFO("bmp zwj runs"); + REQUIRE(runs.size() == size_t(1)); + UNSCOPED_INFO("bmp zwj begin"); + CHECK(runs[0].first == size_t(0)); + UNSCOPED_INFO("bmp zwj end"); + CHECK(runs[0].second == size_t(4)); } // --------------------------------------------------------------- @@ -1206,14 +1251,16 @@ namespace tut // Forward step must move exactly one codepoint through ASCII and skip // past entire emoji clusters in one move so the caret never lands mid- // ZWJ-family. - template<> template<> - void llstring_utf_object_t::test<100>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: stepping forward crosses one character or one whole cluster", "[LLStringUTF]") { // Pure ASCII — one codepoint per step, clamped at size. std::u32string ascii = { (llwchar)'a', (llwchar)'b', (llwchar)'c' }; - ensure_equals("ascii 0->1", wstring_step_grapheme_forward(ascii, 0), size_t(1)); - ensure_equals("ascii 2->3", wstring_step_grapheme_forward(ascii, 2), size_t(3)); - ensure_equals("ascii at end stays", wstring_step_grapheme_forward(ascii, 3), size_t(3)); + UNSCOPED_INFO("ascii 0->1"); + CHECK(wstring_step_grapheme_forward(ascii, 0) == size_t(1)); + UNSCOPED_INFO("ascii 2->3"); + CHECK(wstring_step_grapheme_forward(ascii, 2) == size_t(3)); + UNSCOPED_INFO("ascii at end stays"); + CHECK(wstring_step_grapheme_forward(ascii, 3) == size_t(3)); // ZWJ family in the middle: H, 👨, ZWJ, 👩, ZWJ, 👧, i // 0 1 2 3 4 5 6 @@ -1222,46 +1269,50 @@ namespace tut (llwchar)0x1F469, (llwchar)0x200D, (llwchar)0x1F467, (llwchar)'i' }; - ensure_equals("H->fam start", wstring_step_grapheme_forward(fam, 0), size_t(1)); - ensure_equals("fam start jumps past cluster", - wstring_step_grapheme_forward(fam, 1), size_t(6)); - ensure_equals("from mid-cluster snaps past", - wstring_step_grapheme_forward(fam, 3), size_t(6)); - ensure_equals("fam end -> 'i'", wstring_step_grapheme_forward(fam, 6), size_t(7)); + UNSCOPED_INFO("H->fam start"); + CHECK(wstring_step_grapheme_forward(fam, 0) == size_t(1)); + UNSCOPED_INFO("fam start jumps past cluster"); + CHECK(wstring_step_grapheme_forward(fam, 1) == size_t(6)); + UNSCOPED_INFO("from mid-cluster snaps past"); + CHECK(wstring_step_grapheme_forward(fam, 3) == size_t(6)); + UNSCOPED_INFO("fam end -> 'i'"); + CHECK(wstring_step_grapheme_forward(fam, 6) == size_t(7)); // Regional indicator flag: US = U+1F1FA U+1F1F8. One step covers both. std::u32string flag = { (llwchar)0x1F1FA, (llwchar)0x1F1F8 }; - ensure_equals("flag 0->past both", - wstring_step_grapheme_forward(flag, 0), size_t(2)); + UNSCOPED_INFO("flag 0->past both"); + CHECK(wstring_step_grapheme_forward(flag, 0) == size_t(2)); } // Backward step is the mirror: one codepoint through ASCII, snap to the // start of any emoji cluster we'd otherwise land inside. - template<> template<> - void llstring_utf_object_t::test<101>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: stepping back crosses one character or one whole cluster", "[LLStringUTF]") { std::u32string ascii = { (llwchar)'a', (llwchar)'b', (llwchar)'c' }; - ensure_equals("ascii 3->2", wstring_step_grapheme_backward(ascii, 3), size_t(2)); - ensure_equals("ascii 1->0", wstring_step_grapheme_backward(ascii, 1), size_t(0)); - ensure_equals("ascii at 0 stays", wstring_step_grapheme_backward(ascii, 0), size_t(0)); + UNSCOPED_INFO("ascii 3->2"); + CHECK(wstring_step_grapheme_backward(ascii, 3) == size_t(2)); + UNSCOPED_INFO("ascii 1->0"); + CHECK(wstring_step_grapheme_backward(ascii, 1) == size_t(0)); + UNSCOPED_INFO("ascii at 0 stays"); + CHECK(wstring_step_grapheme_backward(ascii, 0) == size_t(0)); std::u32string fam = { (llwchar)'H', (llwchar)0x1F468, (llwchar)0x200D, (llwchar)0x1F469, (llwchar)0x200D, (llwchar)0x1F467, (llwchar)'i' }; - ensure_equals("end of string -> 'i' start", - wstring_step_grapheme_backward(fam, 7), size_t(6)); - ensure_equals("'i' start snaps back to cluster start", - wstring_step_grapheme_backward(fam, 6), size_t(1)); - ensure_equals("mid-cluster snaps to cluster start", - wstring_step_grapheme_backward(fam, 4), size_t(1)); - ensure_equals("cluster start -> 'H'", - wstring_step_grapheme_backward(fam, 1), size_t(0)); + UNSCOPED_INFO("end of string -> 'i' start"); + CHECK(wstring_step_grapheme_backward(fam, 7) == size_t(6)); + UNSCOPED_INFO("'i' start snaps back to cluster start"); + CHECK(wstring_step_grapheme_backward(fam, 6) == size_t(1)); + UNSCOPED_INFO("mid-cluster snaps to cluster start"); + CHECK(wstring_step_grapheme_backward(fam, 4) == size_t(1)); + UNSCOPED_INFO("cluster start -> 'H'"); + CHECK(wstring_step_grapheme_backward(fam, 1) == size_t(0)); std::u32string flag = { (llwchar)0x1F1FA, (llwchar)0x1F1F8 }; - ensure_equals("flag end -> 0 (whole flag is one cluster)", - wstring_step_grapheme_backward(flag, 2), size_t(0)); + UNSCOPED_INFO("flag end -> 0 (whole flag is one cluster)"); + CHECK(wstring_step_grapheme_backward(flag, 2) == size_t(0)); } // --------------------------------------------------------------- @@ -1270,56 +1321,55 @@ namespace tut // emoji-cluster start and flags still pair up. // --------------------------------------------------------------- - template<> template<> - void llstring_utf_object_t::test<102>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: LLStringOps::isPictographBase is Extended_Pictographic and flags", "[LLStringUTF]") { // Astral emoji: both predicates accept. - ensure("isPictographBase rocket", - LLStringOps::isPictographBase((llwchar)0x1F680)); - ensure("isPictographBase fire", - LLStringOps::isPictographBase((llwchar)0x1F525)); + UNSCOPED_INFO("isPictographBase rocket"); + CHECK(LLStringOps::isPictographBase((llwchar)0x1F680)); + UNSCOPED_INFO("isPictographBase fire"); + CHECK(LLStringOps::isPictographBase((llwchar)0x1F525)); // BMP pictographs that isEmoji rejects but isPictographBase accepts. - ensure("isPictographBase heart", - LLStringOps::isPictographBase((llwchar)0x2764)); - ensure("isPictographBase copyright", - LLStringOps::isPictographBase((llwchar)0x00A9)); - ensure("isPictographBase registered", - LLStringOps::isPictographBase((llwchar)0x00AE)); + UNSCOPED_INFO("isPictographBase heart"); + CHECK(LLStringOps::isPictographBase((llwchar)0x2764)); + UNSCOPED_INFO("isPictographBase copyright"); + CHECK(LLStringOps::isPictographBase((llwchar)0x00A9)); + UNSCOPED_INFO("isPictographBase registered"); + CHECK(LLStringOps::isPictographBase((llwchar)0x00AE)); // Plain ASCII / digits never qualify. - ensure("not isPictographBase 'A'", - !LLStringOps::isPictographBase((llwchar)'A')); - ensure("not isPictographBase '0'", - !LLStringOps::isPictographBase((llwchar)'0')); + UNSCOPED_INFO("not isPictographBase 'A'"); + CHECK_FALSE(LLStringOps::isPictographBase((llwchar)'A')); + UNSCOPED_INFO("not isPictographBase '0'"); + CHECK_FALSE(LLStringOps::isPictographBase((llwchar)'0')); // ZWJ and VS-16 are extenders, not bases. - ensure("not isPictographBase ZWJ", - !LLStringOps::isPictographBase((llwchar)0x200D)); - ensure("not isPictographBase VS-16", - !LLStringOps::isPictographBase((llwchar)0xFE0F)); - ensure("not isPictographBase U+20000", - !LLStringOps::isPictographBase((llwchar)0x20000)); - ensure("not isPictographBase U+1FFF", - !LLStringOps::isPictographBase((llwchar)0x1FFF)); - ensure("not isPictographBase U+3300 boundary", - !LLStringOps::isPictographBase((llwchar)0x3300)); + UNSCOPED_INFO("not isPictographBase ZWJ"); + CHECK_FALSE(LLStringOps::isPictographBase((llwchar)0x200D)); + UNSCOPED_INFO("not isPictographBase VS-16"); + CHECK_FALSE(LLStringOps::isPictographBase((llwchar)0xFE0F)); + UNSCOPED_INFO("not isPictographBase U+20000"); + CHECK_FALSE(LLStringOps::isPictographBase((llwchar)0x20000)); + UNSCOPED_INFO("not isPictographBase U+1FFF"); + CHECK_FALSE(LLStringOps::isPictographBase((llwchar)0x1FFF)); + UNSCOPED_INFO("not isPictographBase U+3300 boundary"); + CHECK_FALSE(LLStringOps::isPictographBase((llwchar)0x3300)); // A flag is two regional indicators and nothing else. Unicode does // not call them pictographic, so they are named here separately; // reading Extended_Pictographic alone would stop flags clustering. - ensure("isPictographBase regional indicator", - LLStringOps::isPictographBase((llwchar)0x1F1FA)); + UNSCOPED_INFO("isPictographBase regional indicator"); + CHECK(LLStringOps::isPictographBase((llwchar)0x1F1FA)); // The old range ran from U+2000 to U+3300, which swept up punctuation, // maths, and every kana and Hangul jamo along the way. - ensure("not isPictographBase EN QUAD", - !LLStringOps::isPictographBase((llwchar)0x2000)); - ensure("not isPictographBase summation", - !LLStringOps::isPictographBase((llwchar)0x2211)); - ensure("not isPictographBase hiragana", - !LLStringOps::isPictographBase((llwchar)0x3042)); - ensure("not isPictographBase katakana", - !LLStringOps::isPictographBase((llwchar)0x30A2)); - ensure("not isPictographBase Hangul jamo", - !LLStringOps::isPictographBase((llwchar)0x3131)); + UNSCOPED_INFO("not isPictographBase EN QUAD"); + CHECK_FALSE(LLStringOps::isPictographBase((llwchar)0x2000)); + UNSCOPED_INFO("not isPictographBase summation"); + CHECK_FALSE(LLStringOps::isPictographBase((llwchar)0x2211)); + UNSCOPED_INFO("not isPictographBase hiragana"); + CHECK_FALSE(LLStringOps::isPictographBase((llwchar)0x3042)); + UNSCOPED_INFO("not isPictographBase katakana"); + CHECK_FALSE(LLStringOps::isPictographBase((llwchar)0x30A2)); + UNSCOPED_INFO("not isPictographBase Hangul jamo"); + CHECK_FALSE(LLStringOps::isPictographBase((llwchar)0x3131)); } // --------------------------------------------------------------- @@ -1329,8 +1379,7 @@ namespace tut // wstring_find_emoji_clusters skips. // --------------------------------------------------------------- - template<> template<> - void llstring_utf_object_t::test<103>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: wstring_emoji_range_at gives the cluster or pictograph at a position", "[LLStringUTF]") { // ZWJ family "H 👨 ZWJ 👩 ZWJ 👧 i" — cluster spans [1, 6). std::u32string fam = { (llwchar)'H', @@ -1341,46 +1390,62 @@ namespace tut auto on_lead = wstring_emoji_range_at(fam, 1); auto inside = wstring_emoji_range_at(fam, 3); auto on_end = wstring_emoji_range_at(fam, 6); - ensure_equals("fam pos==first.first", on_lead.first, size_t(1)); - ensure_equals("fam pos==first.second", on_lead.second, size_t(6)); - ensure_equals("fam mid.first", inside.first, size_t(1)); - ensure_equals("fam mid.second", inside.second, size_t(6)); + UNSCOPED_INFO("fam pos==first.first"); + CHECK(on_lead.first == size_t(1)); + UNSCOPED_INFO("fam pos==first.second"); + CHECK(on_lead.second == size_t(6)); + UNSCOPED_INFO("fam mid.first"); + CHECK(inside.first == size_t(1)); + UNSCOPED_INFO("fam mid.second"); + CHECK(inside.second == size_t(6)); // pos == run.second is past the cluster — falls through to the // codepoint there ('i', non-emoji), so empty range. - ensure_equals("fam past-end empty", on_end.first, on_end.second); + UNSCOPED_INFO("fam past-end empty"); + CHECK(on_end.first == on_end.second); // Plain ASCII: empty range everywhere. std::u32string ascii = { (llwchar)'a', (llwchar)'b', (llwchar)'c' }; auto a0 = wstring_emoji_range_at(ascii, 0); auto a2 = wstring_emoji_range_at(ascii, 2); - ensure_equals("ascii 0 empty", a0.first, a0.second); - ensure_equals("ascii 2 empty", a2.first, a2.second); + UNSCOPED_INFO("ascii 0 empty"); + CHECK(a0.first == a0.second); + UNSCOPED_INFO("ascii 2 empty"); + CHECK(a2.first == a2.second); // Single-codepoint emoji 😀 (U+1F600): not in cluster list, but // the helper synthesises [pos, pos+1). std::u32string lone = { (llwchar)0x1F600 }; auto le = wstring_emoji_range_at(lone, 0); - ensure_equals("lone emoji.first", le.first, size_t(0)); - ensure_equals("lone emoji.second", le.second, size_t(1)); + UNSCOPED_INFO("lone emoji.first"); + CHECK(le.first == size_t(0)); + UNSCOPED_INFO("lone emoji.second"); + CHECK(le.second == size_t(1)); // Out-of-bounds and exact-end positions are empty. auto at_end = wstring_emoji_range_at(ascii, 3); auto past_end = wstring_emoji_range_at(ascii, 99); - ensure_equals("ascii at-end empty", at_end.first, at_end.second); - ensure_equals("ascii oob empty", past_end.first, past_end.second); + UNSCOPED_INFO("ascii at-end empty"); + CHECK(at_end.first == at_end.second); + UNSCOPED_INFO("ascii oob empty"); + CHECK(past_end.first == past_end.second); // Empty wstring: empty range. auto empty = wstring_emoji_range_at(std::u32string(), 0); - ensure_equals("empty wstr empty", empty.first, empty.second); + UNSCOPED_INFO("empty wstr empty"); + CHECK(empty.first == empty.second); // Flag pair 🇺🇸 — one cluster spanning [0, 2). std::u32string flag = { (llwchar)0x1F1FA, (llwchar)0x1F1F8 }; auto f0 = wstring_emoji_range_at(flag, 0); auto f1 = wstring_emoji_range_at(flag, 1); - ensure_equals("flag at lead.first", f0.first, size_t(0)); - ensure_equals("flag at lead.second", f0.second, size_t(2)); - ensure_equals("flag mid.first", f1.first, size_t(0)); - ensure_equals("flag mid.second", f1.second, size_t(2)); + UNSCOPED_INFO("flag at lead.first"); + CHECK(f0.first == size_t(0)); + UNSCOPED_INFO("flag at lead.second"); + CHECK(f0.second == size_t(2)); + UNSCOPED_INFO("flag mid.first"); + CHECK(f1.first == size_t(0)); + UNSCOPED_INFO("flag mid.second"); + CHECK(f1.second == size_t(2)); // BMP pictographs — the orthodox cross bug. isPictographBase accepts // U+2000..U+33FF plus © U+00A9 and ® U+00AE; isEmoji rejects them. @@ -1388,8 +1453,10 @@ namespace tut auto check_bmp = [](llwchar cp, const char* tag) { std::u32string ws { cp }; auto r = wstring_emoji_range_at(ws, 0); - ensure_equals(std::string(tag) + ".first", r.first, size_t(0)); - ensure_equals(std::string(tag) + ".second", r.second, size_t(1)); + UNSCOPED_INFO(std::string(tag) + ".first"); + REQUIRE(r.first == size_t(0)); + UNSCOPED_INFO(std::string(tag) + ".second"); + REQUIRE(r.second == size_t(1)); }; check_bmp((llwchar)0x2626, "orthodox cross"); check_bmp((llwchar)0x00A9, "copyright"); @@ -1403,7 +1470,8 @@ namespace tut auto check_no_range = [](llwchar cp, const char* tag) { std::u32string ws { cp }; auto r = wstring_emoji_range_at(ws, 0); - ensure_equals(std::string(tag) + " empty", r.first, r.second); + UNSCOPED_INFO(std::string(tag) + " empty"); + REQUIRE(r.first == r.second); }; check_no_range((llwchar)0x200D, "bare ZWJ"); check_no_range((llwchar)0xFE0F, "bare VS-16"); @@ -1423,12 +1491,18 @@ namespace tut auto tf0 = wstring_emoji_range_at(two_flags, 0); auto tf2 = wstring_emoji_range_at(two_flags, 2); auto tf3 = wstring_emoji_range_at(two_flags, 3); - ensure_equals("two_flags[0].first", tf0.first, size_t(0)); - ensure_equals("two_flags[0].second", tf0.second, size_t(2)); - ensure_equals("two_flags[2].first", tf2.first, size_t(2)); - ensure_equals("two_flags[2].second", tf2.second, size_t(4)); - ensure_equals("two_flags mid 2nd flag", tf3.first, size_t(2)); - ensure_equals("two_flags mid 2nd second", tf3.second, size_t(4)); + UNSCOPED_INFO("two_flags[0].first"); + CHECK(tf0.first == size_t(0)); + UNSCOPED_INFO("two_flags[0].second"); + CHECK(tf0.second == size_t(2)); + UNSCOPED_INFO("two_flags[2].first"); + CHECK(tf2.first == size_t(2)); + UNSCOPED_INFO("two_flags[2].second"); + CHECK(tf2.second == size_t(4)); + UNSCOPED_INFO("two_flags mid 2nd flag"); + CHECK(tf3.first == size_t(2)); + UNSCOPED_INFO("two_flags mid 2nd second"); + CHECK(tf3.second == size_t(4)); // Mixed "A 🚀 B": ASCII positions return empty, the rocket position // returns its [pos, pos+1) range via the single-codepoint fallback. @@ -1436,10 +1510,14 @@ namespace tut auto m0 = wstring_emoji_range_at(mixed, 0); auto m1 = wstring_emoji_range_at(mixed, 1); auto m2 = wstring_emoji_range_at(mixed, 2); - ensure_equals("mixed A empty", m0.first, m0.second); - ensure_equals("mixed rocket.first", m1.first, size_t(1)); - ensure_equals("mixed rocket.second", m1.second, size_t(2)); - ensure_equals("mixed B empty", m2.first, m2.second); + UNSCOPED_INFO("mixed A empty"); + CHECK(m0.first == m0.second); + UNSCOPED_INFO("mixed rocket.first"); + CHECK(m1.first == size_t(1)); + UNSCOPED_INFO("mixed rocket.second"); + CHECK(m1.second == size_t(2)); + UNSCOPED_INFO("mixed B empty"); + CHECK(m2.first == m2.second); } // --------------------------------------------------------------- @@ -1451,18 +1529,17 @@ namespace tut // the contract so neither group regresses. // --------------------------------------------------------------- - template<> template<> - void llstring_utf_object_t::test<104>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: grapheme alignment moves only positions inside a cluster", "[LLStringUTF]") { // ASCII: every position is already on a boundary, so both halves // are identity functions across the entire range. std::u32string ascii = { (llwchar)'a', (llwchar)'b', (llwchar)'c' }; for (size_t p = 0; p <= ascii.size(); ++p) { - ensure_equals("ascii align_backward", - wstring_grapheme_align_backward(ascii, p), p); - ensure_equals("ascii align_forward", - wstring_grapheme_align_forward(ascii, p), p); + UNSCOPED_INFO("ascii align_backward"); + REQUIRE(wstring_grapheme_align_backward(ascii, p) == p); + UNSCOPED_INFO("ascii align_forward"); + REQUIRE(wstring_grapheme_align_forward(ascii, p) == p); } // ZWJ family "H 👨 ZWJ 👩 ZWJ 👧 i" — cluster spans [1, 6). @@ -1474,43 +1551,43 @@ namespace tut // Endpoint short-circuits: pos == 0 stays 0 (backward); pos >= size // stays size (forward) regardless of trailing emoji. - ensure_equals("fam align_backward(0)", - wstring_grapheme_align_backward(fam, 0), size_t(0)); - ensure_equals("fam align_backward(size)", - wstring_grapheme_align_backward(fam, fam.size()), fam.size()); - ensure_equals("fam align_forward(size)", - wstring_grapheme_align_forward(fam, fam.size()), fam.size()); - ensure_equals("fam align_forward(99)", - wstring_grapheme_align_forward(fam, 99), fam.size()); + UNSCOPED_INFO("fam align_backward(0)"); + CHECK(wstring_grapheme_align_backward(fam, 0) == size_t(0)); + UNSCOPED_INFO("fam align_backward(size)"); + REQUIRE(wstring_grapheme_align_backward(fam, fam.size()) == fam.size()); + UNSCOPED_INFO("fam align_forward(size)"); + REQUIRE(wstring_grapheme_align_forward(fam, fam.size()) == fam.size()); + UNSCOPED_INFO("fam align_forward(99)"); + REQUIRE(wstring_grapheme_align_forward(fam, 99) == fam.size()); // pos == run.first — strictly outside, returned unchanged. This is // the contract LLTextEditor::prevWordPos relies on: a word-walk // that already landed on a cluster start is fine and must not be // pulled backward into something else. - ensure_equals("fam align_backward(1)", - wstring_grapheme_align_backward(fam, 1), size_t(1)); - ensure_equals("fam align_forward(1)", - wstring_grapheme_align_forward(fam, 1), size_t(1)); + UNSCOPED_INFO("fam align_backward(1)"); + CHECK(wstring_grapheme_align_backward(fam, 1) == size_t(1)); + UNSCOPED_INFO("fam align_forward(1)"); + CHECK(wstring_grapheme_align_forward(fam, 1) == size_t(1)); // pos == run.second — also strictly outside, unchanged. - ensure_equals("fam align_backward(6)", - wstring_grapheme_align_backward(fam, 6), size_t(6)); - ensure_equals("fam align_forward(6)", - wstring_grapheme_align_forward(fam, 6), size_t(6)); + UNSCOPED_INFO("fam align_backward(6)"); + CHECK(wstring_grapheme_align_backward(fam, 6) == size_t(6)); + UNSCOPED_INFO("fam align_forward(6)"); + CHECK(wstring_grapheme_align_forward(fam, 6) == size_t(6)); // pos strictly inside [2..5] — backward snaps to 1, forward to 6. for (size_t p = 2; p <= 5; ++p) { - ensure_equals("fam mid align_backward", - wstring_grapheme_align_backward(fam, p), size_t(1)); - ensure_equals("fam mid align_forward", - wstring_grapheme_align_forward(fam, p), size_t(6)); + UNSCOPED_INFO("fam mid align_backward"); + REQUIRE(wstring_grapheme_align_backward(fam, p) == size_t(1)); + UNSCOPED_INFO("fam mid align_forward"); + REQUIRE(wstring_grapheme_align_forward(fam, p) == size_t(6)); } // Outside the cluster — surrounding ASCII positions unchanged. - ensure_equals("fam align_backward(7)", - wstring_grapheme_align_backward(fam, 7), size_t(7)); - ensure_equals("fam align_forward(7)", - wstring_grapheme_align_forward(fam, 7), size_t(7)); + UNSCOPED_INFO("fam align_backward(7)"); + CHECK(wstring_grapheme_align_backward(fam, 7) == size_t(7)); + UNSCOPED_INFO("fam align_forward(7)"); + CHECK(wstring_grapheme_align_forward(fam, 7) == size_t(7)); // Two disjoint clusters in one string — flag at [1,3), family at // [4,9). Inside-the-second-cluster lookups must reach the second @@ -1525,20 +1602,20 @@ namespace tut (llwchar)0x1F467, (llwchar)'!' }; // Flag interior (pos == 2) → flag.first 1. - ensure_equals("two flag mid backward", - wstring_grapheme_align_backward(two, 2), size_t(1)); - ensure_equals("two flag mid forward", - wstring_grapheme_align_forward(two, 2), size_t(3)); + UNSCOPED_INFO("two flag mid backward"); + CHECK(wstring_grapheme_align_backward(two, 2) == size_t(1)); + UNSCOPED_INFO("two flag mid forward"); + CHECK(wstring_grapheme_align_forward(two, 2) == size_t(3)); // Family interior (pos == 6) → family bounds [4, 9). - ensure_equals("two fam mid backward", - wstring_grapheme_align_backward(two, 6), size_t(4)); - ensure_equals("two fam mid forward", - wstring_grapheme_align_forward(two, 6), size_t(9)); + UNSCOPED_INFO("two fam mid backward"); + CHECK(wstring_grapheme_align_backward(two, 6) == size_t(4)); + UNSCOPED_INFO("two fam mid forward"); + CHECK(wstring_grapheme_align_forward(two, 6) == size_t(9)); // Between the two clusters (the space at pos 3) — unchanged. - ensure_equals("two between backward", - wstring_grapheme_align_backward(two, 3), size_t(3)); - ensure_equals("two between forward", - wstring_grapheme_align_forward(two, 3), size_t(3)); + UNSCOPED_INFO("two between backward"); + CHECK(wstring_grapheme_align_backward(two, 3) == size_t(3)); + UNSCOPED_INFO("two between forward"); + CHECK(wstring_grapheme_align_forward(two, 3) == size_t(3)); } // --------------------------------------------------------------- @@ -1552,49 +1629,52 @@ namespace tut // synthetic singleton range). // --------------------------------------------------------------- - template<> template<> - void llstring_utf_object_t::test<105>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: grapheme stepping and emoji ranges in edge cases", "[LLStringUTF]") { // Empty wstring — no grapheme moves possible. std::u32string empty; - ensure_equals("empty step_forward", wstring_step_grapheme_forward(empty, 0), size_t(0)); - ensure_equals("empty step_backward", wstring_step_grapheme_backward(empty, 0), size_t(0)); + UNSCOPED_INFO("empty step_forward"); + CHECK(wstring_step_grapheme_forward(empty, 0) == size_t(0)); + UNSCOPED_INFO("empty step_backward"); + CHECK(wstring_step_grapheme_backward(empty, 0) == size_t(0)); // Out-of-bounds position — both directions clamp into [0, size], as the // header says. The old walker returned pos-1 here, handing a caller an // index past the end of its own string. std::u32string ascii = { (llwchar)'a', (llwchar)'b' }; - ensure_equals("oob step_forward", wstring_step_grapheme_forward(ascii, 99), size_t(2)); - ensure_equals("oob step_backward", wstring_step_grapheme_backward(ascii, 99), size_t(2)); + UNSCOPED_INFO("oob step_forward"); + CHECK(wstring_step_grapheme_forward(ascii, 99) == size_t(2)); + UNSCOPED_INFO("oob step_backward"); + CHECK(wstring_step_grapheme_backward(ascii, 99) == size_t(2)); // Cluster at start of string — backward from inside or just-past // snaps to 0 (cluster.first). std::u32string lead = { (llwchar)0x1F1FA, (llwchar)0x1F1F8, (llwchar)'X' }; - ensure_equals("lead flag step_forward(0)", - wstring_step_grapheme_forward(lead, 0), size_t(2)); - ensure_equals("lead flag step_backward(2)", - wstring_step_grapheme_backward(lead, 2), size_t(0)); + UNSCOPED_INFO("lead flag step_forward(0)"); + CHECK(wstring_step_grapheme_forward(lead, 0) == size_t(2)); + UNSCOPED_INFO("lead flag step_backward(2)"); + CHECK(wstring_step_grapheme_backward(lead, 2) == size_t(0)); // Cluster at end of string — forward from inside snaps to size. std::u32string trail = { (llwchar)'X', (llwchar)0x1F1FA, (llwchar)0x1F1F8 }; - ensure_equals("trail flag step_forward(1)", - wstring_step_grapheme_forward(trail, 1), size_t(3)); - ensure_equals("trail flag step_backward(size)", - wstring_step_grapheme_backward(trail, trail.size()), size_t(1)); + UNSCOPED_INFO("trail flag step_forward(1)"); + CHECK(wstring_step_grapheme_forward(trail, 1) == size_t(3)); + UNSCOPED_INFO("trail flag step_backward(size)"); + REQUIRE(wstring_step_grapheme_backward(trail, trail.size()) == size_t(1)); // Two clusters back-to-back with no separator: step_forward from // inside cluster A must land at A.end (which is also B.first), not // skip into cluster B. std::u32string two_flags = { (llwchar)0x1F1FA, (llwchar)0x1F1F8, (llwchar)0x1F1EF, (llwchar)0x1F1F5 }; - ensure_equals("two flags forward", - wstring_step_grapheme_forward(two_flags, 0), size_t(2)); - ensure_equals("two flags forward 2nd", - wstring_step_grapheme_forward(two_flags, 2), size_t(4)); - ensure_equals("two flags backward", - wstring_step_grapheme_backward(two_flags, 4), size_t(2)); - ensure_equals("two flags backward 1st", - wstring_step_grapheme_backward(two_flags, 2), size_t(0)); + UNSCOPED_INFO("two flags forward"); + CHECK(wstring_step_grapheme_forward(two_flags, 0) == size_t(2)); + UNSCOPED_INFO("two flags forward 2nd"); + CHECK(wstring_step_grapheme_forward(two_flags, 2) == size_t(4)); + UNSCOPED_INFO("two flags backward"); + CHECK(wstring_step_grapheme_backward(two_flags, 4) == size_t(2)); + UNSCOPED_INFO("two flags backward 1st"); + CHECK(wstring_step_grapheme_backward(two_flags, 2) == size_t(0)); // emoji_range_at on a tag char inside a subdivision flag — the // hit-test caller wants the whole flag's range so the tooltip key @@ -1610,14 +1690,18 @@ namespace tut for (size_t p = 1; p < 8; ++p) { auto r = wstring_emoji_range_at(subdiv, p); - ensure_equals("subdiv begin", r.first, size_t(1)); - ensure_equals("subdiv end", r.second, size_t(8)); + UNSCOPED_INFO("subdiv begin"); + REQUIRE(r.first == size_t(1)); + UNSCOPED_INFO("subdiv end"); + REQUIRE(r.second == size_t(8)); } // pos on the surrounding ASCII falls outside the cluster. auto pre = wstring_emoji_range_at(subdiv, 0); auto post = wstring_emoji_range_at(subdiv, 8); - ensure_equals("subdiv pre empty", pre.first, pre.second); - ensure_equals("subdiv post empty", post.first, post.second); + UNSCOPED_INFO("subdiv pre empty"); + CHECK(pre.first == pre.second); + UNSCOPED_INFO("subdiv post empty"); + CHECK(post.first == post.second); } // --------------------------------------------------------------- @@ -1629,8 +1713,7 @@ namespace tut // (e.g. a bug fix in the loop) needs to fail loudly here. // --------------------------------------------------------------- - template<> template<> - void llstring_utf_object_t::test<106>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_emoji_range_at answers the same given the cluster list", "[LLStringUTF]") { // Same mixed-content string used in test<104>: ASCII + flag + // space + ZWJ family + ASCII. Two disjoint clusters at [1,3) and @@ -1645,7 +1728,8 @@ namespace tut (llwchar)'!' }; const std::string utf8 = to_utf8(two); const auto clusters = utf8str_find_emoji_clusters(utf8); - ensure_equals("two clusters", clusters.size(), size_t(2)); + UNSCOPED_INFO("two clusters"); + REQUIRE(clusters.size() == size_t(2)); // Handing the list in has to give the same answer as building it // inside, at every position including the ones inside a character. @@ -1653,161 +1737,295 @@ namespace tut { auto r1 = utf8str_emoji_range_at(utf8, p); auto r2 = utf8str_emoji_range_at(utf8, p, clusters); - ensure_equals("range_at first", r1.first, r2.first); - ensure_equals("range_at second", r1.second, r2.second); + UNSCOPED_INFO("range_at first"); + REQUIRE(r1.first == r2.first); + UNSCOPED_INFO("range_at second"); + REQUIRE(r1.second == r2.second); } // Empty string -- pinning that the with-clusters overload handles a // degenerate empty cluster vector identically to the no-clusters form. const EmojiClusterList no_clusters; - ensure_equals("empty range_at first overload", - utf8str_emoji_range_at(std::string_view(), 0).first, + UNSCOPED_INFO("empty range_at first overload"); + CHECK(utf8str_emoji_range_at(std::string_view(), 0).first == utf8str_emoji_range_at(std::string_view(), 0, no_clusters).first); } + // utf8str_substChar replaces every occurrence of a code point (incl. + // astrals) with another code point, routing through std::u32string so the + // byte-length change is handled for us. + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_substChar replaces every occurrence of a code point", "[LLStringUTF]") + { + const std::string in = "A\xF0\x9F\x9A\x80" "B\xF0\x9F\x9A\x80" "C"; + const std::string out = utf8str_substChar(in, (llwchar)0x1F680, (llwchar)'X'); + UNSCOPED_INFO("substChar astral -> ASCII"); + CHECK(out == std::string("AXBXC")); + + const std::string in2 = "A\xE6\x97\xA5" "B"; + const std::string out2 = utf8str_substChar(in2, (llwchar)0x65E5, (llwchar)'.'); + UNSCOPED_INFO("substChar BMP -> ASCII"); + CHECK(out2 == std::string("A.B")); + } + + // utf8str_makeASCII replaces every non-ASCII llwchar (>0x7F) with '?' + // before serializing back to utf-8. + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_makeASCII turns each non-ASCII character into a question mark", "[LLStringUTF]") + { + const std::string mixed = "A\xE6\x97\xA5" "B\xF0\x9F\x9A\x80" "C"; + const std::string expected = "A?B?C"; + UNSCOPED_INFO("utf8str_makeASCII"); + CHECK(utf8str_makeASCII(mixed) == expected); + UNSCOPED_INFO("utf8str_makeASCII ascii passthrough"); + CHECK(utf8str_makeASCII("Hello") == std::string("Hello")); + } + + // mbcsstring_makeASCII is byte-level: every byte >0x7F becomes '?'. So a + // 3-byte UTF-8 character yields three '?' rather than one. + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: mbcsstring_makeASCII turns each non-ASCII byte into a question mark", "[LLStringUTF]") + { + const std::string mixed = "A\xE6\x97\xA5" "B"; + const std::string expected = "A???B"; + UNSCOPED_INFO("mbcsstring_makeASCII"); + CHECK(mbcsstring_makeASCII(mixed) == expected); + } + + // utf8str_removeCRLF strips CR (0x0D) only — LF is preserved. + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_removeCRLF strips CR and keeps LF", "[LLStringUTF]") + { + UNSCOPED_INFO("removeCRLF crlf"); + CHECK(utf8str_removeCRLF("a\r\nb") == std::string("a\nb")); + UNSCOPED_INFO("removeCRLF cr"); + CHECK(utf8str_removeCRLF("a\rb") == std::string("ab")); + UNSCOPED_INFO("removeCRLF lf"); + CHECK(utf8str_removeCRLF("a\nb") == std::string("a\nb")); + UNSCOPED_INFO("removeCRLF empty"); + CHECK(utf8str_removeCRLF("") == std::string()); + } + + // utf8str_split splits at the last split-token at or before maxlen bytes, + // falling back to a hard byte cut when no token is in range. Non-final + // chunks retain the trailing split-token (current implementation). + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_split splits at the last split-token at or before maxlen bytes", "[LLStringUTF]") + { + { + std::list out; + utf8str_split(out, "Hello there world", 10, ' '); + UNSCOPED_INFO("split 3 parts"); + REQUIRE(out.size() == size_t(3)); + auto it = out.begin(); + UNSCOPED_INFO("split[0]"); + CHECK(*it++ == std::string("Hello")); + UNSCOPED_INFO("split[1]"); + CHECK(*it++ == std::string("there ")); + UNSCOPED_INFO("split[2]"); + CHECK(*it == std::string("world")); + } + { + std::list out; + utf8str_split(out, "short", 10, ' '); + UNSCOPED_INFO("split single"); + REQUIRE(out.size() == size_t(1)); + UNSCOPED_INFO("split single val"); + CHECK(out.front() == std::string("short")); + } + } + + // wchar_utf8_preview formats the code point in hex, plus (when multi-byte) + // the decoded UTF-8 byte sequence also in hex. + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: wchar_utf8_preview gives the code point and its UTF-8 bytes in hex", "[LLStringUTF]") + { + UNSCOPED_INFO("preview ASCII"); + CHECK(wchar_utf8_preview((llwchar)'A') == std::string("41")); + // é = U+00E9 -> 0xC3 0xA9 + UNSCOPED_INFO("preview 2-byte"); + CHECK(wchar_utf8_preview((llwchar)0x00E9) == + std::string("E9 [C3, A9]")); + // Rocket = U+1F680 -> 0xF0 0x9F 0x9A 0x80 + UNSCOPED_INFO("preview astral"); + CHECK(wchar_utf8_preview((llwchar)0x1F680) == + std::string("1F680 [F0, 9F, 9A, 80]")); + } + // The grapheme walkers moved from an emoji-only cluster list to full // UAX #29 via ICU. These are the cases the old walker stepped // through one codepoint at a time. - template<> template<> - void llstring_utf_object_t::test<117>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: grapheme stepping follows UAX #29 beyond emoji", "[LLStringUTF]") { // "e" + COMBINING ACUTE, then "x". The base and its mark are one // cluster; the old walker split them. const std::u32string combining = { (llwchar)'e', (llwchar)0x0301, (llwchar)'x' }; - ensure_equals("combining fwd from 0", wstring_step_grapheme_forward(combining, 0), size_t(2)); - ensure_equals("combining back from 2", wstring_step_grapheme_backward(combining, 2), size_t(0)); - ensure_equals("combining align back mid", wstring_grapheme_align_backward(combining, 1), size_t(0)); - ensure_equals("combining align fwd mid", wstring_grapheme_align_forward(combining, 1), size_t(2)); + UNSCOPED_INFO("combining fwd from 0"); + CHECK(wstring_step_grapheme_forward(combining, 0) == size_t(2)); + UNSCOPED_INFO("combining back from 2"); + CHECK(wstring_step_grapheme_backward(combining, 2) == size_t(0)); + UNSCOPED_INFO("combining align back mid"); + CHECK(wstring_grapheme_align_backward(combining, 1) == size_t(0)); + UNSCOPED_INFO("combining align fwd mid"); + CHECK(wstring_grapheme_align_forward(combining, 1) == size_t(2)); // Hangul LVT: HANGUL SYLLABLE inputs as jamo L + V + T are one cluster. const std::u32string hangul = { (llwchar)0x1100, (llwchar)0x1161, (llwchar)0x11A8, (llwchar)'!' }; - ensure_equals("hangul fwd from 0", wstring_step_grapheme_forward(hangul, 0), size_t(3)); - ensure_equals("hangul back from 3", wstring_step_grapheme_backward(hangul, 3), size_t(0)); + UNSCOPED_INFO("hangul fwd from 0"); + CHECK(wstring_step_grapheme_forward(hangul, 0) == size_t(3)); + UNSCOPED_INFO("hangul back from 3"); + CHECK(wstring_step_grapheme_backward(hangul, 3) == size_t(0)); // Regional indicator pairs still pair up, and a third RI starts a new // cluster rather than joining the first two. const std::u32string flags = { (llwchar)0x1F1FA, (llwchar)0x1F1F8, (llwchar)0x1F1FA }; - ensure_equals("flag pair fwd", wstring_step_grapheme_forward(flags, 0), size_t(2)); - ensure_equals("third RI is its own", wstring_step_grapheme_forward(flags, 2), size_t(3)); + UNSCOPED_INFO("flag pair fwd"); + CHECK(wstring_step_grapheme_forward(flags, 0) == size_t(2)); + UNSCOPED_INFO("third RI is its own"); + CHECK(wstring_step_grapheme_forward(flags, 2) == size_t(3)); // ZWJ family stays one cluster, as before. const std::u32string family = { (llwchar)0x1F468, (llwchar)0x200D, (llwchar)0x1F469, (llwchar)0x200D, (llwchar)0x1F467, (llwchar)'!' }; - ensure_equals("zwj family fwd", wstring_step_grapheme_forward(family, 0), size_t(5)); - ensure_equals("zwj family back", wstring_step_grapheme_backward(family, 5), size_t(0)); + UNSCOPED_INFO("zwj family fwd"); + CHECK(wstring_step_grapheme_forward(family, 0) == size_t(5)); + UNSCOPED_INFO("zwj family back"); + CHECK(wstring_step_grapheme_backward(family, 5) == size_t(0)); // GB4 breaks after LF, which is also what bounds the backward scan. const std::u32string lines = { (llwchar)'a', (llwchar)'\n', (llwchar)'e', (llwchar)0x0301 }; - ensure_equals("break after lf", wstring_step_grapheme_backward(lines, 2), size_t(1)); - ensure_equals("cluster after lf", wstring_step_grapheme_backward(lines, 4), size_t(2)); + UNSCOPED_INFO("break after lf"); + CHECK(wstring_step_grapheme_backward(lines, 2) == size_t(1)); + UNSCOPED_INFO("cluster after lf"); + CHECK(wstring_step_grapheme_backward(lines, 4) == size_t(2)); // Degenerate input: empty, and positions past the end clamp. const std::u32string empty; - ensure_equals("empty fwd", wstring_step_grapheme_forward(empty, 0), size_t(0)); - ensure_equals("empty back", wstring_step_grapheme_backward(empty, 0), size_t(0)); - ensure_equals("past end fwd", wstring_step_grapheme_forward(combining, 99), combining.size()); - ensure_equals("past end align", wstring_grapheme_align_forward(combining, 99), combining.size()); + UNSCOPED_INFO("empty fwd"); + CHECK(wstring_step_grapheme_forward(empty, 0) == size_t(0)); + UNSCOPED_INFO("empty back"); + CHECK(wstring_step_grapheme_backward(empty, 0) == size_t(0)); + UNSCOPED_INFO("past end fwd"); + REQUIRE(wstring_step_grapheme_forward(combining, 99) == combining.size()); + UNSCOPED_INFO("past end align"); + REQUIRE(wstring_grapheme_align_forward(combining, 99) == combining.size()); // Stepping forward from every position must strictly advance, and // backward must strictly retreat, or a caret can wedge. for (size_t p = 0; p < family.size(); ++p) { - ensure("fwd advances", wstring_step_grapheme_forward(family, p) > p); + UNSCOPED_INFO("fwd advances"); + REQUIRE(wstring_step_grapheme_forward(family, p) > p); } for (size_t p = 1; p <= family.size(); ++p) { - ensure("back retreats", wstring_step_grapheme_backward(family, p) < p); + UNSCOPED_INFO("back retreats"); + REQUIRE(wstring_step_grapheme_backward(family, p) < p); } } // Word stepping lands on the start of a word, stepping over the whitespace // between. UAX #29 via ICU, replacing an alnum-or-underscore test. - template<> template<> - void llstring_utf_object_t::test<118>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: word stepping lands on the start of each word", "[LLStringUTF]") { const std::u32string two_words = to_u32("foo bar"); - ensure_equals("fwd to next word", wstring_step_word_forward(two_words, 0), size_t(4)); - ensure_equals("fwd from next word", wstring_step_word_forward(two_words, 4), size_t(7)); - ensure_equals("back from end", wstring_step_word_backward(two_words, 7), size_t(4)); - ensure_equals("back over the space", wstring_step_word_backward(two_words, 4), size_t(0)); - ensure_equals("back from mid-word", wstring_step_word_backward(two_words, 5), size_t(4)); + UNSCOPED_INFO("fwd to next word"); + CHECK(wstring_step_word_forward(two_words, 0) == size_t(4)); + UNSCOPED_INFO("fwd from next word"); + CHECK(wstring_step_word_forward(two_words, 4) == size_t(7)); + UNSCOPED_INFO("back from end"); + CHECK(wstring_step_word_backward(two_words, 7) == size_t(4)); + UNSCOPED_INFO("back over the space"); + CHECK(wstring_step_word_backward(two_words, 4) == size_t(0)); + UNSCOPED_INFO("back from mid-word"); + CHECK(wstring_step_word_backward(two_words, 5) == size_t(4)); // A full stop between letters does not split the word -- UAX #29 wants // example.com and 3.14 to hold together (WB6/WB7, FULL STOP is // MidNumLet). So the word here is "foo.bar" and the next one is "baz". const std::u32string dotted = to_u32("foo.bar baz"); - ensure_equals("dot does not split", wstring_step_word_forward(dotted, 0), size_t(8)); - ensure_equals("fwd from inside the dot word", - wstring_step_word_forward(dotted, 3), size_t(8)); + UNSCOPED_INFO("dot does not split"); + CHECK(wstring_step_word_forward(dotted, 0) == size_t(8)); + UNSCOPED_INFO("fwd from inside the dot word"); + CHECK(wstring_step_word_forward(dotted, 3) == size_t(8)); // A comma does separate, and standing on one used to wedge the cursor: // it is neither alnum nor a space, so both of the old loops declined to // move and the caller had to retry from pos+1 (LLLineEditor::removeWord // still carries that workaround). const std::u32string comma = to_u32("foo, bar"); - ensure_equals("fwd stops on the comma", wstring_step_word_forward(comma, 0), size_t(3)); - ensure_equals("fwd off the comma", wstring_step_word_forward(comma, 3), size_t(5)); - ensure("fwd never stands still", wstring_step_word_forward(comma, 3) > size_t(3)); + UNSCOPED_INFO("fwd stops on the comma"); + CHECK(wstring_step_word_forward(comma, 0) == size_t(3)); + UNSCOPED_INFO("fwd off the comma"); + CHECK(wstring_step_word_forward(comma, 3) == size_t(5)); + UNSCOPED_INFO("fwd never stands still"); + CHECK(wstring_step_word_forward(comma, 3) > size_t(3)); // An apostrophe is inside the word, not a break in it. The old walk // stopped at index 3, mid-word. const std::u32string contraction = to_u32("don't stop"); - ensure_equals("contraction is one word", - wstring_step_word_forward(contraction, 0), size_t(6)); - ensure_equals("back over a contraction", - wstring_step_word_backward(contraction, 10), size_t(6)); + UNSCOPED_INFO("contraction is one word"); + CHECK(wstring_step_word_forward(contraction, 0) == size_t(6)); + UNSCOPED_INFO("back over a contraction"); + CHECK(wstring_step_word_backward(contraction, 10) == size_t(6)); // Tabs are whitespace to step over; the old test was == ' ' only. const std::u32string tabbed = to_u32("foo\tbar"); - ensure_equals("fwd over a tab", wstring_step_word_forward(tabbed, 0), size_t(4)); + UNSCOPED_INFO("fwd over a tab"); + CHECK(wstring_step_word_forward(tabbed, 0) == size_t(4)); // Neither direction crosses a newline, matching what the old walk did // by accident (a newline is neither a word char nor a space). const std::u32string lines = to_u32("ab\ncd"); - ensure_equals("fwd stops at eol", wstring_step_word_forward(lines, 0), size_t(2)); - ensure_equals("fwd parked on eol", wstring_step_word_forward(lines, 2), size_t(2)); - ensure_equals("back stops at bol", wstring_step_word_backward(lines, 5), size_t(3)); - ensure_equals("back parked on bol", wstring_step_word_backward(lines, 3), size_t(3)); + UNSCOPED_INFO("fwd stops at eol"); + CHECK(wstring_step_word_forward(lines, 0) == size_t(2)); + UNSCOPED_INFO("fwd parked on eol"); + CHECK(wstring_step_word_forward(lines, 2) == size_t(2)); + UNSCOPED_INFO("back stops at bol"); + CHECK(wstring_step_word_backward(lines, 5) == size_t(3)); + UNSCOPED_INFO("back parked on bol"); + CHECK(wstring_step_word_backward(lines, 3) == size_t(3)); // Trailing whitespace has no word after it, so the line's end is where // forward stops. const std::u32string trailing = to_u32("foo "); - ensure_equals("fwd into trailing space", - wstring_step_word_forward(trailing, 0), size_t(6)); + UNSCOPED_INFO("fwd into trailing space"); + CHECK(wstring_step_word_forward(trailing, 0) == size_t(6)); // Degenerate input. const std::u32string empty; - ensure_equals("empty fwd", wstring_step_word_forward(empty, 0), size_t(0)); - ensure_equals("empty back", wstring_step_word_backward(empty, 0), size_t(0)); - ensure_equals("past end fwd", wstring_step_word_forward(two_words, 99), two_words.size()); - ensure_equals("past end back", wstring_step_word_backward(two_words, 99), size_t(4)); + UNSCOPED_INFO("empty fwd"); + CHECK(wstring_step_word_forward(empty, 0) == size_t(0)); + UNSCOPED_INFO("empty back"); + CHECK(wstring_step_word_backward(empty, 0) == size_t(0)); + UNSCOPED_INFO("past end fwd"); + REQUIRE(wstring_step_word_forward(two_words, 99) == two_words.size()); + UNSCOPED_INFO("past end back"); + CHECK(wstring_step_word_backward(two_words, 99) == size_t(4)); // Scripts without spaces still segment, rather than swallowing the // whole run the way an alnum test would. const std::u32string cjk = to_u32("\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E ok"); - ensure("cjk advances", wstring_step_word_forward(cjk, 0) > size_t(0)); - ensure("cjk terminates", wstring_step_word_forward(cjk, 0) <= cjk.size()); + UNSCOPED_INFO("cjk advances"); + CHECK(wstring_step_word_forward(cjk, 0) > size_t(0)); + UNSCOPED_INFO("cjk terminates"); + REQUIRE(wstring_step_word_forward(cjk, 0) <= cjk.size()); // Adjacent emoji are separate words, so a word step crosses exactly // one. Callers must not pre-step a grapheme and then ask for the next // word -- that lands two emoji away. const std::u32string two_emoji = { (llwchar)0x1F436, (llwchar)0x1F431, (llwchar)'x' }; - ensure_equals("one emoji forward", wstring_step_word_forward(two_emoji, 0), size_t(1)); - ensure_equals("one emoji back", wstring_step_word_backward(two_emoji, 2), size_t(1)); + UNSCOPED_INFO("one emoji forward"); + CHECK(wstring_step_word_forward(two_emoji, 0) == size_t(1)); + UNSCOPED_INFO("one emoji back"); + CHECK(wstring_step_word_backward(two_emoji, 2) == size_t(1)); // A ZWJ sequence is a single word, however many codepoints it spans. const std::u32string flag_then_dog = { (llwchar)0x1F3F3, (llwchar)0xFE0F, (llwchar)0x200D, (llwchar)0x26A7, (llwchar)0xFE0F, (llwchar)0x1F436 }; - ensure_equals("zwj sequence is one word", - wstring_step_word_forward(flag_then_dog, 0), size_t(5)); - ensure_equals("back over the zwj sequence", - wstring_step_word_backward(flag_then_dog, 6), size_t(5)); + UNSCOPED_INFO("zwj sequence is one word"); + CHECK(wstring_step_word_forward(flag_then_dog, 0) == size_t(5)); + UNSCOPED_INFO("back over the zwj sequence"); + CHECK(wstring_step_word_backward(flag_then_dog, 6) == size_t(5)); } // UAX #14 line break opportunities, which replaced a spaces-plus-CJK-range // heuristic in LLFontGL::maxDrawableBytes. - template<> template<> - void llstring_utf_object_t::test<119>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: line break opportunities follow UAX #14", "[LLStringUTF]") { std::vector breaks; @@ -1815,106 +2033,129 @@ namespace tut // space and the next one starts on the word. This is what the old // start_of_last_word tracking computed, and it has to stay that way. wstring_line_break_opportunities(to_u32("hello world"), breaks); - ensure_equals("two opportunities", breaks.size(), size_t(2)); - ensure_equals("after the space", breaks[0], size_t(6)); - ensure_equals("and the end", breaks[1], size_t(11)); + UNSCOPED_INFO("two opportunities"); + REQUIRE(breaks.size() == size_t(2)); + UNSCOPED_INFO("after the space"); + CHECK(breaks[0] == size_t(6)); + UNSCOPED_INFO("and the end"); + CHECK(breaks[1] == size_t(11)); // The string's end is always an opportunity; 0 never is. wstring_line_break_opportunities(to_u32("unbroken"), breaks); - ensure_equals("single word, one opportunity", breaks.size(), size_t(1)); - ensure_equals("at the end", breaks[0], size_t(8)); + UNSCOPED_INFO("single word, one opportunity"); + REQUIRE(breaks.size() == size_t(1)); + UNSCOPED_INFO("at the end"); + CHECK(breaks[0] == size_t(8)); // A non-breaking space is glue. The old code special-cased U+00A0 by // hand; here it simply produces no opportunity. wstring_line_break_opportunities(to_u32("a\xC2\xA0" "b"), breaks); - ensure_equals("nbsp does not break", breaks.size(), size_t(1)); - ensure_equals("only the end", breaks[0], size_t(3)); + UNSCOPED_INFO("nbsp does not break"); + REQUIRE(breaks.size() == size_t(1)); + UNSCOPED_INFO("only the end"); + CHECK(breaks[0] == size_t(3)); // CJK ideographs may be split between characters... const std::u32string cjk = to_u32("\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E"); wstring_line_break_opportunities(cjk, breaks); - ensure("cjk splits between characters", breaks.size() > size_t(1)); + UNSCOPED_INFO("cjk splits between characters"); + REQUIRE(breaks.size() > size_t(1)); // ...but a line may not begin with closing punctuation, so there is no // opportunity immediately before U+3002 IDEOGRAPHIC FULL STOP. The old // ideograph-range test would have broken there happily. const std::u32string cjk_stop = to_u32("\xE6\x97\xA5\xE6\x9C\xAC\xE3\x80\x82"); wstring_line_break_opportunities(cjk_stop, breaks); - ensure("no break before the full stop", - std::find(breaks.begin(), breaks.end(), size_t(2)) == breaks.end()); + UNSCOPED_INFO("no break before the full stop"); + REQUIRE(std::find(breaks.begin(), breaks.end(), size_t(2)) == breaks.end()); // Every opportunity is in range and strictly ascending, since the // consumer walks them with a single forward cursor. const std::u32string mixed = to_u32("one two\xC2\xA0three, four"); wstring_line_break_opportunities(mixed, breaks); - ensure("mixed has opportunities", !breaks.empty()); + UNSCOPED_INFO("mixed has opportunities"); + REQUIRE_FALSE(breaks.empty()); for (size_t k = 0; k < breaks.size(); ++k) { - ensure("in range", breaks[k] > 0 && breaks[k] <= mixed.size()); + { + INFO("in range"); + REQUIRE(breaks[k] > 0); + REQUIRE(breaks[k] <= mixed.size()); + } if (k > 0) { - ensure("ascending", breaks[k] > breaks[k - 1]); + UNSCOPED_INFO("ascending"); + REQUIRE(breaks[k] > breaks[k - 1]); } } - ensure_equals("ends at the end", breaks.back(), mixed.size()); + UNSCOPED_INFO("ends at the end"); + REQUIRE(breaks.back() == mixed.size()); // Empty input clears the caller's buffer rather than leaving it alone, // since the buffer is reused across lines. wstring_line_break_opportunities(std::u32string(), breaks); - ensure("empty clears", breaks.empty()); + UNSCOPED_INFO("empty clears"); + REQUIRE(breaks.empty()); } // Case conversion, and the index map that lets a search fold its haystack // without losing track of where a match sits in the original. - template<> template<> - void llstring_utf_object_t::test<120>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: case conversion and mapping offsets back from a folded copy", "[LLStringUTF]") { // Uppercasing sharp s produces two characters. towupper could not do // this at all -- it maps one codepoint to one codepoint. std::string sharp_s = "stra\xC3\x9F" "e"; - ensure_equals("sharp s starts at 7 bytes", sharp_s.size(), size_t(7)); + UNSCOPED_INFO("sharp s starts at 7 bytes"); + REQUIRE(sharp_s.size() == size_t(7)); LLStringUtil::toUpper(sharp_s); - ensure_equals("uppercased grows", sharp_s, std::string("STRASSE")); + UNSCOPED_INFO("uppercased grows"); + CHECK(sharp_s == std::string("STRASSE")); std::string plain = "Hello"; LLStringUtil::toLower(plain); - ensure_equals("ordinary lowercase", plain, std::string("hello")); + UNSCOPED_INFO("ordinary lowercase"); + CHECK(plain == std::string("hello")); // U+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE lowercases to two // characters, so every offset after it shifts. This is the case that // breaks an offset taken from a folded copy. const std::string dotted_i = "a\xC4\xB0" "b"; - ensure_equals("four bytes in", dotted_i.size(), size_t(4)); + UNSCOPED_INFO("four bytes in"); + REQUIRE(dotted_i.size() == size_t(4)); std::string folded = dotted_i; LLStringUtil::toLower(folded); - ensure("fold grew", folded.size() > dotted_i.size()); + UNSCOPED_INFO("fold grew"); + REQUIRE(folded.size() > dotted_i.size()); // The point of all this: find 'b' in the folded copy and land on the // right byte in the original. Taken raw, the offset would be past it. const size_t folded_at = folded.find('b'); - ensure("found in fold", folded_at != std::string::npos); - ensure("raw offset would be wrong", folded_at != size_t(3)); - ensure_equals("mapped offset is right", - utf8str_bytes_from_cased_bytes(dotted_i, folded_at, false), size_t(3)); + UNSCOPED_INFO("found in fold"); + REQUIRE(folded_at != std::string::npos); + UNSCOPED_INFO("raw offset would be wrong"); + CHECK(folded_at != size_t(3)); + UNSCOPED_INFO("mapped offset is right"); + CHECK(utf8str_bytes_from_cased_bytes(dotted_i, folded_at, false) == size_t(3)); // Every prefix of the fold maps back onto a character boundary of the // original, and never past its end. for (size_t k = 0; k <= folded.size(); ++k) { const size_t back = utf8str_bytes_from_cased_bytes(dotted_i, k, false); - ensure("never past the end", back <= dotted_i.size()); - ensure("lands on a character start", - back == dotted_i.size() - || (((unsigned char)dotted_i[back]) & 0xC0) != 0x80); + UNSCOPED_INFO("never past the end"); + REQUIRE(back <= dotted_i.size()); + UNSCOPED_INFO("lands on a character start"); + REQUIRE((back == dotted_i.size() + || (((unsigned char)dotted_i[back]) & 0xC0) != 0x80)); } // ASCII stays one-to-one, so an offset needs no mapping at all. const std::string ascii = "ABC"; for (size_t k = 0; k <= ascii.size(); ++k) { - ensure_equals("ascii maps to itself", - utf8str_bytes_from_cased_bytes(ascii, k, false), k); + UNSCOPED_INFO("ascii maps to itself"); + REQUIRE(utf8str_bytes_from_cased_bytes(ascii, k, false) == k); } // Sharp s changes the character count without changing the byte @@ -1925,40 +2166,44 @@ namespace tut const std::string strasse = "stra\xC3\x9F" "e"; std::string upper = strasse; LLStringUtil::toUpper(upper); - ensure_equals("same byte length", upper.size(), strasse.size()); - ensure_equals("but one more character", - utf8str_codepoint_count(upper), utf8str_codepoint_count(strasse) + 1); + UNSCOPED_INFO("same byte length"); + REQUIRE(upper.size() == strasse.size()); + UNSCOPED_INFO("but one more character"); + CHECK(utf8str_codepoint_count(upper) == utf8str_codepoint_count(strasse) + 1); const size_t back_from[] = { 0, 1, 2, 3, 4, 4, 6, 7 }; for (size_t k = 0; k <= upper.size(); ++k) { - ensure_equals("cased offset " + std::to_string(k) + " maps back", - utf8str_bytes_from_cased_bytes(strasse, k, true), back_from[k]); + UNSCOPED_INFO("cased offset " + std::to_string(k) + " maps back"); + REQUIRE(utf8str_bytes_from_cased_bytes(strasse, k, true) == back_from[k]); } } // The narrow forms case UTF-8 now, and the helper that brings an offset // taken against a cased copy back onto the text it was built from. - template<> template<> - void llstring_utf_object_t::test<121>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: narrow case conversion and offsets mapped back from a cased key", "[LLStringUTF]") { std::string s = "stra\xC3\x9F" "e"; LLStringUtil::toUpper(s); - ensure_equals("narrow toUpper is unicode", s, std::string("STRASSE")); + UNSCOPED_INFO("narrow toUpper is unicode"); + CHECK(s == std::string("STRASSE")); std::string acc = "\xC3\xA9" "cole"; LLStringUtil::toUpper(acc); - ensure_equals("narrow toUpper accents", acc, std::string("\xC3\x89" "COLE")); + UNSCOPED_INFO("narrow toUpper accents"); + CHECK(acc == std::string("\xC3\x89" "COLE")); std::string upper = "\xC3\x89" "COLE"; LLStringUtil::toLower(upper); - ensure_equals("narrow toLower accents", upper, std::string("\xC3\xA9" "cole")); + UNSCOPED_INFO("narrow toLower accents"); + CHECK(upper == std::string("\xC3\xA9" "cole")); // ASCII is untouched, which is what the identifier and protocol callers // rely on. std::string ascii = "Image.PNG"; LLStringUtil::toLower(ascii); - ensure_equals("ascii unchanged", ascii, std::string("image.png")); + UNSCOPED_INFO("ascii unchanged"); + CHECK(ascii == std::string("image.png")); // The ff ligature uppercases to "FF": three bytes become two, so an // offset taken from the key sits BEFORE where the same text starts in @@ -1968,14 +2213,16 @@ namespace tut std::string key = label; LLStringUtil::toUpper(key); const size_t at = key.find("XYZ"); - ensure("found in the key", at != std::string::npos); - ensure("the ligature expanded, so the key is shorter", - key.size() < label.size()); + UNSCOPED_INFO("found in the key"); + REQUIRE(at != std::string::npos); + UNSCOPED_INFO("the ligature expanded, so the key is shorter"); + REQUIRE(key.size() < label.size()); // xyz begins at byte 6 of the label and byte 5 of the key. - ensure_equals("offset maps onto the label's own bytes", - utf8str_bytes_from_cased_bytes(label, at, true), size_t(6)); - ensure("the raw offset would have been wrong", at != size_t(6)); + UNSCOPED_INFO("offset maps onto the label's own bytes"); + CHECK(utf8str_bytes_from_cased_bytes(label, at, true) == size_t(6)); + UNSCOPED_INFO("the raw offset would have been wrong"); + CHECK(at != size_t(6)); } // "straße" uppercases to "STRASSE": one codepoint longer, but the same @@ -1985,57 +2232,67 @@ namespace tut std::string key = label; LLStringUtil::toUpper(key); const size_t at = key.find("XYZ"); - ensure("found in the key", at != std::string::npos); - ensure_equals("same byte length either cased or not", key.size(), label.size()); + UNSCOPED_INFO("found in the key"); + REQUIRE(at != std::string::npos); + UNSCOPED_INFO("same byte length either cased or not"); + REQUIRE(key.size() == label.size()); const size_t mapped = utf8str_bytes_from_cased_bytes(label, at, true); - ensure_equals("mapped to the label's own byte offset", mapped, size_t(8)); + UNSCOPED_INFO("mapped to the label's own byte offset"); + CHECK(mapped == size_t(8)); // And the matched span's own length, which is not the key's. const size_t span_begin = utf8str_bytes_from_cased_bytes(label, 0, true); const size_t span_end = utf8str_bytes_from_cased_bytes(label, key.find(' '), true); - ensure_equals("word before the space is seven bytes", span_end - span_begin, size_t(7)); + UNSCOPED_INFO("word before the space is seven bytes"); + CHECK(span_end - span_begin == size_t(7)); // Offset 0 and a whole-string offset are the degenerate ends. - ensure_equals("zero maps to zero", - utf8str_bytes_from_cased_bytes(label, 0, true), size_t(0)); - ensure_equals("whole string maps to its byte count", - utf8str_bytes_from_cased_bytes(label, key.size(), true), size_t(11)); + UNSCOPED_INFO("zero maps to zero"); + CHECK(utf8str_bytes_from_cased_bytes(label, 0, true) == size_t(0)); + UNSCOPED_INFO("whole string maps to its byte count"); + REQUIRE(utf8str_bytes_from_cased_bytes(label, key.size(), true) == size_t(11)); // ASCII text maps one-to-one, so existing offsets are unaffected. const std::string plain = "hello world"; - ensure_equals("ascii maps straight through", - utf8str_bytes_from_cased_bytes(plain, 6, true), size_t(6)); + UNSCOPED_INFO("ascii maps straight through"); + CHECK(utf8str_bytes_from_cased_bytes(plain, 6, true) == size_t(6)); - ensure_equals("empty", utf8str_bytes_from_cased_bytes(std::string(), 4, true), size_t(0)); + UNSCOPED_INFO("empty"); + CHECK(utf8str_bytes_from_cased_bytes(std::string(), 4, true) == size_t(0)); } // "Which word is here" and "where is the next one", the questions // double-click selection, spell check and autoreplace ask. - template<> template<> - void llstring_utf_object_t::test<122>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: the word at a position and the next word from it", "[LLStringUTF]") { const std::u32string text = to_u32("don't stop, ok"); // A contraction is one word. The alnum-or-underscore walk this replaced // stopped at the apostrophe and offered "don" or "t". auto word = wstring_word_range_at(text, 0); - ensure_equals("contraction begin", word.first, size_t(0)); - ensure_equals("contraction end", word.second, size_t(5)); + UNSCOPED_INFO("contraction begin"); + CHECK(word.first == size_t(0)); + UNSCOPED_INFO("contraction end"); + CHECK(word.second == size_t(5)); // Anywhere inside it gives the same word. for (size_t p = 0; p < 5; ++p) { const auto at = wstring_word_range_at(text, p); - ensure_equals("same word from inside", at.first, size_t(0)); - ensure_equals("same word end", at.second, size_t(5)); + UNSCOPED_INFO("same word from inside"); + REQUIRE(at.first == size_t(0)); + UNSCOPED_INFO("same word end"); + REQUIRE(at.second == size_t(5)); } // Whitespace and punctuation are segments too, and neither is a word. const auto space = wstring_word_range_at(text, 5); - ensure("space is not a word", space.first == space.second); + UNSCOPED_INFO("space is not a word"); + CHECK(space.first == space.second); const auto comma = wstring_word_range_at(text, 10); - ensure("comma is not a word", comma.first == comma.second); + UNSCOPED_INFO("comma is not a word"); + CHECK(comma.first == comma.second); // Iterating: each call yields the next word, skipping what is between. std::vector found; @@ -2048,46 +2305,59 @@ namespace tut found.push_back(to_utf8(text.substr(next.first, next.second - next.first))); at = next.second; } - ensure_equals("three words", found.size(), size_t(3)); - ensure_equals("first", found[0], std::string("don't")); - ensure_equals("second", found[1], std::string("stop")); - ensure_equals("third", found[2], std::string("ok")); + UNSCOPED_INFO("three words"); + REQUIRE(found.size() == size_t(3)); + UNSCOPED_INFO("first"); + CHECK(found[0] == std::string("don't")); + UNSCOPED_INFO("second"); + CHECK(found[1] == std::string("stop")); + UNSCOPED_INFO("third"); + CHECK(found[2] == std::string("ok")); // Iteration crosses lines, which the spell checker relies on. const std::u32string lines = to_u32("one\ntwo"); const auto first = wstring_next_word_range(lines, 0); - ensure_equals("first line word end", first.second, size_t(3)); + UNSCOPED_INFO("first line word end"); + CHECK(first.second == size_t(3)); const auto second = wstring_next_word_range(lines, first.second); - ensure_equals("second line word begin", second.first, size_t(4)); - ensure_equals("second line word end", second.second, size_t(7)); + UNSCOPED_INFO("second line word begin"); + CHECK(second.first == size_t(4)); + UNSCOPED_INFO("second line word end"); + CHECK(second.second == size_t(7)); // Past the end, and past the last word, both terminate. const auto none = wstring_next_word_range(lines, 7); - ensure("no more words", none.first == none.second); + UNSCOPED_INFO("no more words"); + CHECK(none.first == none.second); const auto oob = wstring_word_range_at(lines, 99); - ensure("past end is empty", oob.first == oob.second); + UNSCOPED_INFO("past end is empty"); + CHECK(oob.first == oob.second); const std::u32string empty; const auto on_empty = wstring_word_range_at(empty, 0); - ensure("empty string", on_empty.first == on_empty.second); + UNSCOPED_INFO("empty string"); + CHECK(on_empty.first == on_empty.second); // A whole string that is one word -- what autoreplace validates a // keyword with. const std::u32string keyword = to_u32("don't"); const auto whole = wstring_word_range_at(keyword, 0); - ensure("keyword is one word", - whole.first == size_t(0) && whole.second == keyword.size()); + { + INFO("keyword is one word"); + CHECK(whole.first == size_t(0)); + REQUIRE(whole.second == keyword.size()); + } const std::u32string two_words = to_u32("not one"); const auto partial = wstring_word_range_at(two_words, 0); - ensure("two words is not one", partial.second != two_words.size()); + UNSCOPED_INFO("two words is not one"); + REQUIRE(partial.second != two_words.size()); } // Classification above the BMP. Every one of these codepoints used to be // handed to , whose wint_t is 16 bits on Windows -- so each // arrived with its top half cut off and was answered for by whatever // happened to live at the remaining address. - template<> template<> - void llstring_utf_object_t::test<123>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: classification and case above the BMP", "[LLStringUTF]") { // The low half of each is given as the answer it used to produce. const llwchar CJK_EXT_B_A = 0x2000A; // -> U+000A, a line feed @@ -2098,14 +2368,22 @@ namespace tut const llwchar MATH_BOLD_A = 0x1D400; // -> U+D400, a lone surrogate const llwchar GRINNING_FACE = 0x1F600; - ensure("CJK ideograph is not whitespace", !LLStringOps::isSpace(CJK_EXT_B_A)); - ensure("CJK ideograph is alphanumeric", LLStringOps::isAlnum(CJK_EXT_B_A)); - ensure("CJK ideograph is not a digit", !LLStringOps::isDigit(CJK_EXT_B_ONE)); - ensure("CJK ideograph is alphanumeric", LLStringOps::isAlnum(CJK_EXT_B_ONE)); - ensure("Linear B is not whitespace", !LLStringOps::isSpace(LINEAR_B)); - ensure("math capital is a letter", LLStringOps::isAlpha(MATH_BOLD_A)); - ensure("emoji is not alphanumeric", !LLStringOps::isAlnum(GRINNING_FACE)); - ensure("emoji is not whitespace", !LLStringOps::isSpace(GRINNING_FACE)); + UNSCOPED_INFO("CJK ideograph is not whitespace"); + CHECK_FALSE(LLStringOps::isSpace(CJK_EXT_B_A)); + UNSCOPED_INFO("CJK ideograph is alphanumeric"); + CHECK(LLStringOps::isAlnum(CJK_EXT_B_A)); + UNSCOPED_INFO("CJK ideograph is not a digit"); + CHECK_FALSE(LLStringOps::isDigit(CJK_EXT_B_ONE)); + UNSCOPED_INFO("CJK ideograph is alphanumeric"); + CHECK(LLStringOps::isAlnum(CJK_EXT_B_ONE)); + UNSCOPED_INFO("Linear B is not whitespace"); + CHECK_FALSE(LLStringOps::isSpace(LINEAR_B)); + UNSCOPED_INFO("math capital is a letter"); + CHECK(LLStringOps::isAlpha(MATH_BOLD_A)); + UNSCOPED_INFO("emoji is not alphanumeric"); + CHECK_FALSE(LLStringOps::isAlnum(GRINNING_FACE)); + UNSCOPED_INFO("emoji is not whitespace"); + CHECK_FALSE(LLStringOps::isSpace(GRINNING_FACE)); // Case above the BMP: this used to return an unrelated codepoint // rather than fail to convert. @@ -2113,39 +2391,53 @@ namespace tut // failure message rather than compared as they stand. const auto cased = [](llwchar c) { return (U32)c; }; - ensure("Deseret capital is upper", LLStringOps::isUpper(DESERET_CAP)); - ensure_equals("Deseret capital lowercases within Deseret", - cased(LLStringOps::toLower(DESERET_CAP)), cased(DESERET_SMALL)); - ensure_equals("Deseret small uppercases back", - cased(LLStringOps::toUpper(DESERET_SMALL)), cased(DESERET_CAP)); + UNSCOPED_INFO("Deseret capital is upper"); + CHECK(LLStringOps::isUpper(DESERET_CAP)); + UNSCOPED_INFO("Deseret capital lowercases within Deseret"); + CHECK(cased(LLStringOps::toLower(DESERET_CAP)) == cased(DESERET_SMALL)); + UNSCOPED_INFO("Deseret small uppercases back"); + CHECK(cased(LLStringOps::toUpper(DESERET_SMALL)) == cased(DESERET_CAP)); // Case inside the BMP, which towlower left alone entirely. - ensure_equals("Greek sigma lowercases", cased(LLStringOps::toLower(llwchar(0x03A3))), cased(0x03C3)); - ensure_equals("Cyrillic Io lowercases", cased(LLStringOps::toLower(llwchar(0x0401))), cased(0x0451)); - ensure_equals("Cyrillic io uppercases", cased(LLStringOps::toUpper(llwchar(0x0451))), cased(0x0401)); + UNSCOPED_INFO("Greek sigma lowercases"); + CHECK(cased(LLStringOps::toLower(llwchar(0x03A3))) == cased(0x03C3)); + UNSCOPED_INFO("Cyrillic Io lowercases"); + CHECK(cased(LLStringOps::toLower(llwchar(0x0401))) == cased(0x0451)); + UNSCOPED_INFO("Cyrillic io uppercases"); + CHECK(cased(LLStringOps::toUpper(llwchar(0x0451))) == cased(0x0401)); // Whitespace is the Unicode property, not a guess. - ensure("no-break space is whitespace", LLStringOps::isSpace(llwchar(0x00A0))); - ensure("ideographic space is whitespace", LLStringOps::isSpace(llwchar(0x3000))); - ensure("zero width space is not", !LLStringOps::isSpace(llwchar(0x200B))); + UNSCOPED_INFO("no-break space is whitespace"); + CHECK(LLStringOps::isSpace(llwchar(0x00A0))); + UNSCOPED_INFO("ideographic space is whitespace"); + CHECK(LLStringOps::isSpace(llwchar(0x3000))); + UNSCOPED_INFO("zero width space is not"); + CHECK_FALSE(LLStringOps::isSpace(llwchar(0x200B))); // Digits are ASCII on purpose -- every caller reads the run as a number. - ensure("Arabic-Indic zero is not a digit", !LLStringOps::isDigit(llwchar(0x0660))); - ensure("Arabic-Indic zero is alphanumeric", LLStringOps::isAlnum(llwchar(0x0660))); + UNSCOPED_INFO("Arabic-Indic zero is not a digit"); + CHECK_FALSE(LLStringOps::isDigit(llwchar(0x0660))); + UNSCOPED_INFO("Arabic-Indic zero is alphanumeric"); + CHECK(LLStringOps::isAlnum(llwchar(0x0660))); // ASCII keeps answering as it always did. - ensure("A is upper", LLStringOps::isUpper(llwchar('A'))); - ensure("a is lower", LLStringOps::isLower(llwchar('a'))); - ensure("5 is a digit", LLStringOps::isDigit(llwchar('5'))); - ensure("space is space", LLStringOps::isSpace(llwchar(' '))); - ensure("dot is punct", LLStringOps::isPunct(llwchar('.'))); - ensure_equals("A lowercases", cased(LLStringOps::toLower(llwchar('A'))), cased(llwchar('a'))); + UNSCOPED_INFO("A is upper"); + CHECK(LLStringOps::isUpper(llwchar('A'))); + UNSCOPED_INFO("a is lower"); + CHECK(LLStringOps::isLower(llwchar('a'))); + UNSCOPED_INFO("5 is a digit"); + CHECK(LLStringOps::isDigit(llwchar('5'))); + UNSCOPED_INFO("space is space"); + CHECK(LLStringOps::isSpace(llwchar(' '))); + UNSCOPED_INFO("dot is punct"); + CHECK(LLStringOps::isPunct(llwchar('.'))); + UNSCOPED_INFO("A lowercases"); + CHECK(cased(LLStringOps::toLower(llwchar('A'))) == cased(llwchar('a'))); } // The consequence of the above for the cursor: a CJK ideograph whose low // half is a line feed must not read as a run of whitespace to step over. - template<> template<> - void llstring_utf_object_t::test<124>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: an astral ideograph is a word to the cursor and not a gap", "[LLStringUTF]") { std::u32string text = to_u32("ab"); text.push_back(0x2000A); @@ -2156,66 +2448,73 @@ namespace tut // so the cursor stops at the first. Reading their low halves as line // feeds made all three of the ideographs and the space look like one // gap, and the cursor jumped clear to "cd" at 5. - ensure_equals("cursor stops at the ideograph", - wstring_step_word_forward(text, 0), size_t(2)); + UNSCOPED_INFO("cursor stops at the ideograph"); + CHECK(wstring_step_word_forward(text, 0) == size_t(2)); const auto range = wstring_word_range_at(text, 2); - ensure("an ideograph is a word", range.first != range.second); + UNSCOPED_INFO("an ideograph is a word"); + CHECK(range.first != range.second); } // utf8str_grapheme_align_backward: the byte offsets are the caller's own, // and a cut backs off to where a whole character starts. Everything that // trims UTF-8 to a length leans on this. - template<> template<> - void llstring_utf_object_t::test<125>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_grapheme_align_backward backs off to a character start", "[LLStringUTF]") { // ASCII: every offset is already a boundary. const std::string ascii = "abcd"; for (size_t i = 0; i <= ascii.size(); ++i) { - ensure_equals("ascii is all boundaries", - utf8str_grapheme_align_backward(ascii, i), i); + UNSCOPED_INFO("ascii is all boundaries"); + REQUIRE(utf8str_grapheme_align_backward(ascii, i) == i); } // Inside a multi-byte codepoint, back to where it starts. "A" + U+65E5 // (3 bytes) + "B". const std::string cjk = "A\xE6\x97\xA5" "B"; - ensure_equals("before the codepoint", utf8str_grapheme_align_backward(cjk, 1), size_t(1)); - ensure_equals("inside it, byte 2", utf8str_grapheme_align_backward(cjk, 2), size_t(1)); - ensure_equals("inside it, byte 3", utf8str_grapheme_align_backward(cjk, 3), size_t(1)); - ensure_equals("after it", utf8str_grapheme_align_backward(cjk, 4), size_t(4)); + UNSCOPED_INFO("before the codepoint"); + CHECK(utf8str_grapheme_align_backward(cjk, 1) == size_t(1)); + UNSCOPED_INFO("inside it, byte 2"); + CHECK(utf8str_grapheme_align_backward(cjk, 2) == size_t(1)); + UNSCOPED_INFO("inside it, byte 3"); + CHECK(utf8str_grapheme_align_backward(cjk, 3) == size_t(1)); + UNSCOPED_INFO("after it"); + CHECK(utf8str_grapheme_align_backward(cjk, 4) == size_t(4)); // A base and its combining mark are one character: "e" + U+0301. const std::string combining = "e\xCC\x81" "x"; - ensure_equals("between base and mark", - utf8str_grapheme_align_backward(combining, 1), size_t(0)); - ensure_equals("inside the mark", - utf8str_grapheme_align_backward(combining, 2), size_t(0)); - ensure_equals("after the whole cluster", - utf8str_grapheme_align_backward(combining, 3), size_t(3)); + UNSCOPED_INFO("between base and mark"); + CHECK(utf8str_grapheme_align_backward(combining, 1) == size_t(0)); + UNSCOPED_INFO("inside the mark"); + CHECK(utf8str_grapheme_align_backward(combining, 2) == size_t(0)); + UNSCOPED_INFO("after the whole cluster"); + CHECK(utf8str_grapheme_align_backward(combining, 3) == size_t(3)); // A flag is two codepoints and eight bytes, and one character. Cutting // anywhere inside gives nothing rather than half a flag. const std::string flag = "\xF0\x9F\x87\xBA\xF0\x9F\x87\xB8"; for (size_t i = 1; i < flag.size(); ++i) { - ensure_equals("no half flags", utf8str_grapheme_align_backward(flag, i), size_t(0)); + UNSCOPED_INFO("no half flags"); + REQUIRE(utf8str_grapheme_align_backward(flag, i) == size_t(0)); } - ensure_equals("whole flag survives", - utf8str_grapheme_align_backward(flag, flag.size()), flag.size()); + UNSCOPED_INFO("whole flag survives"); + REQUIRE(utf8str_grapheme_align_backward(flag, flag.size()) == flag.size()); // Degenerate input. - ensure_equals("zero", utf8str_grapheme_align_backward(cjk, 0), size_t(0)); - ensure_equals("past end", utf8str_grapheme_align_backward(cjk, 99), cjk.size()); - ensure_equals("empty", utf8str_grapheme_align_backward(std::string(), 0), size_t(0)); + UNSCOPED_INFO("zero"); + CHECK(utf8str_grapheme_align_backward(cjk, 0) == size_t(0)); + UNSCOPED_INFO("past end"); + REQUIRE(utf8str_grapheme_align_backward(cjk, 99) == cjk.size()); + UNSCOPED_INFO("empty"); + CHECK(utf8str_grapheme_align_backward(std::string(), 0) == size_t(0)); } // The walkers work in UTF-8 and the wide entry points are adapters over // them, so walking the same text both ways has to land in the same places. // Text with a byte per codepoint, three, two, and a two-codepoint cluster, // so the two index spaces disagree everywhere after the first space. - template<> template<> - void llstring_utf_object_t::test<126>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: the UTF-8 walkers find every boundary in mixed text", "[LLStringUTF]") { const std::string utf8 = "ab " // ascii @@ -2224,7 +2523,8 @@ namespace tut "\xF0\x9F\x87\xBA\xF0\x9F\x87\xB8" // flag: two codepoints, one cluster " end"; // a=0 b=1 sp=2 CJK=3,6 sp=9 e=10 acute=11 sp=13 flag=14 sp=22 end=23. - ensure_equals("layout", utf8.size(), size_t(26)); + UNSCOPED_INFO("layout"); + REQUIRE(utf8.size() == size_t(26)); // Every grapheme boundary, written out. The combining acute and the // two regional indicators are the ones a byte walk would split. @@ -2235,25 +2535,31 @@ namespace tut for (size_t i = 1; i < count; ++i) { at = utf8str_step_grapheme_forward(utf8, at); - ensure_equals("forward boundary " + std::to_string(i), at, boundaries[i]); + UNSCOPED_INFO("forward boundary " + std::to_string(i)); + REQUIRE(at == boundaries[i]); } - ensure_equals("forward walk ended at the end", at, utf8.size()); + UNSCOPED_INFO("forward walk ended at the end"); + REQUIRE(at == utf8.size()); for (size_t i = count - 1; i > 0; --i) { at = utf8str_step_grapheme_backward(utf8, at); - ensure_equals("backward boundary " + std::to_string(i - 1), at, boundaries[i - 1]); + UNSCOPED_INFO("backward boundary " + std::to_string(i - 1)); + REQUIRE(at == boundaries[i - 1]); } - ensure_equals("backward walk came back to nothing", at, size_t(0)); + UNSCOPED_INFO("backward walk came back to nothing"); + CHECK(at == size_t(0)); // Every offset inside a cluster snaps to that cluster's edges. for (size_t i = 1; i < count; ++i) { for (size_t inside = boundaries[i - 1] + 1; inside < boundaries[i]; ++inside) { - ensure_equals("aligns back", utf8str_grapheme_align_backward(utf8, inside), + UNSCOPED_INFO("aligns back"); + REQUIRE(utf8str_grapheme_align_backward(utf8, inside) == boundaries[i - 1]); - ensure_equals("aligns forward", utf8str_grapheme_align_forward(utf8, inside), + UNSCOPED_INFO("aligns forward"); + REQUIRE(utf8str_grapheme_align_forward(utf8, inside) == boundaries[i]); } } @@ -2264,41 +2570,48 @@ namespace tut for (size_t i = 0; i < sizeof(word_starts) / sizeof(word_starts[0]); ++i) { word_at = utf8str_step_word_forward(utf8, word_at); - ensure_equals("word start " + std::to_string(i), word_at, word_starts[i]); + UNSCOPED_INFO("word start " + std::to_string(i)); + REQUIRE(word_at == word_starts[i]); } // The word under the first ideograph is the ideograph pair. const auto cjk_word = utf8str_word_range_at(utf8, 3); - ensure_equals("CJK word begins", cjk_word.first, size_t(3)); - ensure_equals("CJK word ends", cjk_word.second, size_t(9)); + UNSCOPED_INFO("CJK word begins"); + CHECK(cjk_word.first == size_t(3)); + UNSCOPED_INFO("CJK word ends"); + CHECK(cjk_word.second == size_t(9)); // Line break opportunities: after each space, between the ideographs, // and at the end. Never inside a character or a flag. std::vector breaks; utf8str_line_break_opportunities(utf8, breaks); - ensure("the end is always an opportunity", - !breaks.empty() && breaks.back() == utf8.size()); + { + INFO("the end is always an opportunity"); + REQUIRE_FALSE(breaks.empty()); + REQUIRE(breaks.back() == utf8.size()); + } for (size_t where : breaks) { - ensure("never zero", where != 0); - ensure("always on a grapheme boundary", - std::find(boundaries, boundaries + count, where) != boundaries + count); + UNSCOPED_INFO("never zero"); + REQUIRE(where != 0); + UNSCOPED_INFO("always on a grapheme boundary"); + REQUIRE(std::find(boundaries, boundaries + count, where) != boundaries + count); } - ensure("CJK may be split between the ideographs", - std::find(breaks.begin(), breaks.end(), size_t(6)) != breaks.end()); - ensure("a flag may not be split", - std::find(breaks.begin(), breaks.end(), size_t(18)) == breaks.end()); + UNSCOPED_INFO("CJK may be split between the ideographs"); + REQUIRE(std::find(breaks.begin(), breaks.end(), size_t(6)) != breaks.end()); + UNSCOPED_INFO("a flag may not be split"); + REQUIRE(std::find(breaks.begin(), breaks.end(), size_t(18)) == breaks.end()); // A flag is one cluster of eight bytes, and nothing cuts it in half. const size_t flag = utf8.find("\xF0\x9F\x87\xBA"); - ensure_equals("flag steps whole", - utf8str_step_grapheme_forward(utf8, flag), flag + 8); + UNSCOPED_INFO("flag steps whole"); + CHECK(utf8str_step_grapheme_forward(utf8, flag) == flag + 8); for (size_t inside = 1; inside < 8; ++inside) { - ensure_equals("inside the flag aligns back to its start", - utf8str_grapheme_align_backward(utf8, flag + inside), flag); - ensure_equals("and forward to its end", - utf8str_grapheme_align_forward(utf8, flag + inside), flag + 8); + UNSCOPED_INFO("inside the flag aligns back to its start"); + REQUIRE(utf8str_grapheme_align_backward(utf8, flag + inside) == flag); + UNSCOPED_INFO("and forward to its end"); + REQUIRE(utf8str_grapheme_align_forward(utf8, flag + inside) == flag + 8); } } @@ -2309,8 +2622,7 @@ namespace tut // The two cluster walkers are one set of rules instantiated twice, so // every run the wide walker finds has to be the same run in the UTF-8 // walker's byte coordinates, and there must be no extra ones. - template<> template<> - void llstring_utf_object_t::test<127>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_find_emoji_clusters finds each run in bytes", "[LLStringUTF]") { const std::vector corpus = { std::u32string(), @@ -2346,21 +2658,25 @@ namespace tut {13, true, 0, 13}, // heart on fire, 3+3+3+4 {16, true, 4, 15}, // a + ideograph + ZWJ pair + z }; - ensure_equals("every corpus entry is accounted for", - corpus.size(), sizeof(expected) / sizeof(expected[0])); + UNSCOPED_INFO("every corpus entry is accounted for"); + REQUIRE(corpus.size() == sizeof(expected) / sizeof(expected[0])); for (size_t c = 0; c < corpus.size(); ++c) { const std::string utf8 = to_utf8(corpus[c]); const std::string tag = "corpus " + std::to_string(c); - ensure_equals(tag + ": byte length", utf8.size(), expected[c].bytes); + UNSCOPED_INFO(tag + ": byte length"); + REQUIRE(utf8.size() == expected[c].bytes); const auto runs = utf8str_find_emoji_clusters(utf8); - ensure_equals(tag + ": run count", runs.size(), expected[c].has_run ? size_t(1) : size_t(0)); + UNSCOPED_INFO(tag + ": run count"); + REQUIRE(runs.size() == (expected[c].has_run ? size_t(1) : size_t(0))); if (expected[c].has_run) { - ensure_equals(tag + ": run begin", runs[0].first, expected[c].begin); - ensure_equals(tag + ": run end", runs[0].second, expected[c].end); + UNSCOPED_INFO(tag + ": run begin"); + REQUIRE(runs[0].first == expected[c].begin); + UNSCOPED_INFO(tag + ": run end"); + REQUIRE(runs[0].second == expected[c].end); } } } @@ -2368,33 +2684,35 @@ namespace tut // Byte coordinates pinned outright, so a change that moved both walkers // the same wrong way is still caught. A ZWJ pair sitting behind an // ideograph starts at byte 4, not codepoint 2. - template<> template<> - void llstring_utf_object_t::test<128>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a run after an ideograph is placed in bytes not codepoints", "[LLStringUTF]") { std::u32string wide = { (llwchar)'a', (llwchar)0x65E5, (llwchar)0x1F468, (llwchar)0x200D, (llwchar)0x1F469, (llwchar)'z' }; const std::string utf8 = to_utf8(wide); - ensure_equals("byte length", utf8.size(), size_t(1 + 3 + 4 + 3 + 4 + 1)); + UNSCOPED_INFO("byte length"); + REQUIRE(utf8.size() == size_t(1 + 3 + 4 + 3 + 4 + 1)); const auto runs = utf8str_find_emoji_clusters(utf8); - ensure_equals("one run", runs.size(), size_t(1)); - ensure_equals("begins past 'a' and the ideograph", runs[0].first, size_t(4)); - ensure_equals("ends before 'z'", runs[0].second, size_t(15)); + UNSCOPED_INFO("one run"); + REQUIRE(runs.size() == size_t(1)); + UNSCOPED_INFO("begins past 'a' and the ideograph"); + CHECK(runs[0].first == size_t(4)); + UNSCOPED_INFO("ends before 'z'"); + CHECK(runs[0].second == size_t(15)); } // Malformed UTF-8 must not stall the walk or invent a cluster. Every bad // byte decodes to U+FFFD over one byte, which matches no rule. - template<> template<> - void llstring_utf_object_t::test<129>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: malformed UTF-8 neither stalls the walk nor invents a cluster", "[LLStringUTF]") { - ensure_equals("lone continuation", - utf8str_find_emoji_clusters("\x80\x80\x80").size(), size_t(0)); - ensure_equals("truncated four-byte", - utf8str_find_emoji_clusters("\xF0\x9F\x91").size(), size_t(0)); - ensure_equals("bad lead", - utf8str_find_emoji_clusters("\xFF\xFE").size(), size_t(0)); + UNSCOPED_INFO("lone continuation"); + REQUIRE(utf8str_find_emoji_clusters("\x80\x80\x80").size() == size_t(0)); + UNSCOPED_INFO("truncated four-byte"); + REQUIRE(utf8str_find_emoji_clusters("\xF0\x9F\x91").size() == size_t(0)); + UNSCOPED_INFO("bad lead"); + REQUIRE(utf8str_find_emoji_clusters("\xFF\xFE").size() == size_t(0)); // A bad byte in front of a real cluster still leaves the cluster // findable, at the offset that byte pushed it to. @@ -2402,17 +2720,19 @@ namespace tut text += to_utf8(std::u32string{ (llwchar)0x1F468, (llwchar)0x200D, (llwchar)0x1F469 }); const auto runs = utf8str_find_emoji_clusters(text); - ensure_equals("one run past the bad byte", runs.size(), size_t(1)); - ensure_equals("begins at byte 1", runs[0].first, size_t(1)); - ensure_equals("ends at the end", runs[0].second, text.size()); + UNSCOPED_INFO("one run past the bad byte"); + REQUIRE(runs.size() == size_t(1)); + UNSCOPED_INFO("begins at byte 1"); + CHECK(runs[0].first == size_t(1)); + UNSCOPED_INFO("ends at the end"); + REQUIRE(runs[0].second == text.size()); } // utf8str_emoji_range_at answered at every position in the string -- // including the ones inside a character, which belong to no pictograph. // The expected ranges are written out rather than derived, so a change that // moved the lookup and the walk the same wrong way is still caught. - template<> template<> - void llstring_utf_object_t::test<130>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_emoji_range_at answers at every byte position", "[LLStringUTF]") { const std::u32string wide = { (llwchar)'a', // 1 byte (llwchar)0x2764, (llwchar)0xFE0F,// heart + VS16 @@ -2424,7 +2744,8 @@ namespace tut const std::string utf8 = to_utf8(wide); // a=0 heart=1 VS16=4 ideograph=7 man=10 ZWJ=14 woman=17 (c)=21 z=23. - ensure_equals("layout", utf8.size(), size_t(24)); + UNSCOPED_INFO("layout"); + REQUIRE(utf8.size() == size_t(24)); // The range every byte offset belongs to, written out. Inside a // cluster every byte reports the whole cluster, character start or @@ -2445,62 +2766,71 @@ namespace tut for (const Expect& e : expected) { const auto got = utf8str_emoji_range_at(utf8, e.at); - ensure_equals("range begin at " + std::to_string(e.at), got.first, e.begin); - ensure_equals("range end at " + std::to_string(e.at), got.second, e.end); + UNSCOPED_INFO("range begin at " + std::to_string(e.at)); + REQUIRE(got.first == e.begin); + UNSCOPED_INFO("range end at " + std::to_string(e.at)); + REQUIRE(got.second == e.end); } // The heart carries a presentation selector, so it is a cluster and // its range spans both codepoints -- three bytes plus three. const auto heart = utf8str_emoji_range_at(utf8, 1); - ensure_equals("heart begins at 1", heart.first, size_t(1)); - ensure_equals("heart spans VS16", heart.second, size_t(7)); + UNSCOPED_INFO("heart begins at 1"); + CHECK(heart.first == size_t(1)); + UNSCOPED_INFO("heart spans VS16"); + CHECK(heart.second == size_t(7)); // The lone copyright sign is no cluster, but it is a pictograph base, // so it reports its own two bytes rather than an empty range. const size_t copy_at = 1 + 6 + 3 + 11; const auto copyright = utf8str_emoji_range_at(utf8, copy_at); - ensure_equals("(c) begins", copyright.first, copy_at); - ensure_equals("(c) is two bytes", copyright.second, copy_at + 2); + UNSCOPED_INFO("(c) begins"); + CHECK(copyright.first == copy_at); + UNSCOPED_INFO("(c) is two bytes"); + CHECK(copyright.second == copy_at + 2); // A position inside the ideograph is not a character start and so // belongs to nothing. const auto split = utf8str_emoji_range_at(utf8, 8); - ensure_equals("mid-character is empty", split.first, split.second); + UNSCOPED_INFO("mid-character is empty"); + CHECK(split.first == split.second); // Past the end reports an empty range at the position asked about, // not at the end of the string. const auto past = utf8str_emoji_range_at(utf8, utf8.size() + 4); - ensure_equals("past the end is empty", past.first, past.second); + UNSCOPED_INFO("past the end is empty"); + CHECK(past.first == past.second); } // utf8str_codepoint_count is what a limit expressed in characters gets // compared against, so it has to agree with the UTF-32 form's size(). - template<> template<> - void llstring_utf_object_t::test<132>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_codepoint_count agrees with the UTF-32 size", "[LLStringUTF]") { - ensure_equals("empty", utf8str_codepoint_count(""), size_t(0)); - ensure_equals("ascii", utf8str_codepoint_count("abc"), size_t(3)); + UNSCOPED_INFO("empty"); + CHECK(utf8str_codepoint_count("") == size_t(0)); + UNSCOPED_INFO("ascii"); + CHECK(utf8str_codepoint_count("abc") == size_t(3)); const std::u32string wide = { (llwchar)'a', (llwchar)0x00E9, (llwchar)0x65E5, (llwchar)0x1F600, (llwchar)0x200D, (llwchar)0x1F600 }; const std::string utf8 = to_utf8(wide); - ensure_equals("bytes", utf8.size(), size_t(1 + 2 + 3 + 4 + 3 + 4)); - ensure_equals("agrees with the wide size", - utf8str_codepoint_count(utf8), wide.size()); + UNSCOPED_INFO("bytes"); + REQUIRE(utf8.size() == size_t(1 + 2 + 3 + 4 + 3 + 4)); + UNSCOPED_INFO("agrees with the wide size"); + REQUIRE(utf8str_codepoint_count(utf8) == wide.size()); // Malformed bytes count one each, so the count never disagrees with a // walk over the same text. - ensure_equals("lone continuation bytes", - utf8str_codepoint_count("\x80\x80"), size_t(2)); - ensure_equals("truncated sequence", - utf8str_codepoint_count("\xF0\x9F"), size_t(2)); + UNSCOPED_INFO("lone continuation bytes"); + CHECK(utf8str_codepoint_count("\x80\x80") == size_t(2)); + UNSCOPED_INFO("truncated sequence"); + CHECK(utf8str_codepoint_count("\xF0\x9F") == size_t(2)); } // The UTF-16 pair has to agree with the wide forms it stands beside: the // Win32 IME hands offsets in code units and the editors index bytes, so a // disagreement here is a caret landing in the wrong place mid-composition. - template<> template<> - void llstring_utf_object_t::test<133>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: UTF-16 unit counts and byte counts convert both ways", "[LLStringUTF]") { // 'a', e-acute, an ideograph, an astral emoji (a surrogate pair), 'z'. const std::u32string wide = { (llwchar)'a', (llwchar)0x00E9, (llwchar)0x65E5, @@ -2508,8 +2838,10 @@ namespace tut const std::string utf8 = to_utf8(wide); // Bytes 1 + 2 + 3 + 4 + 1; code units 1 + 1 + 1 + 2 + 1. - ensure_equals("bytes", utf8.size(), size_t(11)); - ensure_equals("units", utf8str_utf16_length(utf8, 0, S32_MAX), S32(6)); + UNSCOPED_INFO("bytes"); + REQUIRE(utf8.size() == size_t(11)); + UNSCOPED_INFO("units"); + CHECK(utf8str_utf16_length(utf8, 0, S32_MAX) == S32(6)); // Every prefix, by hand: the byte offset of each character start and // the code units the text up to it occupies. @@ -2517,8 +2849,8 @@ namespace tut const S32 unit_at[] = { 0, 1, 2, 3, 5, 6 }; for (size_t cp = 0; cp < 6; ++cp) { - ensure_equals("prefix at " + std::to_string(cp), - utf8str_utf16_length(utf8, 0, byte_at[cp]), unit_at[cp]); + UNSCOPED_INFO("prefix at " + std::to_string(cp)); + REQUIRE(utf8str_utf16_length(utf8, 0, byte_at[cp]) == unit_at[cp]); } // Back the other way: a budget in code units becomes a byte count, and @@ -2527,44 +2859,50 @@ namespace tut for (S32 units = 0; units <= 6; ++units) { bool unaligned = false; - ensure_equals("bytes for " + std::to_string(units) + " units", - utf8str_length_from_utf16_length(utf8, 0, units, &unaligned), + UNSCOPED_INFO("bytes for " + std::to_string(units) + " units"); + REQUIRE(utf8str_length_from_utf16_length(utf8, 0, units, &unaligned) == bytes_for_units[units]); - ensure("unaligned only mid-pair", unaligned == (units == 4)); + UNSCOPED_INFO("unaligned only mid-pair"); + REQUIRE(unaligned == (units == 4)); } // Offsets are honoured, not just whole-string calls. - ensure_equals("offset form", utf8str_utf16_length(utf8, 3, S32_MAX), S32(4)); + UNSCOPED_INFO("offset form"); + CHECK(utf8str_utf16_length(utf8, 3, S32_MAX) == S32(4)); // Out of range clamps rather than running off. - ensure_equals("past the end", utf8str_utf16_length(utf8, 999, 999), S32(0)); - ensure_equals("no budget", utf8str_length_from_utf16_length(utf8, 0, 0), S32(0)); + UNSCOPED_INFO("past the end"); + CHECK(utf8str_utf16_length(utf8, 999, 999) == S32(0)); + UNSCOPED_INFO("no budget"); + CHECK(utf8str_length_from_utf16_length(utf8, 0, 0) == S32(0)); } // Valid text has to come back byte-identical -- the clipboard calls this on // every paste, and a sanitizer that rewrites what was already correct would // be worse than none. - template<> template<> - void llstring_utf_object_t::test<134>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_sanitize returns valid text byte for byte", "[LLStringUTF]") { const std::string ascii = "plain ascii"; - ensure_equals("ascii untouched", utf8str_sanitize(ascii), ascii); + UNSCOPED_INFO("ascii untouched"); + CHECK(utf8str_sanitize(ascii) == ascii); const std::u32string wide = { (llwchar)'a', (llwchar)0x00E9, (llwchar)0x65E5, (llwchar)0x1F600, (llwchar)'z' }; const std::string mixed = to_utf8(wide); - ensure_equals("mixed untouched", utf8str_sanitize(mixed), mixed); + UNSCOPED_INFO("mixed untouched"); + CHECK(utf8str_sanitize(mixed) == mixed); - ensure_equals("empty", utf8str_sanitize(std::string_view()), std::string()); + UNSCOPED_INFO("empty"); + CHECK(utf8str_sanitize(std::string_view()) == std::string()); // Embedded NUL is valid UTF-8 and must survive; string_view carries it. const std::string with_nul("a\0b", 3); - ensure_equals("nul survives", utf8str_sanitize(with_nul), with_nul); + UNSCOPED_INFO("nul survives"); + CHECK(utf8str_sanitize(with_nul) == with_nul); } // And malformed text has to come back valid, whatever shape the damage took. - template<> template<> - void llstring_utf_object_t::test<135>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_sanitize makes malformed text valid", "[LLStringUTF]") { // Against simdutf, not against the function under test. Defining // validity as "sanitize leaves it alone" only asks whether the repair @@ -2588,24 +2926,28 @@ namespace tut for (const std::string& s : bad) { - ensure("input really is malformed", !is_valid(s)); + UNSCOPED_INFO("input really is malformed"); + REQUIRE_FALSE(is_valid(s)); // The predicate the widgets ask before deciding whether to repair // has to agree with simdutf, or a widget skips a repair it needed. - ensure_equals("utf8str_is_valid agrees with simdutf", - utf8str_is_valid(s), is_valid(s)); + UNSCOPED_INFO("utf8str_is_valid agrees with simdutf"); + REQUIRE(utf8str_is_valid(s) == is_valid(s)); const std::string fixed = utf8str_sanitize(s); - ensure("output is valid", is_valid(fixed)); - ensure_equals("and the predicate says so too", - utf8str_is_valid(fixed), true); - ensure("the good bytes are still there", - fixed.find("ok") == 0 && fixed.rfind("ok") == fixed.size() - 2); + UNSCOPED_INFO("output is valid"); + REQUIRE(is_valid(fixed)); + UNSCOPED_INFO("and the predicate says so too"); + REQUIRE(utf8str_is_valid(fixed) == true); + { + INFO("the good bytes are still there"); + REQUIRE(fixed.find("ok") == 0); + REQUIRE(fixed.rfind("ok") == fixed.size() - 2); + } } } // A decoder that accepts more than the encoding allows lets a character // reach somewhere a filter already looked for it. - template<> template<> - void llstring_utf_object_t::test<136>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_decode_at accepts only what UTF-8 allows", "[LLStringUTF]") { struct { const char* bytes; const char* what; } bad[] = { { "\xC0\xAF", "overlong slash" }, @@ -2622,25 +2964,29 @@ namespace tut { const std::string s(c.bytes); const LLCodepointAt at = utf8str_decode_at(s, 0); - ensure_equals(std::string(c.what) + " decodes to the replacement", - (S32)at.cp, (S32)0xFFFD); - ensure_equals(std::string(c.what) + " consumes exactly one byte", - (S32)at.next, 1); + UNSCOPED_INFO(std::string(c.what) + " decodes to the replacement"); + REQUIRE((S32)at.cp == (S32)0xFFFD); + UNSCOPED_INFO(std::string(c.what) + " consumes exactly one byte"); + REQUIRE((S32)at.next == 1); } // The shapes that are legal still decode whole. - ensure_equals("ASCII", (S32)utf8str_decode_at(std::string("A"), 0).cp, (S32)'A'); - ensure_equals("two-byte", (S32)utf8str_decode_at(std::string("\xC3\xA9"), 0).cp, (S32)0xE9); - ensure_equals("three-byte", (S32)utf8str_decode_at(std::string("\xE6\x97\xA5"), 0).cp, (S32)0x65E5); - ensure_equals("four-byte", (S32)utf8str_decode_at(std::string("\xF0\x9F\x90\xB6"), 0).cp, (S32)0x1F436); - ensure_equals("four-byte spans four", (S32)utf8str_decode_at(std::string("\xF0\x9F\x90\xB6"), 0).next, (S32)4); + UNSCOPED_INFO("ASCII"); + CHECK((S32)utf8str_decode_at(std::string("A"), 0).cp == (S32)'A'); + UNSCOPED_INFO("two-byte"); + CHECK((S32)utf8str_decode_at(std::string("\xC3\xA9"), 0).cp == (S32)0xE9); + UNSCOPED_INFO("three-byte"); + CHECK((S32)utf8str_decode_at(std::string("\xE6\x97\xA5"), 0).cp == (S32)0x65E5); + UNSCOPED_INFO("four-byte"); + CHECK((S32)utf8str_decode_at(std::string("\xF0\x9F\x90\xB6"), 0).cp == (S32)0x1F436); + UNSCOPED_INFO("four-byte spans four"); + CHECK((S32)utf8str_decode_at(std::string("\xF0\x9F\x90\xB6"), 0).next == (S32)4); } // Shortening text must not empty it. The budget counts codepoints while the // cut lands on a cluster, so a first cluster that spends more codepoints // than the budget allows has nowhere to fall back to except itself. - template<> template<> - void llstring_utf_object_t::test<137>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_symbol_truncate never cuts text to nothing", "[LLStringUTF]") { // A ZWJ family: five codepoints, eighteen bytes, one cluster. const std::string family("\xF0\x9F\x91\xA8\xE2\x80\x8D\xF0\x9F\x91\xA9\xE2\x80\x8D\xF0\x9F\x91\xA7"); @@ -2649,86 +2995,92 @@ namespace tut for (S32 budget = 1; budget <= 5; ++budget) { const std::string cut = utf8str_symbol_truncate(text, budget); - ensure("a budget under the first cluster still returns text", !cut.empty()); - ensure_equals("and returns the whole of that cluster", cut, family); + UNSCOPED_INFO("a budget under the first cluster still returns text"); + REQUIRE_FALSE(cut.empty()); + UNSCOPED_INFO("and returns the whole of that cluster"); + REQUIRE(cut == family); } // Once the budget clears the cluster the next characters come too. - ensure_equals("budget past the cluster takes the next character", - utf8str_symbol_truncate(text, 6), family + "a"); - ensure_equals("input already short enough is untouched", - utf8str_symbol_truncate(std::string("abc"), 10), std::string("abc")); + UNSCOPED_INFO("budget past the cluster takes the next character"); + CHECK(utf8str_symbol_truncate(text, 6) == family + "a"); + UNSCOPED_INFO("input already short enough is untouched"); + CHECK(utf8str_symbol_truncate(std::string("abc"), 10) == std::string("abc")); } // Case-insensitive over ASCII compares bytes, so a spelling the format // never allowed cannot pass for the one it did. - template<> template<> - void llstring_utf_object_t::test<138>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: LLStringUtil::isEqualInsensitiveASCII folds ASCII and nothing else", "[LLStringUTF]") { - ensure("same word, different case", - LLStringUtil::isEqualInsensitiveASCII(std::string("llsd/binary"), std::string("LLSD/Binary"))); - ensure("a different length never matches", - !LLStringUtil::isEqualInsensitiveASCII(std::string("llsd"), std::string("llsd/binary"))); + UNSCOPED_INFO("same word, different case"); + CHECK(LLStringUtil::isEqualInsensitiveASCII(std::string("llsd/binary"), std::string("LLSD/Binary"))); + UNSCOPED_INFO("a different length never matches"); + CHECK_FALSE(LLStringUtil::isEqualInsensitiveASCII(std::string("llsd"), std::string("llsd/binary"))); // Fullwidth 'l' is its own character, whatever a collator makes of it. - ensure("fullwidth is not the ASCII letter", - !LLStringUtil::isEqualInsensitiveASCII( + UNSCOPED_INFO("fullwidth is not the ASCII letter"); + CHECK_FALSE(LLStringUtil::isEqualInsensitiveASCII( std::string("\xEF\xBD\x8C\xEF\xBD\x8Csd/binary"), std::string("llsd/binary"))); // A soft hyphen is a character, not an absence of one. - ensure("an ignorable is still a difference", - !LLStringUtil::isEqualInsensitiveASCII( + UNSCOPED_INFO("an ignorable is still a difference"); + CHECK_FALSE(LLStringUtil::isEqualInsensitiveASCII( std::string("llsd/bin\xC2\xAD" "ary"), std::string("llsd/binary"))); // Above ASCII nothing is folded -- these are simply different bytes. - ensure("no folding above ASCII", - !LLStringUtil::isEqualInsensitiveASCII( + UNSCOPED_INFO("no folding above ASCII"); + CHECK_FALSE(LLStringUtil::isEqualInsensitiveASCII( std::string("caf\xC3\xA9"), std::string("caf\xC3\x89"))); - ensure("empty equals empty", - LLStringUtil::isEqualInsensitiveASCII(std::string(), std::string())); + UNSCOPED_INFO("empty equals empty"); + CHECK(LLStringUtil::isEqualInsensitiveASCII(std::string(), std::string())); } // The word walkers driven by their own offsets. The tests beside these go // through the wstring_* adapters, which prove the ALUtf8View index map is // self-consistent -- not that a byte offset supplied by a caller is read // correctly, which is what every caller in llui now passes. - template<> template<> - void llstring_utf_object_t::test<139>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: the UTF-8 word walkers read byte offsets", "[LLStringUTF]") { // ASCII first, so the byte answers can be read against the wide test. const std::string two_words = "foo bar"; - ensure_equals("fwd to next word", utf8str_step_word_forward(two_words, 0), size_t(4)); - ensure_equals("fwd from next word", utf8str_step_word_forward(two_words, 4), size_t(7)); - ensure_equals("back from end", utf8str_step_word_backward(two_words, 7), size_t(4)); - ensure_equals("back over the space", utf8str_step_word_backward(two_words, 4), size_t(0)); + UNSCOPED_INFO("fwd to next word"); + CHECK(utf8str_step_word_forward(two_words, 0) == size_t(4)); + UNSCOPED_INFO("fwd from next word"); + CHECK(utf8str_step_word_forward(two_words, 4) == size_t(7)); + UNSCOPED_INFO("back from end"); + CHECK(utf8str_step_word_backward(two_words, 7) == size_t(4)); + UNSCOPED_INFO("back over the space"); + CHECK(utf8str_step_word_backward(two_words, 4) == size_t(0)); // And where a character is not a byte. "café bar": the accent makes // the second word start at byte 6 where it is codepoint 5, so these // numbers are wrong under any reading but the byte one. const std::string accented = "caf\xC3\xA9 bar"; - ensure_equals("the fixture string is nine bytes", - accented.size(), size_t(9)); - ensure_equals("fwd over an accented word", - utf8str_step_word_forward(accented, 0), size_t(6)); - ensure_equals("fwd from the last word runs to the end", - utf8str_step_word_forward(accented, 6), size_t(9)); - ensure_equals("back from the end", - utf8str_step_word_backward(accented, 9), size_t(6)); - ensure_equals("back over the space", - utf8str_step_word_backward(accented, 6), size_t(0)); + UNSCOPED_INFO("the fixture string is nine bytes"); + REQUIRE(accented.size() == size_t(9)); + UNSCOPED_INFO("fwd over an accented word"); + CHECK(utf8str_step_word_forward(accented, 0) == size_t(6)); + UNSCOPED_INFO("fwd from the last word runs to the end"); + CHECK(utf8str_step_word_forward(accented, 6) == size_t(9)); + UNSCOPED_INFO("back from the end"); + CHECK(utf8str_step_word_backward(accented, 9) == size_t(6)); + UNSCOPED_INFO("back over the space"); + CHECK(utf8str_step_word_backward(accented, 6) == size_t(0)); // An offset inside the accent is still inside the first word. - ensure_equals("stepping from inside a character", - utf8str_step_word_forward(accented, 4), size_t(6)); + UNSCOPED_INFO("stepping from inside a character"); + CHECK(utf8str_step_word_forward(accented, 4) == size_t(6)); // The walkers stay on their line -- LLTextEditor::nextWordPos crosses // the break itself, and would stall on every newline if this changed. const std::string lines = "ab\ncd"; - ensure_equals("fwd stops at eol", utf8str_step_word_forward(lines, 0), size_t(2)); - ensure_equals("fwd parked on eol", utf8str_step_word_forward(lines, 2), size_t(2)); - ensure_equals("back parked on bol", utf8str_step_word_backward(lines, 3), size_t(3)); + UNSCOPED_INFO("fwd stops at eol"); + CHECK(utf8str_step_word_forward(lines, 0) == size_t(2)); + UNSCOPED_INFO("fwd parked on eol"); + CHECK(utf8str_step_word_forward(lines, 2) == size_t(2)); + UNSCOPED_INFO("back parked on bol"); + CHECK(utf8str_step_word_backward(lines, 3) == size_t(3)); } // Word ranges in bytes: what a double-click selects, and what comes next. - template<> template<> - void llstring_utf_object_t::test<140>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: word ranges in bytes for selection and for the next word", "[LLStringUTF]") { const std::string accented = "caf\xC3\xA9 bar"; @@ -2737,87 +3089,97 @@ namespace tut for (size_t at : { size_t(0), size_t(2), size_t(3), size_t(4) }) { const auto range = utf8str_word_range_at(accented, at); - ensure_equals("word range begins at 0", range.first, size_t(0)); - ensure_equals("word range ends after the accent", range.second, size_t(5)); + UNSCOPED_INFO("word range begins at 0"); + REQUIRE(range.first == size_t(0)); + UNSCOPED_INFO("word range ends after the accent"); + REQUIRE(range.second == size_t(5)); } const auto second = utf8str_word_range_at(accented, 7); - ensure_equals("second word begins at 6", second.first, size_t(6)); - ensure_equals("second word ends at 9", second.second, size_t(9)); + UNSCOPED_INFO("second word begins at 6"); + CHECK(second.first == size_t(6)); + UNSCOPED_INFO("second word ends at 9"); + CHECK(second.second == size_t(9)); // next_word_range finds the word whose end lies past the position -- // the one the position is in, when it is in one, and only otherwise // the following word. From inside the first word that is still the // first word. const auto from_start = utf8str_next_word_range(accented, 0); - ensure_equals("from 0 the word is the one at 0", from_start.first, size_t(0)); - ensure_equals("and it ends after the accent", from_start.second, size_t(5)); + UNSCOPED_INFO("from 0 the word is the one at 0"); + CHECK(from_start.first == size_t(0)); + UNSCOPED_INFO("and it ends after the accent"); + CHECK(from_start.second == size_t(5)); // Standing on the space, the answer is the word after it. const auto from_space = utf8str_next_word_range(accented, 5); - ensure_equals("from the space the next word begins at 6", - from_space.first, size_t(6)); - ensure_equals("and ends at 9", from_space.second, size_t(9)); + UNSCOPED_INFO("from the space the next word begins at 6"); + CHECK(from_space.first == size_t(6)); + UNSCOPED_INFO("and ends at 9"); + CHECK(from_space.second == size_t(9)); // Past the last word there is nothing further to select. const auto none = utf8str_next_word_range(accented, accented.size()); - ensure_equals("nothing begins past the end", none.first, accented.size()); - ensure_equals("nothing ends past the end", none.second, accented.size()); + UNSCOPED_INFO("nothing begins past the end"); + REQUIRE(none.first == accented.size()); + UNSCOPED_INFO("nothing ends past the end"); + REQUIRE(none.second == accented.size()); } // The caret forms differ from the raw walkers in exactly one way: they // cross a line break instead of stalling on it. Both text widgets forward // to these rather than carrying the policy twice, so it is worth pinning // the difference rather than only the agreement. - template<> template<> - void llstring_utf_object_t::test<141>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: the caret word walkers cross line breaks the raw ones stop at", "[LLStringUTF]") { const std::string lines = "one two\nthree four"; // Away from a break the two are the same walk. - ensure_equals("forward agrees mid-line", - utf8str_caret_word_forward(lines, 0), + UNSCOPED_INFO("forward agrees mid-line"); + CHECK(utf8str_caret_word_forward(lines, 0) == utf8str_step_word_forward(lines, 0)); - ensure_equals("backward agrees mid-line", - utf8str_caret_word_backward(lines, 7), + UNSCOPED_INFO("backward agrees mid-line"); + CHECK(utf8str_caret_word_backward(lines, 7) == utf8str_step_word_backward(lines, 7)); // A caret already at the edge of a word must not skip the adjacent // word, and a caret inside a word must land at that word's start. const std::string adjacent = "foo bar"; - ensure_equals("forward from a word end reaches the adjacent word", - utf8str_caret_word_forward(adjacent, 3), size_t(4)); - ensure_equals("backward from inside a word reaches its start", - utf8str_caret_word_backward(adjacent, 5), size_t(4)); + UNSCOPED_INFO("forward from a word end reaches the adjacent word"); + CHECK(utf8str_caret_word_forward(adjacent, 3) == size_t(4)); + UNSCOPED_INFO("backward from inside a word reaches its start"); + CHECK(utf8str_caret_word_backward(adjacent, 5) == size_t(4)); // On the break the raw walker has nowhere to go and says so. - ensure_equals("the raw walker stalls on the newline", - utf8str_step_word_forward(lines, 7), size_t(7)); - ensure("the caret walker crosses it", - utf8str_caret_word_forward(lines, 7) > size_t(7)); + UNSCOPED_INFO("the raw walker stalls on the newline"); + CHECK(utf8str_step_word_forward(lines, 7) == size_t(7)); + UNSCOPED_INFO("the caret walker crosses it"); + CHECK(utf8str_caret_word_forward(lines, 7) > size_t(7)); // And at a line start, going back. - ensure_equals("the raw walker stalls at the line start", - utf8str_step_word_backward(lines, 8), size_t(8)); - ensure("the caret walker crosses back", - utf8str_caret_word_backward(lines, 8) < size_t(8)); + UNSCOPED_INFO("the raw walker stalls at the line start"); + CHECK(utf8str_step_word_backward(lines, 8) == size_t(8)); + UNSCOPED_INFO("the caret walker crosses back"); + CHECK(utf8str_caret_word_backward(lines, 8) < size_t(8)); // Neither runs off either end. - ensure_equals("nothing before the beginning", - utf8str_caret_word_backward(lines, 0), size_t(0)); - ensure_equals("nothing after the end", - utf8str_caret_word_forward(lines, lines.size()), lines.size()); + UNSCOPED_INFO("nothing before the beginning"); + CHECK(utf8str_caret_word_backward(lines, 0) == size_t(0)); + UNSCOPED_INFO("nothing after the end"); + REQUIRE(utf8str_caret_word_forward(lines, lines.size()) == lines.size()); // Repeated stepping terminates rather than parking. size_t at = 0; for (int i = 0; i < 32 && at < lines.size(); ++i) { const size_t next = utf8str_caret_word_forward(lines, at); - ensure("forward never moves backward", next >= at); + UNSCOPED_INFO("forward never moves backward"); + REQUIRE(next >= at); if (next == at) break; at = next; } - ensure_equals("stepping forward reaches the end", at, lines.size()); + UNSCOPED_INFO("stepping forward reaches the end"); + REQUIRE(at == lines.size()); } // --------------------------------------------------------------- @@ -2827,8 +3189,7 @@ namespace tut // LLStringUtil::trim answers for Unicode whitespace, not just ASCII. A // no-break space arrives pasted out of a web page and an ideographic // space sits either side of CJK; a byte-wise isspace() saw neither. - template<> template<> - void llstring_utf_object_t::test<142>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: LLStringUtil::trim answers for Unicode whitespace", "[LLStringUTF]") { const std::string nbsp = "\xC2\xA0"; // U+00A0 const std::string ideo = "\xE3\x80\x80"; // U+3000 @@ -2838,88 +3199,100 @@ namespace tut std::string s = nbsp + "Rye" + nbsp; LLStringUtil::trim(s); - ensure_equals("a no-break space is whitespace", s, std::string("Rye")); + UNSCOPED_INFO("a no-break space is whitespace"); + CHECK(s == std::string("Rye")); s = ideo + cjk + ideo; LLStringUtil::trim(s); - ensure_equals("an ideographic space is whitespace", s, cjk); + UNSCOPED_INFO("an ideographic space is whitespace"); + CHECK(s == cjk); s = em + narrow + " \tx\t " + narrow + em; LLStringUtil::trim(s); - ensure_equals("ASCII and Unicode spaces mixed", s, std::string("x")); + UNSCOPED_INFO("ASCII and Unicode spaces mixed"); + CHECK(s == std::string("x")); s = nbsp + "a"; LLStringUtil::trimHead(s); - ensure_equals("trimHead crosses one", s, std::string("a")); + UNSCOPED_INFO("trimHead crosses one"); + CHECK(s == std::string("a")); s = "a" + nbsp; LLStringUtil::trimTail(s); - ensure_equals("trimTail crosses one", s, std::string("a")); + UNSCOPED_INFO("trimTail crosses one"); + CHECK(s == std::string("a")); s = nbsp + ideo + em; LLStringUtil::trim(s); - ensure("all whitespace trims to nothing", s.empty()); + UNSCOPED_INFO("all whitespace trims to nothing"); + REQUIRE(s.empty()); } // What is not White_Space stays, and malformed bytes are content: a trim // that guessed at them could cut a character in half. - template<> template<> - void llstring_utf_object_t::test<143>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: trim keeps what is not White_Space and malformed bytes", "[LLStringUTF]") { const std::string zwsp = "\xE2\x80\x8B"; // U+200B, not White_Space const std::string bom = "\xEF\xBB\xBF"; // U+FEFF, not White_Space std::string s = zwsp + "a" + zwsp; LLStringUtil::trim(s); - ensure_equals("a zero-width space is not whitespace", s, zwsp + "a" + zwsp); + UNSCOPED_INFO("a zero-width space is not whitespace"); + CHECK(s == zwsp + "a" + zwsp); s = bom + "a" + bom; LLStringUtil::trim(s); - ensure_equals("a byte order mark is not whitespace", s, bom + "a" + bom); + UNSCOPED_INFO("a byte order mark is not whitespace"); + CHECK(s == bom + "a" + bom); s = std::string("\x80") + "a" + std::string("\x80"); LLStringUtil::trim(s); - ensure_equals("a stray continuation byte is content", s.size(), size_t(3)); + UNSCOPED_INFO("a stray continuation byte is content"); + REQUIRE(s.size() == size_t(3)); // U+3000 missing its last byte. The lead byte it shares with a real // ideographic space must not be enough to cut it. s = std::string("a") + "\xE3\x80"; LLStringUtil::trim(s); - ensure_equals("a truncated character is left whole", s.size(), size_t(3)); + UNSCOPED_INFO("a truncated character is left whole"); + REQUIRE(s.size() == size_t(3)); s = " hello "; LLStringUtil::trim(s); - ensure_equals("ASCII trims as it always did", s, std::string("hello")); + UNSCOPED_INFO("ASCII trims as it always did"); + CHECK(s == std::string("hello")); s.clear(); LLStringUtil::trim(s); - ensure("an empty string survives", s.empty()); + UNSCOPED_INFO("an empty string survives"); + REQUIRE(s.empty()); } // The view forms cut where the string forms cut, and utf8str_trim shares // that decision rather than keeping a second one. - template<> template<> - void llstring_utf_object_t::test<144>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: the view forms cut where the string forms cut", "[LLStringUTF]") { const std::string src = std::string("\xC2\xA0") + "\t" + "caf\xC3\xA9" + "\xE3\x80\x80"; const std::string expected = "caf\xC3\xA9"; std::string owned = src; LLStringUtil::trim(owned); - ensure_equals("the string form", owned, expected); + UNSCOPED_INFO("the string form"); + CHECK(owned == expected); std::string_view view = src; LLStringUtil::trim(view); - ensure_equals("the view form cuts the same place", std::string(view), expected); + UNSCOPED_INFO("the view form cuts the same place"); + CHECK(std::string(view) == expected); - ensure_equals("utf8str_trim agrees", utf8str_trim(src), expected); + UNSCOPED_INFO("utf8str_trim agrees"); + CHECK(utf8str_trim(src) == expected); } // Where each cluster starts, walked at once, is where stepping one at a // time stops: the combining acute and the flag's two indicators kept // with what they belong to; none past the end asked for. - template<> template<> - void llstring_utf_object_t::test<145>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_grapheme_starts stops where stepping stops", "[LLStringUTF]") { const std::string utf8 = "ab " @@ -2932,17 +3305,22 @@ namespace tut { stepped.push_back(at); } - ensure("the same stops", utf8str_grapheme_starts(utf8, utf8.size()) == stepped); - ensure("those before the end asked for", utf8str_grapheme_starts(utf8, 11) == std::vector({ 0, 1, 2, 3, 6, 9, 10 })); - ensure("nothing of nothing", utf8str_grapheme_starts(std::string(), 5).empty() && utf8str_grapheme_starts(utf8, 0).empty()); + UNSCOPED_INFO("the same stops"); + REQUIRE(utf8str_grapheme_starts(utf8, utf8.size()) == stepped); + UNSCOPED_INFO("those before the end asked for"); + CHECK(utf8str_grapheme_starts(utf8, 11) == std::vector({ 0, 1, 2, 3, 6, 9, 10 })); + { + INFO("nothing of nothing"); + REQUIRE(utf8str_grapheme_starts(std::string(), 5).empty()); + REQUIRE(utf8str_grapheme_starts(utf8, 0).empty()); + } } // Every word at once is every word utf8str_next_word_range finds one at // a time from the start: over lines, contractions, numbers, scripts // that do not space their words, combining marks, emoji, blank lines // and nothing at all. - template<> template<> - void llstring_utf_object_t::test<146>() + TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_word_ranges finds the words next_word_range does", "[LLStringUTF]") { const std::string texts[] = { "", @@ -2972,10 +3350,12 @@ namespace tut } std::vector> all = { { 9, 9 } }; utf8str_word_ranges(text, all); - ensure("the same words in \"" + text + "\"", all == stepped); + UNSCOPED_INFO("the same words in \"" + text + "\""); + REQUIRE(all == stepped); } std::vector> words; utf8str_word_ranges("don't stop", words); - ensure("a contraction is one word", words == std::vector>({ { 0, 5 }, { 6, 10 } })); + UNSCOPED_INFO("a contraction is one word"); + CHECK(words == std::vector>({ { 0, 5 }, { 6, 10 } })); } } diff --git a/indra/llcommon/tests/lltimer_test.cpp b/indra/llcommon/tests/lltimer_test.cpp index 22e9cf5216..7f791c67bf 100644 --- a/indra/llcommon/tests/lltimer_test.cpp +++ b/indra/llcommon/tests/lltimer_test.cpp @@ -26,7 +26,7 @@ #include "../lltimer.h" -#include "../test/lltut.h" +#include #include #include @@ -34,7 +34,7 @@ #include #include -namespace tut +namespace { struct timer_data { @@ -42,74 +42,69 @@ namespace tut // LLCommon::initClass, which the test runner does not call. timer_data() { LLTimer::initClass(); } }; - typedef test_group timer_group; - typedef timer_group::object timer_object; - tut::timer_group timer_instance("LLTimer"); - template<> template<> - void timer_object::test<1>() + TEST_CASE_METHOD(timer_data, "LLTimer: a new timer reads expired until it is given an expiry", "[LLTimer]") { - set_test_name("a new timer reads expired until it is given an expiry"); LLTimer timer; - ensure("expired", timer.hasExpired()); + UNSCOPED_INFO("expired"); + CHECK(timer.hasExpired()); timer.setTimerExpirySec(10.f); - ensure("not expired once given ten seconds", !timer.hasExpired()); + UNSCOPED_INFO("not expired once given ten seconds"); + CHECK_FALSE(timer.hasExpired()); } - template<> template<> - void timer_object::test<2>() + TEST_CASE_METHOD(timer_data, "LLTimer: resetWithExpiry restarts the timer and keeps the expiry", "[LLTimer]") { - set_test_name("resetWithExpiry restarts the timer and keeps the expiry"); LLTimer timer; ms_sleep(50); timer.resetWithExpiry(10.f); - ensure("not expired", !timer.hasExpired()); - ensure("the whole expiry to run", timer.getRemainingTimeF32() > 9.9f); - ensure("restarted", timer.getElapsedTimeF32() < 0.04f); + UNSCOPED_INFO("not expired"); + CHECK_FALSE(timer.hasExpired()); + UNSCOPED_INFO("the whole expiry to run"); + CHECK(timer.getRemainingTimeF32() > 9.9f); + UNSCOPED_INFO("restarted"); + CHECK(timer.getElapsedTimeF32() < 0.04f); } - template<> template<> - void timer_object::test<3>() + TEST_CASE_METHOD(timer_data, "LLTimer: start() after an expiry clears it which is why resetWithExpiry exists", "[LLTimer]") { - set_test_name("start() after an expiry clears it, which is why resetWithExpiry exists"); LLTimer timer; timer.setTimerExpirySec(10.f); timer.start(); - ensure("start() cleared the expiry", timer.hasExpired()); + UNSCOPED_INFO("start() cleared the expiry"); + CHECK(timer.hasExpired()); } - template<> template<> - void timer_object::test<4>() + TEST_CASE_METHOD(timer_data, "LLTimer: the uptime is the application's and not this timer's", "[LLTimer]") { - set_test_name("the uptime is the application's, not this timer's"); LLTimer timer; ms_sleep(50); timer.reset(); - ensure("the timer was just reset", timer.getElapsedTimeF64() < 0.04); - ensure("the uptime runs from before the reset", LLTimer::getUptimeSeconds() > timer.getElapsedTimeF64()); + UNSCOPED_INFO("the timer was just reset"); + CHECK(timer.getElapsedTimeF64() < 0.04); + UNSCOPED_INFO("the uptime runs from before the reset"); + CHECK(LLTimer::getUptimeSeconds() > timer.getElapsedTimeF64()); } - template<> template<> - void timer_object::test<5>() + TEST_CASE_METHOD(timer_data, "LLTimer: total time matches the calendar and keeps pace with the steady clock", "[LLTimer]") { - set_test_name("the total time reads the calendar once and keeps pace with the steady clock"); const auto calendar = std::chrono::system_clock::now().time_since_epoch(); const F64 calendar_seconds = std::chrono::duration(calendar).count(); const F64 total = LLTimer::getTotalSeconds(); - ensure("anchored to the calendar", std::fabs(total - calendar_seconds) < 1.0); + UNSCOPED_INFO("anchored to the calendar"); + CHECK(std::fabs(total - calendar_seconds) < 1.0); const auto steady_start = std::chrono::steady_clock::now(); const U64 total_start = totalTime(); ms_sleep(200); const F64 steady = std::chrono::duration(std::chrono::steady_clock::now() - steady_start).count(); const F64 counted = (F64)((U64)totalTime() - total_start) / 1000000.0; - ensure("counts as the steady clock does", std::fabs(counted - steady) < 0.005); + UNSCOPED_INFO("counts as the steady clock does"); + CHECK(std::fabs(counted - steady) < 0.005); } - template<> template<> - void timer_object::test<6>() + TEST_CASE_METHOD(timer_data, "LLTimer: total time read on many threads never steps back or runs fast", "[LLTimer]") { - set_test_name("the total time read on many threads at once never steps and never runs fast"); const auto steady_start = std::chrono::steady_clock::now(); const U64 total_start = totalTime(); std::atomic stepped_back{ false }; @@ -136,7 +131,9 @@ namespace tut } const F64 steady = std::chrono::duration(std::chrono::steady_clock::now() - steady_start).count(); const F64 counted = (F64)((U64)totalTime() - total_start) / 1000000.0; - ensure("no thread saw it step back", !stepped_back); - ensure("counted no faster than the steady clock", counted < steady + 0.005); + UNSCOPED_INFO("no thread saw it step back"); + CHECK_FALSE(stepped_back); + UNSCOPED_INFO("counted no faster than the steady clock"); + CHECK(counted < steady + 0.005); } } diff --git a/indra/llcommon/tests/lltrace_test.cpp b/indra/llcommon/tests/lltrace_test.cpp index af8fe63911..fd1a42c0c6 100644 --- a/indra/llcommon/tests/lltrace_test.cpp +++ b/indra/llcommon/tests/lltrace_test.cpp @@ -30,7 +30,7 @@ #include "lltrace.h" #include "lltracethreadrecorder.h" #include "lltracerecording.h" -#include "../test/lltut.h" +#include #ifdef LL_WINDOWS #pragma warning(disable : 4244) // possible loss of data on conversions @@ -59,12 +59,10 @@ LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Grams); LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Milligrams); -namespace tut +namespace { using namespace LLTrace; - namespace - { // A ThreadRecorder makes itself this thread's recorder, with its own // timer stack and current accumulators, and leaves all of them null // when it goes. This takes the main thread's as they were before the @@ -88,7 +86,6 @@ namespace tut LLThreadLocalSingletonPointer::setInstance(mTimers); } }; - } // anonymous namespace struct trace { @@ -97,10 +94,6 @@ namespace tut ThreadRecorder mRecorder; }; - typedef test_group trace_t; - typedef trace_t::object trace_object_t; - tut::trace_t tut_lltrace("LLTrace"); - static CountStatHandle sCupsOfCoffeeConsumed("coffeeconsumed", "Delicious cup of dark roast."); static SampleStatHandle sCaffeineLevelStat("caffeinelevel", "Coffee buzz quotient"); static EventStatHandle sOuncesPerCup("cupsize", "Large, huge, or ginormous"); @@ -121,8 +114,7 @@ namespace tut } // basic data collection - template<> template<> - void trace_object_t::test<1>() + TEST_CASE_METHOD(trace, "LLTrace: recordings collect counts and events and samples only while active", "[LLTrace]") { sample(sCaffeineLevelStat, sCaffeineLevel); @@ -150,30 +142,42 @@ namespace tut //after_3pm.stop(); //all_day.stop(); - ensure("count stats are counted when recording is active", - at_work.getSum(sCupsOfCoffeeConsumed) == 3 - && all_day.getSum(sCupsOfCoffeeConsumed) == 5 - && after_3pm.getSum(sCupsOfCoffeeConsumed) == 2); - ensure("measurement sums are counted when recording is active", - at_work.getSum(sOuncesPerCup) == S32Ounces(48) - && all_day.getSum(sOuncesPerCup) == S32Ounces(80) - && after_3pm.getSum(sOuncesPerCup) == S32Ounces(36)); - ensure("measurement min is specific to when recording is active", - at_work.getMin(sOuncesPerCup) == S32GrandeCup(1) - && all_day.getMin(sOuncesPerCup) == S32TallCup(1) - && after_3pm.getMin(sOuncesPerCup) == S32GrandeCup(1)); - ensure("measurement max is specific to when recording is active", - at_work.getMax(sOuncesPerCup) == S32GrandeCup(1) - && all_day.getMax(sOuncesPerCup) == S32VentiCup(1) - && after_3pm.getMax(sOuncesPerCup) == S32VentiCup(1)); - ensure("sample min is specific to when recording is active", - at_work.getMin(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1)).value() - && all_day.getMin(sCaffeineLevelStat) == F32Milligrams(0.f) - && after_3pm.getMin(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(2)).value()); - ensure("sample max is specific to when recording is active", - at_work.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3)).value() - && all_day.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3) + (S32Ounces)S32VentiCup(1)).value() - && after_3pm.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3) + (S32Ounces)S32VentiCup(1)).value()); + { + INFO("count stats are counted when recording is active"); + CHECK(at_work.getSum(sCupsOfCoffeeConsumed) == 3); + CHECK(all_day.getSum(sCupsOfCoffeeConsumed) == 5); + CHECK(after_3pm.getSum(sCupsOfCoffeeConsumed) == 2); + } + { + INFO("measurement sums are counted when recording is active"); + CHECK(at_work.getSum(sOuncesPerCup) == S32Ounces(48)); + CHECK(all_day.getSum(sOuncesPerCup) == S32Ounces(80)); + CHECK(after_3pm.getSum(sOuncesPerCup) == S32Ounces(36)); + } + { + INFO("measurement min is specific to when recording is active"); + CHECK(at_work.getMin(sOuncesPerCup) == S32GrandeCup(1)); + CHECK(all_day.getMin(sOuncesPerCup) == S32TallCup(1)); + CHECK(after_3pm.getMin(sOuncesPerCup) == S32GrandeCup(1)); + } + { + INFO("measurement max is specific to when recording is active"); + CHECK(at_work.getMax(sOuncesPerCup) == S32GrandeCup(1)); + CHECK(all_day.getMax(sOuncesPerCup) == S32VentiCup(1)); + CHECK(after_3pm.getMax(sOuncesPerCup) == S32VentiCup(1)); + } + { + INFO("sample min is specific to when recording is active"); + CHECK(at_work.getMin(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1)).value()); + CHECK(all_day.getMin(sCaffeineLevelStat) == F32Milligrams(0.f)); + CHECK(after_3pm.getMin(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(2)).value()); + } + { + INFO("sample max is specific to when recording is active"); + CHECK(at_work.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3)).value()); + CHECK(all_day.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3) + (S32Ounces)S32VentiCup(1)).value()); + CHECK(after_3pm.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3) + (S32Ounces)S32VentiCup(1)).value()); + } } } diff --git a/indra/llcommon/tests/lltreeiterators_test.cpp b/indra/llcommon/tests/lltreeiterators_test.cpp index 44932ce7f6..3288ca1db3 100644 --- a/indra/llcommon/tests/lltreeiterators_test.cpp +++ b/indra/llcommon/tests/lltreeiterators_test.cpp @@ -44,20 +44,14 @@ #include "../lltreeiterators.h" #include "../llpointer.h" -#include "../test/lltut.h" +#include /***************************************************************************** * tut test group *****************************************************************************/ -namespace tut +namespace { - struct iter_data - { - }; - typedef test_group iter_group; - typedef iter_group::object iter_object; - tut::iter_group ig("LLTreeIterators"); -} // namespace tut +} // anonymous namespace namespace { @@ -231,10 +225,9 @@ class PlainNode } // anonymous namespace -namespace tut +namespace { - template<> template<> - void iter_object::test<1>() + TEST_CASE("LLTreeIterators: LLLinkedIter walks a non-refcounted list", "[LLTreeIterators]") { // set_test_name("LLLinkedIter -- non-refcounted class"); PlainNode* last(new PlainNode("c")); @@ -251,10 +244,11 @@ namespace tut pni != end; ++pni) { // std::cout << (*pni)->name() << '\n'; - ensure("LLLinkedIter(NULL)", false); + UNSCOPED_INFO("LLLinkedIter(NULL)"); + REQUIRE(false); } - ensure(desc1, - verify(desc1, + UNSCOPED_INFO(desc1); + CHECK(verify(desc1, boost::make_iterator_range(LLLinkedIter(first, boost::bind(&PlainNode::mNext, _1)), LLLinkedIter()), @@ -263,8 +257,8 @@ namespace tut // std::cout << desc2 << ":\n"; // for (LLLinkedIter pni(first, boost::bind(&PlainNode::next, _1)); ! (pni == end); ++pni) // std::cout << (**pni).name() << '\n'; - ensure(desc2, - verify(desc2, + UNSCOPED_INFO(desc2); + CHECK(verify(desc2, boost::make_iterator_range(LLLinkedIter(first, boost::bind(&PlainNode::next, _1)), LLLinkedIter()), @@ -276,11 +270,13 @@ namespace tut } { LLLinkedIter pni(first, boost::bind(&PlainNode::next, _1)); - ensure_equals("first", (*pni++)->name(), "a"); - ensure_equals("second", (*pni )->name(), "b"); + UNSCOPED_INFO("first"); + CHECK((*pni++)->name() == "a"); + UNSCOPED_INFO("second"); + CHECK((*pni )->name() == "b"); } } -} // tut +} // anonymous namespace namespace { @@ -335,10 +331,9 @@ RCNode::~RCNode() } // anonymous namespace -namespace tut +namespace { - template<> template<> - void iter_object::test<2>() + TEST_CASE("LLTreeIterators: LLLinkedIter keeps a refcounted list alive", "[LLTreeIterators]") { // set_test_name("LLLinkedIter -- refcounted class"); LLLinkedIter rcni, end2; @@ -350,20 +345,20 @@ namespace tut rcni = LLLinkedIter(head, boost::bind(&RCNode::next, _1)); } // std::cout << "Now the LLLinkedIter is the only remaining reference to RCNode chain\n"; - ensure_equals(last_RCNode_destroyed, ""); - ensure(rcni != end2); - ensure_equals((*rcni)->name(), "x"); + CHECK(last_RCNode_destroyed == ""); + CHECK(rcni != end2); + CHECK((*rcni)->name() == "x"); ++rcni; - ensure_equals(last_RCNode_destroyed, "x"); - ensure(rcni != end2); - ensure_equals((*rcni)->name(), "y"); + CHECK(last_RCNode_destroyed == "x"); + CHECK(rcni != end2); + CHECK((*rcni)->name() == "y"); ++rcni; - ensure_equals(last_RCNode_destroyed, "y"); - ensure(rcni != end2); - ensure_equals((*rcni)->name(), "z"); + CHECK(last_RCNode_destroyed == "y"); + CHECK(rcni != end2); + CHECK((*rcni)->name() == "z"); ++rcni; - ensure_equals(last_RCNode_destroyed, "z"); - ensure(rcni == end2); + CHECK(last_RCNode_destroyed == "z"); + CHECK(rcni == end2); } } @@ -1093,26 +1088,24 @@ bool LLTreeIter_tests(const std::string& nodename, } // anonymous namespace -namespace tut +namespace { - template<> template<> - void iter_object::test<3>() + TEST_CASE("LLTreeIterators: LLTreeRootIter and LLTreeWalkIter visit each tree in order", "[LLTreeIterators]") { // set_test_name("LLTreeIter tests"); - ensure(LLTreeIter_tests + CHECK(LLTreeIter_tests ("TreeNode", boost::bind(&TreeNode::getParent, _1), boost::bind(&TreeNode::child_begin, _1), boost::bind(&TreeNode::child_end, _1))); - ensure(LLTreeIter_tests > + CHECK(LLTreeIter_tests > ("PlainTree", boost::bind(&PlainTree::mParent, _1), PlainTree_child_begin, PlainTree_child_end)); } - template<> template<> - void iter_object::test<4>() + TEST_CASE("LLTreeIterators: getRootRange() runs from a node to the root", "[LLTreeIterators]") { // set_test_name("getRootRange() tests"); // This test function illustrates the looping techniques described in the @@ -1133,8 +1126,8 @@ namespace tut { // std::cout << node->name() << '\n'; } - ensure(desc1, - verify(desc1, getRootRange(tnB2b), RootExpected())); + UNSCOPED_INFO(desc1); + CHECK(verify(desc1, getRootRange(tnB2b), RootExpected())); EnhancedTreeNodePtr etnroot(example_tree()); EnhancedTreeNodePtr etnB2b(get_B2b @@ -1158,12 +1151,11 @@ namespace tut { // std::cout << node->name() << '\n'; } - ensure(desc2, - verify(desc2, etnB2b->getRootRange(), RootExpected())); + UNSCOPED_INFO(desc2); + CHECK(verify(desc2, etnB2b->getRootRange(), RootExpected())); } - template<> template<> - void iter_object::test<5>() + TEST_CASE("LLTreeIterators: getWalkRange() walks each kind of tree in each order", "[LLTreeIterators]") { // set_test_name("getWalkRange() tests"); // This test function doesn't illustrate the looping permutations for @@ -1173,36 +1165,36 @@ namespace tut // TreeNode, using helper function TreeNodePtr tnroot(example_tree()); std::string desc_tnpre("getWalkRange(tnroot)"); - ensure(desc_tnpre, - verify(desc_tnpre, + UNSCOPED_INFO(desc_tnpre); + CHECK(verify(desc_tnpre, getWalkRange(tnroot), WalkExpected())); std::string desc_tnpost("getWalkRange(tnroot)"); - ensure(desc_tnpost, - verify(desc_tnpost, + UNSCOPED_INFO(desc_tnpost); + CHECK(verify(desc_tnpost, getWalkRange(tnroot), WalkExpected())); std::string desc_tnb("getWalkRange(tnroot)"); - ensure(desc_tnb, - verify(desc_tnb, + UNSCOPED_INFO(desc_tnb); + CHECK(verify(desc_tnb, getWalkRange(tnroot), WalkExpected())); // EnhancedTreeNode, using method EnhancedTreeNodePtr etnroot(example_tree()); std::string desc_etnpre("etnroot->getWalkRange()"); - ensure(desc_etnpre, - verify(desc_etnpre, + UNSCOPED_INFO(desc_etnpre); + CHECK(verify(desc_etnpre, etnroot->getWalkRange(), WalkExpected())); std::string desc_etnpost("etnroot->getWalkRange()"); - ensure(desc_etnpost, - verify(desc_etnpost, + UNSCOPED_INFO(desc_etnpost); + CHECK(verify(desc_etnpost, etnroot->getWalkRange(), WalkExpected())); std::string desc_etnb("etnroot->getWalkRange()"); - ensure(desc_etnb, - verify(desc_etnb, + UNSCOPED_INFO(desc_etnb); + CHECK(verify(desc_etnb, etnroot->getWalkRange(), WalkExpected())); @@ -1210,19 +1202,19 @@ namespace tut PlainTree* ptroot(example_tree()); Cleanup cleanup(ptroot); std::string desc_ptpre("getWalkRange(ptroot)"); - ensure(desc_ptpre, - verify(desc_ptpre, + UNSCOPED_INFO(desc_ptpre); + CHECK(verify(desc_ptpre, getWalkRange(ptroot), WalkExpected())); std::string desc_ptpost("getWalkRange(ptroot)"); - ensure(desc_ptpost, - verify(desc_ptpost, + UNSCOPED_INFO(desc_ptpost); + CHECK(verify(desc_ptpost, getWalkRange(ptroot), WalkExpected())); std::string desc_ptb("getWalkRange(ptroot)"); - ensure(desc_ptb, - verify(desc_ptb, + UNSCOPED_INFO(desc_ptb); + CHECK(verify(desc_ptb, getWalkRange(ptroot), WalkExpected())); } -} // tut +} // anonymous namespace diff --git a/indra/llcommon/tests/llunits_test.cpp b/indra/llcommon/tests/llunits_test.cpp index cb7924f630..1f3d743e18 100644 --- a/indra/llcommon/tests/llunits_test.cpp +++ b/indra/llcommon/tests/llunits_test.cpp @@ -28,7 +28,7 @@ #include "linden_common.h" #include "llunits.h" -#include "../test/lltut.h" +#include namespace LLUnits { @@ -60,147 +60,158 @@ LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Fahrenheit); LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Kelvin); -namespace tut +namespace { using namespace LLUnits; - struct units - { - }; - - typedef test_group units_t; - typedef units_t::object units_object_t; - tut::units_t tut_llunits("LLUnit"); // storage type conversions - template<> template<> - void units_object_t::test<1>() + TEST_CASE("LLUnit: units convert between storage types and integers truncate", "[LLUnit]") { LLUnit float_quatloos; - ensure("default float unit is zero", float_quatloos == F32Quatloos(0.f)); + UNSCOPED_INFO("default float unit is zero"); + CHECK(float_quatloos == F32Quatloos(0.f)); LLUnit float_initialize_quatloos(1); - ensure("non-zero initialized unit", float_initialize_quatloos == F32Quatloos(1.f)); + UNSCOPED_INFO("non-zero initialized unit"); + CHECK(float_initialize_quatloos == F32Quatloos(1.f)); LLUnit int_quatloos; - ensure("default int unit is zero", int_quatloos == S32Quatloos(0)); + UNSCOPED_INFO("default int unit is zero"); + CHECK(int_quatloos == S32Quatloos(0)); int_quatloos = S32Quatloos(42); - ensure("int assignment is preserved", int_quatloos == S32Quatloos(42)); + UNSCOPED_INFO("int assignment is preserved"); + CHECK(int_quatloos == S32Quatloos(42)); float_quatloos = int_quatloos; - ensure("float assignment from int preserves value", float_quatloos == F32Quatloos(42.f)); + UNSCOPED_INFO("float assignment from int preserves value"); + CHECK(float_quatloos == F32Quatloos(42.f)); int_quatloos = float_quatloos; - ensure("int assignment from float preserves value", int_quatloos == S32Quatloos(42)); + UNSCOPED_INFO("int assignment from float preserves value"); + CHECK(int_quatloos == S32Quatloos(42)); float_quatloos = F32Quatloos(42.1f); int_quatloos = float_quatloos; - ensure("int units truncate float units on assignment", int_quatloos == S32Quatloos(42)); + UNSCOPED_INFO("int units truncate float units on assignment"); + CHECK(int_quatloos == S32Quatloos(42)); LLUnit unsigned_int_quatloos(float_quatloos); - ensure("unsigned int can be initialized from signed int", unsigned_int_quatloos == S32Quatloos(42)); + UNSCOPED_INFO("unsigned int can be initialized from signed int"); + CHECK(unsigned_int_quatloos == S32Quatloos(42)); S32Solari int_solari(1); float_quatloos = int_solari; - ensure("fractional units are preserved in conversion from integer to float type", float_quatloos == F32Quatloos(0.25f)); + UNSCOPED_INFO("fractional units are preserved in conversion from integer to float type"); + CHECK(float_quatloos == F32Quatloos(0.25f)); int_quatloos = S32Quatloos(1); F32Solari float_solari = int_quatloos; - ensure("can convert with fractional intermediates from integer to float type", float_solari == F32Solari(4.f)); + UNSCOPED_INFO("can convert with fractional intermediates from integer to float type"); + CHECK(float_solari == F32Solari(4.f)); } // conversions to/from base unit - template<> template<> - void units_object_t::test<2>() + TEST_CASE("LLUnit: units convert to and from their base unit", "[LLUnit]") { LLUnit quatloos(1.f); LLUnit latinum_bars(quatloos); - ensure("conversion between units is automatic via initialization", latinum_bars == F32Latinum(1.f / 4.f)); + UNSCOPED_INFO("conversion between units is automatic via initialization"); + CHECK(latinum_bars == F32Latinum(1.f / 4.f)); latinum_bars = S32Latinum(256); quatloos = latinum_bars; - ensure("conversion between units is automatic via assignment, and bidirectional", quatloos == S32Quatloos(1024)); + UNSCOPED_INFO("conversion between units is automatic via assignment, and bidirectional"); + CHECK(quatloos == S32Quatloos(1024)); LLUnit single_quatloo(1); LLUnit quarter_latinum = single_quatloo; - ensure("division of integer unit preserves fractional values when converted to float unit", quarter_latinum == F32Latinum(0.25f)); + UNSCOPED_INFO("division of integer unit preserves fractional values when converted to float unit"); + CHECK(quarter_latinum == F32Latinum(0.25f)); } // conversions across non-base units - template<> template<> - void units_object_t::test<3>() + TEST_CASE("LLUnit: non-base units convert into each other", "[LLUnit]") { LLUnit quatloos(1024); LLUnit solari(quatloos); - ensure("conversions can work between indirectly related units: Quatloos -> Latinum -> Solari", solari == S32Solari(4096)); + UNSCOPED_INFO("conversions can work between indirectly related units: Quatloos -> Latinum -> Solari"); + CHECK(solari == S32Solari(4096)); LLUnit latinum_bars = solari; - ensure("Non base units can be converted between each other", latinum_bars == S32Latinum(256)); + UNSCOPED_INFO("Non base units can be converted between each other"); + CHECK(latinum_bars == S32Latinum(256)); } // math operations - template<> template<> - void units_object_t::test<4>() + TEST_CASE("LLUnit: arithmetic works with scalars and with other units", "[LLUnit]") { // exercise math operations LLUnit quatloos(1.f); quatloos *= 4.f; - ensure(quatloos == S32Quatloos(4)); + CHECK(quatloos == S32Quatloos(4)); quatloos = quatloos * 2; - ensure(quatloos == S32Quatloos(8)); + CHECK(quatloos == S32Quatloos(8)); quatloos = 2.f * quatloos; - ensure(quatloos == S32Quatloos(16)); + CHECK(quatloos == S32Quatloos(16)); quatloos += F32Quatloos(4.f); - ensure(quatloos == S32Quatloos(20)); + CHECK(quatloos == S32Quatloos(20)); quatloos += S32Quatloos(4); - ensure(quatloos == S32Quatloos(24)); + CHECK(quatloos == S32Quatloos(24)); quatloos = quatloos + S32Quatloos(4); - ensure(quatloos == S32Quatloos(28)); + CHECK(quatloos == S32Quatloos(28)); quatloos = S32Quatloos(4) + quatloos; - ensure(quatloos == S32Quatloos(32)); + CHECK(quatloos == S32Quatloos(32)); quatloos += quatloos * 3; - ensure(quatloos == S32Quatloos(128)); + CHECK(quatloos == S32Quatloos(128)); quatloos -= quatloos / 4 * 3; - ensure(quatloos == S32Quatloos(32)); + CHECK(quatloos == S32Quatloos(32)); quatloos = quatloos - S32Quatloos(8); - ensure(quatloos == S32Quatloos(24)); + CHECK(quatloos == S32Quatloos(24)); quatloos -= S32Quatloos(4); - ensure(quatloos == S32Quatloos(20)); + CHECK(quatloos == S32Quatloos(20)); quatloos -= F32Quatloos(4.f); - ensure(quatloos == S32Quatloos(16)); + CHECK(quatloos == S32Quatloos(16)); quatloos /= 2.f; - ensure(quatloos == S32Quatloos(8)); + CHECK(quatloos == S32Quatloos(8)); quatloos = quatloos / 4; - ensure(quatloos == S32Quatloos(2)); + CHECK(quatloos == S32Quatloos(2)); F32 ratio = quatloos / LLUnit(2.f); - ensure(ratio == 1); + CHECK(ratio == 1); ratio = quatloos / LLUnit(8.f); - ensure(ratio == 1); + CHECK(ratio == 1); quatloos += LLUnit(8.f); - ensure(quatloos == S32Quatloos(4)); + CHECK(quatloos == S32Quatloos(4)); quatloos -= LLUnit(1.f); - ensure(quatloos == S32Quatloos(0)); + CHECK(quatloos == S32Quatloos(0)); } // comparison operators - template<> template<> - void units_object_t::test<5>() + TEST_CASE("LLUnit: units compare across storage types and units", "[LLUnit]") { LLUnit quatloos(1); - ensure("can perform less than comparison against same type", quatloos < S32Quatloos(2)); - ensure("can perform less than comparison against different storage type", quatloos < F32Quatloos(2.f)); - ensure("can perform less than comparison against different units", quatloos < S32Latinum(5)); - ensure("can perform less than comparison against different storage type and units", quatloos < F32Latinum(5.f)); - - ensure("can perform greater than comparison against same type", quatloos > S32Quatloos(0)); - ensure("can perform greater than comparison against different storage type", quatloos > F32Quatloos(0.f)); - ensure("can perform greater than comparison against different units", quatloos > S32Latinum(0)); - ensure("can perform greater than comparison against different storage type and units", quatloos > F32Latinum(0.f)); + UNSCOPED_INFO("can perform less than comparison against same type"); + CHECK(quatloos < S32Quatloos(2)); + UNSCOPED_INFO("can perform less than comparison against different storage type"); + CHECK(quatloos < F32Quatloos(2.f)); + UNSCOPED_INFO("can perform less than comparison against different units"); + CHECK(quatloos < S32Latinum(5)); + UNSCOPED_INFO("can perform less than comparison against different storage type and units"); + CHECK(quatloos < F32Latinum(5.f)); + + UNSCOPED_INFO("can perform greater than comparison against same type"); + CHECK(quatloos > S32Quatloos(0)); + UNSCOPED_INFO("can perform greater than comparison against different storage type"); + CHECK(quatloos > F32Quatloos(0.f)); + UNSCOPED_INFO("can perform greater than comparison against different units"); + CHECK(quatloos > S32Latinum(0)); + UNSCOPED_INFO("can perform greater than comparison against different storage type and units"); + CHECK(quatloos > F32Latinum(0.f)); } @@ -210,116 +221,138 @@ namespace tut } // signature compatibility - template<> template<> - void units_object_t::test<6>() + TEST_CASE("LLUnit: unit values pass as function arguments", "[LLUnit]") { S32Quatloos quatloos(1); - ensure("can pass unit values as argument", accept_explicit_quatloos(S32Quatloos(1))); - ensure("can pass unit values as argument", accept_explicit_quatloos(quatloos)); + UNSCOPED_INFO("can pass unit values as argument"); + CHECK(accept_explicit_quatloos(S32Quatloos(1))); + UNSCOPED_INFO("can pass unit values as argument"); + CHECK(accept_explicit_quatloos(quatloos)); } // implicit units - template<> template<> - void units_object_t::test<7>() + TEST_CASE("LLUnit: implicit units mix with explicit units and plain values", "[LLUnit]") { LLUnit quatloos; LLUnitImplicit quatloos_implicit = quatloos + S32Quatloos(1); - ensure("can initialize implicit unit from explicit", quatloos_implicit == 1); + UNSCOPED_INFO("can initialize implicit unit from explicit"); + CHECK(quatloos_implicit == 1); quatloos = quatloos_implicit; - ensure("can assign implicit unit to explicit unit", quatloos == S32Quatloos(1)); + UNSCOPED_INFO("can assign implicit unit to explicit unit"); + CHECK(quatloos == S32Quatloos(1)); quatloos += quatloos_implicit; - ensure("can perform math operation using mixture of implicit and explicit units", quatloos == S32Quatloos(2)); + UNSCOPED_INFO("can perform math operation using mixture of implicit and explicit units"); + CHECK(quatloos == S32Quatloos(2)); // math operations on implicits quatloos_implicit = 1; - ensure(quatloos_implicit == 1); + CHECK(quatloos_implicit == 1); quatloos_implicit += 2; - ensure(quatloos_implicit == 3); + CHECK(quatloos_implicit == 3); quatloos_implicit *= 2; - ensure(quatloos_implicit == 6); + CHECK(quatloos_implicit == 6); quatloos_implicit -= 1; - ensure(quatloos_implicit == 5); + CHECK(quatloos_implicit == 5); quatloos_implicit /= 5; - ensure(quatloos_implicit == 1); + CHECK(quatloos_implicit == 1); quatloos_implicit = quatloos_implicit + 3 + quatloos_implicit; - ensure(quatloos_implicit == 5); + CHECK(quatloos_implicit == 5); quatloos_implicit = 10 - quatloos_implicit - 1; - ensure(quatloos_implicit == 4); + CHECK(quatloos_implicit == 4); quatloos_implicit = 2 * quatloos_implicit * 2; - ensure(quatloos_implicit == 16); + CHECK(quatloos_implicit == 16); F32 one_half = quatloos_implicit / (quatloos_implicit * 2); - ensure(one_half == 0.5f); + CHECK(one_half == 0.5f); // implicit conversion to POD F32 float_val = quatloos_implicit; - ensure("implicit units convert implicitly to regular values", float_val == 16); + UNSCOPED_INFO("implicit units convert implicitly to regular values"); + CHECK(float_val == 16); S32 int_val = (S32)quatloos_implicit; - ensure("implicit units convert implicitly to regular values", int_val == 16); + UNSCOPED_INFO("implicit units convert implicitly to regular values"); + CHECK(int_val == 16); // conversion of implicits LLUnitImplicit latinum_implicit(2); - ensure("implicit units of different types are comparable", latinum_implicit * 2 == quatloos_implicit); + UNSCOPED_INFO("implicit units of different types are comparable"); + CHECK(latinum_implicit * 2 == quatloos_implicit); quatloos_implicit += F32Quatloos(10); - ensure("can add-assign explicit units", quatloos_implicit == 26); + UNSCOPED_INFO("can add-assign explicit units"); + CHECK(quatloos_implicit == 26); quatloos_implicit -= F32Quatloos(10); - ensure("can subtract-assign explicit units", quatloos_implicit == 16); + UNSCOPED_INFO("can subtract-assign explicit units"); + CHECK(quatloos_implicit == 16); // comparisons - ensure("can compare greater than implicit unit", quatloos_implicit > F32QuatloosImplicit(0.f)); - ensure("can compare greater than non-implicit unit", quatloos_implicit > F32Quatloos(0.f)); - ensure("can compare greater than or equal to implicit unit", quatloos_implicit >= F32QuatloosImplicit(0.f)); - ensure("can compare greater than or equal to non-implicit unit", quatloos_implicit >= F32Quatloos(0.f)); - ensure("can compare less than implicit unit", quatloos_implicit < F32QuatloosImplicit(20.f)); - ensure("can compare less than non-implicit unit", quatloos_implicit < F32Quatloos(20.f)); - ensure("can compare less than or equal to implicit unit", quatloos_implicit <= F32QuatloosImplicit(20.f)); - ensure("can compare less than or equal to non-implicit unit", quatloos_implicit <= F32Quatloos(20.f)); + UNSCOPED_INFO("can compare greater than implicit unit"); + CHECK(quatloos_implicit > F32QuatloosImplicit(0.f)); + UNSCOPED_INFO("can compare greater than non-implicit unit"); + CHECK(quatloos_implicit > F32Quatloos(0.f)); + UNSCOPED_INFO("can compare greater than or equal to implicit unit"); + CHECK(quatloos_implicit >= F32QuatloosImplicit(0.f)); + UNSCOPED_INFO("can compare greater than or equal to non-implicit unit"); + CHECK(quatloos_implicit >= F32Quatloos(0.f)); + UNSCOPED_INFO("can compare less than implicit unit"); + CHECK(quatloos_implicit < F32QuatloosImplicit(20.f)); + UNSCOPED_INFO("can compare less than non-implicit unit"); + CHECK(quatloos_implicit < F32Quatloos(20.f)); + UNSCOPED_INFO("can compare less than or equal to implicit unit"); + CHECK(quatloos_implicit <= F32QuatloosImplicit(20.f)); + UNSCOPED_INFO("can compare less than or equal to non-implicit unit"); + CHECK(quatloos_implicit <= F32Quatloos(20.f)); } // precision tests - template<> template<> - void units_object_t::test<8>() + TEST_CASE("LLUnit: conversions keep full precision and mix signed with unsigned", "[LLUnit]") { U32Bytes max_bytes(U32_MAX); S32Megabytes mega_bytes = max_bytes; - ensure("max available precision is used when converting units", mega_bytes == (S32Megabytes)4095); + UNSCOPED_INFO("max available precision is used when converting units"); + CHECK(mega_bytes == (S32Megabytes)4095); mega_bytes = (S32Megabytes)-5 + (U32Megabytes)1; - ensure("can mix signed and unsigned in units addition", mega_bytes == (S32Megabytes)-4); + UNSCOPED_INFO("can mix signed and unsigned in units addition"); + CHECK(mega_bytes == (S32Megabytes)-4); mega_bytes = (U32Megabytes)5 + (S32Megabytes)-1; - ensure("can mix unsigned and signed in units addition", mega_bytes == (S32Megabytes)4); + UNSCOPED_INFO("can mix unsigned and signed in units addition"); + CHECK(mega_bytes == (S32Megabytes)4); } // default units - template<> template<> - void units_object_t::test<9>() + TEST_CASE("LLUnit: the built-in units convert by their factors", "[LLUnit]") { U32Gigabytes GB(1); U32Megabytes MB(GB); U32Kilobytes KB(GB); U32Bytes B(GB); - ensure("GB -> MB conversion", MB.value() == 1024); - ensure("GB -> KB conversion", KB.value() == 1024 * 1024); - ensure("GB -> B conversion", B.value() == 1024 * 1024 * 1024); + UNSCOPED_INFO("GB -> MB conversion"); + CHECK(MB.value() == 1024); + UNSCOPED_INFO("GB -> KB conversion"); + CHECK(KB.value() == 1024 * 1024); + UNSCOPED_INFO("GB -> B conversion"); + CHECK(B.value() == 1024 * 1024 * 1024); KB = U32Kilobytes(1); U32Kilobits Kb(KB); U32Bits b(KB); - ensure("KB -> Kb conversion", Kb.value() == 8); - ensure("KB -> b conversion", b.value() == 8 * 1024); + UNSCOPED_INFO("KB -> Kb conversion"); + CHECK(Kb.value() == 8); + UNSCOPED_INFO("KB -> b conversion"); + CHECK(b.value() == 8 * 1024); U32Days days(1); U32Hours hours(days); @@ -327,40 +360,53 @@ namespace tut U32Seconds seconds(days); U32Milliseconds ms(days); - ensure("days -> hours conversion", hours.value() == 24); - ensure("days -> minutes conversion", minutes.value() == 24 * 60); - ensure("days -> seconds conversion", seconds.value() == 24 * 60 * 60); - ensure("days -> ms conversion", ms.value() == 24 * 60 * 60 * 1000); + UNSCOPED_INFO("days -> hours conversion"); + CHECK(hours.value() == 24); + UNSCOPED_INFO("days -> minutes conversion"); + CHECK(minutes.value() == 24 * 60); + UNSCOPED_INFO("days -> seconds conversion"); + CHECK(seconds.value() == 24 * 60 * 60); + UNSCOPED_INFO("days -> ms conversion"); + CHECK(ms.value() == 24 * 60 * 60 * 1000); U32Kilometers km(1); U32Meters m(km); U32Centimeters cm(km); U32Millimeters mm(km); - ensure("km -> m conversion", m.value() == 1000); - ensure("km -> cm conversion", cm.value() == 1000 * 100); - ensure("km -> mm conversion", mm.value() == 1000 * 1000); + UNSCOPED_INFO("km -> m conversion"); + CHECK(m.value() == 1000); + UNSCOPED_INFO("km -> cm conversion"); + CHECK(cm.value() == 1000 * 100); + UNSCOPED_INFO("km -> mm conversion"); + CHECK(mm.value() == 1000 * 1000); U32Gigahertz GHz(1); U32Megahertz MHz(GHz); U32Kilohertz KHz(GHz); U32Hertz Hz(GHz); - ensure("GHz -> MHz conversion", MHz.value() == 1000); - ensure("GHz -> KHz conversion", KHz.value() == 1000 * 1000); - ensure("GHz -> Hz conversion", Hz.value() == 1000 * 1000 * 1000); + UNSCOPED_INFO("GHz -> MHz conversion"); + CHECK(MHz.value() == 1000); + UNSCOPED_INFO("GHz -> KHz conversion"); + CHECK(KHz.value() == 1000 * 1000); + UNSCOPED_INFO("GHz -> Hz conversion"); + CHECK(Hz.value() == 1000 * 1000 * 1000); F32Radians rad(6.2831853071795f); S32Degrees deg(rad); - ensure("radians -> degrees conversion", deg.value() == 360); + UNSCOPED_INFO("radians -> degrees conversion"); + CHECK(deg.value() == 360); F32Percent percent(50); F32Ratio ratio(percent); - ensure("percent -> ratio conversion", ratio.value() == 0.5f); + UNSCOPED_INFO("percent -> ratio conversion"); + CHECK(ratio.value() == 0.5f); U32Kilotriangles ktris(1); U32Triangles tris(ktris); - ensure("kilotriangles -> triangles conversion", tris.value() == 1000); + UNSCOPED_INFO("kilotriangles -> triangles conversion"); + CHECK(tris.value() == 1000); } static bool value_near(F32 value, F32 target, F32 threshold) @@ -369,21 +415,24 @@ namespace tut } // linear transforms - template<> template<> - void units_object_t::test<10>() + TEST_CASE("LLUnit: linear transforms convert Celsius to Fahrenheit and back", "[LLUnit]") { F32Celcius float_celcius(100); F32Fahrenheit float_fahrenheit(float_celcius); - ensure("floating point celcius -> fahrenheit conversion using linear transform", value_near(float_fahrenheit.value(), 212, 0.1f) ); + UNSCOPED_INFO("floating point celcius -> fahrenheit conversion using linear transform"); + CHECK(value_near(float_fahrenheit.value(), 212, 0.1f)); float_celcius = float_fahrenheit; - ensure("floating point fahrenheit -> celcius conversion using linear transform (round trip)", value_near(float_celcius.value(), 100.f, 0.1f) ); + UNSCOPED_INFO("floating point fahrenheit -> celcius conversion using linear transform (round trip)"); + CHECK(value_near(float_celcius.value(), 100.f, 0.1f)); S32Celcius int_celcius(100); S32Fahrenheit int_fahrenheit(int_celcius); - ensure("integer celcius -> fahrenheit conversion using linear transform", int_fahrenheit.value() == 212); + UNSCOPED_INFO("integer celcius -> fahrenheit conversion using linear transform"); + CHECK(int_fahrenheit.value() == 212); int_celcius = int_fahrenheit; - ensure("integer fahrenheit -> celcius conversion using linear transform (round trip)", int_celcius.value() == 100); + UNSCOPED_INFO("integer fahrenheit -> celcius conversion using linear transform (round trip)"); + CHECK(int_celcius.value() == 100); } } diff --git a/indra/llcommon/tests/lluri_test.cpp b/indra/llcommon/tests/lluri_test.cpp index b48dd3ef1b..a3544df2b6 100644 --- a/indra/llcommon/tests/lluri_test.cpp +++ b/indra/llcommon/tests/lluri_test.cpp @@ -30,9 +30,9 @@ #include "../llsd.h" #include "../lluri.h" -#include "../test/lltut.h" +#include -namespace tut +namespace { struct URITestData { void checkParts(const LLURI& u, @@ -42,79 +42,82 @@ namespace tut const char* expectedPath, const char* expectedQuery = "") { - ensure_equals("scheme", u.scheme(), expectedScheme); - ensure_equals("opaque", u.opaque(), expectedOpaque); - ensure_equals("authority", u.authority(), expectedAuthority); - ensure_equals("path", u.path(), expectedPath); - ensure_equals("query", u.query(), expectedQuery); + UNSCOPED_INFO("scheme"); + REQUIRE(u.scheme() == expectedScheme); + UNSCOPED_INFO("opaque"); + REQUIRE(u.opaque() == expectedOpaque); + UNSCOPED_INFO("authority"); + REQUIRE(u.authority() == expectedAuthority); + UNSCOPED_INFO("path"); + REQUIRE(u.path() == expectedPath); + UNSCOPED_INFO("query"); + REQUIRE(u.query() == expectedQuery); } void escapeRoundTrip(const std::string& uri_raw_1) { std::string uri_esc_1(LLURI::escape(uri_raw_1)); std::string uri_raw_2(LLURI::unescape(uri_esc_1)); - ensure_equals("escape/unescape raw", uri_raw_2, uri_raw_1); + UNSCOPED_INFO("escape/unescape raw"); + REQUIRE(uri_raw_2 == uri_raw_1); std::string uri_esc_2(LLURI::escape(uri_raw_2)); - ensure_equals("escape/unescape escaped", uri_esc_2, uri_esc_1); + UNSCOPED_INFO("escape/unescape escaped"); + REQUIRE(uri_esc_2 == uri_esc_1); } }; - typedef test_group URITestGroup; - typedef URITestGroup::object URITestObject; - - URITestGroup uriTestGroup("LLURI"); - - template<> template<> - void URITestObject::test<1>() + TEST_CASE_METHOD(URITestData, "LLURI: parts and query map of an http URI", "[LLURI]") { LLURI u("http://abc.com/def/ghi?x=37&y=hello"); - ensure_equals("scheme", u.scheme(), "http"); - ensure_equals("authority", u.authority(), "abc.com"); - ensure_equals("path", u.path(), "/def/ghi"); - ensure_equals("query", u.query(), "x=37&y=hello"); + UNSCOPED_INFO("scheme"); + CHECK(u.scheme() == "http"); + UNSCOPED_INFO("authority"); + CHECK(u.authority() == "abc.com"); + UNSCOPED_INFO("path"); + CHECK(u.path() == "/def/ghi"); + UNSCOPED_INFO("query"); + CHECK(u.query() == "x=37&y=hello"); - ensure_equals("host name", u.hostName(), "abc.com"); - ensure_equals("host port", u.hostPort(), 80); + UNSCOPED_INFO("host name"); + CHECK(u.hostName() == "abc.com"); + UNSCOPED_INFO("host port"); + CHECK(u.hostPort() == 80); LLSD query = u.queryMap(); - ensure_equals("query x", query["x"].asInteger(), 37); - ensure_equals("query y", query["y"].asString(), "hello"); + UNSCOPED_INFO("query x"); + CHECK(query["x"].asInteger() == 37); + UNSCOPED_INFO("query y"); + CHECK(query["y"].asString() == "hello"); query = LLURI::queryMap("x=22.23&y=https://lindenlab.com/"); - ensure_equals("query x", query["x"].asReal(), 22.23); - ensure_equals("query y", query["y"].asURI().asString(), "https://lindenlab.com/"); + UNSCOPED_INFO("query x"); + CHECK(query["x"].asReal() == 22.23); + UNSCOPED_INFO("query y"); + CHECK(query["y"].asURI().asString() == "https://lindenlab.com/"); } - template<> template<> - void URITestObject::test<2>() + TEST_CASE_METHOD(URITestData, "LLURI: an empty string has no parts", "[LLURI]") { - set_test_name("empty string"); checkParts(LLURI(""), "", "", "", ""); } - template<> template<> - void URITestObject::test<3>() + TEST_CASE_METHOD(URITestData, "LLURI: without a scheme the whole string is opaque", "[LLURI]") { - set_test_name("no scheme"); checkParts(LLURI("foo"), "", "foo", "", ""); checkParts(LLURI("foo%3A"), "", "foo:", "", ""); } - template<> template<> - void URITestObject::test<4>() + TEST_CASE_METHOD(URITestData, "LLURI: a scheme without paths leaves no authority or path", "[LLURI]") { - set_test_name("scheme w/o paths"); checkParts(LLURI("mailto:zero@ll.com"), "mailto", "zero@ll.com", "", ""); checkParts(LLURI("silly://abc/def?foo"), "silly", "//abc/def?foo", "", ""); } - template<> template<> - void URITestObject::test<5>() + TEST_CASE_METHOD(URITestData, "LLURI: the authority ends at the first unescaped slash or question mark", "[LLURI]") { - set_test_name("authority section"); checkParts(LLURI("http:///"), "http", "///", "", "/"); @@ -128,10 +131,8 @@ namespace tut "http", "//host?", "host", ""); } - template<> template<> - void URITestObject::test<6>() + TEST_CASE_METHOD(URITestData, "LLURI: the path is unescaped and keeps colons and trailing slashes", "[LLURI]") { - set_test_name("path section"); checkParts(LLURI("http://host/a/b/"), "http", "//host/a/b/", "host", "/a/b/"); @@ -142,10 +143,8 @@ namespace tut "http", "//host/a:b/", "host", "/a:b/"); } - template<> template<> - void URITestObject::test<7>() + TEST_CASE_METHOD(URITestData, "LLURI: the query follows the first question mark and is unescaped", "[LLURI]") { - set_test_name("query string"); checkParts(LLURI("http://host/?"), "http", "//host/?", "host", "/", ""); @@ -159,8 +158,7 @@ namespace tut "http", "//host/??", "host", "/", "?"); } - template<> template<> - void URITestObject::test<8>() + TEST_CASE_METHOD(URITestData, "LLURI: buildHTTP joins and escapes path parts and a query map", "[LLURI]") { LLSD path; path.append("x"); @@ -175,36 +173,34 @@ namespace tut "http", "//host/x/123?123=12&abcd=abc", "host", "/x/123", "123=12&abcd=abc"); - ensure_equals(LLURI::buildHTTP("host", "").asString(), + CHECK(LLURI::buildHTTP("host", "").asString() == "http://host"); - ensure_equals(LLURI::buildHTTP("host", "/").asString(), + CHECK(LLURI::buildHTTP("host", "/").asString() == "http://host/"); - ensure_equals(LLURI::buildHTTP("host", "//").asString(), + CHECK(LLURI::buildHTTP("host", "//").asString() == "http://host/"); - ensure_equals(LLURI::buildHTTP("host", "dir name").asString(), + CHECK(LLURI::buildHTTP("host", "dir name").asString() == "http://host/dir%20name"); - ensure_equals(LLURI::buildHTTP("host", "dir name/").asString(), + CHECK(LLURI::buildHTTP("host", "dir name/").asString() == "http://host/dir%20name/"); - ensure_equals(LLURI::buildHTTP("host", "/dir name").asString(), + CHECK(LLURI::buildHTTP("host", "/dir name").asString() == "http://host/dir%20name"); - ensure_equals(LLURI::buildHTTP("host", "/dir name/").asString(), + CHECK(LLURI::buildHTTP("host", "/dir name/").asString() == "http://host/dir%20name/"); - ensure_equals(LLURI::buildHTTP("host", "dir name/subdir name").asString(), + CHECK(LLURI::buildHTTP("host", "dir name/subdir name").asString() == "http://host/dir%20name/subdir%20name"); - ensure_equals(LLURI::buildHTTP("host", "dir name/subdir name/").asString(), + CHECK(LLURI::buildHTTP("host", "dir name/subdir name/").asString() == "http://host/dir%20name/subdir%20name/"); - ensure_equals(LLURI::buildHTTP("host", "/dir name/subdir name").asString(), + CHECK(LLURI::buildHTTP("host", "/dir name/subdir name").asString() == "http://host/dir%20name/subdir%20name"); - ensure_equals(LLURI::buildHTTP("host", "/dir name/subdir name/").asString(), + CHECK(LLURI::buildHTTP("host", "/dir name/subdir name/").asString() == "http://host/dir%20name/subdir%20name/"); - ensure_equals(LLURI::buildHTTP("host", "//dir name//subdir name//").asString(), + CHECK(LLURI::buildHTTP("host", "//dir name//subdir name//").asString() == "http://host/dir%20name/subdir%20name/"); } - template<> template<> - void URITestObject::test<9>() + TEST_CASE_METHOD(URITestData, "LLURI: buildHTTP takes path parts with unescaped special characters", "[LLURI]") { - set_test_name("test unescaped path components"); LLSD path; path.append("x@*//*$&^"); path.append("123"); @@ -212,10 +208,8 @@ namespace tut "http", "//host/x@*//*$&^/123", "host", "/x@*//*$&^/123"); } - template<> template<> - void URITestObject::test<10>() + TEST_CASE_METHOD(URITestData, "LLURI: buildHTTP takes a query with unescaped special characters", "[LLURI]") { - set_test_name("test unescaped query components"); LLSD path; path.append("x"); path.append("123"); @@ -227,10 +221,8 @@ namespace tut "host", "/x/123", "**@&?//=abc&123=?&*#//"); } - template<> template<> - void URITestObject::test<11>() + TEST_CASE_METHOD(URITestData, "LLURI: buildHTTP takes a host with unescaped special characters", "[LLURI]") { - set_test_name("test unescaped host components"); LLSD path; path.append("x"); path.append("123"); @@ -242,11 +234,8 @@ namespace tut "hi123*33--}{:portstuffs", "/x/123", "123=12&abcd=abc"); } - template<> template<> - void URITestObject::test<12>() + TEST_CASE_METHOD(URITestData, "LLURI: buildHTTP takes a host_port that is really a URI prefix", "[LLURI]") { - set_test_name("test funky host_port values that are actually prefixes"); - checkParts(LLURI::buildHTTP("http://example.com:8080", LLSD()), "http", "//example.com:8080", "example.com:8080", ""); @@ -260,18 +249,18 @@ namespace tut "example.com:8080", "/a/b"); } - template<> template<> - void URITestObject::test<13>() + TEST_CASE_METHOD(URITestData, "LLURI: escape percent-encodes all but the allowed characters", "[LLURI]") { const std::string unreserved = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" "0123456789" "-._~"; - set_test_name("test escape"); - ensure_equals("escaping", LLURI::escape("abcdefg", "abcdef"), "abcdef%67"); - ensure_equals("escaping", LLURI::escape("|/&\\+-_!@", ""), "%7C%2F%26%5C%2B%2D%5F%21%40"); - ensure_equals("escaping as query variable", - LLURI::escape("http://10.0.1.4:12032/agent/god/agent-id/map/layer/?resume=http://station3.ll.com:12032/agent/203ad6df-b522-491d-ba48-4e24eb57aeff/send-postcard", unreserved + ":@!$'()*+,="), + UNSCOPED_INFO("escaping"); + CHECK(LLURI::escape("abcdefg", "abcdef") == "abcdef%67"); + UNSCOPED_INFO("escaping"); + CHECK(LLURI::escape("|/&\\+-_!@", "") == "%7C%2F%26%5C%2B%2D%5F%21%40"); + UNSCOPED_INFO("escaping as query variable"); + CHECK(LLURI::escape("http://10.0.1.4:12032/agent/god/agent-id/map/layer/?resume=http://station3.ll.com:12032/agent/203ad6df-b522-491d-ba48-4e24eb57aeff/send-postcard", unreserved + ":@!$'()*+,=") == "http:%2F%2F10.0.1.4:12032%2Fagent%2Fgod%2Fagent-id%2Fmap%2Flayer%2F%3Fresume=http:%2F%2Fstation3.ll.com:12032%2Fagent%2F203ad6df-b522-491d-ba48-4e24eb57aeff%2Fsend-postcard"); // French cedilla (C with squiggle, like in the word Francais) is UTF-8 C3 A7 @@ -282,24 +271,23 @@ namespace tut std::string cedilla; cedilla.push_back( (char)0xC3 ); cedilla.push_back( (char)0xA7 ); - ensure_equals("escape UTF8", LLURI::escape( cedilla, unreserved), "%C3%A7"); + UNSCOPED_INFO("escape UTF8"); + CHECK(LLURI::escape( cedilla, unreserved) == "%C3%A7"); } - template<> template<> - void URITestObject::test<14>() + TEST_CASE_METHOD(URITestData, "LLURI: escape and unescape of an empty string are empty", "[LLURI]") { - set_test_name("make sure escape and unescape of empty strings return empty strings."); std::string uri_esc(LLURI::escape("")); - ensure("escape string empty", uri_esc.empty()); + UNSCOPED_INFO("escape string empty"); + REQUIRE(uri_esc.empty()); std::string uri_raw(LLURI::unescape("")); - ensure("unescape string empty", uri_raw.empty()); + UNSCOPED_INFO("unescape string empty"); + REQUIRE(uri_raw.empty()); } - template<> template<> - void URITestObject::test<15>() + TEST_CASE_METHOD(URITestData, "LLURI: escape and unescape round-trip URIs", "[LLURI]") { - set_test_name("do some round-trip tests"); escapeRoundTrip("http://secondlife.com"); escapeRoundTrip("http://secondlife.com/url with spaces"); escapeRoundTrip("http://bad[domain]name.com/"); @@ -307,26 +295,20 @@ namespace tut escapeRoundTrip(""); } - template<> template<> - void URITestObject::test<16>() + TEST_CASE_METHOD(URITestData, "LLURI: escape by default escapes colons and slashes too", "[LLURI]") { - set_test_name("Test the default escaping"); // yes -- this mangles the url. This is expected behavior std::string simple("http://secondlife.com"); - ensure_equals( - "simple http", - LLURI::escape(simple), + UNSCOPED_INFO("simple http"); + CHECK(LLURI::escape(simple) == "http%3A%2F%2Fsecondlife.com"); - ensure_equals( - "needs escape", - LLURI::escape("http://get.secondlife.com/windows viewer"), + UNSCOPED_INFO("needs escape"); + CHECK(LLURI::escape("http://get.secondlife.com/windows viewer") == "http%3A%2F%2Fget.secondlife.com%2Fwindows%20viewer"); } - template<> template<> - void URITestObject::test<17>() + TEST_CASE_METHOD(URITestData, "LLURI: escape and unescape round-trip very long strings", "[LLURI]") { - set_test_name("do some round-trip tests with very long strings."); escapeRoundTrip("Welcome to Second Life.We hope you'll have a richly rewarding experience, filled with creativity, self expression and fun.The goals of the Community Standards are simple: treat each other with respect and without harassment, adhere to local standards as indicated by simulator ratings, and refrain from any hate activity which slurs a real-world individual or real-world community. Behavioral Guidelines - The Big Six"); escapeRoundTrip( "'asset_data':b(12100){'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444921\n\ttotal_crc\t323\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.368634403\t0.00781063363\t-0.569040775\n\toldpos\t150.117996\t25.8658009\t8.19664001\n\trotation\t-0.06293071806430816650390625\t-0.6995697021484375\t-0.7002241611480712890625\t0.1277817934751510620117188\n\tchildpos\t-0.00499999989\t-0.0359999985\t0.307999998\n\tchildrot\t-0.515492737293243408203125\t-0.46601200103759765625\t0.529055416584014892578125\t0.4870323240756988525390625\n\tscale" @@ -347,75 +329,89 @@ namespace tut } - template<> template<> - void URITestObject::test<18>() + TEST_CASE_METHOD(URITestData, "LLURI: a secondlife URI splits with or without an authority", "[LLURI]") { LLURI u("secondlife:///app/login?first_name=Testert4&last_name=Tester&web_login_key=test"); // if secondlife is the scheme, LLURI should parse /app/login as path, with no authority - ensure_equals("scheme", u.scheme(), "secondlife"); - ensure_equals("authority", u.authority(), ""); - ensure_equals("path", u.path(), "/app/login"); - ensure_equals("pathmap", u.pathArray()[0].asString(), "app"); - ensure_equals("pathmap", u.pathArray()[1].asString(), "login"); - ensure_equals("query", u.query(), "first_name=Testert4&last_name=Tester&web_login_key=test"); - ensure_equals("query map element", u.queryMap()["last_name"].asString(), "Tester"); + UNSCOPED_INFO("scheme"); + CHECK(u.scheme() == "secondlife"); + UNSCOPED_INFO("authority"); + CHECK(u.authority() == ""); + UNSCOPED_INFO("path"); + CHECK(u.path() == "/app/login"); + UNSCOPED_INFO("pathmap"); + CHECK(u.pathArray()[0].asString() == "app"); + UNSCOPED_INFO("pathmap"); + CHECK(u.pathArray()[1].asString() == "login"); + UNSCOPED_INFO("query"); + CHECK(u.query() == "first_name=Testert4&last_name=Tester&web_login_key=test"); + UNSCOPED_INFO("query map element"); + CHECK(u.queryMap()["last_name"].asString() == "Tester"); u = LLURI("secondlife://Da Boom/128/128/128"); // if secondlife is the scheme, LLURI should parse /128/128/128 as path, with Da Boom as authority - ensure_equals("scheme", u.scheme(), "secondlife"); - ensure_equals("authority", u.authority(), "Da Boom"); - ensure_equals("path", u.path(), "/128/128/128"); - ensure_equals("pathmap", u.pathArray()[0].asString(), "128"); - ensure_equals("pathmap", u.pathArray()[1].asString(), "128"); - ensure_equals("pathmap", u.pathArray()[2].asString(), "128"); - ensure_equals("query", u.query(), ""); + UNSCOPED_INFO("scheme"); + CHECK(u.scheme() == "secondlife"); + UNSCOPED_INFO("authority"); + CHECK(u.authority() == "Da Boom"); + UNSCOPED_INFO("path"); + CHECK(u.path() == "/128/128/128"); + UNSCOPED_INFO("pathmap"); + CHECK(u.pathArray()[0].asString() == "128"); + UNSCOPED_INFO("pathmap"); + CHECK(u.pathArray()[1].asString() == "128"); + UNSCOPED_INFO("pathmap"); + CHECK(u.pathArray()[2].asString() == "128"); + UNSCOPED_INFO("query"); + CHECK(u.query() == ""); } - template<> template<> - void URITestObject::test<19>() + TEST_CASE_METHOD(URITestData, "LLURI: an about: URI has a path and a query", "[LLURI]") { - set_test_name("Parse about: schemes"); LLURI u("about:blank?redirect-http-hack=secondlife%3A%2F%2F%2Fapp%2Flogin%3Ffirst_name%3DCallum%26last_name%3DLinden%26location%3Dspecify%26grid%3Dvaak%26region%3D%2FMorris%2F128%2F128%26web_login_key%3Defaa4795-c2aa-4c58-8966-763c27931e78"); - ensure_equals("scheme", u.scheme(), "about"); - ensure_equals("authority", u.authority(), ""); - ensure_equals("path", u.path(), "blank"); - ensure_equals("pathmap", u.pathArray()[0].asString(), "blank"); - ensure_equals("query", u.query(), "redirect-http-hack=secondlife:///app/login?first_name=Callum&last_name=Linden&location=specify&grid=vaak®ion=/Morris/128/128&web_login_key=efaa4795-c2aa-4c58-8966-763c27931e78"); - ensure_equals("query map element", u.queryMap()["redirect-http-hack"].asString(), "secondlife:///app/login?first_name=Callum&last_name=Linden&location=specify&grid=vaak®ion=/Morris/128/128&web_login_key=efaa4795-c2aa-4c58-8966-763c27931e78"); + UNSCOPED_INFO("scheme"); + CHECK(u.scheme() == "about"); + UNSCOPED_INFO("authority"); + CHECK(u.authority() == ""); + UNSCOPED_INFO("path"); + CHECK(u.path() == "blank"); + UNSCOPED_INFO("pathmap"); + CHECK(u.pathArray()[0].asString() == "blank"); + UNSCOPED_INFO("query"); + CHECK(u.query() == "redirect-http-hack=secondlife:///app/login?first_name=Callum&last_name=Linden&location=specify&grid=vaak®ion=/Morris/128/128&web_login_key=efaa4795-c2aa-4c58-8966-763c27931e78"); + UNSCOPED_INFO("query map element"); + CHECK(u.queryMap()["redirect-http-hack"].asString() == "secondlife:///app/login?first_name=Callum&last_name=Linden&location=specify&grid=vaak®ion=/Morris/128/128&web_login_key=efaa4795-c2aa-4c58-8966-763c27931e78"); } - template<> template<> - void URITestObject::test<20>() + TEST_CASE_METHOD(URITestData, "LLURI: escapePathAndData escapes paths and data and leaves escapes alone", "[LLURI]") { - set_test_name("escapePathAndData uri test"); - // Basics scheme:[//authority]path[?query][#fragment] - ensure_equals(LLURI::escapePathAndData("dirname?query"), + CHECK(LLURI::escapePathAndData("dirname?query") == "dirname?query"); - ensure_equals(LLURI::escapePathAndData("dirname?query=data"), + CHECK(LLURI::escapePathAndData("dirname?query=data") == "dirname?query=data"); - ensure_equals(LLURI::escapePathAndData("host://dirname/subdir name?query#fragment"), + CHECK(LLURI::escapePathAndData("host://dirname/subdir name?query#fragment") == "host://dirname/subdir%20name?query#fragment"); - ensure_equals(LLURI::escapePathAndData("host://dirname/subdir name?query=some@>data#fragment"), + CHECK(LLURI::escapePathAndData("host://dirname/subdir name?query=some@>data#fragment") == "host://dirname/subdir%20name?query=some@%3Edata#fragment"); - ensure_equals(LLURI::escapePathAndData("host://dir[name/subdir name?query=some[data#fra[gment"), + CHECK(LLURI::escapePathAndData("host://dir[name/subdir name?query=some[data#fra[gment") == "host://dir[name/subdir%20name?query=some%5Bdata#fra[gment"); - ensure_equals(LLURI::escapePathAndData("mailto:zero@ll.com"), + CHECK(LLURI::escapePathAndData("mailto:zero@ll.com") == "mailto:zero@ll.com"); // pre-escaped - ensure_equals(LLURI::escapePathAndData("host://dirname/subdir%20name"), + CHECK(LLURI::escapePathAndData("host://dirname/subdir%20name") == "host://dirname/subdir%20name"); // data:[][;base64], - ensure_equals(LLURI::escapePathAndData("data:,Hello, World!"), + CHECK(LLURI::escapePathAndData("data:,Hello, World!") == "data:,Hello%2C%20World%21"); - ensure_equals(LLURI::escapePathAndData("data:text/html,

Hello, World!

"), + CHECK(LLURI::escapePathAndData("data:text/html,

Hello, World!

") == "data:text/html,%3Ch1%3EHello%2C%20World%21%3C%2Fh1%3E"); // pre-escaped - ensure_equals(LLURI::escapePathAndData("data:text/html,%3Ch1%3EHello%2C%20World!"), + CHECK(LLURI::escapePathAndData("data:text/html,%3Ch1%3EHello%2C%20World!") == "data:text/html,%3Ch1%3EHello%2C%20World%21%3C%2Fh1%3E"); // assume that base64 does not need escaping - ensure_equals(LLURI::escapePathAndData("data:image;base64,SGVs/bG8sIFd/vcmxkIQ%3D%3D!-&*?="), + CHECK(LLURI::escapePathAndData("data:image;base64,SGVs/bG8sIFd/vcmxkIQ%3D%3D!-&*?=") == "data:image;base64,SGVs/bG8sIFd/vcmxkIQ%3D%3D!-&*?="); } } diff --git a/indra/llcommon/tests/lluuid_test.cpp b/indra/llcommon/tests/lluuid_test.cpp index 45da7e3705..629b7dd944 100644 --- a/indra/llcommon/tests/lluuid_test.cpp +++ b/indra/llcommon/tests/lluuid_test.cpp @@ -30,15 +30,10 @@ #include "../lluuid.h" -#include "../test/lltut.h" +#include -namespace tut +namespace { - struct lluuid_data { }; - typedef test_group lluuid_t; - typedef lluuid_t::object lluuid_object_t; - tut::lluuid_t tut_lluuid("LLUUID"); - static const std::string kSampleStr = "01020304-0506-0708-090a-0b0c0d0e0f10"; // Compile-time verification that LLUUID is constexpr-constructible. @@ -96,8 +91,6 @@ namespace tut return z ^ (z >> 31); } - namespace - { // Occupancy of a 256-bucket table indexed from one end of the hash or the // other: containers take the index from the low bits (MSVC's // std::unordered_map, any hand-rolled `h & (n-1)`) or from the high bits @@ -125,7 +118,6 @@ namespace tut } } }; - } // anonymous namespace // Structured payloads: a counter walking each 8-byte window, every // one-bit value, a pair of small ints in the outermost lanes, and short @@ -184,70 +176,80 @@ namespace tut "std::hash must be declared avalanching"); // Default construction is null. - template<> template<> - void lluuid_object_t::test<1>() + TEST_CASE("LLUUID: default construction is null and so are null and tnull", "[LLUUID]") { LLUUID id; - ensure("default-constructed isNull", id.isNull()); - ensure("default-constructed !notNull", !id.notNull()); - ensure("default-constructed == LLUUID::null", id == LLUUID::null); + UNSCOPED_INFO("default-constructed isNull"); + CHECK(id.isNull()); + UNSCOPED_INFO("default-constructed !notNull"); + CHECK_FALSE(id.notNull()); + UNSCOPED_INFO("default-constructed == LLUUID::null"); + CHECK(id == LLUUID::null); // null / tnull are the all-zero UUID (verified at runtime since they are // exported out-of-line constants, no longer constexpr). - ensure("LLUUID::null isNull", LLUUID::null.isNull()); - ensure("LLTransactionID::tnull isNull", LLTransactionID::tnull.isNull()); + UNSCOPED_INFO("LLUUID::null isNull"); + CHECK(LLUUID::null.isNull()); + UNSCOPED_INFO("LLTransactionID::tnull isNull"); + CHECK(LLTransactionID::tnull.isNull()); for (int i = 0; i < UUID_BYTES; ++i) { - ensure_equals("default byte zero", (int)id.mData[i], 0); + UNSCOPED_INFO("default byte zero"); + REQUIRE((int)id.mData[i] == 0); } } // setNull zeroes every byte. - template<> template<> - void lluuid_object_t::test<2>() + TEST_CASE("LLUUID: setNull zeroes every byte", "[LLUUID]") { LLUUID id; fill_sample(id); - ensure("filled UUID is not null", id.notNull()); + UNSCOPED_INFO("filled UUID is not null"); + CHECK(id.notNull()); id.setNull(); - ensure("setNull -> isNull", id.isNull()); + UNSCOPED_INFO("setNull -> isNull"); + CHECK(id.isNull()); for (int i = 0; i < UUID_BYTES; ++i) { - ensure_equals("setNull byte zero", (int)id.mData[i], 0); + UNSCOPED_INFO("setNull byte zero"); + REQUIRE((int)id.mData[i] == 0); } } // String parsing round-trip via set() / asString(). - template<> template<> - void lluuid_object_t::test<3>() + TEST_CASE("LLUUID: set() parses a string that asString() gives back", "[LLUUID]") { LLUUID id; - ensure("set valid string", id.set(kSampleStr)); - ensure_equals("asString round-trips", id.asString(), kSampleStr); + UNSCOPED_INFO("set valid string"); + REQUIRE(id.set(kSampleStr)); + UNSCOPED_INFO("asString round-trips"); + CHECK(id.asString() == kSampleStr); for (int i = 0; i < UUID_BYTES; ++i) { - ensure_equals("byte parsed correctly", (int)id.mData[i], i + 1); + UNSCOPED_INFO("byte parsed correctly"); + REQUIRE((int)id.mData[i] == i + 1); } } // set() with an empty string clears to null; invalid input also nulls. - template<> template<> - void lluuid_object_t::test<4>() + TEST_CASE("LLUUID: set() with an empty string clears to null; invalid input also nulls", "[LLUUID]") { LLUUID id; fill_sample(id); - ensure("empty string set returns true", id.set(std::string())); - ensure("empty string -> null", id.isNull()); + UNSCOPED_INFO("empty string set returns true"); + CHECK(id.set(std::string())); + UNSCOPED_INFO("empty string -> null"); + CHECK(id.isNull()); fill_sample(id); - ensure("bogus string set returns false", - !id.set(std::string("not-a-uuid"), false)); - ensure("bogus string -> null", id.isNull()); + UNSCOPED_INFO("bogus string set returns false"); + CHECK_FALSE(id.set(std::string("not-a-uuid"), false)); + UNSCOPED_INFO("bogus string -> null"); + CHECK(id.isNull()); } // Equality / inequality for matching and one-byte-different UUIDs. - template<> template<> - void lluuid_object_t::test<5>() + TEST_CASE("LLUUID: equality and inequality for matching and one-byte-different UUIDs", "[LLUUID]") { LLUUID a, b, c; fill_sample(a); @@ -255,17 +257,20 @@ namespace tut fill_sample(c); c.mData[UUID_BYTES - 1] ^= 0x01; - ensure("a == b", a == b); - ensure("!(a != b)", !(a != b)); - ensure("a != c", a != c); - ensure("!(a == c)", !(a == c)); + UNSCOPED_INFO("a == b"); + CHECK(a == b); + UNSCOPED_INFO("!(a != b)"); + CHECK_FALSE(a != b); + UNSCOPED_INFO("a != c"); + CHECK(a != c); + UNSCOPED_INFO("!(a == c)"); + CHECK_FALSE(a == c); } // Byte-lexicographic sort order. IW's comment in lluuid.cpp warns against // optimizing operator< with word-wide reads precisely because the word // interpretation reverses bytes on LE. This test pins the byte-lex behavior. - template<> template<> - void lluuid_object_t::test<6>() + TEST_CASE("LLUUID: operator< orders bytes lexicographically", "[LLUUID]") { LLUUID low, high; low.setNull(); @@ -273,15 +278,18 @@ namespace tut low.mData[1] = 0x01; // word value 0x100 on LE high.mData[0] = 0x01; // word value 0x001 on LE — but first byte wins - ensure("byte-lex: low < high", low < high); - ensure("byte-lex: high > low", high > low); - ensure("irreflexive: !(low < low)", !(low < low)); - ensure("irreflexive: !(low > low)", !(low > low)); + UNSCOPED_INFO("byte-lex: low < high"); + CHECK(low < high); + UNSCOPED_INFO("byte-lex: high > low"); + CHECK(high > low); + UNSCOPED_INFO("irreflexive: !(low < low)"); + CHECK_FALSE(low < low); + UNSCOPED_INFO("irreflexive: !(low > low)"); + CHECK_FALSE(low > low); } // operator^= is its own inverse (XOR twice cancels) and commutative. - template<> template<> - void lluuid_object_t::test<7>() + TEST_CASE("LLUUID: XOR by an id twice cancels and XOR with itself is null", "[LLUUID]") { LLUUID a, b, original; fill_sample(a); @@ -291,114 +299,122 @@ namespace tut b.set("11223344-5566-7788-99aa-bbccddeeff00"); LLUUID xored = a ^ b; - ensure("XOR changes value", xored != a); + UNSCOPED_INFO("XOR changes value"); + CHECK(xored != a); LLUUID xored_twice = xored ^ b; - ensure_equals("double XOR cancels", xored_twice, original); + UNSCOPED_INFO("double XOR cancels"); + CHECK(xored_twice == original); LLUUID xored_self = a ^ a; - ensure("self-XOR yields null", xored_self.isNull()); + UNSCOPED_INFO("self-XOR yields null"); + CHECK(xored_self.isNull()); } // Bit-level regression for the digest/CRC functions. These values are // computed assuming the host is little-endian (effectively all targets); // they pin the pre-UB-fix numerical output so callers that persist or // compare these checksums are unaffected. - template<> template<> - void lluuid_object_t::test<8>() + TEST_CASE("LLUUID: getCRC32 and getCRC16 give their pinned values", "[LLUUID]") { - ensure_equals("null CRC32 == 0", LLUUID::null.getCRC32(), (U32)0); - ensure_equals("null CRC16 == 0", LLUUID::null.getCRC16(), (U16)0); + UNSCOPED_INFO("null CRC32 == 0"); + CHECK(LLUUID::null.getCRC32() == (U32)0); + UNSCOPED_INFO("null CRC16 == 0"); + CHECK(LLUUID::null.getCRC16() == (U16)0); LLUUID id; fill_sample(id); // mData = {0x01, 0x02, ..., 0x10} // LE U32s: 0x04030201, 0x08070605, 0x0c0b0a09, 0x100f0e0d — sum 0x2824201C - ensure_equals("sample CRC32", id.getCRC32(), (U32)0x2824201C); + UNSCOPED_INFO("sample CRC32"); + CHECK(id.getCRC32() == (U32)0x2824201C); // LE U16s sum: high bytes (2+4+...+16)=72=0x48; low bytes (1+3+...+15)=64=0x40 - ensure_equals("sample CRC16", id.getCRC16(), (U16)0x4840); + UNSCOPED_INFO("sample CRC16"); + CHECK(id.getCRC16() == (U16)0x4840); } // Hash: the four spellings of it agree, and equal IDs hash equally. - template<> template<> - void lluuid_object_t::test<9>() + TEST_CASE("LLUUID: the hash spellings agree and equal ids hash equally", "[LLUUID]") { LLUUID a, b; fill_sample(a); fill_sample(b); - ensure_equals("equal IDs equal std::hash", - std::hash{}(a), std::hash{}(b)); - ensure_equals("std::hash matches hash_value", - std::hash{}(a), hash_value(b)); - ensure_equals("boost::hash matches hash_value", - boost::hash{}(a), hash_value(b)); + UNSCOPED_INFO("equal IDs equal std::hash"); + CHECK(std::hash{}(a) == std::hash{}(b)); + UNSCOPED_INFO("std::hash matches hash_value"); + CHECK(std::hash{}(a) == hash_value(b)); + UNSCOPED_INFO("boost::hash matches hash_value"); + CHECK(boost::hash{}(a) == hash_value(b)); // A different UUID should (overwhelmingly likely) hash differently. LLUUID c; c.set("ffffffff-ffff-ffff-ffff-ffffffffffff"); - ensure_not_equals("distinct IDs typically hash differently", - std::hash{}(a), std::hash{}(c)); + UNSCOPED_INFO("distinct IDs typically hash differently"); + CHECK(std::hash{}(a) != std::hash{}(c)); // LLTransactionID and LLAssetID reach the same hash through the base. LLTransactionID tid; fill_sample(tid); - ensure_equals("LLTransactionID hashes as its LLUUID bytes", - boost::hash{}(tid), hash_value(a)); + UNSCOPED_INFO("LLTransactionID hashes as its LLUUID bytes"); + CHECK(boost::hash{}(tid) == hash_value(a)); const LLAssetID aid = a; - ensure_equals("LLAssetID hashes as its LLUUID bytes", - std::hash{}(aid), hash_value(a)); + UNSCOPED_INFO("LLAssetID hashes as its LLUUID bytes"); + CHECK(std::hash{}(aid) == hash_value(a)); } // generateNewID produces distinct, non-null UUIDs. - template<> template<> - void lluuid_object_t::test<10>() + TEST_CASE("LLUUID: generateNewID produces distinct non-null valid UUIDs", "[LLUUID]") { LLUUID a = LLUUID::generateNewID(); LLUUID b = LLUUID::generateNewID(); - ensure("generated id is not null", a.notNull()); - ensure("generated id is not null (2)", b.notNull()); - ensure("two generated ids differ", a != b); + UNSCOPED_INFO("generated id is not null"); + CHECK(a.notNull()); + UNSCOPED_INFO("generated id is not null (2)"); + CHECK(b.notNull()); + UNSCOPED_INFO("two generated ids differ"); + CHECK(a != b); // validate() should accept the generated string form - ensure("generated id validates", LLUUID::validate(a.asString())); + UNSCOPED_INFO("generated id validates"); + CHECK(LLUUID::validate(a.asString())); } // to_chars emits exactly UUID_STR_LENGTH-1 chars in canonical 8-4-4-4-12 form. - template<> template<> - void lluuid_object_t::test<11>() + TEST_CASE("LLUUID: to_chars emits UUID_STR_LENGTH-1 chars in canonical 8-4-4-4-12 form", "[LLUUID]") { LLUUID id; id.set(kSampleStr); char buf[UUID_STR_LENGTH] = {}; id.to_chars(buf); - ensure_equals("to_chars matches set string", - std::string(buf), kSampleStr); - ensure_equals("to_chars length", strlen(buf), (size_t)(UUID_STR_LENGTH - 1)); + UNSCOPED_INFO("to_chars matches set string"); + CHECK(std::string(buf) == kSampleStr); + UNSCOPED_INFO("to_chars length"); + CHECK(strlen(buf) == (size_t)(UUID_STR_LENGTH - 1)); } // ostream insertion delegates to the canonical 36-char form. - template<> template<> - void lluuid_object_t::test<12>() + TEST_CASE("LLUUID: ostream insertion delegates to the canonical 36-char form", "[LLUUID]") { LLUUID id; id.set(kSampleStr); std::ostringstream os; os << id; - ensure_equals("operator<< matches asString", os.str(), id.asString()); + UNSCOPED_INFO("operator<< matches asString"); + CHECK(os.str() == id.asString()); } // validate() accepts canonical forms and rejects garbage. - template<> template<> - void lluuid_object_t::test<13>() + TEST_CASE("LLUUID: validate() accepts canonical forms and rejects garbage", "[LLUUID]") { - ensure("validate canonical", LLUUID::validate(kSampleStr)); - ensure("reject short string", !LLUUID::validate("abc")); - ensure("reject non-hex characters", - !LLUUID::validate("zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz")); + UNSCOPED_INFO("validate canonical"); + CHECK(LLUUID::validate(kSampleStr)); + UNSCOPED_INFO("reject short string"); + CHECK_FALSE(LLUUID::validate("abc")); + UNSCOPED_INFO("reject non-hex characters"); + CHECK_FALSE(LLUUID::validate("zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz")); } // std::set via the default operator< yields byte-lex iteration. - template<> template<> - void lluuid_object_t::test<14>() + TEST_CASE("LLUUID: std::set via the default operator< yields byte-lex iteration", "[LLUUID]") { LLUUID one, two; one.setNull(); @@ -411,11 +427,11 @@ namespace tut s.insert(one); auto it = s.begin(); - ensure_equals("first sorted entry has lowest first byte", - (int)it->mData[0], 1); + UNSCOPED_INFO("first sorted entry has lowest first byte"); + CHECK((int)it->mData[0] == 1); ++it; - ensure_equals("second sorted entry has next first byte", - (int)it->mData[0], 2); + UNSCOPED_INFO("second sorted entry has next first byte"); + CHECK((int)it->mData[0] == 2); } // Both halves of the value must reach the hash independently. @@ -423,8 +439,7 @@ namespace tut // This is the test that fails the moment someone replaces the hash with // `lo ^ hi` or any other fold of the two halves: a fold gives the same // answer for (lo, hi) and (lo^m, hi^m), and for (lo, hi) and (hi, lo). - template<> template<> - void lluuid_object_t::test<15>() + TEST_CASE("LLUUID: both halves of the value reach the hash independently", "[LLUUID]") { U64 state = 0x5eed01; int xor_equivalent = 0; @@ -439,11 +454,11 @@ namespace tut if (h == hash_value(make_id(lo ^ m, hi ^ m))) ++xor_equivalent; if (h == hash_value(make_id(hi, lo))) ++half_swapped; } - ensure_equals("ids with an equal XOR of halves must not share a hash " - "(the hash is not a fold of the two halves)", - xor_equivalent, 0); - ensure_equals("swapping the two halves must change the hash", - half_swapped, 0); + UNSCOPED_INFO("ids with an equal XOR of halves must not share a hash " + "(the hash is not a fold of the two halves)"); + CHECK(xor_equivalent == 0); + UNSCOPED_INFO("swapping the two halves must change the hash"); + CHECK(half_swapped == 0); } // Strict avalanche: flipping any one of the 128 input bits must flip each @@ -451,8 +466,7 @@ namespace tut // per output bit, so the sampling error is well under a percent and the // [0.35, 0.65] band is generous for a good hash while far out of reach for // a fold (0.016) or a single constant multiply (0.18). - template<> template<> - void lluuid_object_t::test<16>() + TEST_CASE("LLUUID: flipping any input bit flips each output bit about half the time", "[LLUUID]") { const int kBases = 256; const int kOutputBits = (int)(sizeof(size_t) * 8); @@ -482,16 +496,19 @@ namespace tut } } - ensure_equals("no single input bit flip may leave the hash unchanged", - fixed_points, 0); + UNSCOPED_INFO("no single input bit flip may leave the hash unchanged"); + CHECK(fixed_points == 0); for (int o = 0; o < kOutputBits; ++o) { const double rate = (double)flips[o] / (double)trials; - ensure("every output bit must flip near half the time for a " + { + INFO("every output bit must flip near half the time for a " "single-bit input change (output bit " + std::to_string(o) + " flipped " - + std::to_string(rate) + " of the time)", - rate >= 0.35 && rate <= 0.65); + + std::to_string(rate) + " of the time)"); + REQUIRE(rate >= 0.35); + REQUIRE(rate <= 0.65); + } } } @@ -499,8 +516,7 @@ namespace tut // This is what a composed key looks like: a fixed prefix and a counter. // Both ends of the hash have to spread it, because containers index from // the low bits or the high bits depending on which one you reach for. - template<> template<> - void lluuid_object_t::test<17>() + TEST_CASE("LLUUID: a counter in one 32-bit lane spreads over both ends of the hash", "[LLUUID]") { for (int lane = 0; lane < 4; ++lane) { @@ -518,26 +534,25 @@ namespace tut const int mean = (int)(ids.size() / BucketSpread::NUM_BUCKETS); // 16 const std::string where = " (lane " + std::to_string(lane) + ")"; - ensure("counter in one lane must not pile up in the low bits" + where, - spread.mLowMax <= 3 * mean); - ensure("counter in one lane must not pile up in the high bits" + where, - spread.mHighMax <= 3 * mean); - ensure_equals("counter in one lane must reach every low-bit bucket" + where, - spread.mLowEmpty, 0); - ensure_equals("counter in one lane must reach every high-bit bucket" + where, - spread.mHighEmpty, 0); + UNSCOPED_INFO("counter in one lane must not pile up in the low bits" + where); + REQUIRE(spread.mLowMax <= 3 * mean); + UNSCOPED_INFO("counter in one lane must not pile up in the high bits" + where); + REQUIRE(spread.mHighMax <= 3 * mean); + UNSCOPED_INFO("counter in one lane must reach every low-bit bucket" + where); + REQUIRE(spread.mLowEmpty == 0); + UNSCOPED_INFO("counter in one lane must reach every high-bit bucket" + where); + REQUIRE(spread.mHighEmpty == 0); } } // The mixed structured corpus: no full 64-bit collisions at all, and no // bucket carrying more than three times its share at either end. - template<> template<> - void lluuid_object_t::test<18>() + TEST_CASE("LLUUID: the structured corpus has no full collisions and no crowded buckets", "[LLUUID]") { std::vector corpus; build_structured_corpus(corpus); - ensure("structured corpus is large enough to be meaningful", - corpus.size() > 8000); + UNSCOPED_INFO("structured corpus is large enough to be meaningful"); + REQUIRE(corpus.size() > 8000); std::set seen; int collisions = 0; @@ -545,32 +560,31 @@ namespace tut { if (!seen.insert(hash_value(id)).second) ++collisions; } - ensure_equals("structured 16-byte payloads must not collide outright", - collisions, 0); + UNSCOPED_INFO("structured 16-byte payloads must not collide outright"); + CHECK(collisions == 0); const BucketSpread spread(corpus); const int mean = (int)(corpus.size() / BucketSpread::NUM_BUCKETS); - ensure("structured payloads must not pile up in the low bits", - spread.mLowMax <= 3 * mean); - ensure("structured payloads must not pile up in the high bits", - spread.mHighMax <= 3 * mean); + UNSCOPED_INFO("structured payloads must not pile up in the low bits"); + CHECK(spread.mLowMax <= 3 * mean); + UNSCOPED_INFO("structured payloads must not pile up in the high bits"); + CHECK(spread.mHighMax <= 3 * mean); } // Degenerate inputs: the null UUID must not hash to the zero sentinel, and // the 128 one-bit values must all land somewhere different. - template<> template<> - void lluuid_object_t::test<19>() + TEST_CASE("LLUUID: the null UUID hashes nonzero and the one-bit values hash apart", "[LLUUID]") { - ensure_not_equals("the null UUID must not hash to zero", - hash_value(LLUUID::null), (size_t)0); + UNSCOPED_INFO("the null UUID must not hash to zero"); + CHECK(hash_value(LLUUID::null) != (size_t)0); std::set seen; for (int bit = 0; bit < UUID_BYTES * 8; ++bit) { seen.insert(hash_value(make_bit_id(bit))); } - ensure_equals("each of the 128 one-bit values hashes differently", - seen.size(), (size_t)(UUID_BYTES * 8)); + UNSCOPED_INFO("each of the 128 one-bit values hashes differently"); + REQUIRE(seen.size() == (size_t)(UUID_BYTES * 8)); } // Pinned values for the hash. Nothing persists a hash of an LLUUID, so @@ -579,8 +593,7 @@ namespace tut // on purpose means updating these three lines, deliberately. // // The values assume a little-endian host, as test<8> does. - template<> template<> - void lluuid_object_t::test<20>() + TEST_CASE("LLUUID: hash_value gives its pinned values on 64-bit hosts", "[LLUUID]") { if constexpr (sizeof(size_t) == 8) { @@ -589,19 +602,18 @@ namespace tut LLUUID ones; std::memset(ones.mData, 0xff, UUID_BYTES); - ensure_equals("pinned hash of the null UUID", - (U64)hash_value(LLUUID::null), 0xc64369fbab9cf4d7ULL); - ensure_equals("pinned hash of 01020304-0506-0708-090a-0b0c0d0e0f10", - (U64)hash_value(sample), 0x47c8de24eeda6e35ULL); - ensure_equals("pinned hash of the all-ones UUID", - (U64)hash_value(ones), 0x24af15be29dd63f0ULL); + UNSCOPED_INFO("pinned hash of the null UUID"); + CHECK((U64)hash_value(LLUUID::null) == 0xc64369fbab9cf4d7ULL); + UNSCOPED_INFO("pinned hash of 01020304-0506-0708-090a-0b0c0d0e0f10"); + CHECK((U64)hash_value(sample) == 0x47c8de24eeda6e35ULL); + UNSCOPED_INFO("pinned hash of the all-ones UUID"); + CHECK((U64)hash_value(ones) == 0x24af15be29dd63f0ULL); } } // The containers the viewer actually keys by LLUUID, exercised with the // structured corpus rather than with random UUIDs. - template<> template<> - void lluuid_object_t::test<21>() + TEST_CASE("LLUUID: the viewer's LLUUID-keyed maps hold and find every structured key", "[LLUUID]") { std::vector corpus; build_structured_corpus(corpus); @@ -613,18 +625,24 @@ namespace tut flat.emplace(corpus[i], i); node.emplace(corpus[i], i); } - ensure_equals("flat map holds every distinct key", flat.size(), corpus.size()); - ensure_equals("node map holds every distinct key", node.size(), corpus.size()); + UNSCOPED_INFO("flat map holds every distinct key"); + REQUIRE(flat.size() == corpus.size()); + UNSCOPED_INFO("node map holds every distinct key"); + REQUIRE(node.size() == corpus.size()); for (size_t i = 0; i < corpus.size(); ++i) { auto fit = flat.find(corpus[i]); - ensure("flat map finds every key", fit != flat.end()); - ensure_equals("flat map returns the right value", fit->second, i); + UNSCOPED_INFO("flat map finds every key"); + REQUIRE(fit != flat.end()); + UNSCOPED_INFO("flat map returns the right value"); + REQUIRE(fit->second == i); auto nit = node.find(corpus[i]); - ensure("node map finds every key", nit != node.end()); - ensure_equals("node map returns the right value", nit->second, i); + UNSCOPED_INFO("node map finds every key"); + REQUIRE(nit != node.end()); + UNSCOPED_INFO("node map returns the right value"); + REQUIRE(nit->second == i); } // A key built from the same bytes by a different route still matches. @@ -632,7 +650,7 @@ namespace tut probe.set(kSampleStr); LLUUID same; fill_sample(same); - ensure_equals("equal bytes hash equally regardless of how they arrived", - hash_value(probe), hash_value(same)); + UNSCOPED_INFO("equal bytes hash equally regardless of how they arrived"); + CHECK(hash_value(probe) == hash_value(same)); } } diff --git a/indra/llcommon/tests/stringize_test.cpp b/indra/llcommon/tests/stringize_test.cpp index 05a6fcc602..7c2fbdb708 100644 --- a/indra/llcommon/tests/stringize_test.cpp +++ b/indra/llcommon/tests/stringize_test.cpp @@ -46,7 +46,7 @@ // other Linden headers #include "../llsd.h" -#include "../test/lltut.h" +#include namespace tut { @@ -81,46 +81,40 @@ namespace tut std::wstring def; LLSD llsd; }; - typedef test_group stringize_group; - typedef stringize_group::object stringize_object; - tut::stringize_group strzgrp("stringize_h"); - template<> template<> - void stringize_object::test<1>() + TEST_CASE_METHOD(stringize_data, "stringize_h: stringize renders each type and LLSD notation", "[stringize_h]") { - ensure_equals(stringize(c), "c"); - ensure_equals(stringize(s), "17"); - ensure_equals(stringize(i), "34"); - ensure_equals(stringize(l), "68"); - ensure_equals(stringize(f), "3.14159"); - ensure_equals(stringize(d), "3.14159"); - ensure_equals(stringize(abc), "abc def"); + CHECK(stringize(c) == "c"); + CHECK(stringize(s) == "17"); + CHECK(stringize(i) == "34"); + CHECK(stringize(l) == "68"); + CHECK(stringize(f) == "3.14159"); + CHECK(stringize(d) == "3.14159"); + CHECK(stringize(abc) == "abc def"); //ensure_equals(stringize(def), "def ghi"); //Will generate LL_WARNS() due to narrowing. // LLSDNotationFormatter emits Reals at full round-trip precision - ensure_equals(stringize(llsd), "{'abc':'abc def','d':r3.14159265358979,'i':i34}"); + CHECK(stringize(llsd) == "{'abc':'abc def','d':r3.14159265358979,'i':i34}"); } - template<> template<> - void stringize_object::test<2>() + TEST_CASE_METHOD(stringize_data, "stringize_h: STRINGIZE streams an expression with manipulators", "[stringize_h]") { - ensure_equals(STRINGIZE("c is " << c), "c is c"); - ensure_equals(STRINGIZE(std::setprecision(4) << d), "3.142"); + CHECK(STRINGIZE("c is " << c) == "c is c"); + CHECK(STRINGIZE(std::setprecision(4) << d) == "3.142"); } - template<> template<> - void stringize_object::test<3>() + TEST_CASE_METHOD(stringize_data, "stringize_h: wstringize renders each type on Windows", "[stringize_h]") { //Tests rely on validity of wstring_to_utf8str() #if LL_WINDOWS // Windows wstring is a 2byte UTF16 type - ensure_equals(ll_convert(wstringize(c)), ll_convert(std::wstring(L"c"))); - ensure_equals(ll_convert(wstringize(s)), ll_convert(std::wstring(L"17"))); - ensure_equals(ll_convert(wstringize(i)), ll_convert(std::wstring(L"34"))); - ensure_equals(ll_convert(wstringize(l)), ll_convert(std::wstring(L"68"))); - ensure_equals(ll_convert(wstringize(f)), ll_convert(std::wstring(L"3.14159"))); - ensure_equals(ll_convert(wstringize(d)), ll_convert(std::wstring(L"3.14159"))); - ensure_equals(ll_convert(wstringize(abc)), ll_convert(std::wstring(L"abc def"))); - ensure_equals(ll_convert(wstringize(abc)), ll_convert(wstringize(abc.c_str()))); - ensure_equals(ll_convert(wstringize(def)), ll_convert(std::wstring(L"def ghi"))); + REQUIRE(ll_convert(wstringize(c)) == ll_convert(std::wstring(L"c"))); + REQUIRE(ll_convert(wstringize(s)) == ll_convert(std::wstring(L"17"))); + REQUIRE(ll_convert(wstringize(i)) == ll_convert(std::wstring(L"34"))); + REQUIRE(ll_convert(wstringize(l)) == ll_convert(std::wstring(L"68"))); + REQUIRE(ll_convert(wstringize(f)) == ll_convert(std::wstring(L"3.14159"))); + REQUIRE(ll_convert(wstringize(d)) == ll_convert(std::wstring(L"3.14159"))); + REQUIRE(ll_convert(wstringize(abc)) == ll_convert(std::wstring(L"abc def"))); + REQUIRE(ll_convert(wstringize(abc)) == ll_convert(wstringize(abc.c_str()))); + REQUIRE(ll_convert(wstringize(def)) == ll_convert(std::wstring(L"def ghi"))); #else // ensure_equals(ll_convert(wstringize(c)), wstring_to_utf8str(U"c")); // ensure_equals(ll_convert(wstringize(s)), wstring_to_utf8str(U"17")); diff --git a/indra/llcommon/tests/threadsafeschedule_test.cpp b/indra/llcommon/tests/threadsafeschedule_test.cpp index 5e5d6e1259..fd4c007caf 100644 --- a/indra/llcommon/tests/threadsafeschedule_test.cpp +++ b/indra/llcommon/tests/threadsafeschedule_test.cpp @@ -18,7 +18,7 @@ #include // external library headers // other Linden headers -#include "../test/lltut.h" +#include using namespace std::literals::chrono_literals; // ms suffix using namespace std::literals::string_literals; // s suffix @@ -27,20 +27,15 @@ using Queue = LL::ThreadSafeSchedule; /***************************************************************************** * TUT *****************************************************************************/ -namespace tut +namespace { struct threadsafeschedule_data { Queue queue; }; - typedef test_group threadsafeschedule_group; - typedef threadsafeschedule_group::object object; - threadsafeschedule_group threadsafeschedulegrp("threadsafeschedule"); - template<> template<> - void object::test<1>() + TEST_CASE_METHOD(threadsafeschedule_data, "threadsafeschedule: entries pop in timestamp order and a closed queue drains", "[threadsafeschedule]") { - 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 @@ -54,17 +49,25 @@ namespace tut queue.push(now + 100ms, "def"s); queue.close(); auto entry = queue.pop(); - ensure_equals("failed to pop first", std::get<0>(entry), "abc"s); + UNSCOPED_INFO("failed to pop first"); + CHECK(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()); + UNSCOPED_INFO("failed to pop second"); + CHECK(std::get<0>(entry) == "def"s); + UNSCOPED_INFO("queue not closed"); + CHECK(queue.isClosed()); + UNSCOPED_INFO("queue prematurely done"); + CHECK_FALSE(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); + UNSCOPED_INFO("failed to pop third"); + CHECK(popped); + UNSCOPED_INFO("third is wrong"); + CHECK(s == "ghi"s); popped = queue.tryPop(s); - ensure("queue not empty", ! popped); - ensure("queue not done", queue.done()); + UNSCOPED_INFO("queue not empty"); + CHECK_FALSE(popped); + UNSCOPED_INFO("queue not done"); + CHECK(queue.done()); } -} // namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/tuple_test.cpp b/indra/llcommon/tests/tuple_test.cpp index aff129753f..11f75620e9 100644 --- a/indra/llcommon/tests/tuple_test.cpp +++ b/indra/llcommon/tests/tuple_test.cpp @@ -17,31 +17,25 @@ // std headers // external library headers // other Linden headers -#include "../test/lltut.h" +#include /***************************************************************************** * TUT *****************************************************************************/ -namespace tut +namespace { - struct tuple_data + TEST_CASE("tuple: tuple_split undoes tuple_cons", "[tuple]") { - }; - typedef test_group tuple_group; - typedef tuple_group::object object; - tuple_group tuplegrp("tuple"); - - template<> template<> - void object::test<1>() - { - set_test_name("tuple"); std::tuple tup{ "abc", 17 }; std::tuple ptup{ tuple_cons(34, tup) }; std::tuple 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); + UNSCOPED_INFO("tuple_car() fail"); + CHECK(i == 34); + UNSCOPED_INFO("tuple_cdr() (0) fail"); + CHECK(std::get<0>(tup2) == "abc"); + UNSCOPED_INFO("tuple_cdr() (1) fail"); + CHECK(std::get<1>(tup2) == 17); } -} // namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/workqueue_test.cpp b/indra/llcommon/tests/workqueue_test.cpp index 209b6b868c..33992f6351 100644 --- a/indra/llcommon/tests/workqueue_test.cpp +++ b/indra/llcommon/tests/workqueue_test.cpp @@ -19,7 +19,8 @@ #include // external library headers // other Linden headers -#include "../test/lltut.h" +#include +#include "altestfailure.h" #include "../test/catch_and_store_what_in.h" #include "llcond.h" #include "llcoros.h" @@ -34,44 +35,40 @@ using namespace std::literals::string_literals; // s suffix /***************************************************************************** * TUT *****************************************************************************/ -namespace tut +namespace { struct workqueue_data { WorkSchedule queue{"queue"}; }; - typedef test_group workqueue_group; - typedef workqueue_group::object object; - workqueue_group workqueuegrp("workqueue"); - template<> template<> - void object::test<1>() + TEST_CASE_METHOD(workqueue_data, "workqueue: a schedule is findable by name and an unnamed one gets a default", "[workqueue]") { - set_test_name("name"); - ensure_equals("didn't capture name", queue.getKey(), "queue"); - ensure("not findable", WorkSchedule::getInstance("queue") == queue.getWeak().lock()); + UNSCOPED_INFO("didn't capture name"); + CHECK(queue.getKey() == "queue"); + UNSCOPED_INFO("not findable"); + CHECK(WorkSchedule::getInstance("queue") == queue.getWeak().lock()); WorkSchedule q2; - ensure("has no name", LLStringUtil::startsWith(q2.getKey(), "WorkQueue")); + UNSCOPED_INFO("has no name"); + CHECK(LLStringUtil::startsWith(q2.getKey(), "WorkQueue")); } - template<> template<> - void object::test<2>() + TEST_CASE_METHOD(workqueue_data, "workqueue: posted work runs only when the queue is serviced", "[workqueue]") { - 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); + UNSCOPED_INFO("ran too soon"); + CHECK_FALSE(wasRun); queue.runUntilClose(); - ensure("didn't run", wasRun); + UNSCOPED_INFO("didn't run"); + CHECK(wasRun); } - template<> template<> - void object::test<3>() + TEST_CASE_METHOD(workqueue_data, "workqueue: postEvery repeats at its interval until the work returns false", "[workqueue]") { - set_test_name("postEvery"); // record of runs using Shared = std::deque; // This is an example of how to share data between the originator of @@ -114,7 +111,8 @@ namespace tut } // Take a copy of the captured deque. Shared result = data.get(); - ensure_equals("called wrong number of times", result.size(), 3); + UNSCOPED_INFO("called wrong number of times"); + REQUIRE(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. @@ -125,10 +123,12 @@ namespace tut start += interval; try { - ensure(STRINGIZE("call " << i << " too soon"), diff >= interval); - ensure(STRINGIZE("call " << i << " too late"), diff < interval*1.5); + UNSCOPED_INFO((STRINGIZE("call " << i << " too soon"))); + REQUIRE((diff >= interval)); + UNSCOPED_INFO((STRINGIZE("call " << i << " too late"))); + REQUIRE((diff < interval*1.5)); } - catch (const tut::failure&) + catch (const ll_test::Failure&) { auto interval_ms = interval / 1ms; auto diff_ms = diff / 1ms; @@ -139,10 +139,8 @@ namespace tut } } - template<> template<> - void object::test<4>() + TEST_CASE_METHOD(workqueue_data, "workqueue: postTo hands int and string results to the caller's callback", "[workqueue]") { - set_test_name("postTo"); WorkSchedule main("main"); auto qptr = WorkSchedule::getInstance("queue"); int result = 0; @@ -158,7 +156,8 @@ namespace tut qptr->runOne(); // this should run the callback main.runOne(); - ensure_equals("failed to run int callback", result, 17); + UNSCOPED_INFO("failed to run int callback"); + CHECK(result == 17); std::string alpha; // postTo() handles arbitrary return types @@ -168,13 +167,12 @@ namespace tut [&alpha](const std::string& s){ alpha = s; }); qptr->runPending(); main.runPending(); - ensure_equals("failed to run string callback", alpha, "abc"); + UNSCOPED_INFO("failed to run string callback"); + CHECK(alpha == "abc"); } - template<> template<> - void object::test<5>() + TEST_CASE_METHOD(workqueue_data, "workqueue: postTo with void work still runs the callback", "[workqueue]") { - set_test_name("postTo with void return"); WorkSchedule main("main"); auto qptr = WorkSchedule::getInstance("queue"); std::string observe; @@ -188,23 +186,24 @@ namespace tut [&observe](){ observe.append(";main"); }); qptr->runOne(); main.runOne(); - ensure_equals("failed to run both lambdas", observe, "queue;main"); + UNSCOPED_INFO("failed to run both lambdas"); + CHECK(observe == "queue;main"); } - template<> template<> - void object::test<6>() + TEST_CASE_METHOD(workqueue_data, "workqueue: waitForResult throws on the main coroutine but works on others", "[workqueue]") { - 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( [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); + UNSCOPED_INFO("lambda should not have run"); + REQUIRE(stored.empty()); + UNSCOPED_INFO("waitForResult() should have thrown"); + REQUIRE_FALSE(what.empty()); + UNSCOPED_INFO((STRINGIZE("should mention waitForResult: " << what))); + REQUIRE(what.find("waitForResult") != std::string::npos); // Call waitForResult() on a coroutine, with a string result. LLCoros::instance().launch( @@ -218,7 +217,8 @@ namespace tut // 'queue'. Service it. queue.runOne(); llcoro::suspend(); - ensure_equals("bad waitForResult return", stored, "string result"); + UNSCOPED_INFO("bad waitForResult return"); + CHECK(stored == "string result"); // Call waitForResult() on a coroutine, with a void callable. stored.clear(); @@ -233,7 +233,9 @@ namespace tut llcoro::suspend(); queue.runOne(); llcoro::suspend(); - ensure_equals("didn't run coroutine", stored, "ran"); - ensure("void waitForResult() didn't return", done); + UNSCOPED_INFO("didn't run coroutine"); + CHECK(stored == "ran"); + UNSCOPED_INFO("void waitForResult() didn't return"); + CHECK(done); } -} // namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h index 9cd2c69b11..52acc065f1 100644 --- a/indra/llcommon/tests/wrapllerrs.h +++ b/indra/llcommon/tests/wrapllerrs.h @@ -33,7 +33,7 @@ #pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally #endif -#include +#include #include "llerrorcontrol.h" #include "llexception.h" #include "stringize.h" @@ -153,9 +153,7 @@ class CaptureLogRecorder : public LLError::Recorder, public boost::noncopyable if (! required) return std::string(); - throw tut::failure(STRINGIZE("failed to find '" << search - << "' in captured log messages:\n" - << *this)); + FAIL("failed to find '" << search << "' in captured log messages:\n" << *this); } std::ostream& streamto(std::ostream& out) const diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt index 6b51c8dcd9..493440ab90 100644 --- a/indra/llmath/CMakeLists.txt +++ b/indra/llmath/CMakeLists.txt @@ -136,17 +136,18 @@ if(AL_BUILD_TESTS) LIBRARIES llmath llcommon ) - # The batch kernels again at each other x86-64 tier, one binary a tier: code - # the linker folds across files would otherwise run one tier's instructions - # in another's test. The tree's own tier is llmath_tests'; a variant the - # host cannot run skips. + # llcommon's SIMD ops layer and the batch kernels again at each other + # x86-64 tier, one binary a tier: code the linker folds across files would + # otherwise run one tier's instructions in another's test. The tree's own + # tier is llcommon_tests' and llmath_tests'; a variant the host cannot run + # skips. if(BUILD_TARGET_IS_X86_64 AND NOT DARWIN) foreach(tier baseline v2 v3 v4) if(NOT tier STREQUAL AL_ISA_TIER) al_add_test_binary( simd_tests PROJECT llmath - SOURCES tests/alsimdkernels_test.cpp + SOURCES tests/alsimdkernels_test.cpp ${INDRA_SOURCE_DIR}/llcommon/tests/alsimd_test.cpp LIBRARIES llmath llcommon ISA_TIER ${tier} ) From c8b7e58f5bf36ad5c96b592c96db8d572e37ce61 Mon Sep 17 00:00:00 2001 From: Rye Date: Mon, 12 Oct 2026 18:55:09 -0400 Subject: [PATCH 07/10] llcommon's tests read as Catch2 a person would write The idiom pass over TM20's 58 converted files, as for llmath, TM18 and TM19: - every case named for what it checks: 627 of 733 names changed, 232 of them #N, and many taken from set_test_name or comments that ran long, were cut off, or carried commas, quotes or brackets; - messages that only restated their assertion are gone: 3,040 became 760, and 88 CAPTUREs say what string-building said before; - asserting helpers are named for what they do now (47 require_*), with a scoped INFO or CAPTURE in place of a message built by hand; - 261 REQUIREs that guarded nothing are CHECKs, and 11 CHECKs that guard a read after them are REQUIREs. No case was added, removed or moved, and no assertion: per case, each assertion compares what it compared before, in the same order. Some changed only in form: an UNSCOPED_INFO and REQUIRE(false) became a FAIL (six), a helper's name changed inside its call, casts and parentheses that printed nothing went. A few edits are in code this Mac does not compile (Windows-only branches of llprocess, llsdserialize_python, llstring_utf and stringize, and lldate's parser boundary block), each reviewed line by line. tut_parity.py --recounted holds for all 58 files, with eight assertion sites reviewed as never running in a passing case here, and all 62 entries pass. Bugs in the tests that the pass found are left for their own change, since a conversion keeps behaviour: - llstreamtools: a \r check labelled "space at end"; a message said a lone quote is kept where the expectation removes it; the get_word/skip_to_next_word comparison never reaches the hyphen it describes; fullread compares one byte short; return values assigned and never checked; an empty get_brace_count case. - llstring: startsWith/endsWith messages swapped; a branch tests s1 after format rewrote it; checks commented out. - alregex: a check that passes whenever firstByteRange returns false. - alrecoverystore: three front() calls with no size checked. - lleventdispatcher: three matchers with actual and expected swapped. - lazyeventapi: a case that passes trivially and leans on case 1. - llpounceable: an empty compile-fail case. - llsdserialize: the cross-compatible "map" case starts from an empty array; srand() reseeded on every pass; comments copied between cases. - llsdserialize_python: an ECHILD from waitpid passes without the exit code checked. - llsd: REQUIRE(true). - stringize: the Windows case checks nothing elsewhere. - llstring_utf: stale comments, and two deleted messages that were wrong about what their check showed. - llinstancetracker: a snapshot iterator dereferenced three times with its size unchecked. - llsingleton: an empty first case; a bare `throw;` outside a handler. - llunits and lluuid: comments about what is built, cast or tested that the code does not do. - lltimer: bounds of 0.04 s and 5 ms that a loaded machine could miss. - lldeadmantimer: an isExpired() call that drops the CPU arguments, so the two checks after it cannot fail. - lldate: the boundary block names macros that are commented out. - llfile: sizeof a const char*, eight bytes by coincidence. - commonmisc: &bin[0] on a vector not checked non-empty; a max_bytes shorter than the text it parses. - llprocess: one find() checked twice where the second meant an offset; a CHECK inside a try that only a failed REQUIRE reaches; polls that race a script's truncating write (in TUT too). - Assertions off the test's own path: llcond's two REQUIREs and lleventcoro's explicit_wait REQUIRE run inside coroutines, and llprocess's postend REQUIRE inside an event pump's listener. Co-Authored-By: Claude Opus 5.5 --- indra/llcommon/tests/alfilewrite_test.cpp | 30 +- indra/llcommon/tests/alrecoverystore_test.cpp | 307 ++----- indra/llcommon/tests/alregex_test.cpp | 128 +-- indra/llcommon/tests/alsavehistory_test.cpp | 42 +- indra/llcommon/tests/alserialworker_test.cpp | 16 +- indra/llcommon/tests/alsimd_test.cpp | 227 ++--- indra/llcommon/tests/alwatchedfile_test.cpp | 32 +- indra/llcommon/tests/apply_test.cpp | 29 - indra/llcommon/tests/bitpack_test.cpp | 15 +- .../llcommon/tests/classic_callback_test.cpp | 4 - indra/llcommon/tests/commonmisc_test.cpp | 153 +--- indra/llcommon/tests/lazyeventapi_test.cpp | 12 +- indra/llcommon/tests/llapp_test.cpp | 4 - indra/llcommon/tests/llbase64_test.cpp | 4 - indra/llcommon/tests/llcond_test.cpp | 2 - indra/llcommon/tests/lldate_test.cpp | 74 +- indra/llcommon/tests/lldeadmantimer_test.cpp | 176 +--- indra/llcommon/tests/lldependencies_test.cpp | 9 +- indra/llcommon/tests/llerror_test.cpp | 250 +++--- indra/llcommon/tests/lleventcoro_test.cpp | 11 +- .../llcommon/tests/lleventdispatcher_test.cpp | 129 +-- indra/llcommon/tests/lleventfilter_test.cpp | 67 +- indra/llcommon/tests/llevents_test.cpp | 118 +-- indra/llcommon/tests/llfile_test.cpp | 242 ++--- indra/llcommon/tests/llframetimer_test.cpp | 7 - indra/llcommon/tests/llhttpdate_test.cpp | 78 +- indra/llcommon/tests/llinitparam_test.cpp | 128 +-- .../llcommon/tests/llinstancetracker_test.cpp | 31 +- indra/llcommon/tests/llleap_test.cpp | 29 +- .../llcommon/tests/llmainthreadtask_test.cpp | 23 +- indra/llcommon/tests/llmutex_test.cpp | 17 - indra/llcommon/tests/llpounceable_test.cpp | 87 +- indra/llcommon/tests/llprocess_test.cpp | 275 ++---- indra/llcommon/tests/llprocessor_test.cpp | 9 +- indra/llcommon/tests/llprocinfo_test.cpp | 8 +- indra/llcommon/tests/llptrto_test.cpp | 3 +- indra/llcommon/tests/llrand_test.cpp | 127 +-- indra/llcommon/tests/llsd_test.cpp | 400 ++++----- indra/llcommon/tests/llsdjson_test.cpp | 128 +-- .../tests/llsdserialize_python_test.cpp | 25 +- indra/llcommon/tests/llsdserialize_test.cpp | 531 ++++++----- indra/llcommon/tests/llsdutil_test.cpp | 74 +- indra/llcommon/tests/llsingleton_test.cpp | 72 +- indra/llcommon/tests/llstreamtools_test.cpp | 203 ++--- indra/llcommon/tests/llstring_test.cpp | 555 ++++-------- indra/llcommon/tests/llstring_utf_test.cpp | 833 ++++-------------- indra/llcommon/tests/lltimer_test.cpp | 11 - indra/llcommon/tests/lltrace_test.cpp | 44 +- indra/llcommon/tests/lltreeiterators_test.cpp | 24 +- indra/llcommon/tests/llunits_test.cpp | 61 -- indra/llcommon/tests/lluri_test.cpp | 115 +-- indra/llcommon/tests/lluuid_test.cpp | 155 +--- indra/llcommon/tests/stringize_test.cpp | 22 +- .../tests/threadsafeschedule_test.cpp | 6 - indra/llcommon/tests/tuple_test.cpp | 3 - indra/llcommon/tests/workqueue_test.cpp | 25 +- 56 files changed, 1846 insertions(+), 4344 deletions(-) diff --git a/indra/llcommon/tests/alfilewrite_test.cpp b/indra/llcommon/tests/alfilewrite_test.cpp index 7731391a3d..1ac1e58096 100644 --- a/indra/llcommon/tests/alfilewrite_test.cpp +++ b/indra/llcommon/tests/alfilewrite_test.cpp @@ -97,20 +97,15 @@ namespace TEST_CASE_METHOD(alfilewrite_data, "alfilewrite: whole writes a new file or over an old and leaves nothing beside it", "[alfilewrite]") { const std::string file = in("script.lsl"); - UNSCOPED_INFO("a new file"); CHECK(ALFileWrite::whole(file, "default {}\n")); - UNSCOPED_INFO("its text"); CHECK(read(file) == std::string("default {}\n")); - UNSCOPED_INFO("over it"); CHECK(ALFileWrite::whole(file, "x")); UNSCOPED_INFO("the new text, all of the old gone"); CHECK(read(file) == std::string("x")); UNSCOPED_INFO("empty is a text too"); CHECK(ALFileWrite::whole(file, "")); - UNSCOPED_INFO("and the file is empty"); CHECK(read(file) == std::string()); std::error_code ec; - UNSCOPED_INFO("nothing left beside it"); CHECK_FALSE(fs::exists(fsyspath(ALFileWrite::besideOf(file)), ec)); } @@ -124,11 +119,9 @@ namespace put(file, "what was there"); std::error_code ec; fs::permissions(fsyspath(file), fs::perms::owner_read, fs::perm_options::replace, ec); - UNSCOPED_INFO("refused"); CHECK_FALSE(ALFileWrite::whole(file, "new text")); UNSCOPED_INFO("the old text, whole"); CHECK(read(file) == std::string("what was there")); - UNSCOPED_INFO("nothing left beside it"); CHECK_FALSE(fs::exists(fsyspath(ALFileWrite::besideOf(file)), ec)); } @@ -140,11 +133,9 @@ namespace // be written as. std::error_code ec; fs::create_directories(fsyspath(ALFileWrite::besideOf(file)), ec); - UNSCOPED_INFO("written"); CHECK(ALFileWrite::whole(file, "new text")); UNSCOPED_INFO("in place"); CHECK(read(file) == std::string("new text")); - UNSCOPED_INFO("the folder by that name left"); CHECK(fs::is_directory(fsyspath(ALFileWrite::besideOf(file)), ec)); } @@ -157,9 +148,7 @@ namespace put(file, "old"); std::error_code ec; fs::permissions(fsyspath(file), fs::perms::owner_read | fs::perms::owner_write, fs::perm_options::replace, ec); - UNSCOPED_INFO("written"); CHECK(ALFileWrite::whole(file, "new")); - UNSCOPED_INFO("the new text"); CHECK(read(file) == std::string("new")); UNSCOPED_INFO("its permissions as they were"); CHECK(fs::status(fsyspath(file)).permissions() == (fs::perms::owner_read | fs::perms::owner_write)); @@ -170,9 +159,7 @@ namespace { SKIP("no links here"); } - UNSCOPED_INFO("through the link"); CHECK(ALFileWrite::whole(link, "through")); - UNSCOPED_INFO("still a link"); CHECK(fs::is_symlink(fsyspath(link))); UNSCOPED_INFO("the file it names written"); CHECK(read(file) == std::string("through")); @@ -181,7 +168,6 @@ namespace fs::create_symlink(fsyspath(in("nowhere.lsl")), fsyspath(dangling), ec); UNSCOPED_INFO("a link to nothing has nowhere to write"); CHECK_FALSE(ALFileWrite::whole(dangling, "lost")); - UNSCOPED_INFO("and is left a link"); CHECK(fs::is_symlink(fsyspath(dangling))); #endif } @@ -203,7 +189,6 @@ namespace fs::permissions(fsyspath(sub), fs::perms::owner_read | fs::perms::owner_exec, fs::perm_options::replace, ec); const bool written = ALFileWrite::whole(file, "new"); fs::permissions(fsyspath(sub), fs::perms::owner_all, fs::perm_options::replace, ec); - UNSCOPED_INFO("written"); CHECK(written); UNSCOPED_INFO("in place"); CHECK(read(file) == std::string("new")); @@ -213,17 +198,12 @@ namespace TEST_CASE_METHOD(alfilewrite_data, "alfilewrite: a durable write is whole or nothing and never in place", "[alfilewrite]") { const std::string file = in("journal.llsd"); - UNSCOPED_INFO("a new one"); CHECK(ALFileWrite::whole(file, "one", /*durable*/ true)); - UNSCOPED_INFO("over it"); CHECK(ALFileWrite::whole(file, "two", /*durable*/ true)); - UNSCOPED_INFO("the new text"); CHECK(read(file) == std::string("two")); std::error_code ec; - UNSCOPED_INFO("nothing left beside it"); REQUIRE_FALSE(fs::exists(fsyspath(ALFileWrite::besideOf(file)), ec)); fs::create_directories(fsyspath(ALFileWrite::besideOf(file)), ec); - UNSCOPED_INFO("refused"); CHECK_FALSE(ALFileWrite::whole(file, "three", /*durable*/ true)); UNSCOPED_INFO("the old text, whole"); CHECK(read(file) == std::string("two")); @@ -234,9 +214,7 @@ namespace const std::string file = in("include.lsl"); put(file, "integer x;\n"); std::string text = "left over"; - UNSCOPED_INFO("read"); CHECK(ALFileRead::whole(file, text, 64)); - UNSCOPED_INFO("whole"); CHECK(text == std::string("integer x;\n")); { INFO("at the limit"); @@ -248,15 +226,13 @@ namespace CHECK_FALSE(ALFileRead::whole(file, text, 10)); REQUIRE(text.empty()); } - UNSCOPED_INFO("a folder is not read"); CHECK_FALSE(ALFileRead::whole(folder, text, 64)); - UNSCOPED_INFO("nothing is not read"); + UNSCOPED_INFO("a missing file is not read"); CHECK_FALSE(ALFileRead::whole(in("gone.lsl"), text, 64)); #if !LL_WINDOWS const std::string pipe = in("pipe"); if (mkfifo(pipe.c_str(), 0600) == 0) { - UNSCOPED_INFO("a pipe is not read"); CHECK_FALSE(ALFileRead::whole(pipe, text, 64)); } #endif @@ -265,11 +241,8 @@ namespace TEST_CASE_METHOD(alfilewrite_data, "alfilewrite: temp writes in place for the user alone and never through a link", "[alfilewrite]") { const std::string file = in("sl_script_x_id.lsl"); - UNSCOPED_INFO("written"); CHECK(ALFileWrite::temp(file, "default {}")); - UNSCOPED_INFO("its text"); CHECK(read(file) == std::string("default {}")); - UNSCOPED_INFO("again, shorter"); CHECK(ALFileWrite::temp(file, "x")); UNSCOPED_INFO("all of the old gone"); CHECK(read(file) == std::string("x")); @@ -283,7 +256,6 @@ namespace fs::create_symlink(fsyspath(target), fsyspath(link), ec); if (!ec) { - UNSCOPED_INFO("not through a link"); CHECK_FALSE(ALFileWrite::temp(link, "mine")); UNSCOPED_INFO("what it names untouched"); CHECK(read(target) == std::string("somebody's")); diff --git a/indra/llcommon/tests/alrecoverystore_test.cpp b/indra/llcommon/tests/alrecoverystore_test.cpp index 256e61dd88..316bcd456f 100644 --- a/indra/llcommon/tests/alrecoverystore_test.cpp +++ b/indra/llcommon/tests/alrecoverystore_test.cpp @@ -75,20 +75,20 @@ namespace // Entries as a listing reads them, read whole: what they say of // their text is only there then. - static std::vector whole(const ALRecoveryStore& store, std::vector entries) + static std::vector require_whole(const ALRecoveryStore& store, std::vector entries) { for (ALRecoveryEntry& one : entries) { - UNSCOPED_INFO("read whole: " + one.key); + CAPTURE(one.key); REQUIRE(store.load(one)); } return entries; } - static std::optional whole(const ALRecoveryStore& store, std::optional one) + static std::optional require_whole(const ALRecoveryStore& store, std::optional one) { if (one) { - UNSCOPED_INFO("read whole: " + one->key); + CAPTURE(one->key); REQUIRE(store.load(*one)); } return one; @@ -111,9 +111,7 @@ namespace const std::string task = ALRecoveryStore::keyOf(object, item, std::string()); const std::string inventory = ALRecoveryStore::keyOf(LLUUID::null, item, std::string()); const std::string file = ALRecoveryStore::keyOf(LLUUID::null, LLUUID::null, "/scripts/lib.lsl"); - UNSCOPED_INFO("an object's item and the inventory's differ"); CHECK(task != inventory); - UNSCOPED_INFO("a file is its path"); CHECK(file == "disk:/scripts/lib.lsl"); UNSCOPED_INFO("and a key is the same asked twice"); CHECK(ALRecoveryStore::keyOf(object, item, std::string()) == task); @@ -135,32 +133,20 @@ namespace was.compileTarget = "luau"; was.baseAsset = LLUUID::generateNewID(); ALRecoveryEntry back; - UNSCOPED_INFO("it reads"); REQUIRE(ALRecoveryEntry::fromLLSD(was.asLLSD(), back)); UNSCOPED_INFO("the text, whatever is in it"); CHECK(back.text == was.text); - UNSCOPED_INFO("kept"); CHECK(back.state == ALRecoveryEntry::State::Kept); - UNSCOPED_INFO("when"); CHECK(back.when.secondsSinceEpoch() == was.when.secondsSinceEpoch()); - { - INFO("the object and the item"); - CHECK(back.object == was.object); - CHECK(back.item == was.item); - } - { - INFO("where it was"); - CHECK(back.objectName == "Door"); - CHECK(back.region == "Ahern"); - } - { - INFO("what it is"); - CHECK(back.lua); - CHECK_FALSE(back.notecard); - CHECK(back.wrapped); - CHECK(back.compileTarget == "luau"); - CHECK(back.baseAsset == was.baseAsset); - } + CHECK(back.object == was.object); + CHECK(back.item == was.item); + CHECK(back.objectName == "Door"); + CHECK(back.region == "Ahern"); + CHECK(back.lua); + CHECK_FALSE(back.notecard); + CHECK(back.wrapped); + CHECK(back.compileTarget == "luau"); + CHECK(back.baseAsset == was.baseAsset); { INFO("no history said, none read"); REQUIRE_FALSE(back.history.isDefined()); @@ -172,18 +158,11 @@ namespace was.history = history; was.caretLine = 4; was.caretColumn = 7; - UNSCOPED_INFO("it reads again"); CHECK(ALRecoveryEntry::fromLLSD(was.asLLSD(), back)); - { - INFO("the history as it was written"); - CHECK(back.history["undo"][0]["label"].asString() == "rename"); - CHECK(back.history["saved"].asInteger() == 0); - } - { - INFO("and where the caret stood"); - CHECK(back.caretLine == 4); - CHECK(back.caretColumn == 7); - } + CHECK(back.history["undo"][0]["label"].asString() == "rename"); + CHECK(back.history["saved"].asInteger() == 0); + CHECK(back.caretLine == 4); + CHECK(back.caretColumn == 7); UNSCOPED_INFO("something that is no entry is not read as one"); CHECK_FALSE(ALRecoveryEntry::fromLLSD(LLSD("text"), back)); } @@ -191,30 +170,23 @@ namespace TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: a session's entry is written whole then over by its next and forgotten", "[alrecoverystore]") { ALRecoveryStore store(folder, "session-a"); - UNSCOPED_INFO("written"); CHECK(store.write(entry("item:x", "first"))); - UNSCOPED_INFO("written again"); CHECK(store.write(entry("item:x", "second"))); - const std::vector all = whole(store, store.list()); + const std::vector all = require_whole(store, store.list()); UNSCOPED_INFO("one entry for one key"); REQUIRE(all.size() == size_t(1)); - UNSCOPED_INFO("the last text"); CHECK(all.front().text == std::string("second")); - UNSCOPED_INFO("this session's"); CHECK(all.front().session == std::string("session-a")); - UNSCOPED_INFO("unsaved"); CHECK(all.front().state == ALRecoveryEntry::State::Unsaved); for (const std::string& name : files()) { - UNSCOPED_INFO("nothing half written left beside it: " + name); + CAPTURE(name); REQUIRE(name.find(".tmp") == std::string::npos); } UNSCOPED_INFO("this session's own is nothing to offer"); CHECK_FALSE(store.hasOffers()); - UNSCOPED_INFO("nor left by another"); REQUIRE(store.left().empty()); store.forget("item:x"); - UNSCOPED_INFO("forgotten"); REQUIRE(store.list().empty()); } @@ -224,24 +196,16 @@ namespace ALRecoveryStore now(folder, "now"); UNSCOPED_INFO("the crashed session's"); CHECK(before.write(entry("task:o:i", "left behind"))); - UNSCOPED_INFO("this session's"); CHECK(now.write(entry("task:o:i", "typed since"))); UNSCOPED_INFO("both kept"); REQUIRE(now.list().size() == size_t(2)); - const std::vector left = whole(now, now.left()); - UNSCOPED_INFO("one left by another session"); + const std::vector left = require_whole(now, now.left()); REQUIRE(left.size() == size_t(1)); - UNSCOPED_INFO("its text"); CHECK(left.front().text == std::string("left behind")); - const std::optional found = whole(now, now.leftFor("task:o:i")); - { - INFO("found by its key"); - REQUIRE(found.has_value()); - CHECK(found->text == "left behind"); - } - UNSCOPED_INFO("nothing for another key"); + const std::optional found = require_whole(now, now.leftFor("task:o:i")); + REQUIRE(found.has_value()); + CHECK(found->text == "left behind"); REQUIRE_FALSE(now.leftFor("task:o:j").has_value()); - UNSCOPED_INFO("something to offer"); CHECK(now.hasOffers()); now.forget("task:o:i"); UNSCOPED_INFO("this session forgetting its own leaves the other's"); @@ -258,31 +222,21 @@ namespace { ALRecoveryStore store(folder, "mine"); ALRecoveryStore other(folder, "theirs"); - UNSCOPED_INFO("written"); CHECK(store.write(entry("item:gone", "thrown away"))); - UNSCOPED_INFO("and another session's"); CHECK(other.write(entry("item:kept", "never thrown away"))); { const std::vector written = store.list(); const auto mine = std::find_if(written.begin(), written.end(), [](const ALRecoveryEntry& one) { return one.key == "item:gone"; }); - { - INFO("discarded"); - REQUIRE(mine != written.end()); - CHECK(store.discard(*mine)); - } + REQUIRE(mine != written.end()); + CHECK(store.discard(*mine)); } - std::vector all = whole(store, store.list()); + std::vector all = require_whole(store, store.list()); UNSCOPED_INFO("both still listed"); REQUIRE(all.size() == size_t(2)); const auto discarded = std::find_if(all.begin(), all.end(), [](const ALRecoveryEntry& one) { return one.key == "item:gone"; }); - { - INFO("the discarded one"); - REQUIRE(discarded != all.end()); - CHECK(discarded->state == ALRecoveryEntry::State::Discarded); - } - UNSCOPED_INFO("with its text"); + REQUIRE(discarded != all.end()); + CHECK(discarded->state == ALRecoveryEntry::State::Discarded); CHECK(discarded->text == std::string("thrown away")); - UNSCOPED_INFO("in the folder beside"); REQUIRE(files("/discarded").size() == size_t(1)); UNSCOPED_INFO("not among what was left unsaved"); CHECK(store.leftFor("item:gone") == std::nullopt); @@ -298,22 +252,17 @@ namespace REQUIRE(store.leftFor("item:kept").has_value()); // Another session's, discarded from the list of what was left. const std::optional theirs = store.leftFor("item:kept"); - UNSCOPED_INFO("discarded too"); CHECK(store.discard(*theirs)); - UNSCOPED_INFO("gone from what was left"); REQUIRE_FALSE(store.leftFor("item:kept").has_value()); - UNSCOPED_INFO("into the folder beside"); REQUIRE(files("/discarded").size() == size_t(1)); } TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: a file that cannot be read is passed over and left in place", "[alrecoverystore]") { ALRecoveryStore store(folder, "mine"); - UNSCOPED_INFO("written"); CHECK(store.write(entry("item:ok", "fine"))); { LLFILE* broken = LLFile::fopen(folder + "/0000.someone.llsd", LLFILE_MODE("wb")); - UNSCOPED_INFO("made"); REQUIRE(broken != nullptr); fputs("texthalf", broken); fclose(broken); @@ -321,7 +270,6 @@ namespace const std::vector all = store.list(); UNSCOPED_INFO("the one that reads"); REQUIRE(all.size() == size_t(1)); - UNSCOPED_INFO("is the good one"); CHECK(all.front().key == std::string("item:ok")); UNSCOPED_INFO("the other is still there"); CHECK(LLFile::isfile(folder + "/0000.someone.llsd")); @@ -339,28 +287,20 @@ namespace second["item_id"] = LLUUID::generateNewID(); second["name"] = "Home"; card.embedded = LLSD::emptyArray().with(0, first).with(1, second); - UNSCOPED_INFO("written"); CHECK(store.write(card)); - const std::vector all = whole(store, store.list()); - UNSCOPED_INFO("one"); + const std::vector all = require_whole(store, store.list()); REQUIRE(all.size() == size_t(1)); - UNSCOPED_INFO("a notecard"); CHECK(all.front().notecard); UNSCOPED_INFO("its text, the placeholders in it byte for byte"); CHECK(all.front().text == card.text); - UNSCOPED_INFO("both items"); REQUIRE(all.front().embedded.size() == 2); - { - INFO("in the order the text stands them"); - CHECK(all.front().embedded[0]["name"].asString() == "Sunset"); - CHECK(all.front().embedded[1]["name"].asString() == "Home"); - } + CHECK(all.front().embedded[0]["name"].asString() == "Sunset"); + CHECK(all.front().embedded[1]["name"].asString() == "Home"); UNSCOPED_INFO("each whole"); CHECK(all.front().embedded[1]["item_id"].asUUID() == second["item_id"].asUUID()); // Discarded, it keeps them too. - UNSCOPED_INFO("discarded"); CHECK(store.discard(all.front())); - const std::vector thrown = whole(store, store.list()); + const std::vector thrown = require_whole(store, store.list()); { INFO("the items go with it"); REQUIRE(thrown.size() == 1); @@ -368,26 +308,19 @@ namespace } // A script has none, and says so as an empty list. ALRecoveryEntry script; - UNSCOPED_INFO("read"); REQUIRE(ALRecoveryEntry::fromLLSD(entry("item:s", "x").asLLSD(), script)); - { - INFO("no items"); - REQUIRE(script.embedded.isArray()); - REQUIRE(script.embedded.size() == 0); - } + REQUIRE(script.embedded.isArray()); + REQUIRE(script.embedded.size() == 0); } TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: setAside leaves its source; prune sweeps halves; an XML entry reads", "[alrecoverystore]") { ALRecoveryStore store(folder, "mine"); ALRecoveryStore other(folder, "theirs"); - UNSCOPED_INFO("another session's"); CHECK(other.write(entry("item:a", "left"))); - UNSCOPED_INFO("set aside"); CHECK(store.setAside(entry("item:a", "thrown"))); - UNSCOPED_INFO("among the discarded"); REQUIRE(files("/discarded").size() == size_t(1)); - const std::optional left = whole(store, store.leftFor("item:a")); + const std::optional left = require_whole(store, store.leftFor("item:a")); { INFO("what it came from still left"); REQUIRE(left.has_value()); @@ -395,20 +328,16 @@ namespace } UNSCOPED_INFO("and only that is left"); REQUIRE(store.left().size() == size_t(1)); - const std::vector all = whole(store, store.list()); + const std::vector all = require_whole(store, store.list()); const auto thrown = std::find_if(all.begin(), all.end(), [](const ALRecoveryEntry& one) { return one.text == "thrown"; }); - { - INFO("set aside by this session"); - REQUIRE(thrown != all.end()); - REQUIRE(thrown->session == "mine"); - CHECK(thrown->state == ALRecoveryEntry::State::Discarded); - } + REQUIRE(thrown != all.end()); + REQUIRE(thrown->session == "mine"); + CHECK(thrown->state == ALRecoveryEntry::State::Discarded); // Written half by a session that went, and by this one. for (const char* name : { "/0001.theirs.llsd.tmp", "/0002.mine.llsd.tmp" }) { LLFILE* half = LLFile::fopen(folder + name, LLFILE_MODE("wb")); - UNSCOPED_INFO("made"); REQUIRE(half != nullptr); fputs("\n{'key':'ite", half); fclose(half); @@ -426,15 +355,13 @@ namespace ALRecoveryEntry was = entry("item:old", "from before"); was.session = "older"; LLFILE* xml = LLFile::fopen(folder + "/0003.older.llsd", LLFILE_MODE("wb")); - UNSCOPED_INFO("made"); REQUIRE(xml != nullptr); std::ostringstream text; LLSDSerialize::toPrettyXML(was.asLLSD(), text); fputs(text.str().c_str(), xml); fclose(xml); } - const std::vector now = whole(store, store.left()); - UNSCOPED_INFO("read"); + const std::vector now = require_whole(store, store.left()); CHECK(std::any_of(now.begin(), now.end(), [](const ALRecoveryEntry& one) { return one.text == "from before"; })); } @@ -442,12 +369,9 @@ namespace { ALRecoveryStore store(folder, "mine"); ALRecoveryStore other(folder, "theirs"); - UNSCOPED_INFO("left by another session"); CHECK(other.write(entry("item:x", "left before"))); const std::optional took = store.leftFor("item:x"); - UNSCOPED_INFO("found"); REQUIRE(took.has_value()); - UNSCOPED_INFO("kept by this one"); CHECK(store.write(entry("item:x", "typed since"))); ALRecoveryStore::Parting parting; @@ -455,18 +379,15 @@ namespace parting.unsaved = entry("item:x", "typed since, then closed"); parting.tookUp = took; parting.settled = true; - UNSCOPED_INFO("let go of"); CHECK(store.letGo(parting)); - const std::vector all = whole(store, store.list()); - UNSCOPED_INFO("one entry"); + const std::vector all = require_whole(store, store.list()); REQUIRE(all.size() == size_t(1)); { INFO("set aside, as it stood"); CHECK(all.front().state == ALRecoveryEntry::State::Discarded); CHECK(all.front().text == "typed since, then closed"); } - UNSCOPED_INFO("by this session"); CHECK(all.front().session == "mine"); { INFO("what it took up gone"); @@ -479,16 +400,13 @@ namespace { ALRecoveryStore store(folder, "mine"); ALRecoveryStore other(folder, "theirs"); - UNSCOPED_INFO("left by another session"); CHECK(other.write(entry("item:x", "left before"))); const std::optional took = store.leftFor("item:x"); - UNSCOPED_INFO("kept by this one"); CHECK(store.write(entry("item:x", "typed since"))); // A file where the folder of the discarded would be: nothing can be // written there. { LLFILE* blocking = LLFile::fopen(folder + "/discarded", LLFILE_MODE("wb")); - UNSCOPED_INFO("made"); REQUIRE(blocking != nullptr); fclose(blocking); } @@ -498,10 +416,9 @@ namespace parting.unsaved = entry("item:x", "typed since, then closed"); parting.tookUp = took; parting.settled = true; - UNSCOPED_INFO("said to have failed"); CHECK_FALSE(store.letGo(parting)); - const std::vector all = whole(store, store.list()); + const std::vector all = require_whole(store, store.list()); const auto own = std::find_if(all.begin(), all.end(), [](const ALRecoveryEntry& one) { return one.session == "mine"; }); { INFO("its own entry still there"); @@ -510,7 +427,6 @@ namespace } UNSCOPED_INFO("and the one it took up"); REQUIRE(store.leftFor("item:x").has_value()); - UNSCOPED_INFO("a set-aside that fails is said"); CHECK_FALSE(store.setAside(entry("item:y", "anything"))); } @@ -518,18 +434,15 @@ namespace { ALRecoveryStore store(folder, "mine"); ALRecoveryStore other(folder, "theirs"); - UNSCOPED_INFO("left by another session"); CHECK(other.write(entry("item:x", "left before"))); // Holding the kept text where nothing can save it -- the script may // no longer be changed, or never loaded: left to be offered again. - UNSCOPED_INFO("kept by this one"); CHECK(store.write(entry("item:x", "typed since"))); ALRecoveryStore::Parting stuck; stuck.key = "item:x"; stuck.tookUp = store.leftFor("item:x"); stuck.settled = false; - UNSCOPED_INFO("let go of"); CHECK(store.letGo(stuck)); const std::vector after = store.list(); UNSCOPED_INFO("its own forgotten"); @@ -544,7 +457,6 @@ namespace clean.key = "item:x"; clean.tookUp = store.leftFor("item:x"); clean.settled = true; - UNSCOPED_INFO("let go of"); CHECK(store.letGo(clean)); UNSCOPED_INFO("what it took up gone"); REQUIRE_FALSE(store.leftFor("item:x").has_value()); @@ -566,10 +478,8 @@ namespace moved.key = "item:moved"; moved.tookUp = entry("item:moved", "typed in the other window"); moved.carrying = true; - UNSCOPED_INFO("let go of"); CHECK(store.letGo(moved)); - const std::vector all = whole(store, store.list()); - UNSCOPED_INFO("one entry"); + const std::vector all = require_whole(store, store.list()); REQUIRE(all.size() == size_t(1)); { INFO("the carried text set aside"); @@ -579,13 +489,11 @@ namespace // Opened to take up another session's entry, and closed before it // loaded: that entry is still what it was, to be offered again. - UNSCOPED_INFO("left by another session"); CHECK(other.write(entry("item:left", "left before"))); ALRecoveryStore::Parting waiting; waiting.key = "item:left"; waiting.tookUp = store.leftFor("item:left"); waiting.carrying = true; - UNSCOPED_INFO("let go of"); CHECK(store.letGo(waiting)); UNSCOPED_INFO("the entry left"); REQUIRE(store.leftFor("item:left").has_value()); @@ -593,25 +501,19 @@ namespace REQUIRE(files("/discarded").size() == size_t(1)); // A new item opened with a text of its own: nothing to keep. - UNSCOPED_INFO("kept"); CHECK(store.write(entry("item:new", "template"))); ALRecoveryStore::Parting fresh; fresh.key = "item:new"; fresh.carrying = true; - UNSCOPED_INFO("let go of"); CHECK(store.letGo(fresh)); const std::vector after = store.list(); - UNSCOPED_INFO("forgotten"); CHECK(std::none_of(after.begin(), after.end(), [](const ALRecoveryEntry& one) { return one.key == "item:new"; })); } TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: a failed load is retried with delays tripling and a few times only", "[alrecoverystore]") { - UNSCOPED_INFO("after the first"); CHECK(ALRecoveryRetry::delayAfter(1) == ALRecoveryRetry::FIRST); - UNSCOPED_INFO("after the second"); CHECK(ALRecoveryRetry::delayAfter(2) == ALRecoveryRetry::FIRST * 3.0); - UNSCOPED_INFO("after the third"); CHECK(ALRecoveryRetry::delayAfter(3) == ALRecoveryRetry::FIRST * 9.0); UNSCOPED_INFO("none yet is as the first"); CHECK(ALRecoveryRetry::delayAfter(0) == ALRecoveryRetry::FIRST); @@ -634,11 +536,9 @@ namespace history["redo"] = LLSD::emptyArray(); history["saved"] = -1; awkward.history = history; - UNSCOPED_INFO("written"); CHECK(store.write(awkward)); { const std::vector names = files(); - UNSCOPED_INFO("one file"); REQUIRE(names.size() == size_t(1)); std::error_code ec; const std::string written = LLFile::getContents(fsyspath(folder + "/" + names.front()), ec); @@ -656,30 +556,22 @@ namespace CHECK((written.compare(line + 1, 8, "{'caret'") == 0 || written.compare(line + 1, 10, "{'history'") == 0 || written.compare(line + 1, 7, "{'text'") == 0)); } - const std::vector back = whole(store, store.list()); - { - INFO("the text as it was"); - REQUIRE(back.size() == 1); - CHECK(back.front().text == awkward.text); - } - { - INFO("the history as it was"); - CHECK(back.front().history["undo"][0]["edits"][0][5].asString() == awkward.text); - CHECK(back.front().history["undo"][0]["label"].asString() == "it's"); - } + const std::vector back = require_whole(store, store.list()); + REQUIRE(back.size() == 1); + CHECK(back.front().text == awkward.text); + CHECK(back.front().history["undo"][0]["edits"][0][5].asString() == awkward.text); + CHECK(back.front().history["undo"][0]["label"].asString() == "it's"); // Set aside for another session, and two a moment apart. ALRecoveryEntry theirs = entry("item:b", "theirs"); theirs.session = "theirs"; - UNSCOPED_INFO("set aside"); CHECK(store.setAside(theirs)); // Apart by more than the centisecond a date is written to. ms_sleep(20); - UNSCOPED_INFO("and another"); CHECK(store.setAside(entry("item:b", "mine, later"))); UNSCOPED_INFO("both kept"); REQUIRE(files("/discarded").size() == size_t(2)); - const std::vector all = whole(store, store.list()); + const std::vector all = require_whole(store, store.list()); UNSCOPED_INFO("the session it was set aside for"); CHECK(std::any_of(all.begin(), all.end(), [](const ALRecoveryEntry& one) { return one.text == "theirs" && one.session == "theirs"; })); UNSCOPED_INFO("this one where none was named"); @@ -688,33 +580,25 @@ namespace // Left by other sessions: the newest first. ALRecoveryStore older(folder, "older"); ALRecoveryStore newer(folder, "newer"); - UNSCOPED_INFO("older"); CHECK(older.write(entry("item:c", "older"))); // Apart by more than the centisecond a date is written to. ms_sleep(20); - UNSCOPED_INFO("newer"); CHECK(newer.write(entry("item:d", "newer"))); - const std::vector left = whole(store, store.left()); - { - INFO("newest first"); - REQUIRE(left.size() == 2); - CHECK(left.front().text == "newer"); - CHECK(left.back().text == "older"); - } + const std::vector left = require_whole(store, store.left()); + REQUIRE(left.size() == 2); + CHECK(left.front().text == "newer"); + CHECK(left.back().text == "older"); } TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: a discard whose name says no time is pruned by what it says inside", "[alrecoverystore]") { ALRecoveryStore store(folder, "mine"); - UNSCOPED_INFO("set aside"); CHECK(store.setAside(entry("item:a", "old"))); // Renamed to say nothing of when, and then to say it badly. const std::vector names = files("/discarded"); - UNSCOPED_INFO("one"); REQUIRE(names.size() == size_t(1)); const std::string stamped = folder + "/discarded/" + names.front(); const std::string bare = folder + "/discarded/0000.mine.llsd"; - UNSCOPED_INFO("renamed"); CHECK(LLFile::rename(stamped, bare) == 0); store.prune(60.0, LLDate(LLDate::now().secondsSinceEpoch() + 30.0)); UNSCOPED_INFO("young by what it says, it stays"); @@ -723,10 +607,8 @@ namespace UNSCOPED_INFO("old by what it says, it goes"); CHECK_FALSE(LLFile::isfile(bare)); - UNSCOPED_INFO("set aside"); CHECK(store.setAside(entry("item:b", "old"))); const std::string garbled = folder + "/discarded/0001.mine.soon.llsd"; - UNSCOPED_INFO("renamed"); CHECK(LLFile::rename(folder + "/discarded/" + files("/discarded").front(), garbled) == 0); store.prune(60.0, LLDate(LLDate::now().secondsSinceEpoch() + 30.0)); UNSCOPED_INFO("a stamp that is no number read past"); @@ -746,19 +628,18 @@ namespace } store.writeSoon(entry("item:y", "other")); store.flush(); - const std::vector all = whole(store, store.list()); + const std::vector all = require_whole(store, store.list()); UNSCOPED_INFO("one entry a key"); REQUIRE(all.size() == size_t(2)); for (const ALRecoveryEntry& one : all) { - UNSCOPED_INFO("the newest of x: " + one.text); + CAPTURE(one.key, one.text); REQUIRE((one.key != "item:x" || one.text == "typed 19")); - UNSCOPED_INFO("this session's"); REQUIRE(one.session == "session-a"); } for (const std::string& name : files()) { - UNSCOPED_INFO("nothing half written: " + name); + CAPTURE(name); REQUIRE(name.find(".tmp") == std::string::npos); } // Forgotten right after it was asked to be written: the write @@ -768,18 +649,17 @@ namespace store.flush(); const std::vector after = store.list(); { - INFO("x gone: " + std::to_string(after.size())); + INFO("x gone"); REQUIRE(after.size() == 1); CHECK(after.front().key == "item:y"); } - UNSCOPED_INFO("nothing failed"); REQUIRE(store.takeFailures().empty()); // Left waiting as the store goes: written all the same. store.writeSoon(entry("item:z", "as it went")); } ALRecoveryStore again(folder, "session-a"); bool z = false; - for (const ALRecoveryEntry& one : whole(again, again.list())) + for (const ALRecoveryEntry& one : require_whole(again, again.list())) { z |= one.key == "item:z" && one.text == "as it went"; } @@ -795,11 +675,8 @@ namespace nowhere.writeSoon(entry("item:w", "lost")); nowhere.flush(); const std::vector failed = nowhere.takeFailures(); - { - INFO("failed, by key"); - REQUIRE(failed.size() == 1); - CHECK(failed.front() == "item:w"); - } + REQUIRE(failed.size() == 1); + CHECK(failed.front() == "item:w"); UNSCOPED_INFO("and said once"); REQUIRE(nowhere.takeFailures().empty()); // Each asking of its own key leaves another's. @@ -821,16 +698,13 @@ namespace one.when = LLDate(1758772800.0); const std::string said = one.whenSaid(); // The day and the hour are the local zone's; the shape is not. - UNSCOPED_INFO("YYYY-MM-DD HH:MM: " + said); + CAPTURE(said); + UNSCOPED_INFO("YYYY-MM-DD HH:MM"); REQUIRE(said.size() == size_t(16)); - { - INFO("dashes and a colon: " + said); - CHECK(said[4] == '-'); - CHECK(said[7] == '-'); - CHECK(said[10] == ' '); - CHECK(said[13] == ':'); - } - UNSCOPED_INFO("the year: " + said); + CHECK(said[4] == '-'); + CHECK(said[7] == '-'); + CHECK(said[10] == ' '); + CHECK(said[13] == ':'); CHECK(said.starts_with("2025-09-2")); } @@ -844,11 +718,8 @@ namespace REQUIRE_FALSE(store.leftFor("task:a:b")); const std::optional back = store.reclaim("task:a:b"); - UNSCOPED_INFO("reclaimed"); REQUIRE(back.has_value()); - UNSCOPED_INFO("its text"); CHECK(back->text == went.text); - UNSCOPED_INFO("set aside"); CHECK(back->state == ALRecoveryEntry::State::Discarded); UNSCOPED_INFO("when it was written, not when it was set aside"); CHECK(back->when.secondsSinceEpoch() == written.secondsSinceEpoch()); @@ -857,13 +728,12 @@ namespace REQUIRE(files().empty()); REQUIRE(files("/discarded").size() == 1); } - UNSCOPED_INFO("where it is now"); REQUIRE(back->path.find("discarded") != std::string::npos); store.forget("task:a:b"); UNSCOPED_INFO("a tab opened clean forgets nothing of it"); REQUIRE(files("/discarded").size() == 1); - const std::vector listed = whole(store, store.list()); + const std::vector listed = require_whole(store, store.list()); { INFO("offered under Recover Unsaved Changes"); REQUIRE(listed.size() == 1); @@ -892,10 +762,8 @@ namespace big.caretLine = 3; big.caretColumn = 4; big.historyWritten = "{'version':i2,'undo':[{'label':'it\\'s','edits':[]}],'redo':[],'saved':i-1}"; - UNSCOPED_INFO("written"); CHECK(other.write(big)); const std::vector listed = store.left(); - UNSCOPED_INFO("listed"); REQUIRE(listed.size() == size_t(1)); { INFO("with what it shows"); @@ -910,18 +778,11 @@ namespace CHECK(listed.front().caretLine < 0); } ALRecoveryEntry taken = listed.front(); - UNSCOPED_INFO("read whole"); CHECK(store.load(taken)); - { - INFO("its text"); - CHECK(taken.whole); - CHECK(taken.text == big.text); - } - { - INFO("its caret"); - CHECK(taken.caretLine == 3); - CHECK(taken.caretColumn == 4); - } + CHECK(taken.whole); + CHECK(taken.text == big.text); + CHECK(taken.caretLine == 3); + CHECK(taken.caretColumn == 4); { INFO("its history, written as notation, read back"); CHECK(taken.history["undo"][0]["label"].asString() == "it's"); @@ -938,7 +799,6 @@ namespace CHECK_FALSE(store.load(gone)); // Set aside from a listing, it is read whole first: nothing of its // text is lost among the discarded. - UNSCOPED_INFO("written again"); CHECK(other.write(big)); const std::optional again = store.leftFor("item:big"); { @@ -946,24 +806,18 @@ namespace REQUIRE(again); CHECK_FALSE(again->whole); } - UNSCOPED_INFO("discarded"); CHECK(store.discard(*again)); - const std::vector thrown = whole(store, store.list()); - { - INFO("with its text"); - REQUIRE(thrown.size() == 1); - CHECK(thrown.front().text == big.text); - REQUIRE(thrown.front().history["undo"].size() == 1); - } + const std::vector thrown = require_whole(store, store.list()); + REQUIRE(thrown.size() == 1); + CHECK(thrown.front().text == big.text); + REQUIRE(thrown.front().history["undo"].size() == 1); } TEST_CASE_METHOD(alrecoverystore_data, "alrecoverystore: offers expire; discards are capped; offers are counted once a moment", "[alrecoverystore]") { ALRecoveryStore store(folder, "mine"); ALRecoveryStore other(folder, "theirs"); - UNSCOPED_INFO("nothing to offer"); CHECK_FALSE(store.hasOffers()); - UNSCOPED_INFO("left by another session"); CHECK(other.write(entry("item:a", "left"))); UNSCOPED_INFO("counted a moment ago: not yet seen"); CHECK_FALSE(store.hasOffers()); @@ -986,8 +840,7 @@ namespace REQUIRE(store.left().size() == 1); REQUIRE(store.leftFor("item:a").has_value()); } - UNSCOPED_INFO("and read"); - CHECK(whole(store, store.leftFor("item:a"))->text == "left"); + CHECK(require_whole(store, store.leftFor("item:a"))->text == "left"); store.markOffered(store.left(), LLDate(offered.secondsSinceEpoch() + 100.0)); UNSCOPED_INFO("marked once"); CHECK(files().front() == names.front()); @@ -1000,11 +853,11 @@ namespace REQUIRE(files().empty()); CHECK_FALSE(store.hasOffers()); } - UNSCOPED_INFO("this session's own is never offered"); + // This session's own is never offered, nor marked. CHECK(store.write(entry("item:mine", "mine"))); store.markOffered(store.list()); { - INFO("nor marked"); + INFO("this session's own not marked"); REQUIRE(files().size() == 1); REQUIRE(files().front().find(".mine.llsd") != std::string::npos); } @@ -1013,13 +866,11 @@ namespace store.limitDiscarded(3, 1024 * 1024); for (int i = 0; i < 5; ++i) { - UNSCOPED_INFO("set aside"); REQUIRE(store.setAside(entry("item:d" + std::to_string(i), "discarded " + std::to_string(i)))); // Apart by more than the millisecond their names say. ms_sleep(5); } - std::vector kept = whole(store, store.list()); - UNSCOPED_INFO("three kept"); + std::vector kept = require_whole(store, store.list()); REQUIRE(files("/discarded").size() == size_t(3)); { INFO("the newest"); diff --git a/indra/llcommon/tests/alregex_test.cpp b/indra/llcommon/tests/alregex_test.cpp index 7e5ce01b21..cc60691995 100644 --- a/indra/llcommon/tests/alregex_test.cpp +++ b/indra/llcommon/tests/alregex_test.cpp @@ -38,34 +38,23 @@ namespace TEST_CASE("alregex: a pattern compiles or says why not and a failed one matches nothing", "[alregex]") { const ALRegex good("a(b)c"); - UNSCOPED_INFO("compiles"); CHECK(good.ok()); - UNSCOPED_INFO("no error"); REQUIRE(good.error().empty()); - UNSCOPED_INFO("the pattern as given"); CHECK(good.pattern() == std::string("a(b)c")); - UNSCOPED_INFO("its groups"); CHECK(good.groups() == 1); const ALRegex ahead("x(?=y)"); UNSCOPED_INFO("RE2 has no lookahead"); CHECK_FALSE(ahead.ok()); - UNSCOPED_INFO("and says so"); REQUIRE_FALSE(ahead.error().empty()); UNSCOPED_INFO("the pattern is still the one given"); CHECK(ahead.pattern() == std::string("x(?=y)")); - { - INFO("it matches nothing"); - CHECK_FALSE(ahead.search("xy")); - CHECK_FALSE(ahead.match("x")); - } - UNSCOPED_INFO("and has no groups"); + CHECK_FALSE(ahead.search("xy")); + CHECK_FALSE(ahead.match("x")); CHECK(ahead.groups() == 0); const ALRegex none; - UNSCOPED_INFO("none is not ok"); CHECK_FALSE(none.ok()); - UNSCOPED_INFO("and says why"); REQUIRE_FALSE(none.error().empty()); { INFO("and matches nothing, not even nothing"); @@ -73,41 +62,29 @@ namespace CHECK_FALSE(none.search("")); } std::string text = "abc"; - UNSCOPED_INFO("and replaces nothing"); CHECK(none.replaceAll(text, "x") == size_t(0)); - UNSCOPED_INFO("text as it was"); CHECK(text == std::string("abc")); const ALRegex copy = good; - { - INFO("a copy matches as the original"); - CHECK(copy.ok()); - CHECK(copy.match("abc")); - } + CHECK(copy.ok()); + CHECK(copy.match("abc")); } TEST_CASE("alregex: dot takes line breaks and ^ $ match per line unless told; case as asked", "[alregex]") { - UNSCOPED_INFO("dot takes a break"); CHECK(ALRegex("a.b").match("a\nb")); - UNSCOPED_INFO("not when told"); CHECK_FALSE(ALRegex("a.b", ALRegex::NO_DOT_NL).match("a\nb")); UNSCOPED_INFO("$ before a break"); CHECK(ALRegex("x$").search("x\ny")); - UNSCOPED_INFO("^ after one"); + UNSCOPED_INFO("^ after a break"); CHECK(ALRegex("^y").search("x\ny")); - { - INFO("not when told"); - CHECK_FALSE(ALRegex("x$", ALRegex::NO_MULTILINE).search("x\ny")); - CHECK_FALSE(ALRegex("^y", ALRegex::NO_MULTILINE).search("x\ny")); - } + CHECK_FALSE(ALRegex("x$", ALRegex::NO_MULTILINE).search("x\ny")); + CHECK_FALSE(ALRegex("^y", ALRegex::NO_MULTILINE).search("x\ny")); UNSCOPED_INFO("the text's own ends either way"); CHECK(ALRegex("^x$", ALRegex::NO_MULTILINE).match("x")); - UNSCOPED_INFO("case matters"); CHECK_FALSE(ALRegex("abc").match("ABC")); - UNSCOPED_INFO("unless it does not"); CHECK(ALRegex("abc", ALRegex::ICASE).match("aBc")); - UNSCOPED_INFO("a class too"); + UNSCOPED_INFO("a class ignores case too"); CHECK(ALRegex("[a-c]+", ALRegex::ICASE).match("CAB")); } @@ -118,9 +95,7 @@ namespace CHECK(ALRegex("a.z").match(bad)); UNSCOPED_INFO("to a negated class too"); CHECK(ALRegex("a[^x]z").match(bad)); - UNSCOPED_INFO("and \\S"); CHECK(ALRegex("\\S+").match(bad)); - UNSCOPED_INFO("not as UTF-8"); CHECK_FALSE(ALRegex("a.z", ALRegex::UTF8).match(bad)); const std::string good("a\xC3\xA9z"); { @@ -137,15 +112,11 @@ namespace TEST_CASE("alregex: match takes the whole text and search finds from a place or only there", "[alregex]") { const ALRegex word("\\bfoo\\b"); - UNSCOPED_INFO("the whole"); CHECK(word.match("foo")); - UNSCOPED_INFO("not a part"); CHECK_FALSE(word.match("foo bar")); - UNSCOPED_INFO("search finds a part"); CHECK(word.search("a foo b")); ALRegexMatch found; - UNSCOPED_INFO("from a place"); REQUIRE(word.search("foo foo", &found, 1)); UNSCOPED_INFO("the second"); CHECK(found.begin() == size_t(4)); @@ -167,53 +138,39 @@ namespace CHECK_FALSE(ALRegex("^b").search("ab", nullptr, 1, true)); UNSCOPED_INFO("past the end is nothing"); CHECK_FALSE(word.search("foo", &found, 4)); - { - INFO("an empty text"); - CHECK(ALRegex("x*").match("")); - CHECK(ALRegex("x*").match(std::string_view())); - } + CHECK(ALRegex("x*").match("")); + CHECK(ALRegex("x*").match(std::string_view())); } TEST_CASE("alregex: groups say where each is and whether it took part however many", "[alregex]") { const ALRegex date("([0-9]{1,2})/([0-9]{1,2})/([0-9]{4})( ([AP]M))?"); ALRegexMatch found; - UNSCOPED_INFO("matches"); REQUIRE(date.search("on 12/25/2026 then", &found)); - UNSCOPED_INFO("four groups and the whole"); REQUIRE(found.size() == size_t(6)); - UNSCOPED_INFO("the whole"); CHECK(found.str() == std::string("12/25/2026")); - UNSCOPED_INFO("where"); CHECK(found.begin() == size_t(3)); - UNSCOPED_INFO("to"); CHECK(found.end() == size_t(13)); UNSCOPED_INFO("the month"); CHECK(found.str(1) == std::string("12")); UNSCOPED_INFO("the year's place"); CHECK(found.begin(3) == size_t(9)); - UNSCOPED_INFO("its length"); REQUIRE(found.length(3) == size_t(4)); { INFO("an optional group that was not there"); CHECK_FALSE(found.matched(4)); CHECK_FALSE(found.matched(5)); } - UNSCOPED_INFO("is no place"); + UNSCOPED_INFO("a group not there has no place"); CHECK(found.begin(4) == ALRegexMatch::npos); - { - INFO("and nothing"); - REQUIRE(found.str(4).empty()); - REQUIRE(found.length(4) == 0); - } + REQUIRE(found.str(4).empty()); + REQUIRE(found.length(4) == 0); UNSCOPED_INFO("past the groups is nothing"); CHECK_FALSE(found.matched(9)); UNSCOPED_INFO("the whole only, as asked"); CHECK(date.search("12/25/2026", &found, 0, false, 0)); - UNSCOPED_INFO("one"); REQUIRE(found.size() == size_t(1)); - UNSCOPED_INFO("and the groups not"); CHECK_FALSE(found.matched(1)); { @@ -232,15 +189,10 @@ namespace text += static_cast('a' + i % 26); } const ALRegex many(pattern); - UNSCOPED_INFO("thirty groups"); CHECK(many.groups() == 30); - UNSCOPED_INFO("matches"); CHECK(many.match(text, &found)); - UNSCOPED_INFO("all of them"); REQUIRE(found.size() == size_t(31)); - UNSCOPED_INFO("the last"); CHECK(found.str(30) == std::string(1, text[29])); - UNSCOPED_INFO("and where"); CHECK(found.begin(30) == size_t(29)); { @@ -255,23 +207,17 @@ namespace { const ALRegex num("[0-9]+"); std::vector seen; - UNSCOPED_INFO("three"); CHECK(num.forEach("a1 b22 c333", [&](const ALRegexMatch& m) { seen.push_back(m.str()); return true; }) == size_t(3)); - UNSCOPED_INFO("in order"); CHECK(seen == std::vector{ "1", "22", "333" }); seen.clear(); num.forEach("a1 b22 c333", [&](const ALRegexMatch& m) { seen.push_back(m.str()); return seen.size() < 2; }); - UNSCOPED_INFO("stopped at two"); REQUIRE(seen.size() == size_t(2)); std::string text = "a1 b22 c333"; - UNSCOPED_INFO("each"); CHECK(num.replaceEach(text, [](const ALRegexMatch& m) { return "<" + m.str() + ">"; }) == size_t(3)); - UNSCOPED_INFO("made over"); CHECK(text == std::string("a<1> b<22> c<333>")); text = "a1 b22"; - UNSCOPED_INFO("literal"); CHECK(num.replaceAll(text, "\\1$&") == size_t(2)); UNSCOPED_INFO("as it is written"); CHECK(text == std::string("a\\1$& b\\1$&")); @@ -279,16 +225,14 @@ namespace text = "none here"; UNSCOPED_INFO("no match, no change"); CHECK(num.replaceEach(text, [](const ALRegexMatch&) { return std::string("x"); }) == size_t(0)); - UNSCOPED_INFO("as it was"); CHECK(text == std::string("none here")); text = "ab"; UNSCOPED_INFO("empty matches: one at each place"); CHECK(ALRegex("x*").replaceEach(text, [](const ALRegexMatch&) { return std::string("-"); }) == size_t(3)); - UNSCOPED_INFO("and the text between"); CHECK(text == std::string("-a-b-")); size_t count = ALRegex("x*").forEach("ab", [](const ALRegexMatch&) { return true; }); - UNSCOPED_INFO("and in turn"); + UNSCOPED_INFO("forEach sees each empty match"); CHECK(count == size_t(3)); } @@ -296,14 +240,11 @@ namespace { const std::string name = "Bob (Bobby) [x].* $1 \\n ^|?+{2}"; const ALRegex exact("\\b" + ALRegex::escape(name), ALRegex::ICASE); - UNSCOPED_INFO("compiles"); CHECK(exact.ok()); // The match views the text, which outlives it here. const std::string text = "hi " + name + " there"; ALRegexMatch found; - UNSCOPED_INFO("finds it"); REQUIRE(exact.search(text, &found)); - UNSCOPED_INFO("all of it"); CHECK(found.str() == name); UNSCOPED_INFO("and not what it would mean"); CHECK_FALSE(exact.search("hi Bob Bobby")); @@ -312,21 +253,13 @@ namespace TEST_CASE("alregex: firstByteRange gives the bytes a match can begin with or none", "[alregex]") { U8 lo = 0, hi = 0; - UNSCOPED_INFO("a hexadecimal number"); CHECK(ALRegex("0[xX][0-9a-fA-F]+").firstByteRange(lo, hi)); - { - INFO("begins with 0"); - CHECK(lo == '0'); - CHECK(hi <= '1'); - } - UNSCOPED_INFO("a number or a point"); + CHECK(lo == '0'); + CHECK(hi <= '1'); CHECK(ALRegex("[0-9]+|\\.[0-9]+").firstByteRange(lo, hi)); - { - INFO("from the point to 9"); - CHECK(lo == '.'); - CHECK(hi >= '9'); - CHECK(hi <= ':'); - } + CHECK(lo == '.'); + CHECK(hi >= '9'); + CHECK(hi <= ':'); UNSCOPED_INFO("anything is no range"); CHECK_FALSE(ALRegex(".*x").firstByteRange(lo, hi)); UNSCOPED_INFO("nor is a pattern that did not compile"); @@ -334,22 +267,16 @@ namespace // After a word, \b is before what is not one. UNSCOPED_INFO("a boundary before a non-letter"); CHECK((ALRegex("\\b[^a-z]x").firstByteRange(lo, hi) == false || (lo <= '!' && hi >= '_'))); - UNSCOPED_INFO("a label"); CHECK(ALRegex("@[A-Za-z_][A-Za-z0-9_]*").firstByteRange(lo, hi)); - { - INFO("begins with @"); - CHECK(lo == '@'); - CHECK(hi <= 'A'); - } + CHECK(lo == '@'); + CHECK(hi <= 'A'); } TEST_CASE("alregex: a regex streams as its pattern and keeps its flags", "[alregex]") { std::ostringstream out; out << ALRegex("a|b", ALRegex::ICASE); - UNSCOPED_INFO("the pattern"); CHECK(out.str() == std::string("a|b")); - UNSCOPED_INFO("the flags"); CHECK(ALRegex("a", ALRegex::ICASE | ALRegex::NO_DOT_NL).flags() == U32(ALRegex::ICASE | ALRegex::NO_DOT_NL)); } @@ -361,18 +288,13 @@ namespace CHECK_FALSE(set.ok()); CHECK_FALSE(set.compile()); } - UNSCOPED_INFO("the first"); CHECK(set.add("https?://\\S+") == 0); - UNSCOPED_INFO("the second"); CHECK(set.add("[0-9]+") == 1); - UNSCOPED_INFO("the third"); CHECK(set.add("^b$") == 2); std::string error; UNSCOPED_INFO("one that does not compile is not added"); CHECK(set.add("x(?=y)", &error) == -1); - UNSCOPED_INFO("and says why"); REQUIRE_FALSE(error.empty()); - UNSCOPED_INFO("three"); REQUIRE(set.size() == size_t(3)); std::vector hits; { @@ -381,14 +303,10 @@ namespace REQUIRE(hits.empty()); } - { - INFO("made"); - CHECK(set.compile()); - CHECK(set.ok()); - } + CHECK(set.compile()); + CHECK(set.ok()); UNSCOPED_INFO("nothing is added after"); CHECK(set.add("z") == -1); - UNSCOPED_INFO("a pass"); CHECK(set.match("see HTTP://x.com at 10", hits)); std::sort(hits.begin(), hits.end()); UNSCOPED_INFO("the Url in any case, and the number"); diff --git a/indra/llcommon/tests/alsavehistory_test.cpp b/indra/llcommon/tests/alsavehistory_test.cpp index 06c8668a6c..5cb00f8145 100644 --- a/indra/llcommon/tests/alsavehistory_test.cpp +++ b/indra/llcommon/tests/alsavehistory_test.cpp @@ -70,12 +70,12 @@ namespace } // A key's texts, newest first, each read whole. - static std::string texts(const ALSaveHistory& history, const std::string& key) + static std::string require_texts(const ALSaveHistory& history, const std::string& key) { + CAPTURE(key); std::string out; for (ALSavedText one : history.list(key)) { - UNSCOPED_INFO("read whole"); REQUIRE(history.load(one)); out += (out.empty() ? "" : "|") + one.text; } @@ -88,16 +88,11 @@ namespace ALSaveHistory history(folder); // Within the age: dated from now. const F64 now = LLDate::now().secondsSinceEpoch(); - UNSCOPED_INFO("kept"); CHECK(history.keep(saved("item:a", "one", now - 60.0))); - UNSCOPED_INFO("kept"); CHECK(history.keep(saved("item:a", "two", now - 30.0))); - UNSCOPED_INFO("another's"); CHECK(history.keep(saved("item:b", "other", now - 45.0))); const std::vector listed = history.list("item:a"); - UNSCOPED_INFO("two of its own"); REQUIRE(listed.size() == 2U); - UNSCOPED_INFO("newest first"); CHECK(listed[0].when.secondsSinceEpoch() > listed[1].when.secondsSinceEpoch()); { INFO("what a listing reads"); @@ -113,9 +108,8 @@ namespace REQUIRE(listed[0].text.empty()); } UNSCOPED_INFO("read on asking"); - CHECK(texts(history, "item:a") == std::string("two|one")); - UNSCOPED_INFO("the other key's"); - CHECK(texts(history, "item:b") == std::string("other")); + CHECK(require_texts(history, "item:a") == std::string("two|one")); + CHECK(require_texts(history, "item:b") == std::string("other")); UNSCOPED_INFO("none for a key never saved"); REQUIRE(history.list("item:c").empty()); } @@ -124,16 +118,12 @@ namespace { ALSaveHistory history(folder); const F64 now = LLDate::now().secondsSinceEpoch(); - UNSCOPED_INFO("kept"); CHECK(history.keep(saved("item:a", "one", now - 60.0))); - UNSCOPED_INFO("not again"); CHECK_FALSE(history.keep(saved("item:a", "one", now - 50.0))); - UNSCOPED_INFO("kept"); CHECK(history.keep(saved("item:a", "two", now - 40.0))); UNSCOPED_INFO("back to one is a change"); CHECK(history.keep(saved("item:a", "one", now - 30.0))); - UNSCOPED_INFO("three"); - CHECK(texts(history, "item:a") == std::string("one|two|one")); + CHECK(require_texts(history, "item:a") == std::string("one|two|one")); UNSCOPED_INFO("no key, nothing"); CHECK_FALSE(history.keep(saved("", "x", now))); } @@ -147,8 +137,7 @@ namespace { history.keep(saved("item:a", std::to_string(i), now - 100.0 + i)); } - UNSCOPED_INFO("the newest three"); - CHECK(texts(history, "item:a") == std::string("5|4|3")); + CHECK(require_texts(history, "item:a") == std::string("5|4|3")); ALSaveHistory aged(folder + "/aged"); aged.limit(50, 10.0 * DAY, ALSaveHistory::MAX_BYTES); @@ -156,10 +145,10 @@ namespace aged.keep(saved("item:a", "new", THEN + 8.0 * DAY)); aged.prune(LLDate(THEN + 9.0 * DAY)); UNSCOPED_INFO("both within the age"); - CHECK(texts(aged, "item:a") == std::string("new|old")); + CHECK(require_texts(aged, "item:a") == std::string("new|old")); aged.prune(LLDate(THEN + 12.0 * DAY)); UNSCOPED_INFO("the old one past it"); - CHECK(texts(aged, "item:a") == std::string("new")); + CHECK(require_texts(aged, "item:a") == std::string("new")); aged.prune(LLDate(THEN + 30.0 * DAY)); UNSCOPED_INFO("and then the other"); REQUIRE(aged.list("item:a").empty()); @@ -180,9 +169,9 @@ namespace history.limit(50, 1e12, room); history.prune(); UNSCOPED_INFO("the oldest gone"); - CHECK(texts(history, "item:a") == std::string("cccccccccc")); + CHECK(require_texts(history, "item:a") == std::string("cccccccccc")); UNSCOPED_INFO("another key's newer one kept"); - CHECK(texts(history, "item:b") == std::string("bbbbbbbbbb")); + CHECK(require_texts(history, "item:b") == std::string("bbbbbbbbbb")); } TEST_CASE_METHOD(alsavehistory_data, "ALSaveHistory: rekey moves a key's saves behind another's under the new key", "[ALSaveHistory]") @@ -192,15 +181,12 @@ namespace history.keep(saved("task:p:old", "one", now - 60.0)); history.keep(saved("task:p:old", "two", now - 50.0)); history.keep(saved("task:p:new", "three", now - 40.0)); - UNSCOPED_INFO("moved"); CHECK(history.rekey("task:p:old", "task:p:new")); - UNSCOPED_INFO("none left"); REQUIRE(history.list("task:p:old").empty()); UNSCOPED_INFO("all under the new, in time's order"); - CHECK(texts(history, "task:p:new") == std::string("three|two|one")); + CHECK(require_texts(history, "task:p:new") == std::string("three|two|one")); for (const ALSavedText& one : history.list("task:p:new")) { - UNSCOPED_INFO("saying the new key"); REQUIRE(one.key == std::string("task:p:new")); } UNSCOPED_INFO("nothing to move"); @@ -230,7 +216,7 @@ namespace UNSCOPED_INFO("the half-written swept"); CHECK_FALSE(LLFile::isfile(dir + "2.llsd" + std::string(ALFileWrite::BESIDE))); UNSCOPED_INFO("the save still there"); - CHECK(texts(history, "item:a") == std::string("one")); + CHECK(require_texts(history, "item:a") == std::string("one")); } TEST_CASE_METHOD(alsavehistory_data, "ALSaveHistory: count counts a key's saves by file name and not half writes", "[ALSaveHistory]") @@ -239,16 +225,12 @@ namespace const F64 now = LLDate::now().secondsSinceEpoch(); UNSCOPED_INFO("none for a key never saved"); REQUIRE(history.count("item:a") == 0U); - UNSCOPED_INFO("none for no key"); REQUIRE(history.count("") == 0U); history.keep(saved("item:a", "one", now - 60.0)); history.keep(saved("item:a", "two", now - 50.0)); history.keep(saved("item:b", "other", now - 40.0)); - UNSCOPED_INFO("as many as listed"); REQUIRE(history.count("item:a") == history.list("item:a").size()); - UNSCOPED_INFO("two"); REQUIRE(history.count("item:a") == 2U); - UNSCOPED_INFO("the other key's"); REQUIRE(history.count("item:b") == 1U); const std::string kept = history.list("item:a").front().path; const std::string dir = kept.substr(0, kept.find_last_of('/') + 1); diff --git a/indra/llcommon/tests/alserialworker_test.cpp b/indra/llcommon/tests/alserialworker_test.cpp index 0f75a10e97..ebf8df2572 100644 --- a/indra/llcommon/tests/alserialworker_test.cpp +++ b/indra/llcommon/tests/alserialworker_test.cpp @@ -77,14 +77,11 @@ namespace ALSerialWorker worker("alserialworker_test_1"); for (int n = 0; n < 50; ++n) { - UNSCOPED_INFO("taken"); REQUIRE(worker.post([this, n] { note(n); })); } - UNSCOPED_INFO("all done"); - CHECK(waitFor(50)); + REQUIRE(waitFor(50)); for (int n = 0; n < 50; ++n) { - UNSCOPED_INFO("in the order posted"); REQUIRE(done[n] == n); { INFO("on one thread, not this one"); @@ -127,11 +124,8 @@ namespace UNSCOPED_INFO("it returned, and nothing waiting ran"); REQUIRE(done.size() == size_t(1)); bool ran = false; - { - INFO("refused after"); - CHECK_FALSE(worker.post([&ran] { ran = true; })); - CHECK_FALSE(ran); - } + CHECK_FALSE(worker.post([&ran] { ran = true; })); + CHECK_FALSE(ran); worker.close(); UNSCOPED_INFO("closed twice is closed"); CHECK(worker.closing()); @@ -140,17 +134,13 @@ namespace TEST_CASE_METHOD(alserialworker_data, "alserialworker: the viewer starting to quit closes it at once", "[alserialworker]") { ALSerialWorker worker("alserialworker_test_3"); - UNSCOPED_INFO("taken"); CHECK(worker.post([this] { note(1); })); - UNSCOPED_INFO("done"); CHECK(waitFor(1)); LLEventPumps::instance().obtain("LLApp").post(llsd::map("status", "running")); UNSCOPED_INFO("running is nothing"); CHECK_FALSE(worker.closing()); LLEventPumps::instance().obtain("LLApp").post(llsd::map("status", "quitting")); - UNSCOPED_INFO("closed"); CHECK(worker.closing()); - UNSCOPED_INFO("refused"); CHECK_FALSE(worker.post([this] { note(2); })); } } diff --git a/indra/llcommon/tests/alsimd_test.cpp b/indra/llcommon/tests/alsimd_test.cpp index bea6c47453..d722e83246 100644 --- a/indra/llcommon/tests/alsimd_test.cpp +++ b/indra/llcommon/tests/alsimd_test.cpp @@ -104,21 +104,21 @@ namespace return (U32)(d < 0 ? -d : d); } - void ensure_lanes_bits(const std::string& what, f32x4 got, U32 x, U32 y, U32 z, U32 w) + void require_lanes_bits(const std::string& what, f32x4 got, U32 x, U32 y, U32 z, U32 w) { const Lanes l = lanes_of(got); const U32 expected[4] = {x, y, z, w}; for (int i = 0; i < 4; ++i) { - UNSCOPED_INFO(what + " lane " + std::to_string(i)); + INFO(what << " lane " << i); REQUIRE(bits_of(l.v[i]) == expected[i]); } } #if AL_SIMD_DISTINCT_MASK - void ensure_lanes_bits(const std::string& what, mask4 got, U32 x, U32 y, U32 z, U32 w) + void require_lanes_bits(const std::string& what, mask4 got, U32 x, U32 y, U32 z, U32 w) { - ensure_lanes_bits(what, as_f32(got), x, y, z, w); + require_lanes_bits(what, as_f32(got), x, y, z, w); } #endif @@ -128,9 +128,9 @@ namespace return as_i32(set(std::bit_cast(x), std::bit_cast(y), std::bit_cast(z), std::bit_cast(w))); } - void ensure_lanes(const std::string& what, f32x4 got, F32 x, F32 y, F32 z, F32 w) + void require_lanes(const std::string& what, f32x4 got, F32 x, F32 y, F32 z, F32 w) { - ensure_lanes_bits(what, got, bits_of(x), bits_of(y), bits_of(z), bits_of(w)); + require_lanes_bits(what, got, bits_of(x), bits_of(y), bits_of(z), bits_of(w)); } std::string precise(double v) @@ -143,14 +143,14 @@ namespace // The error is measured in double, so that the fast floating-point // contract the test itself is built under cannot approximate the // division that measures an approximation. - void ensure_lanes_relative(const std::string& what, f32x4 got, const double* expected, double relative) + void require_lanes_relative(const std::string& what, f32x4 got, const double* expected, double relative) { const Lanes l = lanes_of(got); for (int i = 0; i < 4; ++i) { const double err = std::fabs((double)l.v[i] - expected[i]) / std::fabs(expected[i]); - UNSCOPED_INFO(what + " lane " + std::to_string(i) + ": " + precise(l.v[i]) + " is off " + - precise(err) + " relative from " + precise(expected[i])); + INFO(what << " lane " << i << ": " << precise(l.v[i]) << " is off " << precise(err) << " relative from " + << precise(expected[i])); REQUIRE(err <= relative); } } @@ -178,79 +178,65 @@ namespace f32x4 b() const { return set(float_of(pb[0]), float_of(pb[1]), float_of(pb[2]), float_of(pb[3])); } }; - // The register is sixteen bytes at sixteen, on every target. TEST_CASE_METHOD(alsimd_data, "alsimd: the register is sixteen bytes at sixteen on every target", "[alsimd]") { - UNSCOPED_INFO("sizeof f32x4"); CHECK(sizeof(f32x4) == (size_t)16); - UNSCOPED_INFO("alignof f32x4"); CHECK(alignof(f32x4) == (size_t)16); - UNSCOPED_INFO("sizeof mask4"); CHECK(sizeof(mask4) == (size_t)16); - UNSCOPED_INFO("sizeof i32x4"); CHECK(sizeof(i32x4) == (size_t)16); } - // Loads and stores move bits. TEST_CASE_METHOD(alsimd_data, "alsimd: loads and stores move bits", "[alsimd]") { alignas(16) F32 src[5] = {float_of(pa[0]), float_of(pa[1]), float_of(pa[2]), float_of(pa[3]), 99.f}; - ensure_lanes_bits("load", load(src), pa[0], pa[1], pa[2], pa[3]); - ensure_lanes_bits("loadu", loadu(src + 1), pa[1], pa[2], pa[3], bits_of(99.f)); - ensure_lanes_bits("load3", load3(src), pa[0], pa[1], pa[2], 0u); - ensure_lanes_bits("load3 offset", load3(src + 1), pa[1], pa[2], pa[3], 0u); + require_lanes_bits("load", load(src), pa[0], pa[1], pa[2], pa[3]); + require_lanes_bits("loadu", loadu(src + 1), pa[1], pa[2], pa[3], bits_of(99.f)); + require_lanes_bits("load3", load3(src), pa[0], pa[1], pa[2], 0u); + require_lanes_bits("load3 offset", load3(src + 1), pa[1], pa[2], pa[3], 0u); alignas(16) F32 dst[8] = {}; store(dst, a()); - UNSCOPED_INFO("store"); CHECK(std::memcmp(dst, src, 16) == 0); storeu(dst + 1, a()); - UNSCOPED_INFO("storeu"); CHECK(std::memcmp(dst + 1, src, 16) == 0); - ensure_lanes_bits("set", a(), pa[0], pa[1], pa[2], pa[3]); - ensure_lanes("set1", set1(-2.5f), -2.5f, -2.5f, -2.5f, -2.5f); - ensure_lanes("zero", zero(), 0.f, 0.f, 0.f, 0.f); + require_lanes_bits("set", a(), pa[0], pa[1], pa[2], pa[3]); + require_lanes("set1", set1(-2.5f), -2.5f, -2.5f, -2.5f, -2.5f); + require_lanes("zero", zero(), 0.f, 0.f, 0.f, 0.f); } - // Lanes and permutations are bit moves. TEST_CASE_METHOD(alsimd_data, "alsimd: lanes and permutations are bit moves", "[alsimd]") { - UNSCOPED_INFO("lane<0>"); CHECK(bits_of(lane<0>(a())) == pa[0]); - UNSCOPED_INFO("lane<1>"); CHECK(bits_of(lane<1>(a())) == pa[1]); - UNSCOPED_INFO("lane<2>"); CHECK(bits_of(lane<2>(a())) == pa[2]); - UNSCOPED_INFO("lane<3>"); CHECK(bits_of(lane<3>(a())) == pa[3]); - ensure_lanes_bits("splat<0>", splat<0>(a()), pa[0], pa[0], pa[0], pa[0]); - ensure_lanes_bits("splat<1>", splat<1>(a()), pa[1], pa[1], pa[1], pa[1]); - ensure_lanes_bits("splat<2>", splat<2>(a()), pa[2], pa[2], pa[2], pa[2]); - ensure_lanes_bits("splat<3>", splat<3>(a()), pa[3], pa[3], pa[3], pa[3]); - - ensure_lanes_bits("shuffle yzxw", shuffle<1, 2, 0, 3>(a()), pa[1], pa[2], pa[0], pa[3]); - ensure_lanes_bits("shuffle zxyw", shuffle<2, 0, 1, 3>(a()), pa[2], pa[0], pa[1], pa[3]); - ensure_lanes_bits("shuffle yxwz", shuffle<1, 0, 3, 2>(a()), pa[1], pa[0], pa[3], pa[2]); - ensure_lanes_bits("shuffle zwxy", shuffle<2, 3, 0, 1>(a()), pa[2], pa[3], pa[0], pa[1]); - ensure_lanes_bits("shuffle wwwx", shuffle<3, 3, 3, 0>(a()), pa[3], pa[3], pa[3], pa[0]); - ensure_lanes_bits("shuffle xyyx", shuffle<0, 1, 1, 0>(a()), pa[0], pa[1], pa[1], pa[0]); - ensure_lanes_bits("shuffle zzyw", shuffle<2, 2, 1, 3>(a()), pa[2], pa[2], pa[1], pa[3]); - ensure_lanes_bits("shuffle wzyx", shuffle<3, 2, 1, 0>(a()), pa[3], pa[2], pa[1], pa[0]); - - ensure_lanes_bits("shuffle2", shuffle2<0, 2, 1, 3>(a(), b()), pa[0], pa[2], pb[1], pb[3]); - ensure_lanes_bits("shuffle2 movelh", shuffle2<0, 1, 0, 1>(a(), b()), pa[0], pa[1], pb[0], pb[1]); - ensure_lanes_bits("shuffle2 high", shuffle2<2, 3, 2, 3>(a(), b()), pa[2], pa[3], pb[2], pb[3]); - ensure_lanes_bits("shuffle2 wxzy", shuffle2<3, 0, 2, 1>(a(), b()), pa[3], pa[0], pb[2], pb[1]); - - ensure_lanes_bits("movelh", movelh(a(), b()), pa[0], pa[1], pb[0], pb[1]); - ensure_lanes_bits("movehl", movehl(a(), b()), pb[2], pb[3], pa[2], pa[3]); - ensure_lanes_bits("unpacklo", unpacklo(a(), b()), pa[0], pb[0], pa[1], pb[1]); - ensure_lanes_bits("unpackhi", unpackhi(a(), b()), pa[2], pb[2], pa[3], pb[3]); + require_lanes_bits("splat<0>", splat<0>(a()), pa[0], pa[0], pa[0], pa[0]); + require_lanes_bits("splat<1>", splat<1>(a()), pa[1], pa[1], pa[1], pa[1]); + require_lanes_bits("splat<2>", splat<2>(a()), pa[2], pa[2], pa[2], pa[2]); + require_lanes_bits("splat<3>", splat<3>(a()), pa[3], pa[3], pa[3], pa[3]); + + require_lanes_bits("shuffle yzxw", shuffle<1, 2, 0, 3>(a()), pa[1], pa[2], pa[0], pa[3]); + require_lanes_bits("shuffle zxyw", shuffle<2, 0, 1, 3>(a()), pa[2], pa[0], pa[1], pa[3]); + require_lanes_bits("shuffle yxwz", shuffle<1, 0, 3, 2>(a()), pa[1], pa[0], pa[3], pa[2]); + require_lanes_bits("shuffle zwxy", shuffle<2, 3, 0, 1>(a()), pa[2], pa[3], pa[0], pa[1]); + require_lanes_bits("shuffle wwwx", shuffle<3, 3, 3, 0>(a()), pa[3], pa[3], pa[3], pa[0]); + require_lanes_bits("shuffle xyyx", shuffle<0, 1, 1, 0>(a()), pa[0], pa[1], pa[1], pa[0]); + require_lanes_bits("shuffle zzyw", shuffle<2, 2, 1, 3>(a()), pa[2], pa[2], pa[1], pa[3]); + require_lanes_bits("shuffle wzyx", shuffle<3, 2, 1, 0>(a()), pa[3], pa[2], pa[1], pa[0]); + + require_lanes_bits("shuffle2", shuffle2<0, 2, 1, 3>(a(), b()), pa[0], pa[2], pb[1], pb[3]); + require_lanes_bits("shuffle2 movelh", shuffle2<0, 1, 0, 1>(a(), b()), pa[0], pa[1], pb[0], pb[1]); + require_lanes_bits("shuffle2 high", shuffle2<2, 3, 2, 3>(a(), b()), pa[2], pa[3], pb[2], pb[3]); + require_lanes_bits("shuffle2 wxzy", shuffle2<3, 0, 2, 1>(a(), b()), pa[3], pa[0], pb[2], pb[1]); + + require_lanes_bits("movelh", movelh(a(), b()), pa[0], pa[1], pb[0], pb[1]); + require_lanes_bits("movehl", movehl(a(), b()), pb[2], pb[3], pa[2], pa[3]); + require_lanes_bits("unpacklo", unpacklo(a(), b()), pa[0], pb[0], pa[1], pb[1]); + require_lanes_bits("unpackhi", unpackhi(a(), b()), pa[2], pb[2], pa[3], pb[3]); } - // Arithmetic is one rounding per lane. TEST_CASE_METHOD(alsimd_data, "alsimd: arithmetic is one rounding per lane", "[alsimd]") { const F32 xs[4] = {1.5f, -3.25f, 1e-3f, 65535.f}; @@ -259,6 +245,7 @@ namespace const f32x4 y = loadu(ys); for (int i = 0; i < 4; ++i) { + CAPTURE(i); volatile F32 sum = xs[i] + ys[i]; volatile F32 diff = xs[i] - ys[i]; volatile F32 prod = xs[i] * ys[i]; @@ -267,22 +254,18 @@ namespace const Lanes d = lanes_of(sub(x, y)); const Lanes p = lanes_of(mul(x, y)); const Lanes q = lanes_of(div(x, y)); - UNSCOPED_INFO("add"); REQUIRE(bits_of(s.v[i]) == bits_of(sum)); - UNSCOPED_INFO("sub"); REQUIRE(bits_of(d.v[i]) == bits_of(diff)); - UNSCOPED_INFO("mul"); REQUIRE(bits_of(p.v[i]) == bits_of(prod)); - UNSCOPED_INFO("div within an ulp"); REQUIRE(ulps_between(q.v[i], quot) <= 1); } // A negative zero as its bits, in and out: /fp:fast lets the compiler // take -0.f for 0.f, and MSVC for arm64 does. const f32x4 sign = set1_bits(0x80000000u); - ensure_lanes_bits("neg", neg(or_(set(1.f, -2.f, 0.f, 0.f), and_(sign, mask_lane<3>()))), + require_lanes_bits("neg", neg(or_(set(1.f, -2.f, 0.f, 0.f), and_(sign, mask_lane<3>()))), bits_of(-1.f), bits_of(2.f), 0x80000000u, 0u); - ensure_lanes_bits("abs", abs(or_(set(1.f, -2.f, 0.f, -INF), and_(sign, mask_lane<2>()))), + require_lanes_bits("abs", abs(or_(set(1.f, -2.f, 0.f, -INF), and_(sign, mask_lane<2>()))), bits_of(1.f), bits_of(2.f), 0u, bits_of(INF)); UNSCOPED_INFO("neg keeps a NaN payload"); CHECK(bits_of(lane<2>(neg(a()))) == (pa[2] ^ 0x80000000u)); @@ -290,8 +273,8 @@ namespace CHECK(bits_of(lane<2>(abs(neg(a())))) == pa[2]); // Denormals pass through: the ops do not rely on flush-to-zero. - ensure_lanes("denormal mul", mul(set(DENORMAL, DENORMAL_BIG, 0.f, 0.f), set1(1.f)), DENORMAL, DENORMAL_BIG, 0.f, 0.f); - ensure_lanes("denormal add", add(set(DENORMAL, 0.f, 0.f, 0.f), set(DENORMAL, 0.f, 0.f, 0.f)), 2 * DENORMAL, 0.f, 0.f, 0.f); + require_lanes("denormal mul", mul(set(DENORMAL, DENORMAL_BIG, 0.f, 0.f), set1(1.f)), DENORMAL, DENORMAL_BIG, 0.f, 0.f); + require_lanes("denormal add", add(set(DENORMAL, 0.f, 0.f, 0.f), set(DENORMAL, 0.f, 0.f, 0.f)), 2 * DENORMAL, 0.f, 0.f, 0.f); } // Fused multiply-add: exact against fma where the machine fuses, exact @@ -310,6 +293,7 @@ namespace const Lanes r_lane = lanes_of(fmadd_lane<2>(a, b, c)); for (int i = 0; i < 4; ++i) { + CAPTURE(i); F32 e_fmadd, e_fmsub, e_fnmadd, e_lane; if constexpr (AL_SIMD_FMA) { @@ -327,13 +311,9 @@ namespace e_fnmadd = cs[i] - p; e_lane = pl + cs[i]; } - UNSCOPED_INFO("fmadd"); REQUIRE(bits_of(r_fmadd.v[i]) == bits_of(e_fmadd)); - UNSCOPED_INFO("fmsub"); REQUIRE(bits_of(r_fmsub.v[i]) == bits_of(e_fmsub)); - UNSCOPED_INFO("fnmadd"); REQUIRE(bits_of(r_fnmadd.v[i]) == bits_of(e_fnmadd)); - UNSCOPED_INFO("fmadd_lane"); REQUIRE(bits_of(r_lane.v[i]) == bits_of(e_lane)); } } @@ -344,15 +324,15 @@ namespace { const f32x4 x = set(1.f, -2.f, 3.5f, -INF); const f32x4 y = set(0.5f, -1.f, 3.5f, 4.f); - ensure_lanes("min", min(x, y), 0.5f, -2.f, 3.5f, -INF); - ensure_lanes("max", max(x, y), 1.f, -1.f, 3.5f, 4.f); + require_lanes("min", min(x, y), 0.5f, -2.f, 3.5f, -INF); + require_lanes("max", max(x, y), 1.f, -1.f, 3.5f, 4.f); const F32 sq[4] = {4.f, 2.f, 1e-6f, 1e12f}; const f32x4 s = sqrt(loadu(sq)); const Lanes l = lanes_of(s); for (int i = 0; i < 4; ++i) { - UNSCOPED_INFO("sqrt"); + CAPTURE(i); REQUIRE(bits_of(l.v[i]) == bits_of(std::sqrt(sq[i]))); } } @@ -374,10 +354,10 @@ namespace // arithmetic around it adds its own few ulps. const double fast = 1.0 / 2048.0; const double full = 1.0 / 1048576.0; - ensure_lanes_relative("rsqrt_fast", rsqrt_fast(x), e_rsqrt, fast); - ensure_lanes_relative("rsqrt", rsqrt(x), e_rsqrt, full); - ensure_lanes_relative("rcp_fast", rcp_fast(x), e_rcp, fast); - ensure_lanes_relative("rcp", rcp(x), e_rcp, full); + require_lanes_relative("rsqrt_fast", rsqrt_fast(x), e_rsqrt, fast); + require_lanes_relative("rsqrt", rsqrt(x), e_rsqrt, full); + require_lanes_relative("rcp_fast", rcp_fast(x), e_rcp, fast); + require_lanes_relative("rcp", rcp(x), e_rcp, full); const F32 small[4] = {1e-10f, 2.f, 1e-20f, 7.f}; double e_small_rsqrt[4], e_small_rcp[4]; @@ -386,8 +366,8 @@ namespace e_small_rsqrt[i] = 1.0 / std::sqrt((double)small[i]); e_small_rcp[i] = 1.0 / (double)small[i]; } - ensure_lanes_relative("rsqrt small", rsqrt(loadu(small)), e_small_rsqrt, full); - ensure_lanes_relative("rcp small", rcp(loadu(small)), e_small_rcp, full); + require_lanes_relative("rsqrt small", rsqrt(loadu(small)), e_small_rsqrt, full); + require_lanes_relative("rcp small", rcp(loadu(small)), e_small_rcp, full); } // Rounding: nearest with ties to even, floor and ceiling, including the @@ -408,107 +388,88 @@ namespace for (int i = 0; i < 4; ++i) { const F32 v = xs[base + i]; - UNSCOPED_INFO("round " + std::to_string(v)); + CAPTURE(v); REQUIRE(bits_of(r.v[i]) == bits_of(std::nearbyint(v))); - UNSCOPED_INFO("floor " + std::to_string(v)); REQUIRE(bits_of(f.v[i]) == bits_of(std::floor(v))); - UNSCOPED_INFO("ceil " + std::to_string(v)); REQUIRE(bits_of(c.v[i]) == bits_of(std::ceil(v))); - UNSCOPED_INFO("cvt_round " + std::to_string(v)); REQUIRE(int_lane(cr, i) == (S32)std::nearbyint(v)); - UNSCOPED_INFO("cvt_trunc " + std::to_string(v)); REQUIRE(int_lane(ct, i) == (S32)v); } } const f32x4 near_zero = set(-0.f, -0.25f, 0.f, -0.5f); if constexpr (AL_SIMD_SSE4 || AL_SIMD_NEON) { - ensure_lanes("round keeps the sign of zero", round(near_zero), -0.f, -0.f, 0.f, -0.f); - ensure_lanes("ceil keeps the sign of zero", ceil(near_zero), -0.f, -0.f, 0.f, -0.f); - ensure_lanes("floor keeps the sign of zero", floor(set(-0.f, 0.f, 0.25f, 0.f)), -0.f, 0.f, 0.f, 0.f); + require_lanes("round keeps the sign of zero", round(near_zero), -0.f, -0.f, 0.f, -0.f); + require_lanes("ceil keeps the sign of zero", ceil(near_zero), -0.f, -0.f, 0.f, -0.f); + require_lanes("floor keeps the sign of zero", floor(set(-0.f, 0.f, 0.25f, 0.f)), -0.f, 0.f, 0.f, 0.f); } else { - ensure_lanes("round to zero below SSE4", round(near_zero), 0.f, 0.f, 0.f, 0.f); - ensure_lanes("ceil to zero below SSE4", ceil(near_zero), 0.f, 0.f, 0.f, 0.f); + require_lanes("round to zero below SSE4", round(near_zero), 0.f, 0.f, 0.f, 0.f); + require_lanes("ceil to zero below SSE4", ceil(near_zero), 0.f, 0.f, 0.f, 0.f); } - ensure_lanes("to_float", to_float(ints(0, -1, 16777216, -12345678)), 0.f, -1.f, 16777216.f, -12345678.f); + require_lanes("to_float", to_float(ints(0, -1, 16777216, -12345678)), 0.f, -1.f, 16777216.f, -12345678.f); const Lanes back = lanes_of(cvt_trunc(set(0.f, -1.f, 16777216.f, -12345678.f))); UNSCOPED_INFO("cvt_trunc round trip"); CHECK(int_lane(back, 3) == -12345678); } - // Bit operations move bits. TEST_CASE_METHOD(alsimd_data, "alsimd: bit operations move bits", "[alsimd]") { - ensure_lanes_bits("and", and_(a(), b()), pa[0] & pb[0], pa[1] & pb[1], pa[2] & pb[2], pa[3] & pb[3]); - ensure_lanes_bits("andnot", andnot(a(), b()), ~pa[0] & pb[0], ~pa[1] & pb[1], ~pa[2] & pb[2], ~pa[3] & pb[3]); - ensure_lanes_bits("or", or_(a(), b()), pa[0] | pb[0], pa[1] | pb[1], pa[2] | pb[2], pa[3] | pb[3]); - ensure_lanes_bits("xor", xor_(a(), b()), pa[0] ^ pb[0], pa[1] ^ pb[1], pa[2] ^ pb[2], pa[3] ^ pb[3]); - ensure_lanes_bits("as_i32 round trip", as_f32(as_i32(a())), pa[0], pa[1], pa[2], pa[3]); - ensure_lanes_bits("as_mask round trip", as_f32(as_mask(a())), pa[0], pa[1], pa[2], pa[3]); + require_lanes_bits("and", and_(a(), b()), pa[0] & pb[0], pa[1] & pb[1], pa[2] & pb[2], pa[3] & pb[3]); + require_lanes_bits("andnot", andnot(a(), b()), ~pa[0] & pb[0], ~pa[1] & pb[1], ~pa[2] & pb[2], ~pa[3] & pb[3]); + require_lanes_bits("or", or_(a(), b()), pa[0] | pb[0], pa[1] | pb[1], pa[2] | pb[2], pa[3] | pb[3]); + require_lanes_bits("xor", xor_(a(), b()), pa[0] ^ pb[0], pa[1] ^ pb[1], pa[2] ^ pb[2], pa[3] ^ pb[3]); + require_lanes_bits("as_i32 round trip", as_f32(as_i32(a())), pa[0], pa[1], pa[2], pa[3]); + require_lanes_bits("as_mask round trip", as_f32(as_mask(a())), pa[0], pa[1], pa[2], pa[3]); } // Masks: the constants, the comparisons with a NaN in play, and what // reads them. TEST_CASE_METHOD(alsimd_data, "alsimd: mask constants and comparisons with a NaN and what reads them", "[alsimd]") { - ensure_lanes_bits("mask_none", mask_none(), MASK_OFF, MASK_OFF, MASK_OFF, MASK_OFF); - ensure_lanes_bits("mask_all", mask_all(), MASK_ON, MASK_ON, MASK_ON, MASK_ON); - ensure_lanes_bits("mask_lane<0>", mask_lane<0>(), MASK_ON, MASK_OFF, MASK_OFF, MASK_OFF); - ensure_lanes_bits("mask_lane<2>", mask_lane<2>(), MASK_OFF, MASK_OFF, MASK_ON, MASK_OFF); - ensure_lanes_bits("mask_lane<3>", mask_lane<3>(), MASK_OFF, MASK_OFF, MASK_OFF, MASK_ON); - ensure_lanes_bits("mask_xyz", mask_xyz(), MASK_ON, MASK_ON, MASK_ON, MASK_OFF); - ensure_lanes_bits("mask_not", mask_not(mask_lane<1>()), MASK_ON, MASK_OFF, MASK_ON, MASK_ON); + require_lanes_bits("mask_none", mask_none(), MASK_OFF, MASK_OFF, MASK_OFF, MASK_OFF); + require_lanes_bits("mask_all", mask_all(), MASK_ON, MASK_ON, MASK_ON, MASK_ON); + require_lanes_bits("mask_lane<0>", mask_lane<0>(), MASK_ON, MASK_OFF, MASK_OFF, MASK_OFF); + require_lanes_bits("mask_lane<2>", mask_lane<2>(), MASK_OFF, MASK_OFF, MASK_ON, MASK_OFF); + require_lanes_bits("mask_lane<3>", mask_lane<3>(), MASK_OFF, MASK_OFF, MASK_OFF, MASK_ON); + require_lanes_bits("mask_xyz", mask_xyz(), MASK_ON, MASK_ON, MASK_ON, MASK_OFF); + require_lanes_bits("mask_not", mask_not(mask_lane<1>()), MASK_ON, MASK_OFF, MASK_ON, MASK_ON); const f32x4 x = set(1.f, 2.f, NAN_QUIET, -0.f); const f32x4 y = set(2.f, 2.f, 2.f, 0.f); - ensure_lanes_bits("cmplt", cmplt(x, y), MASK_ON, MASK_OFF, MASK_OFF, MASK_OFF); - ensure_lanes_bits("cmple", cmple(x, y), MASK_ON, MASK_ON, MASK_OFF, MASK_ON); - ensure_lanes_bits("cmpgt", cmpgt(x, y), MASK_OFF, MASK_OFF, MASK_OFF, MASK_OFF); - ensure_lanes_bits("cmpge", cmpge(x, y), MASK_OFF, MASK_ON, MASK_OFF, MASK_ON); - ensure_lanes_bits("cmpeq", cmpeq(x, y), MASK_OFF, MASK_ON, MASK_OFF, MASK_ON); - ensure_lanes_bits("cmpne", cmpne(x, y), MASK_ON, MASK_OFF, MASK_ON, MASK_OFF); + require_lanes_bits("cmplt", cmplt(x, y), MASK_ON, MASK_OFF, MASK_OFF, MASK_OFF); + require_lanes_bits("cmple", cmple(x, y), MASK_ON, MASK_ON, MASK_OFF, MASK_ON); + require_lanes_bits("cmpgt", cmpgt(x, y), MASK_OFF, MASK_OFF, MASK_OFF, MASK_OFF); + require_lanes_bits("cmpge", cmpge(x, y), MASK_OFF, MASK_ON, MASK_OFF, MASK_ON); + require_lanes_bits("cmpeq", cmpeq(x, y), MASK_OFF, MASK_ON, MASK_OFF, MASK_ON); + require_lanes_bits("cmpne", cmpne(x, y), MASK_ON, MASK_OFF, MASK_ON, MASK_OFF); const f32x4 n = set(INF, -INF, NAN_QUIET, std::numeric_limits::max()); - ensure_lanes_bits("nonfinite", nonfinite(n), MASK_ON, MASK_ON, MASK_ON, MASK_OFF); + require_lanes_bits("nonfinite", nonfinite(n), MASK_ON, MASK_ON, MASK_ON, MASK_OFF); const f32x4 f = set(0.f, -0.f, DENORMAL, -1.f); - ensure_lanes_bits("nonfinite on finite", nonfinite(f), MASK_OFF, MASK_OFF, MASK_OFF, MASK_OFF); + require_lanes_bits("nonfinite on finite", nonfinite(f), MASK_OFF, MASK_OFF, MASK_OFF, MASK_OFF); const mask4 m = cmplt(x, y); - ensure_lanes_bits("select", select(m, a(), b()), pa[0], pb[1], pb[2], pb[3]); - ensure_lanes_bits("select inverted", select(mask_not(m), a(), b()), pb[0], pa[1], pa[2], pa[3]); - ensure_lanes_bits("select xyz", select(mask_xyz(), a(), b()), pa[0], pa[1], pa[2], pb[3]); + require_lanes_bits("select", select(m, a(), b()), pa[0], pb[1], pb[2], pb[3]); + require_lanes_bits("select inverted", select(mask_not(m), a(), b()), pb[0], pa[1], pa[2], pa[3]); + require_lanes_bits("select xyz", select(mask_xyz(), a(), b()), pa[0], pa[1], pa[2], pb[3]); - UNSCOPED_INFO("bits none"); CHECK(bits(mask_none()) == 0u); - UNSCOPED_INFO("bits all"); CHECK(bits(mask_all()) == 0xFu); - UNSCOPED_INFO("bits lane 2"); CHECK(bits(mask_lane<2>()) == 4u); - UNSCOPED_INFO("bits xyz"); CHECK(bits(mask_xyz()) == 7u); - UNSCOPED_INFO("bits cmpge"); CHECK(bits(cmpge(x, y)) == 0xAu); - UNSCOPED_INFO("any none"); CHECK_FALSE(any(mask_none())); - UNSCOPED_INFO("any lane 3"); CHECK(any(mask_lane<3>())); - UNSCOPED_INFO("all all"); CHECK(all(mask_all())); - UNSCOPED_INFO("all xyz is not all"); CHECK_FALSE(all(mask_xyz())); - UNSCOPED_INFO("any3 lane 3 is not any3"); CHECK_FALSE(any3(mask_lane<3>())); - UNSCOPED_INFO("any3 lane 1"); CHECK(any3(mask_lane<1>())); - UNSCOPED_INFO("all3 xyz"); CHECK(all3(mask_xyz())); - UNSCOPED_INFO("all3 with lane 1 clear"); CHECK_FALSE(all3(mask_not(mask_lane<1>()))); - UNSCOPED_INFO("all3 with only lane 3 clear"); CHECK(all3(mask_not(mask_lane<3>()))); } @@ -530,8 +491,8 @@ namespace volatile F32 zw = p2 + p3; const F32 d3 = xy + p2; const F32 d4 = xy + zw; - ensure_lanes("dot3", dot3(a, b), d3, d3, d3, d3); - ensure_lanes("dot4", dot4(a, b), d4, d4, d4, d4); + require_lanes("dot3", dot3(a, b), d3, d3, d3, d3); + require_lanes("dot4", dot4(a, b), d4, d4, d4, d4); // a.yzx * b.zxy - a.zxy * b.yzx volatile F32 first[3] = {as[1] * bs[2], as[2] * bs[0], as[0] * bs[1]}; @@ -554,23 +515,20 @@ namespace const Lanes c = lanes_of(cross3(a, b)); for (int i = 0; i < 3; ++i) { - UNSCOPED_INFO("cross3 lane " + std::to_string(i)); + CAPTURE(i); REQUIRE(bits_of(c.v[i]) == bits_of(expected[i])); } - UNSCOPED_INFO("cross3 w"); CHECK(c.v[3] == 0.f); // The cross product of two axes is the third. - ensure_lanes("x cross y", cross3(set(1.f, 0.f, 0.f, 0.f), set(0.f, 1.f, 0.f, 0.f)), 0.f, 0.f, 1.f, 0.f); - ensure_lanes("y cross z", cross3(set(0.f, 1.f, 0.f, 0.f), set(0.f, 0.f, 1.f, 0.f)), 1.f, 0.f, 0.f, 0.f); + require_lanes("x cross y", cross3(set(1.f, 0.f, 0.f, 0.f), set(0.f, 1.f, 0.f, 0.f)), 0.f, 0.f, 1.f, 0.f); + require_lanes("y cross z", cross3(set(0.f, 1.f, 0.f, 0.f), set(0.f, 0.f, 1.f, 0.f)), 1.f, 0.f, 0.f, 0.f); } - // The feature macros describe the build they came from. TEST_CASE_METHOD(alsimd_data, "alsimd: the feature macros describe the build they came from", "[alsimd]") { UNSCOPED_INFO("one architecture"); CHECK(AL_SIMD_X86 + AL_SIMD_NEON == 1); - UNSCOPED_INFO("a width"); CHECK((AL_SIMD_WIDTH == 4 || AL_SIMD_WIDTH == 8 || AL_SIMD_WIDTH == 16)); if constexpr (AL_SIMD_AVX512) { @@ -643,12 +601,9 @@ namespace unsigned char* dst = dst_line + dst_off; std::memset(dst - 16, 0xEE, size + 32); copy_aligned16(reinterpret_cast(dst), reinterpret_cast(src), size); - const std::string what = "copy of " + std::to_string(size) + " at " + std::to_string(src_off) + "/" + std::to_string(dst_off); - UNSCOPED_INFO(what + " moved every byte"); + CAPTURE(size, src_off, dst_off); REQUIRE(std::memcmp(dst, src, size) == 0); - UNSCOPED_INFO(what + " left the byte before"); REQUIRE(dst[-1] == 0xEE); - UNSCOPED_INFO(what + " left the byte after"); REQUIRE(dst[size] == 0xEE); } } diff --git a/indra/llcommon/tests/alwatchedfile_test.cpp b/indra/llcommon/tests/alwatchedfile_test.cpp index d69b12e4f4..c2c9401146 100644 --- a/indra/llcommon/tests/alwatchedfile_test.cpp +++ b/indra/llcommon/tests/alwatchedfile_test.cpp @@ -144,7 +144,6 @@ namespace ALWatchedFile watched(file, hear()); UNSCOPED_INFO("nothing yet"); CHECK_FALSE(looked(watched)); - UNSCOPED_INFO("nobody told"); REQUIRE(heard.empty()); put("print(2)\n", 200); @@ -154,9 +153,7 @@ namespace CHECK(watched.check()); UNSCOPED_INFO("told once"); REQUIRE(heard.size() == size_t(1)); - UNSCOPED_INFO("of the file"); CHECK(heard[0] == file); - UNSCOPED_INFO("which held what was written"); CHECK(held[0] == std::string("print(2)\n")); UNSCOPED_INFO("and not again"); CHECK_FALSE(looked(watched)); @@ -167,19 +164,14 @@ namespace put("a = 1\n", 100); ALWatchedFile watched(file, hear()); put("a = 2\n", 300); - UNSCOPED_INFO("the first"); CHECK(looked(watched)); // The same size, the same second: the time alone tells them apart. put("a = 3\n", 500); - UNSCOPED_INFO("the second"); CHECK(looked(watched)); // The same time, a size of its own. put("a = 40\n", 500); - UNSCOPED_INFO("the third"); CHECK(looked(watched)); - UNSCOPED_INFO("each heard"); REQUIRE(heard.size() == size_t(3)); - UNSCOPED_INFO("the last as it was written"); CHECK(held[2] == std::string("a = 40\n")); } @@ -201,9 +193,7 @@ namespace put("four\n", 400); UNSCOPED_INFO("the editor's save after it is heard"); CHECK(looked(watched)); - UNSCOPED_INFO("two heard"); REQUIRE(heard.size() == size_t(2)); - UNSCOPED_INFO("the editor's"); CHECK(held[1] == std::string("four\n")); } @@ -218,11 +208,8 @@ namespace put("local x = 2\n", 300); UNSCOPED_INFO("filled: moved again"); CHECK_FALSE(watched.check()); - UNSCOPED_INFO("still"); CHECK(watched.check()); - UNSCOPED_INFO("heard once"); REQUIRE(heard.size() == size_t(1)); - UNSCOPED_INFO("filled"); CHECK(held[0] == std::string("local x = 2\n")); } @@ -242,7 +229,6 @@ namespace fs::last_write_time(fsyspath(file), second, ec); UNSCOPED_INFO("older"); CHECK(looked(watched)); - UNSCOPED_INFO("three heard"); REQUIRE(heard.size() == size_t(3)); } @@ -252,9 +238,7 @@ namespace UNSCOPED_INFO("nothing there, nothing heard"); CHECK_FALSE(looked(watched)); put("made\n", 100); - UNSCOPED_INFO("made"); CHECK(looked(watched)); - UNSCOPED_INFO("its text"); CHECK(held.at(0) == std::string("made\n")); } @@ -271,10 +255,9 @@ namespace put("after\n", 200); ALWatchedFile& watch = *watched; UNSCOPED_INFO("moving"); - CHECK_FALSE(watch.check()); + REQUIRE_FALSE(watch.check()); UNSCOPED_INFO("heard, and let go"); CHECK(watch.check()); - UNSCOPED_INFO("gone"); REQUIRE_FALSE(watched); UNSCOPED_INFO("told of the file all the same"); CHECK(told == file); @@ -292,9 +275,7 @@ namespace }); watched.poll(0.05f); put("print(2)\n", 200); - UNSCOPED_INFO("heard"); REQUIRE(pumped([&]() { return !heard.empty(); })); - UNSCOPED_INFO("what was written"); CHECK(held.at(0) == std::string("print(2)\n")); UNSCOPED_INFO("told on the main loop's thread"); CHECK(told_on == here); @@ -327,15 +308,12 @@ namespace llofstream out(other, std::ios::binary | std::ios::trunc); out << "default { state_entry() {} }\n"; } - UNSCOPED_INFO("heard"); REQUIRE(pumped([&]() { return !heard.empty(); })); - UNSCOPED_INFO("and let go"); REQUIRE_FALSE(kept); LLTimer timer; pumped([&]() { return timer.getElapsedTimeF32() > 0.4f; }); UNSCOPED_INFO("the one kept, only"); REQUIRE(heard.size() == size_t(1)); - UNSCOPED_INFO("which is the file"); CHECK(heard[0] == file); } @@ -343,16 +321,13 @@ namespace { put("12345", 250); const ALFileStamp stamp = ALFileStamp::of(file); - UNSCOPED_INFO("there"); CHECK(stamp.exists); - UNSCOPED_INFO("its size"); REQUIRE(stamp.size == std::uintmax_t(5)); put("12345", 251); UNSCOPED_INFO("a millisecond later is another time"); CHECK_FALSE(ALFileStamp::of(file) == stamp); UNSCOPED_INFO("a folder is no file"); CHECK_FALSE(ALFileStamp::of(folder).exists); - UNSCOPED_INFO("nothing is nothing"); CHECK_FALSE(ALFileStamp::of(file + ".gone").exists); } @@ -368,14 +343,11 @@ namespace CHECK(watched.stamp() == before); UNSCOPED_INFO("what came after it heard"); CHECK(looked(watched)); - UNSCOPED_INFO("once"); REQUIRE(heard.size() == size_t(1)); - UNSCOPED_INFO("as it is now"); CHECK(held.at(0) == std::string("print(2)\n")); // From a look that found the file as it is: nothing to hear. ALWatchedFile current(file, ALFileStamp::of(file), hear()); - UNSCOPED_INFO("nothing"); CHECK_FALSE(looked(current)); // From a look that found nothing there -- the stamp of no file, @@ -386,7 +358,6 @@ namespace CHECK_FALSE(none.there()); UNSCOPED_INFO("the file there now heard"); CHECK(looked(none)); - UNSCOPED_INFO("twice in all"); REQUIRE(heard.size() == size_t(2)); // And seen() takes the file as it is, whatever look it began from. @@ -395,7 +366,6 @@ namespace owned.seen(); UNSCOPED_INFO("the owner's own is no change"); CHECK_FALSE(looked(owned)); - UNSCOPED_INFO("still twice"); REQUIRE(heard.size() == size_t(2)); } } diff --git a/indra/llcommon/tests/apply_test.cpp b/indra/llcommon/tests/apply_test.cpp index 1f9ee69b5f..d028b2ea1c 100644 --- a/indra/llcommon/tests/apply_test.cpp +++ b/indra/llcommon/tests/apply_test.cpp @@ -69,65 +69,45 @@ namespace const LLSD::URI& uri, const LLSD::Binary& bin) { called = true; - UNSCOPED_INFO("b mismatch"); REQUIRE(b == statics::b); - UNSCOPED_INFO("i mismatch"); REQUIRE(i == statics::i); - UNSCOPED_INFO("f mismatch"); REQUIRE(f == statics::f); - UNSCOPED_INFO("s mismatch"); REQUIRE(s == statics::s); - UNSCOPED_INFO("uu mismatch"); REQUIRE(uu == statics::uu); - UNSCOPED_INFO("dt mismatch"); REQUIRE(dt == statics::dt); - UNSCOPED_INFO("uri mismatch"); REQUIRE(uri.asString() == statics::uri.asString()); - UNSCOPED_INFO("bin mismatch"); REQUIRE(bin == statics::bin); } static void strings(std::string s0, std::string s1, std::string s2, std::string s3, std::string s4) { called = true; - UNSCOPED_INFO("s0 mismatch"); REQUIRE(s0 == statics::quick[0]); - UNSCOPED_INFO("s1 mismatch"); REQUIRE(s1 == statics::quick[1]); - UNSCOPED_INFO("s2 mismatch"); REQUIRE(s2 == statics::quick[2]); - UNSCOPED_INFO("s3 mismatch"); REQUIRE(s3 == statics::quick[3]); - UNSCOPED_INFO("s4 mismatch"); REQUIRE(s4 == statics::quick[4]); } static void ints(int i0, int i1, int i2, int i3, int i4) { called = true; - UNSCOPED_INFO("i0 mismatch"); REQUIRE(i0 == statics::fibs[0]); - UNSCOPED_INFO("i1 mismatch"); REQUIRE(i1 == statics::fibs[1]); - UNSCOPED_INFO("i2 mismatch"); REQUIRE(i2 == statics::fibs[2]); - UNSCOPED_INFO("i3 mismatch"); REQUIRE(i3 == statics::fibs[3]); - UNSCOPED_INFO("i4 mismatch"); REQUIRE(i4 == statics::fibs[4]); } static void sdfunc(const LLSD& sd) { called = true; - UNSCOPED_INFO("sd mismatch"); REQUIRE(sd.asInteger() == statics::i); } static void intfunc(int i) { called = true; - UNSCOPED_INFO("i mismatch"); REQUIRE(i == statics::i); } @@ -166,21 +146,18 @@ namespace LL::apply(statics::various, std::make_tuple(statics::b, statics::i, statics::f, statics::s, statics::uu, statics::dt, statics::uri, statics::bin)); - UNSCOPED_INFO("apply(tuple) failed"); CHECK(statics::called); } TEST_CASE_METHOD(apply_data, "apply: apply(array) passes each element as an argument", "[apply]") { LL::apply(statics::ints, statics::fibs); - UNSCOPED_INFO("apply(array) failed"); CHECK(statics::called); } TEST_CASE_METHOD(apply_data, "apply: apply(vector) passes each element as an argument", "[apply]") { LL::apply(statics::strings, statics::quick); - UNSCOPED_INFO("apply(vector) failed"); CHECK(statics::called); } @@ -189,14 +166,12 @@ namespace TEST_CASE_METHOD(apply_data, "apply: apply(LLSD()) calls a function of no arguments", "[apply]") { LL::apply(statics::voidfunc, LLSD()); - UNSCOPED_INFO("apply(LLSD()) failed"); CHECK(statics::called); } TEST_CASE_METHOD(apply_data, "apply: apply with an LLSD scalar converts it for an int parameter", "[apply]") { LL::apply(statics::intfunc, LLSD(statics::i)); - UNSCOPED_INFO("apply(fn(int), LLSD scalar) failed"); CHECK(statics::called); } @@ -205,7 +180,6 @@ namespace // This test verifies that LLSDParam doesn't send the compiler // into infinite recursion when the target is itself LLSD. LL::apply(statics::sdfunc, LLSD(statics::i)); - UNSCOPED_INFO("apply(fn(LLSD), LLSD scalar) failed"); CHECK(statics::called); } @@ -214,7 +188,6 @@ namespace LL::apply(statics::various, llsd::array(statics::b, statics::i, statics::f, statics::s, statics::uu, statics::dt, statics::uri, statics::bin)); - UNSCOPED_INFO("apply(LLSD array) failed"); CHECK(statics::called); } @@ -230,9 +203,7 @@ namespace // LL::apply(statics::collect, strray); // That's what VAPPLY() is for. VAPPLY(statics::collect, strray); - UNSCOPED_INFO("VAPPLY() failed"); CHECK(statics::called); - UNSCOPED_INFO("collected mismatch"); CHECK(statics::collected == statics::quick); } } // anonymous namespace diff --git a/indra/llcommon/tests/bitpack_test.cpp b/indra/llcommon/tests/bitpack_test.cpp index b7d3a15fc6..c80289b812 100644 --- a/indra/llcommon/tests/bitpack_test.cpp +++ b/indra/llcommon/tests/bitpack_test.cpp @@ -36,7 +36,6 @@ namespace { - // pack -> unpack TEST_CASE("LLBitPack: a string packed then unpacked comes back unchanged", "[LLBitPack]") { U8 packbuffer[255]; @@ -53,13 +52,10 @@ namespace LLBitPack bitunpack(packbuffer, pack_bufsize*8); unpack_bufsize = bitunpack.bitUnpack(unpackbuffer, len*8); - UNSCOPED_INFO("bitPack: unpack size should be same as string size prior to pack"); CHECK(len == unpack_bufsize); - UNSCOPED_INFO("str->bitPack->bitUnpack should be equal to string"); CHECK_THAT(ll_test::Memory(str, len), ll_test::MemoryMatches(unpackbuffer, unpack_bufsize)); } - // pack large, unpack in individual bytes TEST_CASE("LLBitPack: a packed string unpacks a byte at a time", "[LLBitPack]") { U8 packbuffer[255]; @@ -75,29 +71,21 @@ namespace LLBitPack bitunpack(packbuffer, pack_bufsize*8); bitunpack.bitUnpack(&unpackbuffer[0], 8); - UNSCOPED_INFO("bitPack: individual unpack: 0"); CHECK(unpackbuffer[0] == (U8) str[0]); bitunpack.bitUnpack(&unpackbuffer[0], 8); - UNSCOPED_INFO("bitPack: individual unpack: 1"); CHECK(unpackbuffer[0] == (U8) str[1]); bitunpack.bitUnpack(&unpackbuffer[0], 8); - UNSCOPED_INFO("bitPack: individual unpack: 2"); CHECK(unpackbuffer[0] == (U8) str[2]); bitunpack.bitUnpack(&unpackbuffer[0], 8); - UNSCOPED_INFO("bitPack: individual unpack: 3"); CHECK(unpackbuffer[0] == (U8) str[3]); bitunpack.bitUnpack(&unpackbuffer[0], 8); - UNSCOPED_INFO("bitPack: individual unpack: 4"); CHECK(unpackbuffer[0] == (U8) str[4]); bitunpack.bitUnpack(&unpackbuffer[0], 8); - UNSCOPED_INFO("bitPack: individual unpack: 5"); CHECK(unpackbuffer[0] == (U8) str[5]); bitunpack.bitUnpack(unpackbuffer, 8*4); // Life - UNSCOPED_INFO("bitPack: 4 bytes unpack:"); - REQUIRE_THAT(ll_test::Memory(unpackbuffer, 4), ll_test::MemoryMatches(str+6, 4)); + CHECK_THAT(ll_test::Memory(unpackbuffer, 4), ll_test::MemoryMatches(str+6, 4)); } - // U32 packing TEST_CASE("LLBitPack: a U32 packed then unpacked comes back unchanged", "[LLBitPack]") { U8 packbuffer[255]; @@ -113,7 +101,6 @@ namespace // since packing and unpacking is done on same machine in the unit test run, // endianness should not matter bitunpack.bitUnpack((U8*) &res, sizeof(res)*8); - UNSCOPED_INFO("U32->bitPack->bitUnpack->U32 should be equal"); CHECK(num == res); } } diff --git a/indra/llcommon/tests/classic_callback_test.cpp b/indra/llcommon/tests/classic_callback_test.cpp index 25bc17b3dd..c960980933 100644 --- a/indra/llcommon/tests/classic_callback_test.cpp +++ b/indra/llcommon/tests/classic_callback_test.cpp @@ -135,13 +135,9 @@ namespace // effect. auto occb{ makeClassicCallback(std::move(ocb)) }; std::string result{ otherAPI(occb.get_callback(), occb.get_userdata()) }; - UNSCOPED_INFO("failed to return callback result"); CHECK(result == "hello back!"); - UNSCOPED_INFO("failed to set int"); CHECK(sData.mi == 17); - UNSCOPED_INFO("failed to set string"); CHECK(sData.ms == "hello world"); - UNSCOPED_INFO("failed to set double"); CHECK(sData.mf == 3.0); } } // anonymous namespace diff --git a/indra/llcommon/tests/commonmisc_test.cpp b/indra/llcommon/tests/commonmisc_test.cpp index 4e8deb84b8..e6102076f8 100644 --- a/indra/llcommon/tests/commonmisc_test.cpp +++ b/indra/llcommon/tests/commonmisc_test.cpp @@ -83,7 +83,6 @@ namespace size_t same_as_hash1 = llhash(same_as_str1); - UNSCOPED_INFO("Hashes from identical strings should be equal"); CHECK(hash1 == same_as_hash1); char str[100]; @@ -95,7 +94,6 @@ namespace size_t hash3 = llhash(str); - UNSCOPED_INFO("Hashes from same pointer but different string should not be equal"); CHECK(hash2 != hash3); } @@ -106,9 +104,7 @@ namespace std::string hello; std::string world; mem >> hello >> world; - UNSCOPED_INFO("first word"); CHECK(hello == std::string("hello")); - UNSCOPED_INFO("second word"); CHECK(world == std::string("world")); } @@ -120,19 +116,12 @@ namespace LLMemoryStream mstr((U8*)str.c_str(), static_cast(str.size())); LLSD response; S32 count = LLSDSerialize::fromNotation(response, mstr, str.size()); - UNSCOPED_INFO("stream parsed"); - REQUIRE(response.isDefined()); - UNSCOPED_INFO("stream parse count"); + CHECK(response.isDefined()); CHECK(count == 13); - UNSCOPED_INFO("sd type"); CHECK(response.type() == LLSD::TypeMap); - UNSCOPED_INFO("map element count"); - REQUIRE(response.size() == 6); - UNSCOPED_INFO("value connect"); - CHECK(response["connect"].asBoolean() == true); - UNSCOPED_INFO("value region_x"); + CHECK(response.size() == 6); + CHECK(response["connect"].asBoolean()); CHECK(response["region_x"].asInteger() == 8192); - UNSCOPED_INFO("value region_y"); CHECK(response["region_y"].asInteger() == 8192); } @@ -151,28 +140,22 @@ namespace sd = buf; std::stringstream str; S32 count = LLSDSerialize::toNotation(sd, str); - UNSCOPED_INFO("output count"); CHECK(count == 1); std::string actual(str.str()); - UNSCOPED_INFO("formatted binary encoding"); CHECK(actual == streamed); sd.clear(); LLSDSerialize::fromNotation(sd, str, str.str().size()); std::vector after; after = sd.asBinary(); - UNSCOPED_INFO("binary decoded size"); REQUIRE(after.size() == decoded.size()); - UNSCOPED_INFO("binary decoding"); - REQUIRE(memcmp( - &after[0], - decoded.c_str(), - decoded.size()) == 0); + CHECK(memcmp(&after[0], decoded.c_str(), decoded.size()) == 0); } TEST_CASE("LLSD: random binary round-trips through notation", "[LLSD]") { for(S32 i = 0; i < 100; ++i) { + CAPTURE(i); // gen up a starting point typedef std::vector buf_t; buf_t source; @@ -186,20 +169,12 @@ namespace std::stringstream str; S32 count = LLSDSerialize::toNotation(sd, str); sd.clear(); - UNSCOPED_INFO("format count"); REQUIRE(count == 1); LLSD sd2; count = LLSDSerialize::fromNotation(sd2, str, str.str().size()); - UNSCOPED_INFO("parse count"); REQUIRE(count == 1); buf_t dest = sd2.asBinary(); - str.str(""); - str << "binary encoding size " << i; - UNSCOPED_INFO(str.str().c_str()); REQUIRE(dest.size() == source.size()); - str.str(""); - str << "binary encoding " << i; - UNSCOPED_INFO(str.str().c_str()); REQUIRE(source == dest); } } @@ -218,9 +193,7 @@ namespace sd, serialized, serialized.str().size()); - UNSCOPED_INFO("parse count"); CHECK(count == 1); - UNSCOPED_INFO("String streaming"); CHECK(sd.asString() == expected); } @@ -228,6 +201,7 @@ namespace { for(S32 i = 0; i < 100; ++i) { + CAPTURE(i); // gen up a starting point typedef std::vector buf_t; buf_t source; @@ -243,12 +217,9 @@ namespace str << "\""; LLSD sd; S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size()); - UNSCOPED_INFO("binary parse"); REQUIRE(count == 1); buf_t actual = sd.asBinary(); - UNSCOPED_INFO("binary size"); REQUIRE(actual.size() == (size_t)size); - UNSCOPED_INFO("binary data"); REQUIRE(memcmp(&source[0], &actual[0], size) == 0); } } @@ -262,12 +233,9 @@ namespace str << "'"; LLSD sd; S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size()); - UNSCOPED_INFO("parse count"); CHECK(count == 1); std::string actual = sd.asString(); - UNSCOPED_INFO("string sizes"); - REQUIRE(actual.size() == expected.size()); - UNSCOPED_INFO("string content"); + CHECK(actual.size() == expected.size()); CHECK(actual == expected); } @@ -284,23 +252,14 @@ namespace sd, stream, stream.str().size()); - UNSCOPED_INFO("parse count"); CHECK(count == 12); - UNSCOPED_INFO("bool value"); - CHECK(sd["connect"].asBoolean() == true); - UNSCOPED_INFO("message value"); + CHECK(sd["connect"].asBoolean()); CHECK(sd["message"].asString() == msg); - UNSCOPED_INFO("pos x"); CHECK(sd["position"][0].asReal() == 45.65); - UNSCOPED_INFO("pos y"); CHECK(sd["position"][1].asReal() == 100.1); - UNSCOPED_INFO("pos z"); CHECK(sd["position"][2].asReal() == 25.5); - UNSCOPED_INFO("look x"); CHECK(sd["look_at"][0].asReal() == 0.0); - UNSCOPED_INFO("look y"); CHECK(sd["look_at"][1].asReal() == 1.0); - UNSCOPED_INFO("look z"); CHECK(sd["look_at"][2].asReal() == 0.0); } @@ -313,15 +272,10 @@ namespace response, resp, resp.str().size()); - UNSCOPED_INFO("parse count"); CHECK(count == 5); - UNSCOPED_INFO("sd type"); CHECK(response.type() == LLSD::TypeMap); - UNSCOPED_INFO("map element count"); - REQUIRE(response.size() == 4); - UNSCOPED_INFO("singlechar"); + CHECK(response.size() == 4); CHECK(response["singlechar"].asString() == "a"); - UNSCOPED_INFO("empty"); CHECK(response["empty"].asString() == ""); } @@ -333,27 +287,19 @@ namespace LLSD sd; LLMemoryStream mstr((U8*)str.c_str(), static_cast(str.size())); S32 count = LLSDSerialize::fromNotation(sd, mstr, str.size()); - UNSCOPED_INFO("parse count"); CHECK(count == 5); - UNSCOPED_INFO("sd created"); REQUIRE(sd.isDefined()); - UNSCOPED_INFO("sd type"); CHECK(sd.type() == LLSD::TypeMap); - UNSCOPED_INFO("map element count"); REQUIRE(sd.size() == 4); - UNSCOPED_INFO("label"); CHECK(sd["label"].asString() == "short binary test"); std::vector bin = sd["singlebinary"].asBinary(); std::vector expected; expected.resize(1); expected[0] = 'A'; - UNSCOPED_INFO("single binary"); CHECK(memcmp(&bin[0], &expected[0], 1) == 0); - UNSCOPED_INFO("single string"); CHECK(sd["singlerawstring"].asString() == std::string("A")); - UNSCOPED_INFO("end"); CHECK(sd["endoftest"].asString() == "end"); } @@ -366,16 +312,12 @@ namespace << "'}"; std::string expected_str("{'message':'parcel \\'\\' is naughty.'}"); std::string actual_str = str.str(); - UNSCOPED_INFO("stream contents"); CHECK(actual_str == expected_str); LLSD sd; S32 count = LLSDSerialize::fromNotation(sd, str, actual_str.size()); - UNSCOPED_INFO("parse count"); CHECK(count == 2); - UNSCOPED_INFO("valid parse"); - REQUIRE(sd.isDefined()); + CHECK(sd.isDefined()); std::string actual = sd["message"].asString(); - UNSCOPED_INFO("message contents"); CHECK(actual == message); } @@ -386,9 +328,7 @@ namespace str << "'" << LLSDNotationFormatter::escapeString(expected) << "'"; LLSD sd; S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size()); - UNSCOPED_INFO("parse count"); CHECK(count == 1); - UNSCOPED_INFO("string value"); CHECK(sd.asString() == expected); } @@ -399,9 +339,7 @@ namespace str << "'" << LLSDNotationFormatter::escapeString(expected) << "'"; LLSD sd; S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size()); - UNSCOPED_INFO("parse count"); CHECK(count == 1); - UNSCOPED_INFO("string value"); CHECK(sd.asString() == expected); } @@ -409,6 +347,7 @@ namespace { for(S32 i = 0; i < 1000; ++i) { + CAPTURE(i); // gen up a starting point std::string expected; srand(1337 + i); /* Flawfinder: ignore */ @@ -421,7 +360,6 @@ namespace str << "'" << LLSDNotationFormatter::escapeString(expected) << "'"; LLSD sd; S32 count = LLSDSerialize::fromNotation(sd, str, expected.size()); - UNSCOPED_INFO("parse count"); REQUIRE(count == 1); std::string actual = sd.asString(); /* @@ -453,7 +391,6 @@ namespace LL_WARNS() << "actual: " << a_str.str() << LL_ENDL; } */ - UNSCOPED_INFO("string value"); REQUIRE(actual == expected); } } @@ -468,20 +405,14 @@ namespace istr.str(param); LLSD param_sd; LLSDSerialize::fromNotation(param_sd, istr, param.size()); - UNSCOPED_INFO("parsed type"); CHECK(param_sd.type() == LLSD::TypeArray); LLSD version_sd = param_sd[0]; - UNSCOPED_INFO("version type"); CHECK(version_sd.type() == LLSD::TypeMap); - UNSCOPED_INFO("has version"); - REQUIRE(version_sd.has("version")); - UNSCOPED_INFO("version number"); + CHECK(version_sd.has("version")); CHECK(version_sd["version"].asInteger() == 1); LLSD src_sd = param_sd[1]; - UNSCOPED_INFO("src type"); CHECK(src_sd.type() == LLSD::TypeMap); LLSD dst_sd = param_sd[2]; - UNSCOPED_INFO("dst type"); CHECK(dst_sd.type() == LLSD::TypeMap); } @@ -492,19 +423,13 @@ namespace istr.str(val); LLSD sd; LLSDSerialize::fromNotation(sd, istr, val.size()); - UNSCOPED_INFO("parsed type"); CHECK(sd.type() == LLSD::TypeArray); - UNSCOPED_INFO("parsed size"); REQUIRE(sd.size() == 1); LLSD failures = sd[0]["failures"]; - UNSCOPED_INFO("no failures."); - REQUIRE(failures.isUndefined()); + CHECK(failures.isUndefined()); LLSD success = sd[0]["successfuls"]; - UNSCOPED_INFO("success type"); CHECK(success.type() == LLSD::TypeArray); - UNSCOPED_INFO("success size"); REQUIRE(success.size() == 1); - UNSCOPED_INFO("success instance type"); CHECK(success[0].type() == LLSD::TypeUUID); } @@ -515,32 +440,20 @@ namespace istr.str(val); LLSD sd; LLSDSerialize::fromNotation(sd, istr, val.size()); - UNSCOPED_INFO("parsed type"); CHECK(sd.type() == LLSD::TypeArray); - UNSCOPED_INFO("parsed size"); REQUIRE(sd.size() == 5); - UNSCOPED_INFO("element 0 false"); - CHECK(sd[0].asBoolean() == false); - UNSCOPED_INFO("element 1 true"); - CHECK(sd[1].asBoolean() == true); - UNSCOPED_INFO("element 2 false"); - CHECK(sd[2].asBoolean() == false); - UNSCOPED_INFO("element 3 true"); - CHECK(sd[3].asBoolean() == true); + CHECK_FALSE(sd[0].asBoolean()); + CHECK(sd[1].asBoolean()); + CHECK_FALSE(sd[2].asBoolean()); + CHECK(sd[3].asBoolean()); LLSD map = sd[4]; - UNSCOPED_INFO("element 4 type"); CHECK(map.type() == LLSD::TypeMap); - UNSCOPED_INFO("map foo type"); CHECK(map["foo"].type() == LLSD::TypeBoolean); - UNSCOPED_INFO("map foo value"); - CHECK(map["foo"].asBoolean() == true); - UNSCOPED_INFO("map bar type"); + CHECK(map["foo"].asBoolean()); CHECK(map["bar"].type() == LLSD::TypeBoolean); - UNSCOPED_INFO("map bar value"); - CHECK(map["bar"].asBoolean() == false); + CHECK_FALSE(map["bar"].asBoolean()); } - // U64_to_str TEST_CASE("U64_conversion: U64_to_str writes the digits and truncates to the buffer", "[U64_conversion]") { U64 val; @@ -553,21 +466,18 @@ namespace U64_to_str(val, result, sizeof(result)); result_str = (const char*) result; - UNSCOPED_INFO("U64_to_str converted 1.1"); CHECK(val_str == result_str); val = 0; val_str = "0"; U64_to_str(val, result, sizeof(result)); result_str = (const char*) result; - UNSCOPED_INFO("U64_to_str converted 1.2"); CHECK(val_str == result_str); val = U64L(18446744073709551615); // 0xFFFFFFFFFFFFFFFF val_str = "18446744073709551615"; U64_to_str(val, result, sizeof(result)); result_str = (const char*) result; - UNSCOPED_INFO("U64_to_str converted 1.3"); CHECK(val_str == result_str); // overflow - will result in warning at compile time @@ -575,28 +485,24 @@ namespace val_str = "0"; U64_to_str(val, result, sizeof(result)); result_str = (const char*) result; - UNSCOPED_INFO("U64_to_str converted 1.4"); CHECK(val_str == result_str); val = U64(-1); // 0xFFFFFFFFFFFFFFFF == 18446744073709551615 val_str = "18446744073709551615"; U64_to_str(val, result, sizeof(result)); result_str = (const char*) result; - UNSCOPED_INFO("U64_to_str converted 1.5"); CHECK(val_str == result_str); val = U64L(10000000000000000000); // testing preserving of 0s val_str = "10000000000000000000"; U64_to_str(val, result, sizeof(result)); result_str = (const char*) result; - UNSCOPED_INFO("U64_to_str converted 1.6"); CHECK(val_str == result_str); val = 1; // testing no leading 0s val_str = "1"; U64_to_str(val, result, sizeof(result)); result_str = (const char*) result; - UNSCOPED_INFO("U64_to_str converted 1.7"); CHECK(val_str == result_str); val = U64L(18446744073709551615); // testing exact sized buffer for result @@ -604,7 +510,6 @@ namespace memset(result, 'A', sizeof(result)); // initialize buffer with all 'A' U64_to_str(val, result, sizeof("18446744073709551615")); //pass in the exact size result_str = (const char*) result; - UNSCOPED_INFO("U64_to_str converted 1.8"); CHECK(val_str == result_str); val = U64L(18446744073709551615); // testing smaller sized buffer for result @@ -612,11 +517,9 @@ namespace memset(result, 'A', sizeof(result)); // initialize buffer with all 'A' U64_to_str(val, result, 5); //pass in a size of 5. should only copy first 4 integers and add a null terminator result_str = (const char*) result; - UNSCOPED_INFO("U64_to_str converted 1.9"); CHECK(val_str == result_str); } - // str_to_U64 TEST_CASE("U64_conversion: str_to_U64 reads the first run of digits and wraps on overflow", "[U64_conversion]") { U64 val; @@ -624,59 +527,48 @@ namespace val = U64L(18446744073709551610); // slightly less than MAX_U64 result = str_to_U64("18446744073709551610"); - UNSCOPED_INFO("str_to_U64 converted 2.1"); CHECK(val == result); val = U64L(0); // empty string result = str_to_U64(LLStringUtil::null); - UNSCOPED_INFO("str_to_U64 converted 2.2"); CHECK(val == result); val = U64L(0); // 0 result = str_to_U64("0"); - UNSCOPED_INFO("str_to_U64 converted 2.3"); CHECK(val == result); val = U64L(18446744073709551615); // 0xFFFFFFFFFFFFFFFF result = str_to_U64("18446744073709551615"); - UNSCOPED_INFO("str_to_U64 converted 2.4"); CHECK(val == result); // overflow - will result in warning at compile time val = U64L(18446744073709551615) + 1; // overflow 0xFFFFFFFFFFFFFFFF + 1 == 0 result = str_to_U64("18446744073709551616"); - UNSCOPED_INFO("str_to_U64 converted 2.5"); CHECK(val == result); val = U64L(1234); // process till first non-integral character result = str_to_U64("1234A5678"); - UNSCOPED_INFO("str_to_U64 converted 2.6"); CHECK(val == result); val = U64L(5678); // skip all non-integral characters result = str_to_U64("ABCD5678"); - UNSCOPED_INFO("str_to_U64 converted 2.7"); CHECK(val == result); // should it skip negative sign and process // rest of string or return 0 val = U64L(1234); // skip initial negative sign result = str_to_U64("-1234"); - UNSCOPED_INFO("str_to_U64 converted 2.8"); CHECK(val == result); val = U64L(5678); // stop at negative sign in the middle result = str_to_U64("5678-1234"); - UNSCOPED_INFO("str_to_U64 converted 2.9"); CHECK(val == result); val = U64L(0); // no integers result = str_to_U64("AaCD"); - UNSCOPED_INFO("str_to_U64 converted 2.10"); CHECK(val == result); } - // U64_to_F64 TEST_CASE("U64_conversion: U64_to_F64 converts values from zero to the largest U64", "[U64_conversion]") { F64 val; @@ -684,38 +576,31 @@ namespace result = 18446744073709551610.0; val = U64_to_F64(U64L(18446744073709551610)); - UNSCOPED_INFO("U64_to_F64 converted 3.1"); CHECK(val == result); result = 18446744073709551615.0; // 0xFFFFFFFFFFFFFFFF val = U64_to_F64(U64L(18446744073709551615)); - UNSCOPED_INFO("U64_to_F64 converted 3.2"); CHECK(val == result); result = 0.0; // overflow 0xFFFFFFFFFFFFFFFF + 1 == 0 // overflow - will result in warning at compile time val = U64_to_F64(U64L(18446744073709551615)+1); - UNSCOPED_INFO("U64_to_F64 converted 3.3"); CHECK(val == result); result = 0.0; // 0 val = U64_to_F64(U64L(0)); - UNSCOPED_INFO("U64_to_F64 converted 3.4"); CHECK(val == result); result = 1.0; // odd val = U64_to_F64(U64L(1)); - UNSCOPED_INFO("U64_to_F64 converted 3.5"); CHECK(val == result); result = 2.0; // even val = U64_to_F64(U64L(2)); - UNSCOPED_INFO("U64_to_F64 converted 3.6"); CHECK(val == result); result = F64(0x7FFFFFFFFFFFFFFF) * 1.0; // 0x7FFFFFFFFFFFFFFF val = U64_to_F64(U64L(0x7FFFFFFFFFFFFFFF)); - UNSCOPED_INFO("U64_to_F64 converted 3.7"); CHECK(val == result); } } diff --git a/indra/llcommon/tests/lazyeventapi_test.cpp b/indra/llcommon/tests/lazyeventapi_test.cpp index 133068c927..6e172e10dd 100644 --- a/indra/llcommon/tests/lazyeventapi_test.cpp +++ b/indra/llcommon/tests/lazyeventapi_test.cpp @@ -92,7 +92,6 @@ namespace // 'register' still a keyword until C++17 MyRegistrar regster; LLEventPumps::instance().obtain("Test").post(llsd::map("op", "set", "data", "hey")); - UNSCOPED_INFO("failed to set data"); CHECK(data.asString() == "hey"); } @@ -102,8 +101,7 @@ namespace // it has been destroyed, we fully expect NOT to reach a MyListener // instance with this post. LLEventPumps::instance().obtain("Test").post(llsd::map("op", "set", "data", "moot")); - UNSCOPED_INFO("accidentally set data"); - REQUIRE_FALSE(data.isDefined()); + CHECK_FALSE(data.isDefined()); } TEST_CASE_METHOD(lazyeventapi_data, "lazyeventapi: instance_snapshot finds a LazyEventAPI and its metadata", "[lazyeventapi]") @@ -121,25 +119,17 @@ namespace UNSCOPED_INFO("Failed to find MyRegistrar via LLInstanceTracker"); REQUIRE(found); - UNSCOPED_INFO("wrong API name"); CHECK(found->getName() == "Test"); - UNSCOPED_INFO("wrong API desc"); CHECK_THAT(found->getDesc(), Catch::Matchers::ContainsSubstring("test LLEventAPI")); - UNSCOPED_INFO("wrong API field"); CHECK(found->getDispatchKey() == "op"); // Normally we'd just iterate over *found. But for test purposes, // actually capture the range of NameDesc pairs in a vector. std::vector ops{ found->begin(), found->end() }; - UNSCOPED_INFO("failed to find operations"); REQUIRE(ops.size() == 1); - UNSCOPED_INFO("wrong operation name"); CHECK(ops[0].first == "set"); - UNSCOPED_INFO("wrong operation desc"); CHECK_THAT(ops[0].second, Catch::Matchers::ContainsSubstring("set operation")); LLSD metadata{ found->getMetadata(ops[0].first) }; - UNSCOPED_INFO("bad metadata name"); CHECK(metadata["name"].asString() == ops[0].first); - UNSCOPED_INFO("bad metadata desc"); CHECK(metadata["desc"].asString() == ops[0].second); } } // anonymous namespace diff --git a/indra/llcommon/tests/llapp_test.cpp b/indra/llcommon/tests/llapp_test.cpp index 021ad62a29..964d6c6b3c 100644 --- a/indra/llcommon/tests/llapp_test.cpp +++ b/indra/llcommon/tests/llapp_test.cpp @@ -70,9 +70,7 @@ namespace mApp->setOptionData(LLApp::PRIORITY_DEFAULT, defaults); LLSD datadir_sd = mApp->getOption("datadir"); - UNSCOPED_INFO("data type"); CHECK(datadir_sd.type() == LLSD::TypeString); - UNSCOPED_INFO("data value"); CHECK(datadir_sd.asString() == std::string("data")); } @@ -81,11 +79,9 @@ namespace LLSD options; options["boolean-test"] = true; mApp->setOptionData(LLApp::PRIORITY_GENERAL_CONFIGURATION, options); - UNSCOPED_INFO("bool set"); CHECK(mApp->getOption("boolean-test").asBoolean()); options["boolean-test"] = false; mApp->setOptionData(LLApp::PRIORITY_RUNTIME_OVERRIDE, options); - UNSCOPED_INFO("bool unset"); CHECK_FALSE(mApp->getOption("boolean-test").asBoolean()); } } diff --git a/indra/llcommon/tests/llbase64_test.cpp b/indra/llcommon/tests/llbase64_test.cpp index 14a5636d0e..0a3af78e8b 100644 --- a/indra/llcommon/tests/llbase64_test.cpp +++ b/indra/llcommon/tests/llbase64_test.cpp @@ -41,17 +41,14 @@ namespace std::string result; result = LLBase64::encode(NULL, 0); - UNSCOPED_INFO("encode nothing"); CHECK(result == ""); LLUUID nothing; result = LLBase64::encode(¬hing.mData[0], UUID_BYTES); - UNSCOPED_INFO("encode blank uuid"); CHECK(result == "AAAAAAAAAAAAAAAAAAAAAA=="); LLUUID id("526a1e07-a19d-baed-84c4-ff08a488d15e"); result = LLBase64::encode(&id.mData[0], UUID_BYTES); - UNSCOPED_INFO("encode random uuid"); CHECK(result == "UmoeB6Gduu2ExP8IpIjRXg=="); } @@ -62,7 +59,6 @@ namespace U8 blob[40] = { 115, 223, 172, 255, 140, 70, 49, 125, 236, 155, 45, 199, 101, 17, 164, 131, 230, 19, 80, 64, 112, 53, 135, 98, 237, 12, 26, 72, 126, 14, 145, 143, 118, 196, 11, 177, 132, 169, 195, 134 }; result = LLBase64::encode(&blob[0], 40); - UNSCOPED_INFO("encode 40 bytes"); CHECK(result == "c9+s/4xGMX3smy3HZRGkg+YTUEBwNYdi7QwaSH4OkY92xAuxhKnDhg=="); } diff --git a/indra/llcommon/tests/llcond_test.cpp b/indra/llcommon/tests/llcond_test.cpp index d4b27d559c..95f897657e 100644 --- a/indra/llcommon/tests/llcond_test.cpp +++ b/indra/llcommon/tests/llcond_test.cpp @@ -33,9 +33,7 @@ namespace TEST_CASE_METHOD(llcond_data, "llcond: a wait for the value it already holds returns at once", "[llcond]") { cond.set_one(1); - UNSCOPED_INFO("wait_for_equal() failed"); CHECK(cond.wait_for_equal(F32Milliseconds(1), 1)); - UNSCOPED_INFO("wait_for_unequal() should have failed"); CHECK_FALSE(cond.wait_for_unequal(F32Milliseconds(1), 1)); } diff --git a/indra/llcommon/tests/lldate_test.cpp b/indra/llcommon/tests/lldate_test.cpp index c6f31dc8e5..bee9279b72 100644 --- a/indra/llcommon/tests/lldate_test.cpp +++ b/indra/llcommon/tests/lldate_test.cpp @@ -82,59 +82,55 @@ namespace tut std::string expected_string; bool result; expected_string = VALID_DATE; - UNSCOPED_INFO("Valid Date failed"); CHECK(expected_string == date.asString()); result = date.fromString(VALID_DATE_LEAP); expected_string = VALID_DATE_LEAP; - UNSCOPED_INFO("VALID_DATE_LEAP failed"); CHECK(expected_string == date.asString()); result = date.fromString(VALID_DATE_HOUR_BOUNDARY); expected_string = VALID_DATE_HOUR_BOUNDARY; - UNSCOPED_INFO("VALID_DATE_HOUR_BOUNDARY failed"); CHECK(expected_string == date.asString()); result = date.fromString(VALID_DATE_FRACTIONAL_SECS); expected_string = VALID_DATE_FRACTIONAL_SECS; - UNSCOPED_INFO("VALID_DATE_FRACTIONAL_SECS failed"); CHECK(expected_string == date.asString()); result = date.fromString(INVALID_DATE_MISSING_YEAR); UNSCOPED_INFO("INVALID_DATE_MISSING_YEAR should have failed"); - CHECK(result == false); + CHECK_FALSE(result); result = date.fromString(INVALID_DATE_MISSING_MONTH); UNSCOPED_INFO("INVALID_DATE_MISSING_MONTH should have failed"); - CHECK(result == false); + CHECK_FALSE(result); result = date.fromString(INVALID_DATE_MISSING_DATE); UNSCOPED_INFO("INVALID_DATE_MISSING_DATE should have failed"); - CHECK(result == false); + CHECK_FALSE(result); result = date.fromString(INVALID_DATE_MISSING_T); UNSCOPED_INFO("INVALID_DATE_MISSING_T should have failed"); - CHECK(result == false); + CHECK_FALSE(result); result = date.fromString(INVALID_DATE_MISSING_HOUR); UNSCOPED_INFO("INVALID_DATE_MISSING_HOUR should have failed"); - CHECK(result == false); + CHECK_FALSE(result); result = date.fromString(INVALID_DATE_MISSING_MIN); UNSCOPED_INFO("INVALID_DATE_MISSING_MIN should have failed"); - CHECK(result == false); + CHECK_FALSE(result); result = date.fromString(INVALID_DATE_MISSING_SEC); UNSCOPED_INFO("INVALID_DATE_MISSING_SEC should have failed"); - CHECK(result == false); + CHECK_FALSE(result); result = date.fromString(INVALID_DATE_MISSING_Z); UNSCOPED_INFO("INVALID_DATE_MISSING_Z should have failed"); - CHECK(result == false); + CHECK_FALSE(result); result = date.fromString(INVALID_DATE_EMPTY); UNSCOPED_INFO("INVALID_DATE_EMPTY should have failed"); - CHECK(result == false); + CHECK_FALSE(result); } /* Invalid Value Handling */ @@ -147,37 +143,37 @@ namespace tut result = date.fromString(INVALID_DATE_24HOUR_BOUNDARY); UNSCOPED_INFO("INVALID_DATE_24HOUR_BOUNDARY should have failed"); - REQUIRE(result == false); - UNSCOPED_INFO("INVALID_DATE_24HOUR_BOUNDARY date still set to old value on failure!"); - REQUIRE(date.secondsSinceEpoch() == 0); + CHECK_FALSE(result); + UNSCOPED_INFO("a failed parse leaves the date unchanged"); + CHECK(date.secondsSinceEpoch() == 0); result = date.fromString(INVALID_DATE_LEAP); UNSCOPED_INFO("INVALID_DATE_LEAP should have failed"); - REQUIRE(result == false); + CHECK_FALSE(result); result = date.fromString(INVALID_DATE_HOUR); UNSCOPED_INFO("INVALID_DATE_HOUR should have failed"); - REQUIRE(result == false); + CHECK_FALSE(result); result = date.fromString(INVALID_DATE_MIN); UNSCOPED_INFO("INVALID_DATE_MIN should have failed"); - REQUIRE(result == false); + CHECK_FALSE(result); result = date.fromString(INVALID_DATE_SEC); UNSCOPED_INFO("INVALID_DATE_SEC should have failed"); - REQUIRE(result == false); + CHECK_FALSE(result); result = date.fromString(INVALID_DATE_YEAR); UNSCOPED_INFO("INVALID_DATE_YEAR should have failed"); - REQUIRE(result == false); + CHECK_FALSE(result); result = date.fromString(INVALID_DATE_MONTH); UNSCOPED_INFO("INVALID_DATE_MONTH should have failed"); - REQUIRE(result == false); + CHECK_FALSE(result); result = date.fromString(INVALID_DATE_DAY); UNSCOPED_INFO("INVALID_DATE_DAY should have failed"); - REQUIRE(result == false); + CHECK_FALSE(result); #endif } @@ -188,7 +184,6 @@ namespace tut std::istringstream stream(VALID_DATE); std::string expected_string = VALID_DATE; date.fromStream(stream); - UNSCOPED_INFO("fromStream failed"); CHECK(date.asString() == expected_string); } @@ -196,7 +191,6 @@ namespace tut { LLDate date1(VALID_DATE); LLDate date2(date1); - UNSCOPED_INFO("LLDate(const LLDate& date) constructor failed"); CHECK(date1.asString() == date2.asString()); } @@ -204,9 +198,7 @@ namespace tut { LLDate date1(VALID_DATE); LLDate date2(date1.secondsSinceEpoch()); - UNSCOPED_INFO("secondsSinceEpoch not equal"); CHECK(date1.secondsSinceEpoch() == date2.secondsSinceEpoch()); - UNSCOPED_INFO("LLDate created using secondsSinceEpoch not equal"); CHECK(date1.asString() == date2.asString()); } @@ -216,7 +208,6 @@ namespace tut std::ostringstream stream; stream << date; std::string expected_str = VALID_DATE; - UNSCOPED_INFO("ostringstream failed"); CHECK(expected_str == stream.str()); } @@ -229,9 +220,7 @@ namespace tut std::ostringstream out_stream; out_stream << date; - UNSCOPED_INFO("<< failed"); CHECK(date.asString() == expected_str); - UNSCOPED_INFO("<< to >> failed"); CHECK(stream.str() == out_stream.str()); } @@ -242,25 +231,15 @@ namespace tut LLDate later(2000.0); LLDate also_earlier(1000.0); - UNSCOPED_INFO("earlier < later"); CHECK(earlier < later); - UNSCOPED_INFO("later > earlier"); CHECK(later > earlier); - UNSCOPED_INFO("earlier <= later"); CHECK(earlier <= later); - UNSCOPED_INFO("later >= earlier"); CHECK(later >= earlier); - UNSCOPED_INFO("equal <="); CHECK(earlier <= also_earlier); - UNSCOPED_INFO("equal >="); CHECK(earlier >= also_earlier); - UNSCOPED_INFO("equal =="); CHECK(earlier == also_earlier); - UNSCOPED_INFO("differ !="); CHECK(earlier != later); - UNSCOPED_INFO("!(equal <)"); CHECK_FALSE(earlier < also_earlier); - UNSCOPED_INFO("!(equal >)"); CHECK_FALSE(earlier > also_earlier); } @@ -268,21 +247,15 @@ namespace tut TEST_CASE("LLDate: isNull and notNull follow the seconds set", "[LLDate]") { LLDate d; - UNSCOPED_INFO("default isNull"); CHECK(d.isNull()); - UNSCOPED_INFO("default !notNull"); CHECK_FALSE(d.notNull()); d.secondsSinceEpoch(1234.5); - UNSCOPED_INFO("after set, !isNull"); CHECK_FALSE(d.isNull()); - UNSCOPED_INFO("after set, notNull"); CHECK(d.notNull()); - UNSCOPED_INFO("getter round-trips"); CHECK(d.secondsSinceEpoch() == 1234.5); d.secondsSinceEpoch(0.0); - UNSCOPED_INFO("zero -> isNull again"); CHECK(d.isNull()); } @@ -291,14 +264,11 @@ namespace tut TEST_CASE("LLDate: the date string formatters give output for the epoch value", "[LLDate]") { LLDate d; - UNSCOPED_INFO("toLocalDateString does not throw"); - REQUIRE(d.toLocalDateString(std::string("%Y-%m-%d")).size() > 0); - UNSCOPED_INFO("toHTTPDateString does not throw"); - REQUIRE(d.toHTTPDateString(std::string("%Y-%m-%d")).size() > 0); + CHECK(d.toLocalDateString(std::string("%Y-%m-%d")).size() > 0); + CHECK(d.toHTTPDateString(std::string("%Y-%m-%d")).size() > 0); // RFC1123 wraps toHTTPDateString; verifies the day-of-week token // is present (a strftime success indicator that doesn't pin locale). std::string rfc = d.asRFC1123(); - UNSCOPED_INFO("asRFC1123 produces output"); - REQUIRE(rfc.size() > 0); + CHECK(rfc.size() > 0); } } diff --git a/indra/llcommon/tests/lldeadmantimer_test.cpp b/indra/llcommon/tests/lldeadmantimer_test.cpp index 36ce6df34f..06cb890e99 100644 --- a/indra/llcommon/tests/lldeadmantimer_test.cpp +++ b/indra/llcommon/tests/lldeadmantimer_test.cpp @@ -68,13 +68,9 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: a new timer is not expired U64 count(U64L(8)); LLDeadmanTimer timer(10.0, false); - UNSCOPED_INFO("WOCM isExpired() returns false after ctor()"); - CHECK(timer.isExpired(0, started, stopped, count) == false); - UNSCOPED_INFO("WOCM t1 - isExpired() does not modify started"); + CHECK_FALSE(timer.isExpired(0, started, stopped, count)); CHECK_THAT(started, ll_test::WithinBits(F64(42.0), 2)); - UNSCOPED_INFO("WOCM t1 - isExpired() does not modify stopped"); CHECK_THAT(stopped, ll_test::WithinBits(F64(97.0), 2)); - UNSCOPED_INFO("WOCM t1 - isExpired() does not modify count"); CHECK(count == U64L(8)); } @@ -84,17 +80,11 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: a new timer is not expired U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); LLDeadmanTimer timer(10.0, true); - UNSCOPED_INFO("WCM isExpired() returns false after ctor()"); - CHECK(timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu) == false); - UNSCOPED_INFO("WCM t1 - isExpired() does not modify started"); + CHECK_FALSE(timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu)); CHECK_THAT(started, ll_test::WithinBits(F64(42.0), 2)); - UNSCOPED_INFO("WCM t1 - isExpired() does not modify stopped"); CHECK_THAT(stopped, ll_test::WithinBits(F64(97.0), 2)); - UNSCOPED_INFO("WCM t1 - isExpired() does not modify count"); CHECK(count == U64L(8)); - UNSCOPED_INFO("WCM t1 - isExpired() does not modify user_cpu"); CHECK(user_cpu == U64L(29000)); - UNSCOPED_INFO("WCM t1 - isExpired() does not modify sys_cpu"); CHECK(sys_cpu == U64L(57000)); } } @@ -109,8 +99,7 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: an unstarted zero-horizon t U64 count(U64L(8)); LLDeadmanTimer timer(0.0, false); // Zero is pre-expired - UNSCOPED_INFO("WOCM isExpired() still returns false with 0.0 time ctor()"); - CHECK(timer.isExpired(0, started, stopped, count) == false); + CHECK_FALSE(timer.isExpired(0, started, stopped, count)); } { @@ -119,8 +108,7 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: an unstarted zero-horizon t U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); LLDeadmanTimer timer(0.0, true); // Zero is pre-expired - UNSCOPED_INFO("WCM isExpired() still returns false with 0.0 time ctor()"); - CHECK(timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu) == false); + CHECK_FALSE(timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu)); } } @@ -136,9 +124,8 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: a started zero-horizon time LLDeadmanTimer timer(0.0, false); timer.start(0); - UNSCOPED_INFO("WOCM isExpired() returns true with 0.0 horizon time"); - CHECK(timer.isExpired(0, started, stopped, count) == true); - UNSCOPED_INFO("WOCM expired timer with no bell ringing has stopped == started"); + CHECK(timer.isExpired(0, started, stopped, count)); + UNSCOPED_INFO("with no bell rung, stopped stays equal to started"); CHECK_THAT(started, ll_test::WithinBits(stopped, 8)); } { @@ -148,9 +135,8 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: a started zero-horizon time LLDeadmanTimer timer(0.0, true); timer.start(0); - UNSCOPED_INFO("WCM isExpired() returns true with 0.0 horizon time"); - CHECK(timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu) == true); - UNSCOPED_INFO("WCM expired timer with no bell ringing has stopped == started"); + CHECK(timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu)); + UNSCOPED_INFO("with no bell rung, stopped stays equal to started"); CHECK_THAT(started, ll_test::WithinBits(stopped, 8)); } } @@ -167,9 +153,8 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: a pre-expired timer ignores timer.start(0); timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1); - UNSCOPED_INFO("WOCM isExpired() returns true with 0.0 horizon time after bell ring"); - CHECK(timer.isExpired(0, started, stopped, count) == true); - UNSCOPED_INFO("WOCM ringBell has no impact on expired timer leaving stopped == started"); + CHECK(timer.isExpired(0, started, stopped, count)); + UNSCOPED_INFO("the bell leaves stopped equal to started"); CHECK_THAT(started, ll_test::WithinBits(stopped, 8)); } { @@ -180,9 +165,8 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: a pre-expired timer ignores timer.start(0); timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1); - UNSCOPED_INFO("WCM isExpired() returns true with 0.0 horizon time after bell ring"); - CHECK(timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu) == true); - UNSCOPED_INFO("WCM ringBell has no impact on expired timer leaving stopped == started"); + CHECK(timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu)); + UNSCOPED_INFO("the bell leaves stopped equal to started"); CHECK_THAT(started, ll_test::WithinBits(stopped, 8)); } } @@ -198,13 +182,9 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: start(0) leaves a timer une LLDeadmanTimer timer(10.0, false); timer.start(0); - UNSCOPED_INFO("WOCM isExpired() returns false after starting with 10.0 horizon time"); - CHECK(timer.isExpired(0, started, stopped, count) == false); - UNSCOPED_INFO("WOCM t5 - isExpired() does not modify started"); + CHECK_FALSE(timer.isExpired(0, started, stopped, count)); CHECK_THAT(started, ll_test::WithinBits(F64(42.0), 2)); - UNSCOPED_INFO("WOCM t5 - isExpired() does not modify stopped"); CHECK_THAT(stopped, ll_test::WithinBits(F64(97.0), 2)); - UNSCOPED_INFO("WOCM t5 - isExpired() does not modify count"); CHECK(count == U64L(8)); } { @@ -214,17 +194,11 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: start(0) leaves a timer une LLDeadmanTimer timer(10.0, true); timer.start(0); - UNSCOPED_INFO("WCM isExpired() returns false after starting with 10.0 horizon time"); - CHECK(timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu) == false); - UNSCOPED_INFO("WCM t5 - isExpired() does not modify started"); + CHECK_FALSE(timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu)); CHECK_THAT(started, ll_test::WithinBits(F64(42.0), 2)); - UNSCOPED_INFO("WCM t5 - isExpired() does not modify stopped"); CHECK_THAT(stopped, ll_test::WithinBits(F64(97.0), 2)); - UNSCOPED_INFO("WCM t5 - isExpired() does not modify count"); CHECK(count == U64L(8)); - UNSCOPED_INFO("WCM t5 - isExpired() does not modify user_cpu"); CHECK(user_cpu == U64L(29000)); - UNSCOPED_INFO("WCM t5 - isExpired() does not modify sys_cpu"); CHECK(sys_cpu == U64L(57000)); } } @@ -246,13 +220,9 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: a timer started less than a LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0)); timer.start(the_past); - UNSCOPED_INFO("WOCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past"); - CHECK(timer.isExpired(now, started, stopped, count) == false); - UNSCOPED_INFO("WOCM t6 - isExpired() does not modify started"); + CHECK_FALSE(timer.isExpired(now, started, stopped, count)); CHECK_THAT(started, ll_test::WithinBits(F64(42.0), 2)); - UNSCOPED_INFO("WOCM t6 - isExpired() does not modify stopped"); CHECK_THAT(stopped, ll_test::WithinBits(F64(97.0), 2)); - UNSCOPED_INFO("WOCM t6 - isExpired() does not modify count"); CHECK(count == U64L(8)); } { @@ -268,17 +238,11 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: a timer started less than a LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0)); timer.start(the_past); - UNSCOPED_INFO("WCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past"); - CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == false); - UNSCOPED_INFO("WCM t6 - isExpired() does not modify started"); + CHECK_FALSE(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu)); CHECK_THAT(started, ll_test::WithinBits(F64(42.0), 2)); - UNSCOPED_INFO("WCM t6 - isExpired() does not modify stopped"); CHECK_THAT(stopped, ll_test::WithinBits(F64(97.0), 2)); - UNSCOPED_INFO("t6 - isExpired() does not modify count"); CHECK(count == U64L(8)); - UNSCOPED_INFO("WCM t6 - isExpired() does not modify user_cpu"); CHECK(user_cpu == U64L(29000)); - UNSCOPED_INFO("WCM t6 - isExpired() does not modify sys_cpu"); CHECK(sys_cpu == U64L(57000)); } } @@ -300,9 +264,8 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: a timer started more than a LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0)); timer.start(the_past); - UNSCOPED_INFO("WOCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past"); - CHECK(timer.isExpired(now,started, stopped, count) == true); - UNSCOPED_INFO("WOCM t7 - starting before horizon still gives equal started / stopped"); + CHECK(timer.isExpired(now,started, stopped, count)); + UNSCOPED_INFO("starting before the horizon still gives equal started and stopped"); CHECK_THAT(started, ll_test::WithinBits(stopped, 8)); } { @@ -318,9 +281,8 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: a timer started more than a LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0)); timer.start(the_past); - UNSCOPED_INFO("WCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past"); - CHECK(timer.isExpired(now,started, stopped, count, user_cpu, sys_cpu) == true); - UNSCOPED_INFO("WOCM t7 - starting before horizon still gives equal started / stopped"); + CHECK(timer.isExpired(now,started, stopped, count, user_cpu, sys_cpu)); + UNSCOPED_INFO("starting before the horizon still gives equal started and stopped"); CHECK_THAT(started, ll_test::WithinBits(stopped, 8)); } } @@ -342,19 +304,14 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: isExpired reports an expiry LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0)); timer.start(the_past); - UNSCOPED_INFO("WOCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past"); - CHECK(timer.isExpired(now, started, stopped, count) == true); + CHECK(timer.isExpired(now, started, stopped, count)); started = 42.0; stopped = 97.0; count = U64L(8); - UNSCOPED_INFO("WOCM t8 - second isExpired() returns false after true"); - CHECK(timer.isExpired(now, started, stopped, count) == false); - UNSCOPED_INFO("WOCM t8 - 2nd isExpired() does not modify started"); + CHECK_FALSE(timer.isExpired(now, started, stopped, count)); CHECK_THAT(started, ll_test::WithinBits(F64(42.0), 2)); - UNSCOPED_INFO("WOCM t8 - 2nd isExpired() does not modify stopped"); CHECK_THAT(stopped, ll_test::WithinBits(F64(97.0), 2)); - UNSCOPED_INFO("WOCM t8 - 2nd isExpired() does not modify count"); CHECK(count == U64L(8)); } { @@ -370,25 +327,18 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: isExpired reports an expiry LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0)); timer.start(the_past); - UNSCOPED_INFO("WCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past"); - CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == true); + CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu)); started = 42.0; stopped = 97.0; count = U64L(8); user_cpu = 29000; sys_cpu = 57000; - UNSCOPED_INFO("WCM t8 - second isExpired() returns false after true"); - CHECK(timer.isExpired(now, started, stopped, count) == false); - UNSCOPED_INFO("WCM t8 - 2nd isExpired() does not modify started"); + CHECK_FALSE(timer.isExpired(now, started, stopped, count)); CHECK_THAT(started, ll_test::WithinBits(F64(42.0), 2)); - UNSCOPED_INFO("WCM t8 - 2nd isExpired() does not modify stopped"); CHECK_THAT(stopped, ll_test::WithinBits(F64(97.0), 2)); - UNSCOPED_INFO("WCM t8 - 2nd isExpired() does not modify count"); CHECK(count == U64L(8)); - UNSCOPED_INFO("WCM t8 - 2nd isExpired() does not modify user_cpu"); CHECK(user_cpu == U64L(29000)); - UNSCOPED_INFO("WCM t8 - 2nd isExpired() does not modify sys_cpu"); CHECK(sys_cpu == U64L(57000)); } } @@ -427,22 +377,17 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: ringing the bell keeps a ti timer.ringBell(now, 1); now += float_time_to_u64(1.0); timer.ringBell(now, 1); - UNSCOPED_INFO("WOCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings"); - CHECK(timer.isExpired(now, started, stopped, count) == false); + CHECK_FALSE(timer.isExpired(now, started, stopped, count)); F64 last_good_ring(u64_time_to_float(now)); // Jump forward and expire now += float_time_to_u64(10.0); - UNSCOPED_INFO("WOCM t9 - 5.0 horizon timer expires on 10-second jump"); - CHECK(timer.isExpired(now, started, stopped, count) == true); - UNSCOPED_INFO("WOCM t9 - started matches start() time"); + CHECK(timer.isExpired(now, started, stopped, count)); CHECK_THAT(started, ll_test::WithinBits(real_start, 4)); - UNSCOPED_INFO("WOCM t9 - stopped matches last ringBell() time"); CHECK_THAT(stopped, ll_test::WithinBits(last_good_ring, 4)); - UNSCOPED_INFO("WOCM t9 - 10 good ringBell()s"); CHECK(count == U64L(10)); - UNSCOPED_INFO("WOCM t9 - single read only"); - CHECK(timer.isExpired(now, started, stopped, count) == false); + UNSCOPED_INFO("an expiry is reported once"); + CHECK_FALSE(timer.isExpired(now, started, stopped, count)); } { // With cpu metrics @@ -474,22 +419,17 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: ringing the bell keeps a ti timer.ringBell(now, 1); now += float_time_to_u64(1.0); timer.ringBell(now, 1); - UNSCOPED_INFO("WCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings"); - CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == false); + CHECK_FALSE(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu)); F64 last_good_ring(u64_time_to_float(now)); // Jump forward and expire now += float_time_to_u64(10.0); - UNSCOPED_INFO("WCM t9 - 5.0 horizon timer expires on 10-second jump"); - CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == true); - UNSCOPED_INFO("WCM t9 - started matches start() time"); + CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu)); CHECK_THAT(started, ll_test::WithinBits(real_start, 4)); - UNSCOPED_INFO("WCM t9 - stopped matches last ringBell() time"); CHECK_THAT(stopped, ll_test::WithinBits(last_good_ring, 4)); - UNSCOPED_INFO("WCM t9 - 10 good ringBell()s"); CHECK(count == U64L(10)); - UNSCOPED_INFO("WCM t9 - single read only"); - CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == false); + UNSCOPED_INFO("an expiry is reported once"); + CHECK_FALSE(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu)); } } @@ -527,22 +467,17 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: a timer restarted after exp timer.ringBell(now, 1); now += float_time_to_u64(1.0); timer.ringBell(now, 1); - UNSCOPED_INFO("WOCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings"); - CHECK(timer.isExpired(now, started, stopped, count) == false); + CHECK_FALSE(timer.isExpired(now, started, stopped, count)); F64 last_good_ring(u64_time_to_float(now)); // Jump forward and expire now += float_time_to_u64(10.0); - UNSCOPED_INFO("WOCM t10 - 5.0 horizon timer expires on 10-second jump"); - CHECK(timer.isExpired(now, started, stopped, count) == true); - UNSCOPED_INFO("WOCM t10 - started matches start() time"); + CHECK(timer.isExpired(now, started, stopped, count)); CHECK_THAT(started, ll_test::WithinBits(real_start, 4)); - UNSCOPED_INFO("WOCM t10 - stopped matches last ringBell() time"); CHECK_THAT(stopped, ll_test::WithinBits(last_good_ring, 4)); - UNSCOPED_INFO("WOCM t10 - 10 good ringBell()s"); CHECK(count == U64L(10)); - UNSCOPED_INFO("WOCM t10 - single read only"); - CHECK(timer.isExpired(now, started, stopped, count) == false); + UNSCOPED_INFO("an expiry is reported once"); + CHECK_FALSE(timer.isExpired(now, started, stopped, count)); // Jump forward and restart now += float_time_to_u64(1.0); @@ -566,22 +501,17 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: a timer restarted after exp timer.ringBell(now, 1); now += float_time_to_u64(1.0); timer.ringBell(now, 1); - UNSCOPED_INFO("WOCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings"); - CHECK(timer.isExpired(now, started, stopped, count) == false); + CHECK_FALSE(timer.isExpired(now, started, stopped, count)); last_good_ring = u64_time_to_float(now); // Jump forward and expire now += float_time_to_u64(10.0); - UNSCOPED_INFO("WOCM t10 - 5.0 horizon timer expires on 8-second jump"); - CHECK(timer.isExpired(now, started, stopped, count) == true); - UNSCOPED_INFO("WOCM t10 - 2nd started matches start() time"); + CHECK(timer.isExpired(now, started, stopped, count)); CHECK_THAT(started, ll_test::WithinBits(real_start, 4)); - UNSCOPED_INFO("WOCM t10 - 2nd stopped matches last ringBell() time"); CHECK_THAT(stopped, ll_test::WithinBits(last_good_ring, 4)); - UNSCOPED_INFO("WOCM t10 - 8 good ringBell()s"); CHECK(count == U64L(8)); - UNSCOPED_INFO("WOCM t10 - single read only - 2nd start"); - CHECK(timer.isExpired(now, started, stopped, count) == false); + UNSCOPED_INFO("an expiry is reported once after the restart"); + CHECK_FALSE(timer.isExpired(now, started, stopped, count)); } { // With cpu metrics @@ -614,22 +544,17 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: a timer restarted after exp timer.ringBell(now, 1); now += float_time_to_u64(1.0); timer.ringBell(now, 1); - UNSCOPED_INFO("WCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings"); - CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == false); + CHECK_FALSE(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu)); F64 last_good_ring(u64_time_to_float(now)); // Jump forward and expire now += float_time_to_u64(10.0); - UNSCOPED_INFO("WCM t10 - 5.0 horizon timer expires on 10-second jump"); - CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == true); - UNSCOPED_INFO("WCM t10 - started matches start() time"); + CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu)); CHECK_THAT(started, ll_test::WithinBits(real_start, 4)); - UNSCOPED_INFO("WCM t10 - stopped matches last ringBell() time"); CHECK_THAT(stopped, ll_test::WithinBits(last_good_ring, 4)); - UNSCOPED_INFO("WCM t10 - 10 good ringBell()s"); CHECK(count == U64L(10)); - UNSCOPED_INFO("WCM t10 - single read only"); - CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == false); + UNSCOPED_INFO("an expiry is reported once"); + CHECK_FALSE(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu)); // Jump forward and restart now += float_time_to_u64(1.0); @@ -653,22 +578,17 @@ TEST_CASE_METHOD(deadmantimer_test, "LLDeadmanTimer: a timer restarted after exp timer.ringBell(now, 1); now += float_time_to_u64(1.0); timer.ringBell(now, 1); - UNSCOPED_INFO("WCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings"); - CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == false); + CHECK_FALSE(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu)); last_good_ring = u64_time_to_float(now); // Jump forward and expire now += float_time_to_u64(10.0); - UNSCOPED_INFO("WCM t10 - 5.0 horizon timer expires on 8-second jump"); - CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == true); - UNSCOPED_INFO("WCM t10 - 2nd started matches start() time"); + CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu)); CHECK_THAT(started, ll_test::WithinBits(real_start, 4)); - UNSCOPED_INFO("WCM t10 - 2nd stopped matches last ringBell() time"); CHECK_THAT(stopped, ll_test::WithinBits(last_good_ring, 4)); - UNSCOPED_INFO("WCM t10 - 8 good ringBell()s"); CHECK(count == U64L(8)); - UNSCOPED_INFO("WCM t10 - single read only - 2nd start"); - CHECK(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu) == false); + UNSCOPED_INFO("an expiry is reported once after the restart"); + CHECK_FALSE(timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu)); } } diff --git a/indra/llcommon/tests/lldependencies_test.cpp b/indra/llcommon/tests/lldependencies_test.cpp index f1b51dcfee..4bf0149a44 100644 --- a/indra/llcommon/tests/lldependencies_test.cpp +++ b/indra/llcommon/tests/lldependencies_test.cpp @@ -166,10 +166,8 @@ namespace deps.add("lazy"); CHECK(sorted_keys(deps) == StringList{"lazy"}); deps.add("jumps"); - UNSCOPED_INFO("found lazy"); - REQUIRE(deps.get("lazy")); - UNSCOPED_INFO("not found dog."); - REQUIRE_FALSE(deps.get("dog.")); + CHECK(deps.get("lazy")); + CHECK_FALSE(deps.get("dog.")); // NOTE: Maybe it's overkill to test each of these intermediate // results before all the interdependencies have been specified. My // thought is simply that if the order changes, I'd like to know why. @@ -181,7 +179,6 @@ namespace // Test key accessors UNSCOPED_INFO("empty before deps for missing key"); CHECK(is_empty(deps.get_before_range("bogus"))); - UNSCOPED_INFO("empty before deps for jumps"); CHECK(is_empty(deps.get_before_range("jumps"))); CHECK(instance_from_range< std::set >(deps.get_before_range("The")) == std::set{ "dog.", "fox" }); // resume building dependencies @@ -271,7 +268,7 @@ namespace // test all iterator-flavored versions of get_after_range() StringList def{"def"}; - UNSCOPED_INFO("empty abc before list"); + UNSCOPED_INFO("abc has nothing before it"); CHECK(is_empty(nideps.get_before_range(nideps.get_range().begin()))); CHECK(instance_from_range(nideps.get_after_range(nideps.get_range().begin())) == def); diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp index 76f54fcdd1..bf7064d68a 100644 --- a/indra/llcommon/tests/llerror_test.cpp +++ b/indra/llcommon/tests/llerror_test.cpp @@ -127,10 +127,6 @@ namespace tut std::string message(int n) { - std::ostringstream test_name; - test_name << "testing message " << n << ", not enough messages"; - - UNSCOPED_INFO(test_name.str()); REQUIRE(n < countMessages()); return mMessages[n]; } @@ -188,17 +184,13 @@ namespace tut return std::dynamic_pointer_cast(mRecorder)->message(n); } - void ensure_message_count(int expectedCount) + void require_message_count(int expectedCount) { - UNSCOPED_INFO("message count"); REQUIRE(countMessages() == expectedCount); } std::string message_field(int msgnum, LogFieldIndex fieldnum) { - std::ostringstream test_name; - test_name << "testing message " << msgnum << ", not enough messages"; - UNSCOPED_INFO(test_name.str()); REQUIRE(msgnum < countMessages()); std::string msg(message(msgnum)); @@ -246,21 +238,15 @@ namespace tut return msg.substr(start_field, fieldlen); } - void ensure_message_field_equals(int msgnum, LogFieldIndex fieldnum, const std::string& expectedText) + void require_message_field(int msgnum, LogFieldIndex fieldnum, const std::string& expectedText) { - std::ostringstream test_name; - test_name << "testing message " << msgnum << " field " << FieldName[fieldnum] << "\n message: \"" << message(msgnum) << "\"\n "; - - UNSCOPED_INFO(test_name.str()); + INFO("message " << msgnum << " field " << FieldName[fieldnum] << ": \"" << message(msgnum) << "\""); REQUIRE(message_field(msgnum, fieldnum) == expectedText); } - void ensure_message_does_not_contain(int n, const std::string& expectedText) + void require_message_lacks(int n, const std::string& expectedText) { - std::ostringstream test_name; - test_name << "testing message " << n; - - UNSCOPED_INFO(test_name.str()); + CAPTURE(n); REQUIRE_THAT(message(n), !Catch::Matchers::ContainsSubstring(expectedText)); } }; @@ -271,8 +257,8 @@ namespace tut LL_INFOS() << "test" << LL_ENDL; LL_INFOS() << "bob" << LL_ENDL; - ensure_message_field_equals(0, MSG_FIELD, "test"); - ensure_message_field_equals(1, MSG_FIELD, "bob"); + require_message_field(0, MSG_FIELD, "test"); + require_message_field(1, MSG_FIELD, "bob"); } } @@ -297,41 +283,41 @@ namespace tut #endif LLError::setDefaultLevel(LLError::LEVEL_DEBUG); writeSome(); - ensure_message_field_equals(0, MSG_FIELD, "one"); - ensure_message_field_equals(0, LEVEL_FIELD, "DEBUG"); - ensure_message_field_equals(0, TAGS_FIELD, "#WriteTag#AnotherTag#"); - ensure_message_field_equals(1, MSG_FIELD, "two"); - ensure_message_field_equals(1, LEVEL_FIELD, "INFO"); - ensure_message_field_equals(1, TAGS_FIELD, "#WriteTag#"); - ensure_message_field_equals(2, MSG_FIELD, "three"); - ensure_message_field_equals(2, LEVEL_FIELD, "WARNING"); - ensure_message_field_equals(2, TAGS_FIELD, "#WriteTag#"); - ensure_message_field_equals(3, MSG_FIELD, "four"); - ensure_message_field_equals(3, LEVEL_FIELD, "ERROR"); - ensure_message_field_equals(3, TAGS_FIELD, "#WriteTag#"); - ensure_message_count(4); + require_message_field(0, MSG_FIELD, "one"); + require_message_field(0, LEVEL_FIELD, "DEBUG"); + require_message_field(0, TAGS_FIELD, "#WriteTag#AnotherTag#"); + require_message_field(1, MSG_FIELD, "two"); + require_message_field(1, LEVEL_FIELD, "INFO"); + require_message_field(1, TAGS_FIELD, "#WriteTag#"); + require_message_field(2, MSG_FIELD, "three"); + require_message_field(2, LEVEL_FIELD, "WARNING"); + require_message_field(2, TAGS_FIELD, "#WriteTag#"); + require_message_field(3, MSG_FIELD, "four"); + require_message_field(3, LEVEL_FIELD, "ERROR"); + require_message_field(3, TAGS_FIELD, "#WriteTag#"); + require_message_count(4); LLError::setDefaultLevel(LLError::LEVEL_INFO); writeSome(); - ensure_message_field_equals(4, MSG_FIELD, "two"); - ensure_message_field_equals(5, MSG_FIELD, "three"); - ensure_message_field_equals(6, MSG_FIELD, "four"); - ensure_message_count(7); + require_message_field(4, MSG_FIELD, "two"); + require_message_field(5, MSG_FIELD, "three"); + require_message_field(6, MSG_FIELD, "four"); + require_message_count(7); LLError::setDefaultLevel(LLError::LEVEL_WARN); writeSome(); - ensure_message_field_equals(7, MSG_FIELD, "three"); - ensure_message_field_equals(8, MSG_FIELD, "four"); - ensure_message_count(9); + require_message_field(7, MSG_FIELD, "three"); + require_message_field(8, MSG_FIELD, "four"); + require_message_count(9); LLError::setDefaultLevel(LLError::LEVEL_ERROR); writeSome(); - ensure_message_field_equals(9, MSG_FIELD, "four"); - ensure_message_count(10); + require_message_field(9, MSG_FIELD, "four"); + require_message_count(10); LLError::setDefaultLevel(LLError::LEVEL_NONE); writeSome(); - ensure_message_count(10); + require_message_count(10); } TEST_CASE_METHOD(ErrorTestData, "error: the output names the level of each message", "[error]") @@ -341,11 +327,11 @@ namespace tut SKIP("Debug messages disabled"); #endif writeSome(); - ensure_message_field_equals(0, LEVEL_FIELD, "DEBUG"); - ensure_message_field_equals(1, LEVEL_FIELD, "INFO"); - ensure_message_field_equals(2, LEVEL_FIELD, "WARNING"); - ensure_message_field_equals(3, LEVEL_FIELD, "ERROR"); - ensure_message_count(4); + require_message_field(0, LEVEL_FIELD, "DEBUG"); + require_message_field(1, LEVEL_FIELD, "INFO"); + require_message_field(2, LEVEL_FIELD, "WARNING"); + require_message_field(3, LEVEL_FIELD, "ERROR"); + require_message_count(4); } TEST_CASE_METHOD(ErrorTestData, "error: abbreviateFile strips the source root and leaves other paths", "[error]") @@ -367,10 +353,8 @@ namespace tut // argument unchanged, THEN check. } while (abbreviateFile != prev); - UNSCOPED_INFO("file name abbreviation"); CHECK_THAT(abbreviateFile, Catch::Matchers::EndsWith("llcommon/tests/llerror_test.cpp")); - UNSCOPED_INFO("file name abbreviation"); CHECK_THAT(abbreviateFile, !Catch::Matchers::ContainsSubstring("indra")); std::string someFile = @@ -382,7 +366,6 @@ namespace tut ; std::string someAbbreviation = LLError::abbreviateFile(someFile); - UNSCOPED_INFO("non-indra file abbreviation"); CHECK(someAbbreviation == someFile); } } @@ -492,21 +475,19 @@ namespace std::string logFromClassWithLogTypeMember(bool id) { ClassWithLogType c; return c.logFromMember(id); } std::string logFromClassWithLogTypeStatic(bool id) { return ClassWithLogType::logFromStatic(id); } - void ensure_has(const std::string& message, + void require_has(const std::string& message, const std::string& actual, const std::string& expected) { std::string::size_type n1 = actual.find(expected); if (n1 == std::string::npos) { - std::stringstream ss; - ss << message << ": " << "expected to find a copy of '" << expected - << "' in actual '" << actual << "'"; - FAIL(ss.str().c_str()); + FAIL(message << ": " << "expected to find a copy of '" << expected + << "' in actual '" << actual << "'"); } } typedef std::string (*LogFromFunction)(bool); - void testLogName(LLError::RecorderPtr recorder, LogFromFunction f, + void require_log_name(LLError::RecorderPtr recorder, LogFromFunction f, const std::string& class_name = "") { std::dynamic_pointer_cast(recorder)->clearMessages(); @@ -516,16 +497,16 @@ namespace std::string messageWithoutName = std::dynamic_pointer_cast(recorder)->message(0); std::string messageWithName = std::dynamic_pointer_cast(recorder)->message(1); - ensure_has(name + " logged without name", + require_has(name + " logged without name", messageWithoutName, name); - ensure_has(name + " logged with name", + require_has(name + " logged with name", messageWithName, name); if (!class_name.empty()) { - ensure_has(name + "logged without name", + require_has(name + " logged without name", messageWithoutName, class_name); - ensure_has(name + "logged with name", + require_has(name + " logged with name", messageWithName, class_name); } } @@ -556,13 +537,13 @@ namespace tut LLError::setDefaultLevel(LLError::LEVEL_DEBUG); setWantsMultiline(true); writeMsgNeedsEscaping(); // but should not be now - ensure_message_field_equals(0, MSG_FIELD, "backslash\\"); - ensure_message_field_equals(1, MSG_FIELD, "newline\nafternewline"); - ensure_message_field_equals(2, MSG_FIELD, "return\rafterreturn"); - ensure_message_field_equals(3, MSG_FIELD, "backslash\\backslash\\"); - ensure_message_field_equals(4, MSG_FIELD, "backslash\\newline\nanothernewline\nafternewline"); - ensure_message_field_equals(5, MSG_FIELD, "backslash\\returnnewline\r\n\\afterbackslash"); - ensure_message_count(6); + require_message_field(0, MSG_FIELD, "backslash\\"); + require_message_field(1, MSG_FIELD, "newline\nafternewline"); + require_message_field(2, MSG_FIELD, "return\rafterreturn"); + require_message_field(3, MSG_FIELD, "backslash\\backslash\\"); + require_message_field(4, MSG_FIELD, "backslash\\newline\nanothernewline\nafternewline"); + require_message_field(5, MSG_FIELD, "backslash\\returnnewline\r\n\\afterbackslash"); + require_message_count(6); } } @@ -571,12 +552,12 @@ namespace tut // class/function information in output TEST_CASE_METHOD(ErrorTestData, "error: messages name the function and class they came from", "[error]") { - testLogName(mRecorder, logFromGlobal); - testLogName(mRecorder, logFromStatic); - testLogName(mRecorder, logFromAnon); - testLogName(mRecorder, logFromNamespace); - testLogName(mRecorder, logFromClassWithLogTypeMember, "ClassWithLogType"); - testLogName(mRecorder, logFromClassWithLogTypeStatic, "ClassWithLogType"); + require_log_name(mRecorder, logFromGlobal); + require_log_name(mRecorder, logFromStatic); + require_log_name(mRecorder, logFromAnon); + require_log_name(mRecorder, logFromNamespace); + require_log_name(mRecorder, logFromClassWithLogTypeMember, "ClassWithLogType"); + require_log_name(mRecorder, logFromClassWithLogTypeStatic, "ClassWithLogType"); } } @@ -621,14 +602,14 @@ namespace tut TEST_CASE_METHOD(ErrorTestData, "error: logging while building a message logs both", "[error]") { outerLogger(); - ensure_message_field_equals(0, MSG_FIELD, "inside"); - ensure_message_field_equals(1, MSG_FIELD, "outside(moo)"); - ensure_message_count(2); + require_message_field(0, MSG_FIELD, "inside"); + require_message_field(1, MSG_FIELD, "outside(moo)"); + require_message_count(2); metaLogger(); - ensure_message_field_equals(2, MSG_FIELD, "logging"); - ensure_message_field_equals(3, MSG_FIELD, "meta(baz)"); - ensure_message_count(4); + require_message_field(2, MSG_FIELD, "logging"); + require_message_field(3, MSG_FIELD, "meta(baz)"); + require_message_count(4); } // special handling of LL_ERRS() calls @@ -636,11 +617,10 @@ namespace tut { std::string location = errorReturningLocation(); - ensure_message_field_equals(0, LOCATION_FIELD, location); - ensure_message_field_equals(0, MSG_FIELD, "die"); - ensure_message_count(1); + require_message_field(0, LOCATION_FIELD, location); + require_message_field(0, MSG_FIELD, "die"); + require_message_count(1); - UNSCOPED_INFO("fatal callback called"); CHECK(fatalWasCalled); } } @@ -667,13 +647,13 @@ namespace tut setWantsTime(false); ufoSighting(); - ensure_message_field_equals(0, MSG_FIELD, "ufo"); - ensure_message_does_not_contain(0, roswell()); + require_message_field(0, MSG_FIELD, "ufo"); + require_message_lacks(0, roswell()); setWantsTime(true); ufoSighting(); - ensure_message_field_equals(1, MSG_FIELD, "ufo"); - ensure_message_field_equals(1, TIME_FIELD, roswell()); + require_message_field(1, MSG_FIELD, "ufo"); + require_message_field(1, TIME_FIELD, roswell()); } // output order @@ -686,7 +666,6 @@ namespace tut function; writeReturningLocationAndFunction(location, function); - UNSCOPED_INFO("order is time level tags location function message"); CHECK(message(0) == roswell() + " INFO " + "# " /* no tag */ + location + " " + function + " : " + "apple"); } @@ -699,10 +678,8 @@ namespace tut LL_INFOS() << "boo" << LL_ENDL; - ensure_message_field_equals(0, MSG_FIELD, "boo"); - UNSCOPED_INFO("alt recorder count"); + require_message_field(0, MSG_FIELD, "boo"); CHECK(std::dynamic_pointer_cast(altRecorder)->countMessages() == 1); - UNSCOPED_INFO("alt recorder message 0"); CHECK_THAT(std::dynamic_pointer_cast(altRecorder)->message(0), Catch::Matchers::ContainsSubstring("boo")); LLError::setTimeFunction(roswell); @@ -715,14 +692,10 @@ namespace tut std::string when = roswell(); - ensure_message_does_not_contain(1, when); - UNSCOPED_INFO("alt recorder count"); + require_message_lacks(1, when); CHECK(std::dynamic_pointer_cast(altRecorder)->countMessages() == 2); - UNSCOPED_INFO("alt recorder message 1"); CHECK_THAT(std::dynamic_pointer_cast(altRecorder)->message(1), !Catch::Matchers::ContainsSubstring(when)); - UNSCOPED_INFO("another recorder count"); CHECK(std::dynamic_pointer_cast(anotherRecorder)->countMessages() == 1); - UNSCOPED_INFO("another recorder message 0"); CHECK_THAT(std::dynamic_pointer_cast(anotherRecorder)->message(0), Catch::Matchers::ContainsSubstring(when)); LLError::removeRecorder(altRecorder); @@ -763,12 +736,12 @@ namespace tut TestAlpha::doAll(); TestBeta::doAll(); - ensure_message_field_equals(0, MSG_FIELD, "aim west"); - ensure_message_field_equals(1, MSG_FIELD, "ate eels"); - ensure_message_field_equals(2, MSG_FIELD, "buy iron"); - ensure_message_field_equals(3, MSG_FIELD, "bad word"); - ensure_message_field_equals(4, MSG_FIELD, "big easy"); - ensure_message_count(5); + require_message_field(0, MSG_FIELD, "aim west"); + require_message_field(1, MSG_FIELD, "ate eels"); + require_message_field(2, MSG_FIELD, "buy iron"); + require_message_field(3, MSG_FIELD, "bad word"); + require_message_field(4, MSG_FIELD, "big easy"); + require_message_count(5); } // filtering by function, and that it will override class filtering @@ -780,9 +753,9 @@ namespace tut LLError::setFunctionLevel("TestBeta::doError", LLError::LEVEL_NONE); TestBeta::doAll(); - ensure_message_field_equals(0, MSG_FIELD, "buy iron"); - ensure_message_field_equals(1, MSG_FIELD, "bad word"); - ensure_message_count(2); + require_message_field(0, MSG_FIELD, "buy iron"); + require_message_field(1, MSG_FIELD, "bad word"); + require_message_count(2); } // filtering by file @@ -800,10 +773,10 @@ namespace tut TestAlpha::doAll(); TestBeta::doAll(); - ensure_message_field_equals(0, MSG_FIELD, "any idea"); - ensure_message_field_equals(1, MSG_FIELD, "aim west"); - ensure_message_field_equals(2, MSG_FIELD, "bad word"); - ensure_message_count(3); + require_message_field(0, MSG_FIELD, "any idea"); + require_message_field(1, MSG_FIELD, "aim west"); + require_message_field(2, MSG_FIELD, "bad word"); + require_message_count(3); } // proper cached, efficient lookup of filtering @@ -812,32 +785,26 @@ namespace tut LLError::setDefaultLevel(LLError::LEVEL_NONE); TestAlpha::doInfo(); - ensure_message_count(0); - UNSCOPED_INFO("first check"); + require_message_count(0); CHECK(LLError::shouldLogCallCount() == 1); TestAlpha::doInfo(); - ensure_message_count(0); - UNSCOPED_INFO("second check"); + require_message_count(0); CHECK(LLError::shouldLogCallCount() == 1); LLError::setClassLevel("TestAlpha", LLError::LEVEL_DEBUG); TestAlpha::doInfo(); - ensure_message_count(1); - UNSCOPED_INFO("third check"); + require_message_count(1); CHECK(LLError::shouldLogCallCount() == 2); TestAlpha::doInfo(); - ensure_message_count(2); - UNSCOPED_INFO("fourth check"); + require_message_count(2); CHECK(LLError::shouldLogCallCount() == 2); LLError::setClassLevel("TestAlpha", LLError::LEVEL_WARN); TestAlpha::doInfo(); - ensure_message_count(2); - UNSCOPED_INFO("fifth check"); + require_message_count(2); CHECK(LLError::shouldLogCallCount() == 3); TestAlpha::doInfo(); - ensure_message_count(2); - UNSCOPED_INFO("sixth check"); + require_message_count(2); CHECK(LLError::shouldLogCallCount() == 3); } @@ -869,10 +836,10 @@ namespace tut TestAlpha::doAll(); TestBeta::doAll(); - ensure_message_field_equals(0, MSG_FIELD, "any idea"); - ensure_message_field_equals(1, MSG_FIELD, "aim west"); - ensure_message_field_equals(2, MSG_FIELD, "bad word"); - ensure_message_count(3); + require_message_field(0, MSG_FIELD, "any idea"); + require_message_field(1, MSG_FIELD, "aim west"); + require_message_field(2, MSG_FIELD, "bad word"); + require_message_count(3); // make sure reconfiguring works LLSD config2; @@ -882,11 +849,11 @@ namespace tut TestAlpha::doAll(); TestBeta::doAll(); - ensure_message_field_equals(3, MSG_FIELD, "aim west"); - ensure_message_field_equals(4, MSG_FIELD, "ate eels"); - ensure_message_field_equals(5, MSG_FIELD, "bad word"); - ensure_message_field_equals(6, MSG_FIELD, "big easy"); - ensure_message_count(7); + require_message_field(3, MSG_FIELD, "aim west"); + require_message_field(4, MSG_FIELD, "ate eels"); + require_message_field(5, MSG_FIELD, "bad word"); + require_message_field(6, MSG_FIELD, "big easy"); + require_message_count(7); } } @@ -900,13 +867,13 @@ namespace tut #endif LLError::setDefaultLevel(LLError::LEVEL_DEBUG); writeMsgNeedsEscaping(); - ensure_message_field_equals(0, MSG_FIELD, "backslash\\\\"); - ensure_message_field_equals(1, MSG_FIELD, "newline\\nafternewline"); - ensure_message_field_equals(2, MSG_FIELD, "return\\rafterreturn"); - ensure_message_field_equals(3, MSG_FIELD, "backslash\\\\backslash\\\\"); - ensure_message_field_equals(4, MSG_FIELD, "backslash\\\\newline\\nanothernewline\\nafternewline"); - ensure_message_field_equals(5, MSG_FIELD, "backslash\\\\returnnewline\\r\\n\\\\afterbackslash"); - ensure_message_count(6); + require_message_field(0, MSG_FIELD, "backslash\\\\"); + require_message_field(1, MSG_FIELD, "newline\\nafternewline"); + require_message_field(2, MSG_FIELD, "return\\rafterreturn"); + require_message_field(3, MSG_FIELD, "backslash\\\\backslash\\\\"); + require_message_field(4, MSG_FIELD, "backslash\\\\newline\\nanothernewline\\nafternewline"); + require_message_field(5, MSG_FIELD, "backslash\\\\returnnewline\\r\\n\\\\afterbackslash"); + require_message_count(6); } } @@ -932,9 +899,8 @@ namespace tut std::string location = writeTagWithSpaceReturningLocation(); std::string expected = "Space is not allowed in a log tag at " + location; - ensure_message_field_equals(0, LEVEL_FIELD, "ERROR"); - ensure_message_field_equals(0, MSG_FIELD, expected); - UNSCOPED_INFO("fatal callback called"); + require_message_field(0, LEVEL_FIELD, "ERROR"); + require_message_field(0, MSG_FIELD, expected); CHECK(fatalWasCalled); } } diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index 9de231f97f..f1c8a0009e 100644 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -170,7 +170,6 @@ namespace debug("about to suspend"); stringdata = future.get(); mSync.bump(); - UNSCOPED_INFO("Got it"); REQUIRE(stringdata == "received"); } END @@ -300,7 +299,7 @@ namespace LLSD first{LLSDMap("desc", "first")("value", 0)}; bool consumed = pump.post(first); UNSCOPED_INFO("should not have consumed first"); - REQUIRE_FALSE(consumed); + CHECK_FALSE(consumed); // now launch the coro LL_DEBUGS() << "test() launching listener coro" << LL_ENDL; running = true; @@ -329,7 +328,7 @@ namespace LLSD second{llsd::map("desc", "second", "value", 1)}; consumed = pump.post(second); UNSCOPED_INFO("should have consumed second"); - REQUIRE(consumed); + CHECK(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 @@ -339,7 +338,7 @@ namespace LLSD third{llsd::map("desc", "third", "value", 2)}; consumed = pump.post(third); UNSCOPED_INFO("should NOT yet have consumed third"); - REQUIRE_FALSE(consumed); + CHECK_FALSE(consumed); // now just wait for coro to finish -- which it eventually will, given // that all its suspend calls have short timeouts. while (running) @@ -348,9 +347,7 @@ namespace llcoro::suspendUntilTimeout(0.1f); } // okay, verify expected results - UNSCOPED_INFO("should have received three values"); - REQUIRE_THAT(data, - ll_test::LLSDEquals(llsd::array(first, second, third))); + CHECK_THAT(data, ll_test::LLSDEquals(llsd::array(first, second, third))); LL_DEBUGS() << "test() done" << LL_ENDL; } diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp index 6e8a7ab1bc..98c9974756 100644 --- a/indra/llcommon/tests/lleventdispatcher_test.cpp +++ b/indra/llcommon/tests/lleventdispatcher_test.cpp @@ -325,9 +325,9 @@ void freenb(NPARAMSb) *****************************************************************************/ namespace tut { - static void ensure_has(const std::string& outer, const std::string& inner) + static void require_contains(const std::string& outer, const std::string& inner) { - UNSCOPED_INFO(stringize("'", outer, "' does not contain '", inner, "'")); + CAPTURE(outer, inner); REQUIRE(outer.find(inner) != std::string::npos); } @@ -336,7 +336,7 @@ namespace tut { std::string what = catch_what(std::forward(func)); - ensure_has(what, exc_frag); + require_contains(what, exc_frag); return what; } @@ -636,12 +636,10 @@ namespace tut DescMap forgotten(descs.begin(), descs.end()); for (LLEventDispatcher::NameDesc nd: work) { + CAPTURE(nd.first); DescMap::iterator found = forgotten.find(nd.first); - UNSCOPED_INFO(stringize("LLEventDispatcher records function '", nd.first, - "' we didn't enter")); + UNSCOPED_INFO("LLEventDispatcher records a function we didn't enter"); REQUIRE(found != forgotten.end()); - UNSCOPED_INFO(stringize("LLEventDispatcher desc '", nd.second, - "' doesn't match what we entered: '", found->second, "'")); REQUIRE(nd.second == found->second); // found in our map the name from LLEventDispatcher, good, erase // our map entry @@ -663,10 +661,9 @@ namespace tut Vars* varsfor(const std::string& name) { + CAPTURE(name); VarsMap::const_iterator found = funcvars.find(name); - UNSCOPED_INFO(stringize("No Vars* for ", name)); REQUIRE(found != funcvars.end()); - UNSCOPED_INFO(stringize("NULL Vars* for ", name)); REQUIRE(found->second); return found->second; } @@ -695,8 +692,8 @@ namespace tut LLSD getMetadata(const std::string& name) { + CAPTURE(name); LLSD meta(work.getMetadata(name)); - UNSCOPED_INFO(stringize("No metadata for ", name)); REQUIRE(meta.isDefined()); return meta; } @@ -715,26 +712,11 @@ namespace tut return map; } - // If I call this ensure_equals(), it blocks visibility of all other - // ensure_equals() overloads. Normally I could say 'using - // baseclass::ensure_equals;' and fix that, but I don't know what the - // base class is! - void ensure_llsd(const std::string& msg, const LLSD& actual, const LLSD& expected, U32 bits) + void require_llsd_equals(const LLSD& actual, const LLSD& expected, U32 bits) { - std::ostringstream out; - if (! msg.empty()) - { - out << msg << ": "; - } - out << "expected " << expected << ", actual " << actual; - UNSCOPED_INFO(out.str()); + CAPTURE(expected, actual); REQUIRE(llsd_equals(actual, expected, bits)); } - - void ensure_llsd(const LLSD& actual, const LLSD& expected, U32 bits) - { - ensure_llsd("", actual, expected, bits); - } }; // Call cases: @@ -767,7 +749,7 @@ namespace tut std::string threw = catch_what([this, &ae](){ work.add("freena_err", "freena", freena, ae); }); - ensure_has(threw, "must be an array"); + require_contains(threw, "must be an array"); } } @@ -776,7 +758,7 @@ namespace tut std::string threw = catch_what([this](){ work.add("freena_err", "freena", freena, llsd::array("a", "b"), 17); }); - ensure_has(threw, "must be a map or an array"); + require_contains(threw, "must be a map or an array"); } TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: map-style registration with too many array defaults", "[lleventdispatcher]") @@ -786,7 +768,7 @@ namespace tut llsd::array("a", "b"), llsd::array(17, 0.9, "gack")); }); - ensure_has(threw, "shorter than"); + require_contains(threw, "shorter than"); } TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: map-style registration with too many map defaults", "[lleventdispatcher]") @@ -796,9 +778,9 @@ namespace tut llsd::array("a", "b"), LLSDMap("b", 17)("foo", 3.14)("bar", "sinister")); }); - ensure_has(threw, "nonexistent params"); - ensure_has(threw, "foo"); - ensure_has(threw, "bar"); + require_contains(threw, "nonexistent params"); + require_contains(threw, "foo"); + require_contains(threw, "bar"); } TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: query all lists every registered name and desc", "[lleventdispatcher]") @@ -808,9 +790,7 @@ namespace tut TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: query all after remove() omits only the removed name", "[lleventdispatcher]") { - UNSCOPED_INFO("remove('bogus') returned true"); CHECK_FALSE(work.remove("bogus")); - UNSCOPED_INFO("remove('real') returned false"); CHECK(work.remove("free1")); // Of course, remove that from 'descs' too... descs.erase("free1"); @@ -828,21 +808,16 @@ namespace tut for (LLSD nm: inArray(names)) { LLSD metadata(getMetadata(nm)); - UNSCOPED_INFO("name mismatch"); REQUIRE_THAT(metadata["name"], ll_test::LLSDEquals(nm)); REQUIRE(metadata["desc"].asString() == descs[nm]); - UNSCOPED_INFO("should not have required structure"); REQUIRE(metadata["required"].isUndefined()); - UNSCOPED_INFO("should not have optional"); REQUIRE(metadata["optional"].isUndefined()); std::string name_req(nm.asString() + "_req"); metadata = getMetadata(name_req); REQUIRE(metadata["name"].asString() == name_req); REQUIRE(metadata["desc"].asString() == descs[name_req]); - UNSCOPED_INFO("required mismatch"); REQUIRE_THAT(required, ll_test::LLSDEquals(metadata["required"])); - UNSCOPED_INFO("should not have optional"); REQUIRE(metadata["optional"].isUndefined()); } } @@ -866,13 +841,11 @@ namespace tut req[arity - 1] = LLSD(); for (LLSD nm: inArray(names)) { + CAPTURE(nm); LLSD metadata(getMetadata(nm)); - UNSCOPED_INFO("name mismatch"); REQUIRE_THAT(metadata["name"], ll_test::LLSDEquals(nm)); REQUIRE(metadata["desc"].asString() == descs[nm]); - UNSCOPED_INFO(stringize("mismatched required for ", nm.asString())); REQUIRE_THAT(metadata["required"], ll_test::LLSDEquals(req)); - UNSCOPED_INFO("should not have optional"); REQUIRE(metadata["optional"].isUndefined()); } } @@ -886,12 +859,9 @@ namespace tut for (LLSD nm: inArray(names)) { LLSD metadata(getMetadata(nm)); - UNSCOPED_INFO("name mismatch"); REQUIRE_THAT(metadata["name"], ll_test::LLSDEquals(nm)); REQUIRE(metadata["desc"].asString() == descs[nm]); - UNSCOPED_INFO("should not have required"); REQUIRE((metadata["required"].isUndefined() || metadata["required"].size() == 0)); - UNSCOPED_INFO("should not have optional"); REQUIRE(metadata["optional"].isUndefined()); } } @@ -916,20 +886,17 @@ namespace tut { LLSD adft(eq[0]); LLSD mdft(eq[1]); + CAPTURE(adft, mdft); // We can't just compare the results of the two getMetadata() // calls, because they contain ["name"], which are different. So // capture them, verify that each ["name"] is as expected, then // remove for comparing the rest. LLSD ameta(getMetadata(adft)); LLSD mmeta(getMetadata(mdft)); - UNSCOPED_INFO("adft name"); REQUIRE_THAT(adft, ll_test::LLSDEquals(ameta["name"])); - UNSCOPED_INFO("mdft name"); REQUIRE_THAT(mdft, ll_test::LLSDEquals(mmeta["name"])); ameta.erase("name"); mmeta.erase("name"); - UNSCOPED_INFO(stringize("metadata for ", adft.asString(), - " vs. ", mdft.asString())); REQUIRE_THAT(ameta, ll_test::LLSDEquals(mmeta)); } } @@ -1033,14 +1000,11 @@ namespace tut // Loop through 'names' for (LLSD nm: inArray(names)) { + CAPTURE(nm); LLSD metadata(getMetadata(nm)); - UNSCOPED_INFO("name mismatch"); REQUIRE_THAT(metadata["name"], ll_test::LLSDEquals(nm)); - UNSCOPED_INFO(nm.asString()); REQUIRE(metadata["desc"].asString() == descs[nm]); - UNSCOPED_INFO(stringize(nm, " required mismatch")); REQUIRE_THAT(metadata["required"], ll_test::LLSDEquals(required)); - UNSCOPED_INFO(stringize(nm, " optional mismatch")); REQUIRE_THAT(metadata["optional"], ll_test::LLSDEquals(optional)); } } @@ -1048,13 +1012,9 @@ namespace tut TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: try_call() returns whether it found the name", "[lleventdispatcher]") { - UNSCOPED_INFO("try_call(bogus name, LLSD()) returned true"); CHECK_FALSE(work.try_call("freek", LLSD())); - UNSCOPED_INFO("try_call(bogus name) returned true"); CHECK_FALSE(work.try_call(LLSDMap("op", "freek"))); - UNSCOPED_INFO("try_call(real name, LLSD()) returned false"); CHECK(work.try_call("free0_array", LLSD())); - UNSCOPED_INFO("try_call(real name) returned false"); CHECK(work.try_call(LLSDMap("op", "free0_map"))); } @@ -1062,8 +1022,8 @@ namespace tut { call_exc("freek", LLSD(), "not found"); std::string threw = call_exc("", LLSDMap("op", "freek"), "bad"); - ensure_has(threw, "op"); - ensure_has(threw, "freek"); + require_contains(threw, "op"); + require_contains(threw, "freek"); } TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: call with event key", "[lleventdispatcher]") @@ -1107,7 +1067,6 @@ namespace tut // Should be able to pass 'answer' to Callables registered // without 'required'. work(tr.name, answer); - UNSCOPED_INFO("answer mismatch"); REQUIRE_THAT(tr.llsd, ll_test::LLSDEquals(answer)); // Should NOT be able to pass 'answer' to Callables registered // with 'required'. @@ -1115,7 +1074,6 @@ namespace tut // But SHOULD be able to pass 'matching' to Callables registered // with 'required'. work(tr.name_req, matching); - UNSCOPED_INFO("matching mismatch"); REQUIRE_THAT(tr.llsd, ll_test::LLSDEquals(matching)); } } @@ -1216,12 +1174,12 @@ namespace tut { for (LLSD::String a: ab) { + CAPTURE(funcsab[a], a); // Reset the Vars instance before each call Vars* vars(varsfor(funcsab[a])); *vars = Vars(); work(funcsab[a], args[a]); - ensure_llsd(stringize(funcsab[a].asString(), ": expect[\"", a, "\"] mismatch"), - vars->inspect(), expect[a], 7); // 7 bits ~= 2 decimal digits + require_llsd_equals(vars->inspect(), expect[a], 7); // 7 bits ~= 2 decimal digits } } } @@ -1309,12 +1267,12 @@ namespace tut { for (LLSD::String name: inArray(names[a])) { + CAPTURE(name, a); // Reset the Vars instance Vars* vars(varsfor(name)); *vars = Vars(); work(name, args[a]); - ensure_llsd(stringize(name, ": expect[\"", a, "\"] mismatch"), - vars->inspect(), expect[a], 7); // 7 bits, 2 decimal digits + require_llsd_equals(vars->inspect(), expect[a], 7); // 7 bits, 2 decimal digits // intercept LL_WARNS for the two overfull cases? } } @@ -1363,7 +1321,6 @@ namespace tut { DispatchResult service; LLSD result{ service("strfunc", "a string") }; - UNSCOPED_INFO("strfunc() mismatch"); CHECK(result.asString() == "got a string"); } @@ -1371,15 +1328,13 @@ namespace tut { DispatchResult service; LLSD result{ service("voidfunc", LLSD()) }; - UNSCOPED_INFO("voidfunc() returned defined"); - REQUIRE(result.isUndefined()); + CHECK(result.isUndefined()); } TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: Integer result", "[lleventdispatcher]") { DispatchResult service; LLSD result{ service("intfunc", -17) }; - UNSCOPED_INFO("intfunc() mismatch"); CHECK(result.asInteger() == 17); } @@ -1387,7 +1342,6 @@ namespace tut { DispatchResult service; LLSD result{ service("llsdfunc", llsd::map("op", "llsdfunc", "reqid", 17)) }; - UNSCOPED_INFO("llsdfunc() mismatch"); CHECK_THAT(result, ll_test::LLSDEquals(llsd::map("op", "llsdfunc", "reqid", 17, "with", "string"))); } @@ -1396,7 +1350,6 @@ namespace tut { DispatchResult service; LLSD result{ service("mapfunc", llsd::array(-12, "value")) }; - UNSCOPED_INFO("mapfunc() mismatch"); CHECK_THAT(result, ll_test::LLSDEquals(llsd::map("i", 12, "str", "got value"))); } @@ -1404,7 +1357,6 @@ namespace tut { DispatchResult service; LLSD result{ service("arrayfunc", llsd::array(-8, "word")) }; - UNSCOPED_INFO("arrayfunc() mismatch"); CHECK_THAT(result, ll_test::LLSDEquals(llsd::array(8, "got word"))); } @@ -1423,11 +1375,9 @@ namespace tut LLCaptureListener result; service.post(llsd::map("op", "nosuchfunc", "reqid", 17, "reply", result.getName())); LLSD reply{ result.get() }; - UNSCOPED_INFO("no reply"); REQUIRE(reply.isDefined()); - UNSCOPED_INFO("reqid not echoed"); CHECK(reply["reqid"].asInteger() == 17); - ensure_has(reply["error"].asString(), "nosuchfunc"); + require_contains(reply["error"].asString(), "nosuchfunc"); } TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: listener call to void function", "[lleventdispatcher]") @@ -1441,7 +1391,7 @@ namespace tut "op", func, "reqid", 17, "reply", result.getName())); - UNSCOPED_INFO("reply from " + func); + CAPTURE(func); REQUIRE(result.get().asString() == "non-empty"); } } @@ -1456,9 +1406,7 @@ namespace tut "reqid", 17, "reply", result.getName())); LLSD reply{ result.get() }; - UNSCOPED_INFO("reqid not echoed"); CHECK(reply["reqid"].asInteger() == 17); - UNSCOPED_INFO("bad reply from strfunc"); CHECK(reply["data"].asString() == "got a string"); } @@ -1472,11 +1420,8 @@ namespace tut "reqid", 17, "reply", result.getName())); LLSD reply{ result.get() }; - UNSCOPED_INFO("reqid not echoed"); CHECK(reply["reqid"].asInteger() == 17); - UNSCOPED_INFO("bad i from mapfunc"); CHECK(reply["i"].asInteger() == 7); - UNSCOPED_INFO("bad str from mapfunc"); CHECK(reply["str"] == "got value"); } @@ -1493,10 +1438,8 @@ namespace tut "reqid", 17, "reply", result.getName())); LLSD reply{ result.get() }; - UNSCOPED_INFO("reqid not echoed"); CHECK(reply["reqid"].asInteger() == 17); reply.erase("reqid"); - UNSCOPED_INFO("bad map batch"); CHECK_THAT(reply, ll_test::LLSDEquals(llsd::map( "strfunc", "got some string", @@ -1520,14 +1463,12 @@ namespace tut "reqid", 17, "reply", result.getName())); LLSD reply{ result.get() }; - UNSCOPED_INFO("reqid not echoed"); CHECK(reply["reqid"].asInteger() == 17); reply.erase("reqid"); auto error{ reply["error"].asString() }; reply.erase("error"); - ensure_has(error, "badfunc"); - ensure_has(error, "missing"); - UNSCOPED_INFO("bad partial batch"); + require_contains(error, "badfunc"); + require_contains(error, "missing"); CHECK_THAT(reply, ll_test::LLSDEquals(llsd::map( "strfunc", "got some string", @@ -1554,7 +1495,7 @@ namespace tut // no "reply" }, "badfunc"); - ensure_has(error, "missing"); + require_contains(error, "missing"); } TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: batched array success", "[lleventdispatcher]") @@ -1576,10 +1517,8 @@ namespace tut "reqid", 17, "reply", result.getName())); LLSD reply{ result.get() }; - UNSCOPED_INFO("reqid not echoed"); CHECK(reply["reqid"].asInteger() == 17); reply.erase("reqid"); - UNSCOPED_INFO("bad array batch"); CHECK_THAT(reply, ll_test::LLSDEquals(llsd::map( "data", llsd::array( @@ -1608,14 +1547,12 @@ namespace tut "reqid", 17, "reply", result.getName())); LLSD reply{ result.get() }; - UNSCOPED_INFO("reqid not echoed"); CHECK(reply["reqid"].asInteger() == 17); reply.erase("reqid"); auto error{ reply["error"] }; reply.erase("error"); - ensure_has(error, "[1]"); - ensure_has(error, "unsupported"); - UNSCOPED_INFO("bad array batch"); + require_contains(error, "[1]"); + require_contains(error, "unsupported"); CHECK_THAT(reply, ll_test::LLSDEquals(llsd::map("data", llsd::array("got some string")))); } @@ -1642,6 +1579,6 @@ namespace tut // no "reply" }, "[1]"); - ensure_has(error, "unsupported"); + require_contains(error, "unsupported"); } } // namespace tut diff --git a/indra/llcommon/tests/lleventfilter_test.cpp b/indra/llcommon/tests/lleventfilter_test.cpp index ad92ab7728..3bcc4d44fb 100644 --- a/indra/llcommon/tests/lleventfilter_test.cpp +++ b/indra/llcommon/tests/lleventfilter_test.cpp @@ -37,7 +37,6 @@ #include #include #include "altestmatchers.h" -#include "stringize.h" #include "llsdutil.h" #include "listener.h" #include "tests/wrapllerrs.h" @@ -179,9 +178,9 @@ namespace Listener listener0; Listener listener1; - void check_listener(const std::string& desc, const Listener& listener, const LLSD& got) + void require_last_event(const std::string& desc, const Listener& listener, const LLSD& got) { - UNSCOPED_INFO((STRINGIZE(listener << ' ' << desc))); + INFO(listener << ' ' << desc); REQUIRE_THAT(listener.getLastEvent(), ll_test::LLSDEquals(got)); } }; @@ -207,15 +206,15 @@ namespace LLTempBoundListener temp2( listener1.listenTo(filter)); driver.post(1); - check_listener("direct", listener0, LLSD(1)); - check_listener("filtered", listener1, LLSD(0)); + require_last_event("direct", listener0, LLSD(1)); + require_last_event("filtered", listener1, LLSD(0)); // Okay, construct an LLSD map matching the pattern LLSD data; data["foo"] = "bar"; data["random"] = 17; driver.post(data); - check_listener("direct", listener0, data); - check_listener("filtered", listener1, data); + require_last_event("direct", listener0, data); + require_last_event("filtered", listener1, data); } TEST_CASE_METHOD(filter_data, "lleventfilter: LLEventTimeout::actionAfter() acts only when it times out", "[lleventfilter]") @@ -235,15 +234,15 @@ namespace // when we pump mainloop. Do that right now to take the logic path // before either the anticipated event arrives or the timer expires. mainloop.post(17); - check_listener("no timeout 1", listener1, LLSD(0)); + require_last_event("no timeout 1", listener1, LLSD(0)); // Expected event arrives... driver.post(1); - check_listener("event passed thru", listener0, LLSD(1)); + require_last_event("event passed thru", listener0, LLSD(1)); // Should have canceled the timer. Verify that by asserting that the // time has expired, then pumping mainloop again. filter.forceTimeout(); mainloop.post(17); - check_listener("no timeout 2", listener1, LLSD(0)); + require_last_event("no timeout 2", listener1, LLSD(0)); // Verify chained actionAfter() calls, that is, that a second // actionAfter() resets the timer established by the first // actionAfter(). @@ -262,29 +261,29 @@ namespace boost::bind(&Listener::call, boost::ref(listener1), LLSD("timeout"))); // Look ma, no timeout! mainloop.post(17); - check_listener("no timeout 3", listener1, LLSD(0)); + require_last_event("no timeout 3", listener1, LLSD(0)); // Now let the updated actionAfter() timer expire. filter.forceTimeout(); // Notice the timeout. mainloop.post(17); - check_listener("timeout", listener1, LLSD("timeout")); + require_last_event("timeout", listener1, LLSD("timeout")); // Timing out cancels the timer. Verify that. listener1.reset(0); filter.forceTimeout(); mainloop.post(17); - check_listener("no timeout 4", listener1, LLSD(0)); + require_last_event("no timeout 4", listener1, LLSD(0)); // Reset the timer and then cancel() it. filter.actionAfter(20, boost::bind(&Listener::call, boost::ref(listener1), LLSD("timeout"))); // neither expired nor satisified mainloop.post(17); - check_listener("no timeout 5", listener1, LLSD(0)); + require_last_event("no timeout 5", listener1, LLSD(0)); // cancel filter.cancel(); // timeout! filter.forceTimeout(); mainloop.post(17); - check_listener("no timeout 6", listener1, LLSD(0)); + require_last_event("no timeout 6", listener1, LLSD(0)); } TEST_CASE_METHOD(filter_data, "lleventfilter: LLEventTimeout::eventAfter() posts only when it times out", "[lleventfilter]") @@ -299,27 +298,27 @@ namespace // when we pump mainloop. Do that right now to take the logic path // before either the anticipated event arrives or the timer expires. mainloop.post(17); - check_listener("no timeout 1", listener0, LLSD(0)); + require_last_event("no timeout 1", listener0, LLSD(0)); // Expected event arrives... driver.post(1); - check_listener("event passed thru", listener0, LLSD(1)); + require_last_event("event passed thru", listener0, LLSD(1)); // Should have canceled the timer. Verify that by asserting that the // time has expired, then pumping mainloop again. filter.forceTimeout(); mainloop.post(17); - check_listener("no timeout 2", listener0, LLSD(1)); + require_last_event("no timeout 2", listener0, LLSD(1)); // Set timer again. filter.eventAfter(20, LLSD("timeout")); // Now let the timer expire. filter.forceTimeout(); // Notice the timeout. mainloop.post(17); - check_listener("timeout", listener0, LLSD("timeout")); + require_last_event("timeout", listener0, LLSD("timeout")); // Timing out cancels the timer. Verify that. listener0.reset(0); filter.forceTimeout(); mainloop.post(17); - check_listener("no timeout 3", listener0, LLSD(0)); + require_last_event("no timeout 3", listener0, LLSD(0)); } TEST_CASE_METHOD(filter_data, "lleventfilter: LLEventTimeout::errorAfter() raises an error only on timeout", "[lleventfilter]") @@ -335,15 +334,15 @@ namespace // when we pump mainloop. Do that right now to take the logic path // before either the anticipated event arrives or the timer expires. mainloop.post(17); - check_listener("no timeout 1", listener0, LLSD(0)); + require_last_event("no timeout 1", listener0, LLSD(0)); // Expected event arrives... driver.post(1); - check_listener("event passed thru", listener0, LLSD(1)); + require_last_event("event passed thru", listener0, LLSD(1)); // Should have canceled the timer. Verify that by asserting that the // time has expired, then pumping mainloop again. filter.forceTimeout(); mainloop.post(17); - check_listener("no timeout 2", listener0, LLSD(1)); + require_last_event("no timeout 2", listener0, LLSD(1)); // Set timer again. filter.errorAfter(20, "timeout"); // Now let the timer expire. @@ -352,13 +351,12 @@ namespace std::string threw = capture.catch_llerrs([this](){ mainloop.post(17); }); - UNSCOPED_INFO("errorAfter() timeout exception"); CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("timeout")); // Timing out cancels the timer. Verify that. listener0.reset(0); filter.forceTimeout(); mainloop.post(17); - check_listener("no timeout 3", listener0, LLSD(0)); + require_last_event("no timeout 3", listener0, LLSD(0)); } TEST_CASE_METHOD(filter_data, "lleventfilter: LLEventThrottle delivers at most one event an interval", "[lleventfilter]") @@ -371,46 +369,38 @@ namespace // 1: post(): event immediately passed to listeners, next no sooner than 4 throttle.advance(1); throttle.post("1"); - UNSCOPED_INFO("1"); CHECK(cat.result == "1"); // delivered immediately // 2: post(): deferred: waiting for 3 seconds to elapse throttle.advance(1); throttle.post("2"); - UNSCOPED_INFO("2"); CHECK(cat.result == "1"); // "2" not yet delivered // 3: post(): deferred throttle.advance(1); throttle.post("3"); - UNSCOPED_INFO("3"); CHECK(cat.result == "1"); // "3" not yet delivered // 4: no post() call, but event delivered to listeners; next no sooner than 7 throttle.advance(1); - UNSCOPED_INFO("4"); CHECK(cat.result == "13"); // "3" delivered // 6: post(): deferred throttle.advance(2); throttle.post("6"); - UNSCOPED_INFO("6"); CHECK(cat.result == "13"); // "6" not yet delivered // 7: no post() call, but event delivered; next no sooner than 10 throttle.advance(1); - UNSCOPED_INFO("7"); CHECK(cat.result == "136"); // "6" delivered // 12: post(): immediately passed to listeners, next no sooner than 15 throttle.advance(5); throttle.post(";12"); - UNSCOPED_INFO("12"); CHECK(cat.result == "136;12"); // "12" delivered // 17: post(): immediately passed to listeners, next no sooner than 20 throttle.advance(5); throttle.post(";17"); - UNSCOPED_INFO("17"); CHECK(cat.result == "136;12;17"); // "17" delivered } // A PUMP keeps what no listener consumed, and replays it on the next listen(). template - static void check_mail_drop_replay() + static void require_mail_drop_replay() { PUMP pump(typeid(PUMP).name()); LLSD data{LLSD::emptyArray()}; @@ -431,7 +421,6 @@ namespace pump.post("first"); } // first post() should certainly be received by listener - UNSCOPED_INFO("first"); REQUIRE_THAT(data, ll_test::LLSDEquals(llsd::array("first"))); // the question is, since consumed was true, did it queue the value? data = LLSD::emptyArray(); @@ -440,7 +429,6 @@ namespace // listen() call LLTempBoundListener conn = pump.listen("lambda", lambda); } - UNSCOPED_INFO("empty1"); REQUIRE_THAT(data, ll_test::LLSDEquals(LLSD::emptyArray())); data = LLSD::emptyArray(); // now let's NOT consume the posted data @@ -451,7 +439,6 @@ namespace pump.post("third"); } // the two events still arrive - UNSCOPED_INFO("second,third1"); REQUIRE_THAT(data, ll_test::LLSDEquals(llsd::array("second", "third"))); data = LLSD::emptyArray(); { @@ -461,25 +448,23 @@ namespace LLTempBoundListener conn = pump.listen("lambda", lambda); } // unconsumed events were delivered again - UNSCOPED_INFO("second,third2"); REQUIRE_THAT(data, ll_test::LLSDEquals(llsd::array("second", "third"))); data = LLSD::emptyArray(); { // when we reconnect this time, no more unconsumed events LLTempBoundListener conn = pump.listen("lambda", lambda); } - UNSCOPED_INFO("empty2"); REQUIRE_THAT(data, ll_test::LLSDEquals(LLSD::emptyArray())); } TEST_CASE_METHOD(filter_data, "lleventfilter: LLEventMailDrop replays unconsumed events on the next listen", "[lleventfilter]") { - check_mail_drop_replay(); + require_mail_drop_replay(); } TEST_CASE_METHOD(filter_data, "lleventfilter: LLEventLogProxyFor replays unconsumed events", "[lleventfilter]") { - check_mail_drop_replay< LLEventLogProxyFor >(); + require_mail_drop_replay< LLEventLogProxyFor >(); } } // anonymous namespace diff --git a/indra/llcommon/tests/llevents_test.cpp b/indra/llcommon/tests/llevents_test.cpp index 970111d5e9..f06de5ead1 100644 --- a/indra/llcommon/tests/llevents_test.cpp +++ b/indra/llcommon/tests/llevents_test.cpp @@ -49,7 +49,6 @@ #include #include #include "catch_and_store_what_in.h" -#include "stringize.h" template static T make(const T& value) @@ -91,9 +90,9 @@ struct events_data Listener listener0; Listener listener1; - void check_listener(const std::string& desc, const Listener& listener, LLSD::Integer got) + void require_last_event(const std::string& desc, const Listener& listener, LLSD::Integer got) { - UNSCOPED_INFO((STRINGIZE(listener << ' ' << desc))); + INFO(listener << ' ' << desc); REQUIRE(listener.getLastEvent().asInteger() == got); } }; @@ -106,12 +105,9 @@ TEST_CASE_METHOD(events_data, "events: basic operations", "[events]") // ensure_equals("initial pump", pumps.mPumpMap.size(), 1); size_t initial_pumps(pumps.mPumpMap.size()); LLEventPump& per_frame(pumps.obtain("per-frame")); - UNSCOPED_INFO("first explicit pump"); - REQUIRE(pumps.mPumpMap.size() == initial_pumps + 1); + CHECK(pumps.mPumpMap.size() == initial_pumps + 1); // Verify that per_frame was instantiated as an LLEventStream. - UNSCOPED_INFO("LLEventStream leaf class"); - REQUIRE(dynamic_cast (&per_frame)); - UNSCOPED_INFO("enabled"); + CHECK(dynamic_cast (&per_frame)); CHECK(per_frame.enabled()); // Trivial test, but posting an event to an EventPump with no // listeners should not blow up. The test is relevant because defining @@ -121,34 +117,27 @@ TEST_CASE_METHOD(events_data, "events: basic operations", "[events]") // last listener, which is meaningless if there were no listeners. per_frame.post(0); LLBoundListener connection = listener0.listenTo(per_frame); - UNSCOPED_INFO("connected"); CHECK(connection.connected()); - UNSCOPED_INFO("not blocked"); CHECK_FALSE(connection.blocked()); per_frame.post(1); - check_listener("received", listener0, 1); + require_last_event("received", listener0, 1); { // block the connection LLEventPump::Blocker block(connection); - UNSCOPED_INFO("blocked"); CHECK(connection.blocked()); per_frame.post(2); - check_listener("not updated", listener0, 1); + require_last_event("not updated", listener0, 1); } // unblock - UNSCOPED_INFO("unblocked"); CHECK_FALSE(connection.blocked()); per_frame.post(3); - check_listener("unblocked", listener0, 3); + require_last_event("unblocked", listener0, 3); LLBoundListener sameConnection = per_frame.getListener(listener0.getName()); - UNSCOPED_INFO("still connected"); CHECK(sameConnection.connected()); - UNSCOPED_INFO("still not blocked"); CHECK_FALSE(sameConnection.blocked()); { // block it again LLEventPump::Blocker block(sameConnection); - UNSCOPED_INFO("re-blocked"); CHECK(sameConnection.blocked()); per_frame.post(4); - check_listener("re-blocked", listener0, 3); + require_last_event("re-blocked", listener0, 3); } // unblock std::string threw = catch_what( [&per_frame, this](){ @@ -167,27 +156,25 @@ TEST_CASE_METHOD(events_data, "events: basic operations", "[events]") // do it right this time listener1.listenTo(per_frame); per_frame.post(5); - check_listener("got", listener0, 5); - check_listener("got", listener1, 5); + require_last_event("got", listener0, 5); + require_last_event("got", listener1, 5); per_frame.enable(false); per_frame.post(6); - check_listener("didn't get", listener0, 5); - check_listener("didn't get", listener1, 5); + require_last_event("didn't get", listener0, 5); + require_last_event("didn't get", listener1, 5); per_frame.enable(); per_frame.post(7); - check_listener("got", listener0, 7); - check_listener("got", listener1, 7); + require_last_event("got", listener0, 7); + require_last_event("got", listener1, 7); per_frame.stopListening(listener0.getName()); - UNSCOPED_INFO("disconnected 0"); CHECK_FALSE(connection.connected()); - UNSCOPED_INFO("disconnected 1"); CHECK_FALSE(sameConnection.connected()); per_frame.post(8); - check_listener("disconnected", listener0, 7); - check_listener("still connected", listener1, 8); + require_last_event("disconnected", listener0, 7); + require_last_event("still connected", listener1, 8); per_frame.stopListening(listener1.getName()); per_frame.post(9); - check_listener("disconnected", listener1, 8); + require_last_event("disconnected", listener1, 8); } TEST_CASE_METHOD(events_data, "events: callstop() returning true", "[events]") @@ -199,20 +186,15 @@ TEST_CASE_METHOD(events_data, "events: callstop() returning true", "[events]") LLBoundListener bound1 = listener1.listenTo(per_frame, &Listener::call, // after listener0 make(LLEventPump::NameList{ listener0.getName() })); - UNSCOPED_INFO("enabled"); CHECK(per_frame.enabled()); - UNSCOPED_INFO("connected 0"); CHECK(bound0.connected()); - UNSCOPED_INFO("unblocked 0"); CHECK_FALSE(bound0.blocked()); - UNSCOPED_INFO("connected 1"); CHECK(bound1.connected()); - UNSCOPED_INFO("unblocked 1"); CHECK_FALSE(bound1.blocked()); per_frame.post(1); - check_listener("got", listener0, 1); + require_last_event("got", listener0, 1); // Because listener0.callstop() returns true, control never reaches listener1.call(). - check_listener("got", listener1, 0); + require_last_event("got", listener1, 0); } TEST_CASE_METHOD(events_data, "events: an explicit LLEventStream registers itself until destroyed", "[events]") @@ -226,24 +208,16 @@ TEST_CASE_METHOD(events_data, "events: an explicit LLEventStream registers itsel LLEventStream myEventStream("stream"); localInstance = &myEventStream; LLEventPump& stream(pumps.obtain("stream")); - UNSCOPED_INFO("found named LLEventStream instance"); CHECK(&stream == localInstance); - UNSCOPED_INFO("registered new instance"); - REQUIRE(pumps.mPumpMap.size() == registered + 1); - UNSCOPED_INFO("explicit instance not owned"); - REQUIRE(pumps.mOurPumps.size() == owned); + CHECK(pumps.mPumpMap.size() == registered + 1); + CHECK(pumps.mOurPumps.size() == owned); } // destroy myEventStream -- should unregister - UNSCOPED_INFO("destroyed instance unregistered"); - REQUIRE(pumps.mPumpMap.size() == registered); - UNSCOPED_INFO("destroyed instance not owned"); - REQUIRE(pumps.mOurPumps.size() == owned); + CHECK(pumps.mPumpMap.size() == registered); + CHECK(pumps.mOurPumps.size() == owned); LLEventPump& stream(pumps.obtain("stream")); - UNSCOPED_INFO("new LLEventStream instance"); CHECK(&stream != localInstance); - UNSCOPED_INFO("obtain()ed instance registered"); - REQUIRE(pumps.mPumpMap.size() == registered + 1); - UNSCOPED_INFO("obtain()ed instance owned"); - REQUIRE(pumps.mOurPumps.size() == owned + 1); + CHECK(pumps.mPumpMap.size() == registered + 1); + CHECK(pumps.mOurPumps.size() == owned + 1); } TEST_CASE_METHOD(events_data, "events: stopListening() frees the name and a bogus name has no connection", "[events]") @@ -254,9 +228,7 @@ TEST_CASE_METHOD(events_data, "events: stopListening() frees the name and a bogu // should not throw because stopListening() should have removed name listener0.listenTo(login, &Listener::callstop); LLBoundListener wrong = login.getListener("bogus"); - UNSCOPED_INFO("bogus connection disconnected"); CHECK_FALSE(wrong.connected()); - UNSCOPED_INFO("bogus connection blocked"); CHECK(wrong.blocked()); } @@ -275,12 +247,12 @@ TEST_CASE_METHOD(events_data, "events: chaining LLEventPump instances", "[events listener0.reset(0); listener1.reset(0); upstream.post(1); - check_listener("got unfiltered", listener0, 1); - check_listener("got unfiltered", listener1, 1); + require_last_event("got unfiltered", listener0, 1); + require_last_event("got unfiltered", listener1, 1); filter0.enable(false); upstream.post(2); - check_listener("didn't get filtered", listener0, 1); - check_listener("got filtered", listener1, 2); + require_last_event("didn't get filtered", listener0, 1); + require_last_event("got filtered", listener1, 2); } TEST_CASE_METHOD(events_data, "events: listener dependency order", "[events]") @@ -322,21 +294,15 @@ TEST_CASE_METHOD(events_data, "events: listener dependency order", "[events]") // change; go ahead and change the test to match. // Establish that it contains: // - the name and runtime type of the LLEventPump - UNSCOPED_INFO("LLEventPump type"); - REQUIRE_THAT(threw, Catch::Matchers::ContainsSubstring(typeid(button).name())); - UNSCOPED_INFO("LLEventPump name"); + CHECK_THAT(threw, Catch::Matchers::ContainsSubstring(typeid(button).name())); CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("'button'")); // - the name of the new listener that caused the problem - UNSCOPED_INFO("new listener name"); CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("'spot'")); // - a synopsis of the problematic dependencies. - UNSCOPED_INFO("cyclic dependencies"); CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("\"Mary\" -> before (\"spot\")")); - UNSCOPED_INFO("cyclic dependencies"); CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("after (\"spot\") -> \"checked\"")); - UNSCOPED_INFO("cyclic dependencies"); CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("after (\"Mary\", \"checked\") -> \"spot\"")); button.listen("yellow", @@ -356,18 +322,14 @@ TEST_CASE_METHOD(events_data, "events: listener dependency order", "[events]") // Same remarks about the specific wording of the exception. Just // ensure that it contains enough information to clarify the // problem and what must be done to resolve it. - UNSCOPED_INFO("LLEventPump type"); - REQUIRE_THAT(threw, Catch::Matchers::ContainsSubstring(typeid(button).name())); - UNSCOPED_INFO("LLEventPump name"); + CHECK_THAT(threw, Catch::Matchers::ContainsSubstring(typeid(button).name())); CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("'button'")); UNSCOPED_INFO("new listener name"); CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("'of'")); UNSCOPED_INFO("prev listener name"); CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("'yellow'")); // std::cout << "Thrown Exception: " << threw << std::endl; - UNSCOPED_INFO("old order"); CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("was: Mary, checked, yellow, shoelaces")); - UNSCOPED_INFO("new order"); CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("now: Mary, checked, shoelaces, of, yellow")); button.post(4); CHECK(collector.result == make(StringVec{ "Mary", "checked", "yellow", "shoelaces" })); @@ -383,19 +345,16 @@ TEST_CASE_METHOD(events_data, "events: tweaked and untweaked LLEventPump instanc // then another with a duplicate name. LLEventStream bob2("bob"); }); - UNSCOPED_INFO("Caught DupPumpName"); - REQUIRE_FALSE(threw.empty()); + CHECK_FALSE(threw.empty()); } // delete first 'bob' LLEventStream bob("bob"); // should work, previous one unregistered LLEventStream bob1("bob", true);// allowed to tweak name - UNSCOPED_INFO("tweaked LLEventStream name"); CHECK(bob1.getName() == "bob1"); std::vector > streams; for (int i = 2; i <= 10; ++i) { streams.push_back(std::shared_ptr(new LLEventStream("bob", true))); } - UNSCOPED_INFO("last tweaked LLEventStream name"); CHECK(streams.back()->getName() == "bob10"); } @@ -411,21 +370,20 @@ TEST_CASE_METHOD(events_data, "events: LLListenerOrPumpName calls a listener or // Passing a boost::bind() expression to LLListenerOrPumpName listener0.reset(0); eventSource(boost::bind(&Listener::call, boost::ref(listener0), _1)); - check_listener("got by listener", listener0, 17); + require_last_event("got by listener", listener0, 17); // Passing a string LLEventPump name to LLListenerOrPumpName listener0.reset(0); LLEventStream random("random"); listener0.listenTo(random); eventSource("random"); - check_listener("got by pump name", listener0, 17); + require_last_event("got by pump name", listener0, 17); std::string threw = catch_what( [](){ LLListenerOrPumpName empty; empty(17); }); - UNSCOPED_INFO("threw Empty"); - REQUIRE_FALSE(threw.empty()); + CHECK_FALSE(threw.empty()); } @@ -464,7 +422,7 @@ TEST_CASE_METHOD(events_data, "events: a destroyed plain listener still looks co boost::ref(tempListener), _1)); heaptest.post(1); - check_listener("received", tempListener, 1); + require_last_event("received", tempListener, 1); } // presumably this will make newListener go away? // verify that UNSCOPED_INFO("TempListener destroyed"); @@ -501,7 +459,7 @@ TEST_CASE_METHOD(events_data, "events: destroying a bound LLEventTrackable disco boost::bind(&TempTrackableListener::call, boost::ref(tempListener), _1)); heaptest.post(1); - check_listener("received", tempListener, 1); + require_last_event("received", tempListener, 1); } // presumably this will make tempListener go away? // verify that UNSCOPED_INFO("TempTrackableListener destroyed"); @@ -525,7 +483,7 @@ TEST_CASE_METHOD(events_data, "events: deleting a bound LLEventTrackable pointer boost::bind(&TempTrackableListener::call, newListener, _1)); heaptest.post(1); - check_listener("received", *newListener, 1); + require_last_event("received", *newListener, 1); // explicitly destroy newListener delete newListener; } diff --git a/indra/llcommon/tests/llfile_test.cpp b/indra/llcommon/tests/llfile_test.cpp index e4cecb9cfa..dcba540256 100644 --- a/indra/llcommon/tests/llfile_test.cpp +++ b/indra/llcommon/tests/llfile_test.cpp @@ -78,55 +78,42 @@ namespace { // Test creating directories and files and deleting them and checking if the // relevant status functions work as expected - UNSCOPED_INFO("LLFile::tmpdir() empty"); REQUIRE_FALSE(tempdir.empty()); - UNSCOPED_INFO("LLFile::tmpdir() doesn't exist"); CHECK(LLFile::exists(tempdir)); - UNSCOPED_INFO("LLFile::tmpdir() is not a directory"); CHECK(LLFile::isdir(tempdir)); - UNSCOPED_INFO("LLFile::tmpdir() should not be a file"); CHECK_FALSE(LLFile::isfile(tempdir)); // Make sure there is nothing left from a previous test run clear_entire_dir(testdir); - UNSCOPED_INFO("llfile_test should not exist anymore"); CHECK_FALSE(LLFile::exists(testdir)); int rc = LLFile::mkdir(testdir); - UNSCOPED_INFO("LLFile::mkdir() failed"); + UNSCOPED_INFO("mkdir"); CHECK(rc == 0); - UNSCOPED_INFO("llfile_test should be a directory"); CHECK(LLFile::isdir(testdir)); rc = LLFile::mkdir(testdir); - UNSCOPED_INFO("LLFile::mkdir() should not fail when the directory already exists"); + UNSCOPED_INFO("mkdir of a directory that exists"); CHECK(rc == 0); std::filesystem::path testfile1 = testdir; testfile1.append("llfile_test.dat"); - UNSCOPED_INFO("llfile_test1.dat should not yet exist"); CHECK_FALSE(LLFile::exists(testfile1)); const char* testdata = "testdata"; S64 bytes = LLFile::write(testfile1, testdata, 0, sizeof(testdata)); - UNSCOPED_INFO("LLFile::write() did not write correctly"); CHECK(bytes == sizeof(testdata)); rc = LLFile::remove(testfile1); - UNSCOPED_INFO("LLFile::remove() for file test_file.dat"); + UNSCOPED_INFO("remove of the file"); CHECK(rc == 0); - UNSCOPED_INFO("llfile_test.dat should not exist anymore"); CHECK_FALSE(LLFile::exists(testfile1)); - UNSCOPED_INFO("llfile_test.dat should not be a file"); CHECK_FALSE(LLFile::isfile(testfile1)); - UNSCOPED_INFO("llfile_test.dat should not be a directory"); CHECK_FALSE(LLFile::isdir(testfile1)); - UNSCOPED_INFO("llfile_test.dat should not be a symlink"); CHECK_FALSE(LLFile::islink(testfile1)); rc = LLFile::remove(testdir); - UNSCOPED_INFO("LLFile::remove() for directory llfile_test failed"); + UNSCOPED_INFO("remove of the directory"); CHECK(rc == 0); - UNSCOPED_INFO("llfile_test should not exist anymore"); CHECK_FALSE(LLFile::exists(testdir)); } @@ -134,7 +121,6 @@ namespace { // High level static file IO functions to read and write data files LLFile::mkdir(testdir); - UNSCOPED_INFO("llfile_test should exist"); CHECK(LLFile::isdir(testdir)); std::filesystem::path testfile1 = testdir; @@ -144,54 +130,45 @@ namespace std::string testdata2("datateststuff"); std::time_t current = time(nullptr); S64 bytes = LLFile::write(testfile1, testdata1.c_str(), 0, testdata1.length()); - UNSCOPED_INFO("LLFile::write() did not write correctly"); - REQUIRE(bytes == testdata1.length()); - UNSCOPED_INFO("llfile_test.dat should exist"); + UNSCOPED_INFO("write"); + CHECK(bytes == testdata1.length()); CHECK(LLFile::exists(testfile1)); - UNSCOPED_INFO("llfile_test.dat should be a file"); CHECK(LLFile::isfile(testfile1)); - UNSCOPED_INFO("llfile_test.dat should not be a directory"); CHECK_FALSE(LLFile::isdir(testfile1)); bytes = LLFile::size(testfile1); - UNSCOPED_INFO("LLFile::size() did not return the correct size"); - REQUIRE(bytes == testdata1.length()); + UNSCOPED_INFO("size"); + CHECK(bytes == testdata1.length()); std::string data = LLFile::getContents(testfile1); - UNSCOPED_INFO("LLFile::getContents() did not return the correct size data"); - REQUIRE(data.length() == testdata1.length()); - UNSCOPED_INFO("LLFile::getContents() did not read correct data"); - REQUIRE_THAT(ll_test::Memory(testdata1.c_str(), (U32)testdata1.length()), ll_test::MemoryMatches(data.c_str(), (U32)data.length())); + CHECK(data.length() == testdata1.length()); + CHECK_THAT(ll_test::Memory(testdata1.c_str(), (U32)testdata1.length()), ll_test::MemoryMatches(data.c_str(), (U32)data.length())); char buffer[1024]; bytes = LLFile::read(testfile1, buffer, 0, testdata1.length()); - UNSCOPED_INFO("LLFile:read() did not return the correct size"); + UNSCOPED_INFO("read"); REQUIRE(bytes == testdata1.length()); - UNSCOPED_INFO("LLFile::read() did not read correct data"); - REQUIRE_THAT(ll_test::Memory(testdata1.c_str(), (U32)bytes), ll_test::MemoryMatches(buffer, (U32)bytes)); + CHECK_THAT(ll_test::Memory(testdata1.c_str(), (U32)bytes), ll_test::MemoryMatches(buffer, (U32)bytes)); // What if we try to read more data than there is in the file? bytes = LLFile::read(testfile1, buffer, 0, bytes + 10); - UNSCOPED_INFO("LLFile:read() did not correctly stop on eof"); + UNSCOPED_INFO("read past the end of the file"); REQUIRE(bytes == testdata1.length()); - UNSCOPED_INFO("LLFile::read() did not read correct data"); - REQUIRE_THAT(ll_test::Memory(testdata1.c_str(), (U32)bytes), ll_test::MemoryMatches(buffer, (U32)bytes)); + CHECK_THAT(ll_test::Memory(testdata1.c_str(), (U32)bytes), ll_test::MemoryMatches(buffer, (U32)bytes)); // Let's append more data bytes = LLFile::write(testfile1, testdata2.c_str(), -1, testdata2.length()); - UNSCOPED_INFO("LLFile::write() did not write correctly"); - REQUIRE(bytes == testdata2.length()); + UNSCOPED_INFO("append"); + CHECK(bytes == testdata2.length()); bytes = LLFile::size(testfile1); - UNSCOPED_INFO("LLFile::size() did not return the correct size"); + UNSCOPED_INFO("size after the append"); REQUIRE(bytes == testdata1.length() + testdata2.length()); bytes = LLFile::read(testfile1, buffer, 0, bytes); - UNSCOPED_INFO("LLFile:read() did not read correct number of bytes"); - REQUIRE(bytes == testdata1.length() + testdata2.length()); - UNSCOPED_INFO("LLFile:read() did not read correct testdata1"); - REQUIRE_THAT(ll_test::Memory(testdata1.c_str(), (U32)testdata1.length()), ll_test::MemoryMatches(buffer, (U32)testdata1.length())); - UNSCOPED_INFO("LLFile:read() did not read correct testdata2"); - REQUIRE_THAT(ll_test::Memory(testdata2.c_str(), (U32)testdata2.length()), ll_test::MemoryMatches(buffer + testdata1.length(), (U32)testdata2.length())); + UNSCOPED_INFO("read after the append"); + CHECK(bytes == testdata1.length() + testdata2.length()); + CHECK_THAT(ll_test::Memory(testdata1.c_str(), (U32)testdata1.length()), ll_test::MemoryMatches(buffer, (U32)testdata1.length())); + CHECK_THAT(ll_test::Memory(testdata2.c_str(), (U32)testdata2.length()), ll_test::MemoryMatches(buffer + testdata1.length(), (U32)testdata2.length())); } TEST_CASE_METHOD(llfile_test, "llfile_test: an LLFile writes reads seeks and tells", "[llfile_test]") @@ -211,77 +188,67 @@ namespace std::error_code ec; LLFile fileout(testfile, LLFile::out, ec); - UNSCOPED_INFO("LLFile constructor did not open correctly"); REQUIRE((bool)fileout); - UNSCOPED_INFO("error_code from LLFile constructor should not indicate an error"); - REQUIRE_FALSE(ec); + UNSCOPED_INFO("constructor"); + CHECK_FALSE(ec); if (fileout) { S64 length = fileout.size(ec); - UNSCOPED_INFO("freshly created file should be empty"); CHECK(length == 0); - UNSCOPED_INFO("error_code from LLFile::size() should not indicate an error"); - REQUIRE_FALSE(ec); + UNSCOPED_INFO("size"); + CHECK_FALSE(ec); S64 bytes = fileout.write(data, sizeof(data), ec); - UNSCOPED_INFO("LLFile::write() did not write correctly"); + UNSCOPED_INFO("write"); CHECK(bytes == sizeof(data)); - UNSCOPED_INFO("error_code from LLFile::write() should not indicate an error"); - REQUIRE_FALSE(ec); + UNSCOPED_INFO("write"); + CHECK_FALSE(ec); bytes = fileout.write(data, sizeof(data), ec); - UNSCOPED_INFO("LLFile::write() did not write correctly"); + UNSCOPED_INFO("second write"); CHECK(bytes == sizeof(data)); - UNSCOPED_INFO("error_code from LLFile::write() should not indicate an error"); - REQUIRE_FALSE(ec); + UNSCOPED_INFO("second write"); + CHECK_FALSE(ec); bytes = fileout.size(ec); - UNSCOPED_INFO("LLFile::size() returned wrong size"); + UNSCOPED_INFO("size after the writes"); CHECK(bytes == 2 * sizeof(data)); - UNSCOPED_INFO("error_code from LLFile::size() should not indicate an error"); - REQUIRE_FALSE(ec); + UNSCOPED_INFO("size after the writes"); + CHECK_FALSE(ec); fileout.close(); } LLFile filein(testfile, LLFile::in, ec); - UNSCOPED_INFO("LLFile constructor did not open correctly"); REQUIRE((bool)filein); - UNSCOPED_INFO("error_code from LLFile constructor should not indicate an error"); - REQUIRE_FALSE(ec); + UNSCOPED_INFO("constructor"); + CHECK_FALSE(ec); if (filein) { S64 length = filein.size(ec); - UNSCOPED_INFO("LLFile::size() returned wrong size"); - CHECK(length == 2 * sizeof(data)); - UNSCOPED_INFO("error_code from LLFile::size() should not indicate an error"); - REQUIRE_FALSE(ec); + REQUIRE(length == 2 * sizeof(data)); + UNSCOPED_INFO("size"); + CHECK_FALSE(ec); char* buffer = (char*)malloc(length); S64 bytes = filein.read(buffer, length, ec); - UNSCOPED_INFO("LLFile::read() did not read correctly"); + UNSCOPED_INFO("read"); CHECK(bytes == length); - UNSCOPED_INFO("error_code from LLFile::read() should not indicate an error"); - REQUIRE_FALSE(ec); - UNSCOPED_INFO("LLFile:read() did not read correct data1"); - REQUIRE_THAT(ll_test::Memory(data, (U32)sizeof(data)), ll_test::MemoryMatches(buffer, (U32)sizeof(data))); - UNSCOPED_INFO("LLFile:read() did not read correct data2"); - REQUIRE_THAT(ll_test::Memory(data, (U32)sizeof(data)), ll_test::MemoryMatches(buffer + sizeof(data), (U32)sizeof(data))); + UNSCOPED_INFO("read"); + CHECK_FALSE(ec); + CHECK_THAT(ll_test::Memory(data, (U32)sizeof(data)), ll_test::MemoryMatches(buffer, (U32)sizeof(data))); + CHECK_THAT(ll_test::Memory(data, (U32)sizeof(data)), ll_test::MemoryMatches(buffer + sizeof(data), (U32)sizeof(data))); S64 offset = filein.tell(ec); - UNSCOPED_INFO("LLFile::tell() returned a bad offset"); CHECK(offset == length); - UNSCOPED_INFO("error_code from LLFile::read() should not indicate an error"); - REQUIRE_FALSE(ec); + UNSCOPED_INFO("tell"); + CHECK_FALSE(ec); offset = sizeof(data) / 2; int rc = filein.seek(offset, ec); - UNSCOPED_INFO("LLFile::seek() indicated an error"); CHECK(rc == 0); - UNSCOPED_INFO("error_code from LLFile::seek() should not indicate an error"); - REQUIRE_FALSE(ec); + UNSCOPED_INFO("seek"); + CHECK_FALSE(ec); bytes = filein.read(buffer, 2 * sizeof(data), ec); - UNSCOPED_INFO("LLFile::read() did not read correctly"); + UNSCOPED_INFO("read from the middle"); CHECK(bytes == sizeof(data) + offset); - UNSCOPED_INFO("error_code from LLFile::read() should not indicate an error"); - REQUIRE_FALSE(ec); - UNSCOPED_INFO("LLFile:read() did not read correct data3"); - REQUIRE_THAT(ll_test::Memory((char*)data + offset, (U32)offset), ll_test::MemoryMatches(buffer, (U32)offset)); - UNSCOPED_INFO("LLFile:read() did not read correct data4"); - REQUIRE_THAT(ll_test::Memory((char*)data, (U32)sizeof(data)), ll_test::MemoryMatches(buffer + offset, (U32)sizeof(data))); + UNSCOPED_INFO("read from the middle"); + CHECK_FALSE(ec); + CHECK_THAT(ll_test::Memory((char*)data + offset, (U32)offset), ll_test::MemoryMatches(buffer, (U32)offset)); + CHECK_THAT(ll_test::Memory((char*)data, (U32)sizeof(data)), ll_test::MemoryMatches(buffer + offset, (U32)sizeof(data))); filein.close(); free(buffer); @@ -301,33 +268,29 @@ namespace std::error_code ec; LLFile file(testfile, LLFile::out | LLFile::noreplace, ec); - UNSCOPED_INFO("LLFile constructor should not have opened the already existing file"); - CHECK(!file); - UNSCOPED_INFO("error_code from LLFile constructor should indicate an error"); - REQUIRE((bool)ec); + UNSCOPED_INFO("noreplace on a file that exists"); + CHECK_FALSE(file); + CHECK((bool)ec); LLFile::remove(testfile); file = LLFile(testfile, LLFile::out | LLFile::app | LLFile::trunc, ec); - UNSCOPED_INFO("LLFile constructor should not have opened the file with conflicting flags"); - CHECK(!file); - UNSCOPED_INFO("error_code from LLFile constructor should indicate an error"); - REQUIRE((bool)ec); + UNSCOPED_INFO("app with trunc"); + CHECK_FALSE(file); + CHECK((bool)ec); file = LLFile(testfile, LLFile::out | LLFile::app | LLFile::noreplace, ec); - UNSCOPED_INFO("LLFile constructor should not have opened the file with conflicting flags"); - CHECK(!file); - UNSCOPED_INFO("error_code from LLFile constructor should indicate an error"); - REQUIRE((bool)ec); + UNSCOPED_INFO("app with noreplace"); + CHECK_FALSE(file); + CHECK((bool)ec); testfile = testdir; testfile.append("llfile_test"); testfile.append("llfile_test.bin"); file = LLFile(testfile, LLFile::in, ec); - UNSCOPED_INFO("LLFile constructor should not have been able to open the file in the non-existing directory"); - CHECK(!file); - UNSCOPED_INFO("error_code from LLFile constructor should indicate an error"); - REQUIRE((bool)ec); + UNSCOPED_INFO("a directory that does not exist"); + CHECK_FALSE(file); + CHECK((bool)ec); } TEST_CASE_METHOD(llfile_test, "llfile_test: mkdir write read and remove work with Unicode names", "[llfile_test]") @@ -342,68 +305,54 @@ namespace // Clean up any previous test artifacts clear_entire_dir(testdir_unicode); - UNSCOPED_INFO("Unicode test directory should not exist"); CHECK_FALSE(LLFile::exists(testdir_unicode)); // Create the Unicode directory int rc = LLFile::mkdir(testdir_unicode); - UNSCOPED_INFO("LLFile::mkdir() failed for Unicode directory"); + UNSCOPED_INFO("mkdir"); CHECK(rc == 0); - UNSCOPED_INFO("Unicode test directory should exist"); CHECK(LLFile::isdir(testdir_unicode)); - UNSCOPED_INFO("Unicode test file should not exist"); CHECK_FALSE(LLFile::exists(testfile_unicode)); // Write to the Unicode file const char* testdata = "unicode_testdata"; S64 bytes = LLFile::write(testfile_unicode, testdata, 0, strlen(testdata)); - UNSCOPED_INFO("Unicode test file should exist"); CHECK(LLFile::exists(testfile_unicode)); - UNSCOPED_INFO("Unicode test file should be a file"); CHECK(LLFile::isfile(testfile_unicode)); - UNSCOPED_INFO("LLFile::write() did not write correctly to Unicode file"); + UNSCOPED_INFO("write"); CHECK(bytes == (S64)strlen(testdata)); // Read back the data char buffer[64] = {}; bytes = LLFile::read(testfile_unicode, buffer, 0, sizeof(buffer)); - UNSCOPED_INFO("LLFile::read() did not read correctly from Unicode file"); - CHECK(bytes == (S64)strlen(testdata)); - UNSCOPED_INFO("LLFile::read() did not read correct Unicode data"); - REQUIRE_THAT(ll_test::Memory(testdata, (U32)bytes), ll_test::MemoryMatches(buffer, (U32)bytes)); + UNSCOPED_INFO("read"); + REQUIRE(bytes == (S64)strlen(testdata)); + CHECK_THAT(ll_test::Memory(testdata, (U32)bytes), ll_test::MemoryMatches(buffer, (U32)bytes)); // Remove the file and directory rc = LLFile::remove(testfile_unicode); - UNSCOPED_INFO("LLFile::remove() failed for Unicode file"); + UNSCOPED_INFO("remove of the file"); CHECK(rc == 0); - UNSCOPED_INFO("Unicode test file should not exist after removal"); CHECK_FALSE(LLFile::exists(testfile_unicode)); rc = LLFile::remove(testdir_unicode); - UNSCOPED_INFO("LLFile::remove() failed for Unicode directory"); + UNSCOPED_INFO("remove of the directory"); CHECK(rc == 0); - UNSCOPED_INFO("Unicode test directory should not exist after removal"); CHECK_FALSE(LLFile::exists(testdir_unicode)); } TEST_CASE_METHOD(llfile_test, "llfile_test: isSafeFileName accepts ordinary file names", "[llfile_test]") { // Ordinary file names pass isSafeFileName() - UNSCOPED_INFO("xml"); CHECK(LLFile::isSafeFileName("lsl_keywords.xml")); - UNSCOPED_INFO("two extensions"); CHECK(LLFile::isSafeFileName("secondlife.d.luau")); - UNSCOPED_INFO("no extension"); CHECK(LLFile::isSafeFileName("keywords")); - UNSCOPED_INFO("inner dots"); CHECK(LLFile::isSafeFileName("a..b")); - UNSCOPED_INFO("inner space"); CHECK(LLFile::isSafeFileName("my file.txt")); UNSCOPED_INFO("hidden"); CHECK(LLFile::isSafeFileName(".luaurc")); - UNSCOPED_INFO("leading dots"); CHECK(LLFile::isSafeFileName("..name")); UNSCOPED_INFO("unicode"); CHECK(LLFile::isSafeFileName("\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB.bin")); @@ -413,15 +362,10 @@ namespace { // Nothing, the directory itself or its parent, and names Windows would // shorten when creating them - UNSCOPED_INFO("empty"); CHECK_FALSE(LLFile::isSafeFileName("")); - UNSCOPED_INFO("dot"); CHECK_FALSE(LLFile::isSafeFileName(".")); - UNSCOPED_INFO("dot dot"); CHECK_FALSE(LLFile::isSafeFileName("..")); - UNSCOPED_INFO("three dots"); CHECK_FALSE(LLFile::isSafeFileName("...")); - UNSCOPED_INFO("trailing dot"); CHECK_FALSE(LLFile::isSafeFileName("name.")); UNSCOPED_INFO("trailing space"); CHECK_FALSE(LLFile::isSafeFileName("name.xml ")); @@ -430,21 +374,14 @@ namespace TEST_CASE_METHOD(llfile_test, "llfile_test: isSafeFileName refuses path separators of either platform", "[llfile_test]") { // Separators of either platform, at either end or inside - UNSCOPED_INFO("parent posix"); CHECK_FALSE(LLFile::isSafeFileName("../../evil.xml")); - UNSCOPED_INFO("parent windows"); CHECK_FALSE(LLFile::isSafeFileName("..\\..\\evil.xml")); - UNSCOPED_INFO("absolute posix"); CHECK_FALSE(LLFile::isSafeFileName("/etc/evil")); - UNSCOPED_INFO("absolute windows"); CHECK_FALSE(LLFile::isSafeFileName("\\Windows\\evil.dll")); UNSCOPED_INFO("unc"); CHECK_FALSE(LLFile::isSafeFileName("\\\\host\\share\\evil")); - UNSCOPED_INFO("subdirectory posix"); CHECK_FALSE(LLFile::isSafeFileName("sub/evil.xml")); - UNSCOPED_INFO("subdirectory windows"); CHECK_FALSE(LLFile::isSafeFileName("sub\\evil.xml")); - UNSCOPED_INFO("trailing separator"); CHECK_FALSE(LLFile::isSafeFileName("evil/")); } @@ -459,79 +396,51 @@ namespace CHECK_FALSE(LLFile::isSafeFileName("C:evil.dll")); UNSCOPED_INFO("stream"); CHECK_FALSE(LLFile::isSafeFileName("name.xml:evil")); - UNSCOPED_INFO("star"); CHECK_FALSE(LLFile::isSafeFileName("a*b")); - UNSCOPED_INFO("question"); CHECK_FALSE(LLFile::isSafeFileName("a?b")); - UNSCOPED_INFO("quote"); CHECK_FALSE(LLFile::isSafeFileName("a\"b")); - UNSCOPED_INFO("less"); CHECK_FALSE(LLFile::isSafeFileName("ab")); - UNSCOPED_INFO("pipe"); CHECK_FALSE(LLFile::isSafeFileName("a|b")); - UNSCOPED_INFO("tab"); CHECK_FALSE(LLFile::isSafeFileName("a\tb")); - UNSCOPED_INFO("newline"); CHECK_FALSE(LLFile::isSafeFileName("a\nb")); UNSCOPED_INFO("unit separator"); CHECK_FALSE(LLFile::isSafeFileName("a\x1F" "b")); - UNSCOPED_INFO("only nul"); CHECK_FALSE(LLFile::isSafeFileName(std::string(1, '\0'))); - UNSCOPED_INFO("inner nul"); CHECK_FALSE(LLFile::isSafeFileName(std::string("name.xml\0evil", 13))); - UNSCOPED_INFO("trailing nul"); CHECK_FALSE(LLFile::isSafeFileName(std::string("name.xml\0", 9))); } TEST_CASE_METHOD(llfile_test, "llfile_test: isSafeFileName allows up to 255 bytes", "[llfile_test]") { // The length limit is inclusive - UNSCOPED_INFO("at limit"); CHECK(LLFile::isSafeFileName(std::string(255, 'a'))); - UNSCOPED_INFO("over limit"); CHECK_FALSE(LLFile::isSafeFileName(std::string(256, 'a'))); } TEST_CASE_METHOD(llfile_test, "llfile_test: isSafeFileName refuses Windows device names", "[llfile_test]") { // Windows device names, in any case, with or without an extension - UNSCOPED_INFO("con"); CHECK_FALSE(LLFile::isSafeFileName("CON")); - UNSCOPED_INFO("prn"); CHECK_FALSE(LLFile::isSafeFileName("PRN")); - UNSCOPED_INFO("aux"); CHECK_FALSE(LLFile::isSafeFileName("AUX")); - UNSCOPED_INFO("nul"); CHECK_FALSE(LLFile::isSafeFileName("NUL")); - UNSCOPED_INFO("conin"); CHECK_FALSE(LLFile::isSafeFileName("CONIN$")); - UNSCOPED_INFO("conout"); CHECK_FALSE(LLFile::isSafeFileName("CONOUT$")); - UNSCOPED_INFO("com0"); CHECK_FALSE(LLFile::isSafeFileName("COM0")); - UNSCOPED_INFO("com9"); CHECK_FALSE(LLFile::isSafeFileName("COM9")); - UNSCOPED_INFO("lpt0"); CHECK_FALSE(LLFile::isSafeFileName("LPT0")); - UNSCOPED_INFO("lpt9"); CHECK_FALSE(LLFile::isSafeFileName("LPT9")); UNSCOPED_INFO("com superscript one"); CHECK_FALSE(LLFile::isSafeFileName("COM\xC2\xB9")); UNSCOPED_INFO("lpt superscript three"); CHECK_FALSE(LLFile::isSafeFileName("LPT\xC2\xB3")); - UNSCOPED_INFO("lower case"); CHECK_FALSE(LLFile::isSafeFileName("con")); - UNSCOPED_INFO("mixed case"); CHECK_FALSE(LLFile::isSafeFileName("cOm1")); - UNSCOPED_INFO("lower case conout"); CHECK_FALSE(LLFile::isSafeFileName("conout$")); - UNSCOPED_INFO("extension"); CHECK_FALSE(LLFile::isSafeFileName("NUL.xml")); - UNSCOPED_INFO("two extensions"); CHECK_FALSE(LLFile::isSafeFileName("nul.tar.gz")); UNSCOPED_INFO("superscript with extension"); CHECK_FALSE(LLFile::isSafeFileName("com\xC2\xB2.txt")); @@ -542,23 +451,16 @@ namespace TEST_CASE_METHOD(llfile_test, "llfile_test: isSafeFileName accepts names that only look like devices", "[llfile_test]") { // Names that only look like devices are files - UNSCOPED_INFO("longer word"); CHECK(LLFile::isSafeFileName("CONSOLE.xml")); - UNSCOPED_INFO("two digits"); CHECK(LLFile::isSafeFileName("COM10")); - UNSCOPED_INFO("no digit"); CHECK(LLFile::isSafeFileName("LPT")); - UNSCOPED_INFO("device prefix"); CHECK(LLFile::isSafeFileName("nul_keywords.xml")); - UNSCOPED_INFO("device suffix"); CHECK(LLFile::isSafeFileName("icon.xml")); - UNSCOPED_INFO("device extension"); CHECK(LLFile::isSafeFileName("keywords.con")); UNSCOPED_INFO("leading space"); CHECK(LLFile::isSafeFileName(" CON")); UNSCOPED_INFO("only spaces before extension"); CHECK(LLFile::isSafeFileName(" .xml")); - UNSCOPED_INFO("hidden device"); CHECK(LLFile::isSafeFileName(".con")); } } // anonymous namespace diff --git a/indra/llcommon/tests/llframetimer_test.cpp b/indra/llcommon/tests/llframetimer_test.cpp index 73f9466fb3..8461502b13 100644 --- a/indra/llcommon/tests/llframetimer_test.cpp +++ b/indra/llcommon/tests/llframetimer_test.cpp @@ -49,7 +49,6 @@ namespace LLFrameTimer timer; timer.setExpiryAt(seconds_since_epoch); F64 expires_at = timer.expiresAt(); - UNSCOPED_INFO("set expiry matches get expiry"); CHECK_THAT(expires_at, ll_test::WithinDistance(seconds_since_epoch, 0.001)); @@ -62,14 +61,12 @@ namespace LLFrameTimer timer; timer.setExpiryAt(seconds_since_epoch); F64 expires_at = timer.expiresAt(); - UNSCOPED_INFO("set expiry matches get expiry 1"); CHECK_THAT(expires_at, ll_test::WithinDistance(seconds_since_epoch, 0.001)); seconds_since_epoch += 10.0; timer.setExpiryAt(seconds_since_epoch); expires_at = timer.expiresAt(); - UNSCOPED_INFO("set expiry matches get expiry 2"); CHECK_THAT(expires_at, ll_test::WithinDistance(seconds_since_epoch, 0.001)); @@ -101,7 +98,6 @@ namespace LLFrameTimer::updateFrameTime(); iterations_until_expiration++; } - UNSCOPED_INFO("timer took too long to expire"); CHECK(iterations_until_expiration <= 10); } @@ -131,7 +127,6 @@ namespace LLFrameTimer::updateFrameTime(); const F32 ran = timer.getElapsedTimeF32(); timer.stop(); - UNSCOPED_INFO("stopped"); CHECK_FALSE(timer.getStarted()); UNSCOPED_INFO("the time it ran"); CHECK_THAT(timer.getElapsedTimeF32(), ll_test::WithinBits(ran, 12)); @@ -154,7 +149,6 @@ namespace LLFrameTimer::updateFrameTime(); timer.pause(); timer.reset(); - UNSCOPED_INFO("still paused"); CHECK_FALSE(timer.getStarted()); UNSCOPED_INFO("no time run"); CHECK_THAT(timer.getElapsedTimeF32(), ll_test::WithinBits(0.f, 12)); @@ -167,7 +161,6 @@ namespace CHECK_THAT(timer.getElapsedTimeF32(), ll_test::WithinBits(0.f, 12)); timer.pause(); timer.start(); - UNSCOPED_INFO("started"); CHECK(timer.getStarted()); UNSCOPED_INFO("start() runs from zero, paused or not"); CHECK_THAT(timer.getElapsedTimeF32(), ll_test::WithinBits(0.f, 12)); diff --git a/indra/llcommon/tests/llhttpdate_test.cpp b/indra/llcommon/tests/llhttpdate_test.cpp index 1443a58f85..76c20afb7b 100644 --- a/indra/llcommon/tests/llhttpdate_test.cpp +++ b/indra/llcommon/tests/llhttpdate_test.cpp @@ -53,7 +53,6 @@ namespace { static std::string epoch_expected = "Thursday, 01 Jan 1970 00:00:00 GMT" ; - UNSCOPED_INFO("Check Epoch in RFC 1123"); CHECK(epoch_expected == some_date.asRFC1123()); } @@ -62,7 +61,6 @@ namespace static std::string expected = "Wednesday, 18 Jul 2007 22:17:24 GMT" ; some_date = LLDate(1184797044.037586); - UNSCOPED_INFO("Check some timestamp in RFC 1123"); CHECK(expected == some_date.asRFC1123()); } @@ -93,35 +91,33 @@ namespace strftime(expected, 255, "%A, %d %b %Y %H:%M:%S GMT", result); actual = some_date.asRFC1123(); // probably not a good idea to use strcmp but this is just a unit test - UNSCOPED_INFO("Current time in RFC 1123"); CHECK(std::string_view(expected) == std::string_view(actual.c_str())); } - // LC_TIME, LLStringUtil's locale and the one LLDate last set LC_TIME - // to, put back as they were however the test ends. LLDate sets LC_TIME - // itself when the locale it saw last changes, so one date formatted in - // the restored locale brings it up to date before LC_TIME is restored. - struct TimeLocaleRestore + // LC_TIME, LLStringUtil's locale and the one LLDate last set LC_TIME + // to, put back as they were however the test ends. LLDate sets LC_TIME + // itself when the locale it saw last changes, so one date formatted in + // the restored locale brings it up to date before LC_TIME is restored. + struct TimeLocaleRestore + { + std::string mLocale{ LLStringUtil::getLocale() }; + std::string mTimeLocale{ setlocale(LC_TIME, NULL) }; + + ~TimeLocaleRestore() { - std::string mLocale{ LLStringUtil::getLocale() }; - std::string mTimeLocale{ setlocale(LC_TIME, NULL) }; - - ~TimeLocaleRestore() - { - LLStringUtil::setLocale(mLocale); - struct tm any{}; - LLDate::toHTTPDateString(&any, "%Y"); - setlocale(LC_TIME, mTimeLocale.c_str()); - } - }; - - static void test_date_string(const std::string &locale, struct tm *t, - const std::string &fmt, const std::string &expected) + LLStringUtil::setLocale(mLocale); + struct tm any{}; + LLDate::toHTTPDateString(&any, "%Y"); + setlocale(LC_TIME, mTimeLocale.c_str()); + } + }; + + static void require_http_date_string(const std::string &locale, struct tm *t, + const std::string &fmt, const std::string &expected) { std::string result = LLDate::toHTTPDateString(t, fmt); LLStringUtil::toLower(result); - std::string label = std::string("toHTTPDateString - ") + locale; - UNSCOPED_INFO(label.c_str()); + CAPTURE(locale, fmt); REQUIRE(result == expected); } @@ -142,15 +138,15 @@ namespace } LLStringUtil::setLocale(en_locale); - test_date_string(en_locale, t, "%d %B %Y - %H:%M", "08 september 2009 - 01:40"); - test_date_string(en_locale, t, "%H", "01"); - test_date_string(en_locale, t, "%M", "40"); - test_date_string(en_locale, t, "%I", "01"); - test_date_string(en_locale, t, "%d", "08"); - test_date_string(en_locale, t, "%Y", "2009"); - test_date_string(en_locale, t, "%p", "am"); - test_date_string(en_locale, t, "%A", "tuesday"); - test_date_string(en_locale, t, "%B", "september"); + require_http_date_string(en_locale, t, "%d %B %Y - %H:%M", "08 september 2009 - 01:40"); + require_http_date_string(en_locale, t, "%H", "01"); + require_http_date_string(en_locale, t, "%M", "40"); + require_http_date_string(en_locale, t, "%I", "01"); + require_http_date_string(en_locale, t, "%d", "08"); + require_http_date_string(en_locale, t, "%Y", "2009"); + require_http_date_string(en_locale, t, "%p", "am"); + require_http_date_string(en_locale, t, "%A", "tuesday"); + require_http_date_string(en_locale, t, "%B", "september"); setlocale(LC_TIME, fr_locale); if (strcmp(setlocale(LC_TIME, NULL), fr_locale) != 0) @@ -159,13 +155,13 @@ namespace } LLStringUtil::setLocale(fr_locale); - test_date_string(fr_locale, t, "%d %B %Y - %H:%M", "08 septembre 2009 - 01:40"); - test_date_string(fr_locale, t, "%H", "01"); - test_date_string(fr_locale, t, "%M", "40"); - test_date_string(fr_locale, t, "%I", "01"); - test_date_string(fr_locale, t, "%d", "08"); - test_date_string(fr_locale, t, "%Y", "2009"); - test_date_string(fr_locale, t, "%A", "mardi"); - test_date_string(fr_locale, t, "%B", "septembre"); + require_http_date_string(fr_locale, t, "%d %B %Y - %H:%M", "08 septembre 2009 - 01:40"); + require_http_date_string(fr_locale, t, "%H", "01"); + require_http_date_string(fr_locale, t, "%M", "40"); + require_http_date_string(fr_locale, t, "%I", "01"); + require_http_date_string(fr_locale, t, "%d", "08"); + require_http_date_string(fr_locale, t, "%Y", "2009"); + require_http_date_string(fr_locale, t, "%A", "mardi"); + require_http_date_string(fr_locale, t, "%B", "septembre"); } } diff --git a/indra/llcommon/tests/llinitparam_test.cpp b/indra/llcommon/tests/llinitparam_test.cpp index 3a7ee1a2cb..41e2737fe3 100644 --- a/indra/llcommon/tests/llinitparam_test.cpp +++ b/indra/llcommon/tests/llinitparam_test.cpp @@ -129,10 +129,10 @@ struct ChoiceOfThings : public LLInitParam::ChoiceBlock {} }; -// The same, built only by test 9. A choice block starts with its first -// alternative chosen only when it is the first of its type, the one whose -// construction builds the type's descriptor: with a type of its own, test 9 -// sees that whatever ran before it. +// The same, built only by the case on choosing one alternative. A choice block +// starts with its first alternative chosen only when it is the first of its +// type, the one whose construction builds the type's descriptor: with a type of +// its own, that case sees that whatever ran before it. struct FirstChoiceOfThings : public LLInitParam::ChoiceBlock { Alternative number; @@ -241,15 +241,10 @@ namespace { SimpleBlock block; - UNSCOPED_INFO("nothing provided yet"); CHECK_FALSE(block.name.isProvided()); - UNSCOPED_INFO("nothing provided yet"); CHECK_FALSE(block.count.isProvided()); - UNSCOPED_INFO("declared default survives"); CHECK(block.count() == 7); - UNSCOPED_INFO("declared default survives"); - CHECK(block.flag() == false); - UNSCOPED_INFO("block itself is not provided"); + CHECK_FALSE(block.flag()); CHECK_FALSE(block.isProvided()); } @@ -258,13 +253,9 @@ namespace SimpleBlock block; block.count = 42; - UNSCOPED_INFO("assignment provides"); CHECK(block.count.isProvided()); - UNSCOPED_INFO("assignment stores"); CHECK(block.count() == 42); - UNSCOPED_INFO("sibling untouched"); CHECK_FALSE(block.flag.isProvided()); - UNSCOPED_INFO("block became provided"); CHECK(block.isProvided()); } @@ -275,7 +266,6 @@ namespace CHECK_FALSE(block.validateBlock(false)); block.name = std::string("widget"); - UNSCOPED_INFO("supplied mandatory passes validation"); CHECK(block.validateBlock(false)); } @@ -285,9 +275,7 @@ namespace block.count = 3; // declared on SimpleBlock block.scale = 2.f; // declared on DerivedBlock - UNSCOPED_INFO("base parameter readable"); CHECK(block.count() == 3); - UNSCOPED_INFO("derived parameter readable"); CHECK(block.scale() == 2.f); // The base's Mandatory is aggregated into the derived table, so it @@ -295,32 +283,24 @@ namespace UNSCOPED_INFO("inherited mandatory still required"); CHECK_FALSE(block.validateBlock(false)); block.name = std::string("derived"); - UNSCOPED_INFO("inherited mandatory satisfied"); CHECK(block.validateBlock(false)); } TEST_CASE("llinitparam: named values resolve by name and by number", "[llinitparam]") { S32 value = 0; - UNSCOPED_INFO("known name resolves"); CHECK(TestNames::getValueFromName("two", value)); - UNSCOPED_INFO("known name resolves to its value"); CHECK(value == 2); - UNSCOPED_INFO("unknown name does not resolve"); CHECK_FALSE(TestNames::getValueFromName("nine", value)); SimpleBlock block; block.named = std::string("one"); - UNSCOPED_INFO("assignment by name sets the value"); CHECK(block.named() == 1); - UNSCOPED_INFO("assignment by name records the name"); CHECK(block.named.getValueName() == std::string("one")); block.named = 2; - UNSCOPED_INFO("assignment by value sets the value"); CHECK(block.named() == 2); - UNSCOPED_INFO("assignment by value clears the name"); - REQUIRE(block.named.getValueName().empty()); + CHECK(block.named.getValueName().empty()); } TEST_CASE("llinitparam: Multiple counts elements and enforces its range", "[llinitparam]") @@ -328,8 +308,7 @@ namespace OuterBlock block; UNSCOPED_INFO("empty list is below AtLeast<2>"); CHECK_FALSE(block.list.isValid()); - UNSCOPED_INFO("empty list has no elements"); - REQUIRE(block.list.size() == size_t(0)); + CHECK(block.list.size() == size_t(0)); block.list.add(10); UNSCOPED_INFO("one element is still below AtLeast<2>"); @@ -337,25 +316,19 @@ namespace block.list.add(20); UNSCOPED_INFO("two elements satisfy AtLeast<2>"); - CHECK(block.list.isValid()); - UNSCOPED_INFO("elements are kept in order"); + REQUIRE(block.list.isValid()); CHECK(block.list()[0]() == 10); - UNSCOPED_INFO("elements are kept in order"); CHECK(block.list()[1]() == 20); } TEST_CASE("llinitparam: a parameter that is a block is provided by its children", "[llinitparam]") { OuterBlock block; - UNSCOPED_INFO("untouched sub-block is not provided"); CHECK_FALSE(block.inner.isProvided()); block.inner.a = 5; - UNSCOPED_INFO("setting a child provides the sub-block"); CHECK(block.inner.isProvided()); - UNSCOPED_INFO("and provides the enclosing block"); CHECK(block.isProvided()); - UNSCOPED_INFO("child value readable"); CHECK(block.inner.a() == 5); UNSCOPED_INFO("sibling child keeps its default"); CHECK(block.inner.b() == 0); @@ -371,13 +344,11 @@ namespace destination.count = 1; destination.fillFrom(source); - UNSCOPED_INFO("fillFrom leaves a provided value alone"); CHECK(destination.count() == 1); UNSCOPED_INFO("fillFrom supplies an absent value"); - CHECK(destination.flag() == true); + CHECK(destination.flag()); destination.overwriteFrom(source); - UNSCOPED_INFO("overwriteFrom replaces a provided value"); CHECK(destination.count() == 100); } @@ -388,19 +359,13 @@ namespace CHECK(choice.number.isChosen()); choice.text = std::string("hello"); - UNSCOPED_INFO("assignment chooses"); CHECK(choice.text.isChosen()); - UNSCOPED_INFO("and unchooses the previous alternative"); CHECK_FALSE(choice.number.isChosen()); - UNSCOPED_INFO("the displaced alternative is no longer provided"); CHECK_FALSE(choice.number.isProvided()); - UNSCOPED_INFO("the chosen alternative reads back"); CHECK(choice.text() == std::string("hello")); choice.number = 3; - UNSCOPED_INFO("choosing back works"); CHECK(choice.number.isChosen()); - UNSCOPED_INFO("and displaces the other"); CHECK_FALSE(choice.text.isProvided()); } @@ -415,22 +380,16 @@ namespace LLParamSDParser parser; parser.writeSD(sd, written); - UNSCOPED_INFO("provided scalars are written"); REQUIRE(sd.has("count")); - UNSCOPED_INFO("written value is the one set"); CHECK(sd["count"].asInteger() == 55); SimpleBlock read; LLParamSDParser reader; reader.readSD(sd, read); - UNSCOPED_INFO("string survives"); CHECK(read.name() == std::string("round")); - UNSCOPED_INFO("integer survives"); CHECK(read.count() == 55); - UNSCOPED_INFO("boolean survives"); - CHECK(read.flag() == true); - UNSCOPED_INFO("reading marks parameters provided"); + CHECK(read.flag()); CHECK(read.count.isProvided()); } @@ -447,20 +406,15 @@ namespace LLParamSDParser parser; parser.writeSD(sd, written); - UNSCOPED_INFO("sub-block is written under its own name"); REQUIRE(sd.has("inner")); - UNSCOPED_INFO("sub-block child is written"); CHECK(sd["inner"]["a"].asInteger() == 11); OuterBlock read; LLParamSDParser reader; reader.readSD(sd, read); - UNSCOPED_INFO("nested value survives"); CHECK(read.inner.a() == 11); - UNSCOPED_INFO("nested value survives"); CHECK(read.inner.b() == 22); - UNSCOPED_INFO("list length survives"); REQUIRE(read.list.size() == size_t(3)); UNSCOPED_INFO("list order survives"); CHECK(read.list()[2]() == 3); @@ -475,16 +429,13 @@ namespace LLParamSDParser parser; parser.writeSD(sd, written); - UNSCOPED_INFO("the name is what gets written"); CHECK(sd["named"].asString() == std::string("two")); SimpleBlock read; LLParamSDParser reader; reader.readSD(sd, read); - UNSCOPED_INFO("the name resolves back to its value"); CHECK(read.named() == 2); - UNSCOPED_INFO("and the name is remembered"); CHECK(read.named.getValueName() == std::string("two")); } @@ -497,9 +448,7 @@ namespace LLParamSDParser parser; parser.readSD(sd, block); - UNSCOPED_INFO("the synonym provided the parameter"); CHECK(block.width.isProvided()); - UNSCOPED_INFO("through to the same storage"); CHECK(block.width() == 64); } @@ -513,7 +462,6 @@ namespace LLParamSDParser parser; parser.readSD(sd, block); - UNSCOPED_INFO("the live parameter is read"); CHECK(block.kept() == 5); UNSCOPED_INFO("the block is valid despite the ignored key"); CHECK(block.validateBlock(false)); @@ -523,19 +471,15 @@ namespace { PointBlock whole; whole.point.set(TestPoint(3, 4)); - UNSCOPED_INFO("whole assignment reaches the value"); CHECK(whole.point().mX == 3); - UNSCOPED_INFO("whole assignment reaches the value"); CHECK(whole.point().mY == 4); - UNSCOPED_INFO("and splats out to the components"); + UNSCOPED_INFO("whole assignment splats out to the components"); CHECK(whole.point.x() == 3); PointBlock components; components.point.x = 8; components.point.y = 9; - UNSCOPED_INFO("component assignment builds the value"); CHECK(components.point().mX == 8); - UNSCOPED_INFO("component assignment builds the value"); CHECK(components.point().mY == 9); } @@ -553,9 +497,7 @@ namespace LLParamSDParser reader; reader.readSD(sd, read); - UNSCOPED_INFO("x survives"); CHECK(read.point().mX == 12); - UNSCOPED_INFO("y survives"); CHECK(read.point().mY == 34); } @@ -564,14 +506,12 @@ namespace // Lazy<> keeps its block on the heap rather than deriving from it, so // an untouched one costs a pointer and serialises to nothing at all. OuterBlock untouched; - UNSCOPED_INFO("lazy sub-block starts unprovided"); CHECK_FALSE(untouched.lazy_inner.isProvided()); LLSD empty_sd; LLParamSDParser empty_parser; empty_parser.writeSD(empty_sd, untouched); - UNSCOPED_INFO("an unfilled lazy block writes nothing"); - REQUIRE_FALSE(empty_sd.has("lazy_inner")); + CHECK_FALSE(empty_sd.has("lazy_inner")); LLSD sd; sd["lazy_inner"]["a"] = 77; @@ -580,9 +520,7 @@ namespace LLParamSDParser parser; parser.readSD(sd, filled); - UNSCOPED_INFO("parsing into it provides it"); CHECK(filled.lazy_inner.isProvided()); - UNSCOPED_INFO("and the value is reachable"); CHECK(filled.lazy_inner().a() == 77); } @@ -594,18 +532,13 @@ namespace SimpleBlock copy(original); - UNSCOPED_INFO("values copy"); CHECK(copy.count() == 99); - UNSCOPED_INFO("values copy"); CHECK(copy.name() == std::string("copy me")); - UNSCOPED_INFO("provided-ness copies"); CHECK(copy.count.isProvided()); - UNSCOPED_INFO("unprovided stays unprovided"); CHECK_FALSE(copy.flag.isProvided()); // The copy must own its storage: changing it must not disturb the original. copy.count = 1; - UNSCOPED_INFO("the original is untouched"); CHECK(original.count() == 99); } @@ -623,7 +556,7 @@ namespace LLInitParam::BlockDescriptor::getAllDescriptors(); UNSCOPED_INFO("every constructed block type registered"); - REQUIRE(all.size() >= 5); + CHECK(all.size() >= 5); size_t named_entries = 0; for (const LLInitParam::BlockDescriptor* descriptor : all) @@ -635,15 +568,12 @@ namespace // DerivedBlock aggregates SimpleBlock's four parameters on top of its // own, which is the duplication the census exists to measure. - UNSCOPED_INFO("a derived block carries its base's parameters too"); - REQUIRE(DerivedBlock::getBlockDescriptor().namedParams().size() == - SimpleBlock::getBlockDescriptor().namedParams().size() + 1); + CHECK(DerivedBlock::getBlockDescriptor().namedParams().size() == + SimpleBlock::getBlockDescriptor().namedParams().size() + 1); const std::string report = LLInitParam::BlockDescriptor::getStatsReport(); - UNSCOPED_INFO("the census reports something"); - REQUIRE_FALSE(report.empty()); - UNSCOPED_INFO("the census names what it counted"); - REQUIRE(report.find("block types registered") != std::string::npos); + CHECK_FALSE(report.empty()); + CHECK(report.find("block types registered") != std::string::npos); } TEST_CASE("llinitparam: merging reports whether it actually took anything", "[llinitparam]") @@ -653,24 +583,18 @@ namespace // one looked modified by a merge that moved nothing. OuterBlock empty_source; OuterBlock destination; - UNSCOPED_INFO("filling from an empty block changes nothing"); - REQUIRE_FALSE(destination.fillFrom(empty_source)); - UNSCOPED_INFO("and leaves the list alone"); - REQUIRE(destination.list.size() == size_t(0)); + CHECK_FALSE(destination.fillFrom(empty_source)); + CHECK(destination.list.size() == size_t(0)); OuterBlock source; source.list.add(1); source.list.add(2); - UNSCOPED_INFO("filling from a block with values does change it"); CHECK(destination.fillFrom(source)); - UNSCOPED_INFO("which arrive in the destination"); - REQUIRE(destination.list.size() == size_t(2)); + CHECK(destination.list.size() == size_t(2)); // Already-filled destination, still-empty source. - UNSCOPED_INFO("an empty source still changes nothing"); CHECK_FALSE(destination.fillFrom(empty_source)); - UNSCOPED_INFO("and does not disturb what is there"); - REQUIRE(destination.list.size() == size_t(2)); + CHECK(destination.list.size() == size_t(2)); } TEST_CASE("llinitparam: a value filling in its own components is not a choice", "[llinitparam]") @@ -688,9 +612,7 @@ namespace PointChoice choice; - UNSCOPED_INFO("nothing has been chosen yet"); CHECK_FALSE(choice.point.isChosen()); - UNSCOPED_INFO("nothing has been chosen yet"); CHECK_FALSE(choice.scalar.isChosen()); } @@ -698,19 +620,14 @@ namespace { PointChoice choice; choice.scalar = 5; - UNSCOPED_INFO("the provided alternative is the choice"); CHECK(choice.scalar.isChosen()); - UNSCOPED_INFO("and reads back"); CHECK(choice.scalar() == 5); // Reaching into the components of the other alternative counts as // providing it, the same way assigning the whole value would. choice.point.x = 3; - UNSCOPED_INFO("the later alternative takes over"); CHECK(choice.point.isChosen()); - UNSCOPED_INFO("and the earlier one steps down"); CHECK_FALSE(choice.scalar.isChosen()); - UNSCOPED_INFO("the component drives the value"); CHECK(choice.point().mX == 3); } @@ -727,11 +644,8 @@ namespace PointChoice destination; destination.fillFrom(source); - UNSCOPED_INFO("the source's choice carried over"); CHECK(destination.scalar.isChosen()); - UNSCOPED_INFO("the untouched alternative did not seize it"); CHECK_FALSE(destination.point.isChosen()); - UNSCOPED_INFO("and the value came with it"); CHECK(destination.scalar() == 5); } } diff --git a/indra/llcommon/tests/llinstancetracker_test.cpp b/indra/llcommon/tests/llinstancetracker_test.cpp index 7dfea355be..d3207614b4 100644 --- a/indra/llcommon/tests/llinstancetracker_test.cpp +++ b/indra/llcommon/tests/llinstancetracker_test.cpp @@ -75,9 +75,9 @@ struct Unkeyed: public LLInstanceTracker } // anonymous namespace /***************************************************************************** -* TUT +* Tests *****************************************************************************/ -namespace tut +namespace { TEST_CASE("llinstancetracker: Keyed instances are found by key and forgotten when destroyed", "[llinstancetracker]") { @@ -87,23 +87,21 @@ namespace tut CHECK(Keyed::instanceCount() == 1); auto found = Keyed::getInstance("one"); UNSCOPED_INFO("couldn't find stack Keyed"); - REQUIRE(bool(found)); - UNSCOPED_INFO("found wrong Keyed instance"); - REQUIRE(found.get() == &one); + CHECK(bool(found)); + CHECK(found.get() == &one); { std::unique_ptr two(new Keyed("two")); CHECK(Keyed::instanceCount() == 2); auto found = Keyed::getInstance("two"); UNSCOPED_INFO("couldn't find heap Keyed"); - REQUIRE(bool(found)); - UNSCOPED_INFO("found wrong Keyed instance"); - REQUIRE(found.get() == two.get()); + CHECK(bool(found)); + CHECK(found.get() == two.get()); } CHECK(Keyed::instanceCount() == 1); } auto found = Keyed::getInstance("one"); UNSCOPED_INFO("Keyed key lives too long"); - REQUIRE_FALSE(found); + CHECK_FALSE(found); CHECK(Keyed::instanceCount() == 0); } @@ -125,7 +123,6 @@ namespace tut dangling = found; } // make that instance vanish // check the now-invalid pointer to the destroyed instance - UNSCOPED_INFO("weak_ptr failed to track destruction"); CHECK(dangling.expired()); CHECK(Unkeyed::instanceCount() == 0); } @@ -148,7 +145,7 @@ namespace tut // Use ensure() here because ensure_equals would want to display // mismatched values, and frankly that wouldn't help much. UNSCOPED_INFO("didn't reach end"); - REQUIRE(ki == keys.end()); + CHECK(ki == keys.end()); // Use a somewhat different approach to order independence with // instance_snapshot(): explicitly capture the instances we know in a @@ -164,7 +161,7 @@ namespace tut REQUIRE(instances.erase(&ref) == 1); } UNSCOPED_INFO("unreported instance"); - REQUIRE(instances.size() == 0); + CHECK(instances.size() == 0); } TEST_CASE("llinstancetracker: instance_snapshot lists each Unkeyed once", "[llinstancetracker]") @@ -184,7 +181,7 @@ namespace tut } UNSCOPED_INFO("unreported instance"); - REQUIRE(instances.size() == 0); + CHECK(instances.size() == 0); } TEST_CASE("llinstancetracker: delete Keyed with outstanding instance_snapshot", "[llinstancetracker]") @@ -198,7 +195,7 @@ namespace tut // traversing the snapshot should reflect the deletion // avoid ensure_equals() because it requires the ability to stream the // two values to std::ostream - REQUIRE(snapshot.begin() == snapshot.end()); + CHECK(snapshot.begin() == snapshot.end()); } TEST_CASE("llinstancetracker: delete Keyed with outstanding key_snapshot", "[llinstancetracker]") @@ -212,7 +209,7 @@ namespace tut // traversing the snapshot should reflect the deletion // avoid ensure_equals() because it requires the ability to stream the // two values to std::ostream - REQUIRE(snapshot.begin() == snapshot.end()); + CHECK(snapshot.begin() == snapshot.end()); } TEST_CASE("llinstancetracker: delete Unkeyed with outstanding instance_snapshot", "[llinstancetracker]") @@ -226,7 +223,7 @@ namespace tut // traversing the snapshot should reflect the deletion // avoid ensure_equals() because it requires the ability to stream the // two values to std::ostream - REQUIRE(snapshot.begin() == snapshot.end()); + CHECK(snapshot.begin() == snapshot.end()); } TEST_CASE("llinstancetracker: a throwing subclass constructor leaves no instance behind", "[llinstancetracker]") @@ -267,4 +264,4 @@ namespace tut REQUIRE(existing.find(&ref) != existing.end()); } } -} // namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp index b3e3194c54..394f4e4efc 100644 --- a/indra/llcommon/tests/llleap_test.cpp +++ b/indra/llcommon/tests/llleap_test.cpp @@ -78,9 +78,8 @@ static void waitfor(const LLLeapVector& instances, int timeout=60) sleep(1); LLEventPumps::instance().obtain("mainloop").post(LLSD()); } - UNSCOPED_INFO((STRINGIZE("at least 1 of " << instances.size() - << " LLLeap instances timed out (" - << timeout << " seconds) without terminating"))); + INFO("at least 1 of " << instances.size() + << " LLLeap instances timed out (" << timeout << " seconds) without terminating"); REQUIRE(i < timeout); } @@ -240,7 +239,6 @@ namespace tut CaptureLog log(LLError::LEVEL_WARN); waitfor(LLLeap::create(ll_test::currentTestName(), StringVec{PYTHON, script.getPath().string()})); - UNSCOPED_INFO("error log line"); CHECK_THAT(log.messageWith("invalid protocol"), Catch::Matchers::ContainsSubstring("Hello from Python!")); } @@ -253,7 +251,6 @@ namespace tut CaptureLog log(LLError::LEVEL_WARN); waitfor(LLLeap::create(ll_test::currentTestName(), StringVec{PYTHON, script.getPath().string()})); - UNSCOPED_INFO("error log line"); CHECK_THAT(log.messageWith("Discarding"), Catch::Matchers::ContainsSubstring("Hello from Python!")); } @@ -265,7 +262,6 @@ namespace tut CaptureLog log(LLError::LEVEL_WARN); waitfor(LLLeap::create(ll_test::currentTestName(), StringVec{PYTHON, script.getPath().string()})); - UNSCOPED_INFO("error log line"); CHECK_THAT(log.messageWith("invalid protocol"), Catch::Matchers::ContainsSubstring("5a2:")); } @@ -274,11 +270,9 @@ namespace tut std::string threw = catch_what([](){ LLLeap::create("empty", StringVec()); }); - UNSCOPED_INFO("LLLeap::Error"); CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("no plugin")); // try the suppress-exception variant - UNSCOPED_INFO("bad launch returned non-NULL"); - REQUIRE_FALSE(LLLeap::create("empty", StringVec(), false)); + CHECK_FALSE(LLLeap::create("empty", StringVec(), false)); } TEST_CASE_METHOD(llleap_data, "llleap: create with a missing executable throws or returns null", "[llleap]") @@ -294,8 +288,7 @@ namespace tut log.messageWith("failed"); log.messageWith(BADPYTHON); // try the suppress-exception variant - UNSCOPED_INFO("bad launch returned non-NULL"); - REQUIRE_FALSE(LLLeap::create("bad exe", BADPYTHON, false)); + CHECK_FALSE(LLLeap::create("bad exe", BADPYTHON, false)); } namespace @@ -352,9 +345,9 @@ namespace tut return false; } - void ensure() const + void require_success() const { - UNSCOPED_INFO(std::string("never posted to ") + getName()); + UNSCOPED_INFO("never posted to " << getName()); REQUIRE(mData.isDefined()); // Post an empty string for success, non-empty string is failure message. UNSCOPED_INFO(mData); @@ -381,7 +374,7 @@ namespace tut "send(pump='" << result.getName() << "', data=result)\n";}); waitfor(LLLeap::create(ll_test::currentTestName(), StringVec{PYTHON, script.getPath().string()})); - result.ensure(); + result.require_success(); } namespace @@ -442,11 +435,11 @@ namespace tut "send(pump='" << result.getName() << "', data=result)\n";}); waitfor(LLLeap::create(ll_test::currentTestName(), StringVec{PYTHON, script.getPath().string()}), 300); // needs more realtime than most tests - result.ensure(); + result.require_success(); } - // This is the body of test<10>, extracted so we can run it over a number - // of large-message sizes. + // This is the body of the very large message test, extracted so we can + // run it over a number of large-message sizes. static void test_large_message(const std::string& PYTHON, const std::string& reader_module, const std::string& test_name, size_t size) { @@ -509,7 +502,7 @@ namespace tut waitfor(LLLeap::create(test_name, StringVec{PYTHON, script.getPath().string(), stringize(size)}), 180); // try a longer timeout - result.ensure(); + result.require_success(); } namespace diff --git a/indra/llcommon/tests/llmainthreadtask_test.cpp b/indra/llcommon/tests/llmainthreadtask_test.cpp index f8885bf3c0..b0b09e1f66 100644 --- a/indra/llcommon/tests/llmainthreadtask_test.cpp +++ b/indra/llcommon/tests/llmainthreadtask_test.cpp @@ -50,20 +50,18 @@ namespace ran = true; return true; }); - UNSCOPED_INFO("didn't run lambda"); CHECK(ran); - UNSCOPED_INFO("didn't return result"); CHECK(result); } - // LockStatic finds its one instance a process by the type's name, even - // in an anonymous namespace, so the name is this test's own. - struct MainThreadTaskStatic - { - std::mutex mMutex; // LockStatic looks for mMutex - bool ran{false}; - }; - typedef llthread::LockStatic LockStatic; + // LockStatic finds its one instance a process by the type's name, even + // in an anonymous namespace, so the name is this test's own. + struct MainThreadTaskStatic + { + std::mutex mMutex; // LockStatic looks for mMutex + bool ran{false}; + }; + typedef llthread::LockStatic LockStatic; TEST_CASE_METHOD(llmainthreadtask_data, "llmainthreadtask: dispatch from another thread runs the task on the main thread", "[llmainthreadtask]") { @@ -73,7 +71,7 @@ namespace // exceptions it might throw and deliver them via future std::packaged_task thread_work( [this, &result](){ - // unblock test<2>()'s yield_until(1) + // unblock the test's yield_until(1) mSync.set(1); // dispatch work to main thread -- should block here bool on_main( @@ -84,7 +82,7 @@ namespace // indicate whether task was run on the main thread return on_main_thread(); })); - // wait for test<2>() to unblock us again + // wait for the test to unblock us again mSync.yield_until(3); result = on_main; }); @@ -132,7 +130,6 @@ namespace thread.join(); // deliver any exception thrown by thread_work thread_result.get(); - UNSCOPED_INFO("ran changed"); CHECK(LockStatic()->ran); UNSCOPED_INFO("didn't run on main thread"); CHECK(result); diff --git a/indra/llcommon/tests/llmutex_test.cpp b/indra/llcommon/tests/llmutex_test.cpp index b8a99cd56a..6fb5dc2725 100644 --- a/indra/llcommon/tests/llmutex_test.cpp +++ b/indra/llcommon/tests/llmutex_test.cpp @@ -40,25 +40,17 @@ namespace TEST_CASE_METHOD(llmutex_data, "llmutex: lock marks ownership and unlock clears it", "[llmutex]") { - UNSCOPED_INFO("unlocked mutex reports self-locked"); CHECK_FALSE(mMutex.isSelfLocked()); - UNSCOPED_INFO("unlocked mutex has an owner"); CHECK(mMutex.lockingThread() == LLThread::id_t()); mMutex.lock(); - UNSCOPED_INFO("locked mutex does not report self-locked"); CHECK(mMutex.isSelfLocked()); - UNSCOPED_INFO("locking thread not recorded"); CHECK(mMutex.lockingThread() == LLThread::currentID()); - UNSCOPED_INFO("isLocked() false while held"); CHECK(mMutex.isLocked()); mMutex.unlock(); - UNSCOPED_INFO("still self-locked after unlock"); CHECK_FALSE(mMutex.isSelfLocked()); - UNSCOPED_INFO("owner not cleared on unlock"); CHECK(mMutex.lockingThread() == LLThread::id_t()); - UNSCOPED_INFO("isLocked() true after unlock"); CHECK_FALSE(mMutex.isLocked()); } @@ -68,7 +60,6 @@ namespace mMutex.lock(); mMutex.lock(); - UNSCOPED_INFO("self-lock lost during recursion"); CHECK(mMutex.isSelfLocked()); mMutex.unlock(); @@ -79,23 +70,19 @@ namespace CHECK(mMutex.isLocked()); mMutex.unlock(); - UNSCOPED_INFO("not released after balanced unlocks"); CHECK_FALSE(mMutex.isLocked()); - UNSCOPED_INFO("still self-locked after balanced unlocks"); CHECK_FALSE(mMutex.isSelfLocked()); } TEST_CASE_METHOD(llmutex_data, "llmutex: trylock recurses on a self-held mutex", "[llmutex]") { mMutex.lock(); - UNSCOPED_INFO("trylock failed on a mutex this thread already holds"); CHECK(mMutex.trylock()); mMutex.unlock(); UNSCOPED_INFO("recursive trylock did not need a matching unlock"); CHECK(mMutex.isLocked()); mMutex.unlock(); - UNSCOPED_INFO("not released after balanced unlocks"); CHECK_FALSE(mMutex.isLocked()); } @@ -140,18 +127,14 @@ namespace { { LLMutexTrylock lock(&mMutex); - UNSCOPED_INFO("trylock scope failed on a free mutex"); CHECK(lock.isLocked()); - UNSCOPED_INFO("trylock scope did not take ownership"); CHECK(mMutex.isSelfLocked()); } - UNSCOPED_INFO("trylock scope did not release"); CHECK_FALSE(mMutex.isLocked()); // A null mutex is legal and locks nothing -- callers pass member pointers that // may not be constructed yet. LLMutexTrylock null_lock(nullptr); - UNSCOPED_INFO("null trylock scope claimed a lock"); CHECK_FALSE(null_lock.isLocked()); } diff --git a/indra/llcommon/tests/llpounceable_test.cpp b/indra/llcommon/tests/llpounceable_test.cpp index 6564768047..f847c3d28d 100644 --- a/indra/llcommon/tests/llpounceable_test.cpp +++ b/indra/llcommon/tests/llpounceable_test.cpp @@ -82,8 +82,7 @@ namespace // before LLPounceable's constructor runs. That's the whole point of // implementing it with an LLSingleton queue. This models (say) // LLPounceableStatic. - UNSCOPED_INFO("static_check should still be null"); - REQUIRE_FALSE(static_check); + CHECK_FALSE(static_check); Data myData("test<1>"); // gForward and static_check let go of myData with the test, however // it ends, rather than point past it at whatever runs next. @@ -96,8 +95,7 @@ namespace } } let_go; gForward = &myData; // should run setter - UNSCOPED_INFO("static_check should be &myData"); - REQUIRE(static_check == &myData); + CHECK(static_check == &myData); } TEST_CASE("llpounceable: each LLPounceableQueue instance has its own queue", "[llpounceable]") @@ -109,20 +107,15 @@ namespace LLPounceable a, b; a.callWhenReady(boost::bind(setter, &aptr, _1)); b.callWhenReady(boost::bind(setter, &bptr, _1)); - UNSCOPED_INFO("aptr should be null"); - REQUIRE_FALSE(aptr); - UNSCOPED_INFO("bptr should be null"); - REQUIRE_FALSE(bptr); + CHECK_FALSE(aptr); + CHECK_FALSE(bptr); Data adata("a"), bdata("b"); a = &adata; - UNSCOPED_INFO("aptr should be &adata"); - REQUIRE(aptr == &adata); + CHECK(aptr == &adata); // but we haven't yet set b - UNSCOPED_INFO("bptr should still be null"); - REQUIRE_FALSE(bptr); + CHECK_FALSE(bptr); b = &bdata; - UNSCOPED_INFO("bptr should be &bdata"); - REQUIRE(bptr == &bdata); + CHECK(bptr == &bdata); } TEST_CASE("llpounceable: each LLPounceableStatic instance has its own queue", "[llpounceable]") @@ -134,20 +127,15 @@ namespace LLPounceable a, b; a.callWhenReady(boost::bind(setter, &aptr, _1)); b.callWhenReady(boost::bind(setter, &bptr, _1)); - UNSCOPED_INFO("aptr should be null"); - REQUIRE_FALSE(aptr); - UNSCOPED_INFO("bptr should be null"); - REQUIRE_FALSE(bptr); + CHECK_FALSE(aptr); + CHECK_FALSE(bptr); Data adata("a"), bdata("b"); a = &adata; - UNSCOPED_INFO("aptr should be &adata"); - REQUIRE(aptr == &adata); + CHECK(aptr == &adata); // but we haven't yet set b - UNSCOPED_INFO("bptr should still be null"); - REQUIRE_FALSE(bptr); + CHECK_FALSE(bptr); b = &bdata; - UNSCOPED_INFO("bptr should be &bdata"); - REQUIRE(bptr == &bdata); + CHECK(bptr == &bdata); } TEST_CASE("llpounceable: LLPounceable looks like T", "[llpounceable]") @@ -162,18 +150,13 @@ namespace LLPounceable pounceable(&a); // should be able to pass LLPounceable to function accepting T setter(&aptr, pounceable); - UNSCOPED_INFO("aptr should be &a"); - REQUIRE(aptr == &a); + CHECK(aptr == &a); // should be able to dereference with * - UNSCOPED_INFO("deref with *"); CHECK((*pounceable).mData == "a"); // should be able to dereference with -> - UNSCOPED_INFO("deref with ->"); CHECK(pounceable->mData == "a"); // bool operations - UNSCOPED_INFO("test with operator bool()"); - REQUIRE(pounceable); - UNSCOPED_INFO("test with operator !()"); + CHECK(pounceable); CHECK_FALSE(! pounceable); } @@ -186,42 +169,26 @@ namespace pounceable.callWhenReady(boost::bind(setter, &p1, _1)); pounceable.callWhenReady(boost::bind(setter, &p2, _1)); // should still be pending - UNSCOPED_INFO("p1 should be null"); - REQUIRE_FALSE(p1); - UNSCOPED_INFO("p2 should be null"); - REQUIRE_FALSE(p2); - UNSCOPED_INFO("p3 should be null"); - REQUIRE_FALSE(p3); + CHECK_FALSE(p1); + CHECK_FALSE(p2); + CHECK_FALSE(p3); pounceable = 0; // assigning a new empty value shouldn't flush the queue - UNSCOPED_INFO("p1 should still be null"); - REQUIRE_FALSE(p1); - UNSCOPED_INFO("p2 should still be null"); - REQUIRE_FALSE(p2); - UNSCOPED_INFO("p3 should still be null"); - REQUIRE_FALSE(p3); + CHECK_FALSE(p1); + CHECK_FALSE(p2); + CHECK_FALSE(p3); // using whichever syntax pounceable.reset(0); - // try to make ensure messages distinct... tough to pin down which - // ensure() failed if multiple ensure() calls in the same test have - // the same message! - UNSCOPED_INFO("p1 should again be null"); - REQUIRE_FALSE(p1); - UNSCOPED_INFO("p2 should again be null"); - REQUIRE_FALSE(p2); - UNSCOPED_INFO("p3 should again be null"); - REQUIRE_FALSE(p3); + CHECK_FALSE(p1); + CHECK_FALSE(p2); + CHECK_FALSE(p3); pounceable.reset(&a); // should flush queue - UNSCOPED_INFO("p1 should be &a"); - REQUIRE(p1 == &a); - UNSCOPED_INFO("p2 should be &a"); - REQUIRE(p2 == &a); - UNSCOPED_INFO("p3 still not set"); - REQUIRE_FALSE(p3); + CHECK(p1 == &a); + CHECK(p2 == &a); + CHECK_FALSE(p3); // immediate call pounceable.callWhenReady(boost::bind(setter, &p3, _1)); - UNSCOPED_INFO("p3 should be &a"); - REQUIRE(p3 == &a); + CHECK(p3 == &a); } TEST_CASE("llpounceable: queued calls run in order and firing empties the queue", "[llpounceable]") diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp index f609fd0b5b..c8aa57c239 100644 --- a/indra/llcommon/tests/llprocess_test.cpp +++ b/indra/llcommon/tests/llprocess_test.cpp @@ -58,25 +58,23 @@ static std::string apr_strerror_helper(apr_status_t rv) * Helpers *****************************************************************************/ -#define ensure_equals_(left, right) \ +#define require_equals_(left, right) \ do { \ auto _left_val = (left); \ auto _right_val = (right); \ if (_left_val != _right_val) { \ - std::string _msg = std::string(#left) + " != " + std::string(#right); \ - UNSCOPED_INFO(_msg); REQUIRE_THAT(_left_val, ll_test::LLSDEquals(_right_val)); \ + UNSCOPED_INFO(#left " != " #right); REQUIRE_THAT(_left_val, ll_test::LLSDEquals(_right_val)); \ } else { \ REQUIRE_THAT(_left_val, ll_test::LLSDEquals(_right_val)); \ } \ } while(0) -#define aprchk(expr) aprchk_(#expr, (expr)) -static void aprchk_(const char* call, apr_status_t rv, apr_status_t expected=APR_SUCCESS) +#define require_apr(expr) require_apr_(#expr, (expr)) +static void require_apr_(const char* call, apr_status_t rv, apr_status_t expected=APR_SUCCESS) { if (rv != expected) { - std::string msg = std::string(call) + " => " + std::to_string(rv) + ": " + apr_strerror_helper(rv); - UNSCOPED_INFO(msg); + UNSCOPED_INFO(call << " => " << rv << ": " << apr_strerror_helper(rv)); REQUIRE_THAT(rv, ll_test::LLSDEquals(expected)); } else @@ -105,8 +103,7 @@ static std::string readfile(const std::filesystem::path& pathname, const std::st std::string output; if (!std::getline(inf, output)) { - UNSCOPED_INFO("No output " + use_desc); - REQUIRE(false); + FAIL("No output " << use_desc); } std::string more; while (std::getline(inf, more)) @@ -151,8 +148,7 @@ static void waitfor(LLProcess& proc, int timeout=60) } // Pump once more after the process exits to flush any final events such as EOF. yield(0); - std::string msg = "process took longer than " + std::to_string(timeout) + " seconds to terminate"; - UNSCOPED_INFO(msg); + INFO("process took longer than " << timeout << " seconds to terminate"); REQUIRE(i < timeout); } @@ -165,8 +161,7 @@ static void waitfor(LLProcess::handle h, const std::string& desc, int timeout=60 } // Pump once more after the process exits to flush any final events such as EOF. yield(0); - std::string msg = "process took longer than " + std::to_string(timeout) + " seconds to terminate"; - UNSCOPED_INFO(msg); + INFO("process took longer than " << timeout << " seconds to terminate"); REQUIRE(i < timeout); } @@ -204,8 +199,7 @@ struct PythonProcessLauncher try { mPy = LLProcess::create(mParams); - std::string msg = "Couldn't launch " + mDesc + " script"; - UNSCOPED_INFO(msg); + UNSCOPED_INFO("Couldn't launch " << mDesc << " script"); REQUIRE(bool(mPy)); } catch (const ll_test::Failure&) @@ -433,10 +427,7 @@ namespace Sleep(AUTOKILL_HELPER_POLL_INTERVAL_MS); LLEventPumps::instance().obtain("mainloop").post(LLSD()); } - UNSCOPED_INFO((STRINGIZE("expected " << expected_count - << " child pids within " << timeout - << " seconds"))); - REQUIRE(false); + FAIL("expected " << expected_count << " child pids within " << timeout << " seconds"); return {}; } @@ -457,7 +448,6 @@ namespace params.executable = get_current_executable_path(); params.desc = desc + " helper"; LLProcessPtr helper = LLProcess::create(params); - UNSCOPED_INFO("helper launched"); REQUIRE(bool(helper)); std::vector pids = wait_for_helper_pids(pidfile.getPath().string(), child_count); @@ -468,7 +458,7 @@ namespace // SYNCHRONIZE lets the test wait for the child to terminate, while // PROCESS_QUERY_LIMITED_INFORMATION keeps the requested access minimal. HANDLE handle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION, false, pid); - UNSCOPED_INFO((STRINGIZE("opened child process handle for pid " << pid))); + CAPTURE(pid); REQUIRE(handle != nullptr); handles.push_back(handle); } @@ -479,8 +469,7 @@ namespace } waitfor(*helper); - UNSCOPED_INFO("helper exited"); - REQUIRE(helper->getStatus().mState == LLProcess::EXITED); + CHECK(helper->getStatus().mState == LLProcess::EXITED); for (HANDLE handle : handles) { @@ -685,9 +674,9 @@ namespace tut // Run the child process. apr_procattr_t *procattr = NULL; - aprchk(apr_procattr_create(&procattr, pool.getAPRPool())); - aprchk(apr_procattr_io_set(procattr, APR_CHILD_BLOCK, APR_CHILD_BLOCK, APR_CHILD_BLOCK)); - aprchk(apr_procattr_cmdtype_set(procattr, APR_PROGRAM_PATH)); + require_apr(apr_procattr_create(&procattr, pool.getAPRPool())); + require_apr(apr_procattr_io_set(procattr, APR_CHILD_BLOCK, APR_CHILD_BLOCK, APR_CHILD_BLOCK)); + require_apr(apr_procattr_cmdtype_set(procattr, APR_PROGRAM_PATH)); std::vector argv; apr_proc_t child; @@ -701,11 +690,11 @@ namespace tut argv.push_back(scriptname.c_str()); argv.push_back(NULL); - aprchk(apr_proc_create(&child, argv[0], - &argv[0], - NULL, // if we wanted to pass explicit environment - procattr, - pool.getAPRPool())); + require_apr(apr_proc_create(&child, argv[0], + &argv[0], + NULL, // if we wanted to pass explicit environment + procattr, + pool.getAPRPool())); // We do not want this child process to outlive our APR pool. On // destruction of the pool, forcibly kill the process. Tell APR to try @@ -763,7 +752,7 @@ namespace tut ++history.back().tries; continue; } - aprchk_("apr_file_gets(buf, sizeof(buf), dfli->second)", rv); + require_apr_("apr_file_gets(buf, sizeof(buf), dfli->second)", rv); // Is it even possible to get APR_SUCCESS but read 0 bytes? // Hope not, but defend against that anyway. if (buf[0]) @@ -805,35 +794,35 @@ namespace tut wi.rv = apr_proc_wait(wi.child, &wi.rc, &wi.why, APR_NOWAIT); } // 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); + require_apr_("apr_proc_wait(wi->child, &wi->rc, &wi->why, APR_NOWAIT)", wi.rv, APR_CHILD_DONE); // 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 + // Perform these require_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); + require_equals_(wi.why, APR_PROC_EXIT); + require_equals_(wi.rc, 0); unsigned i = 0; UNSCOPED_INFO("blocking I/O on child pipe (0)"); CHECK(history[i].tries); - ensure_equals_(history[i].which, "out"); - ensure_equals_(history[i].what, "stdout after wait" EOL); + require_equals_(history[i].which, "out"); + require_equals_(history[i].what, "stdout after wait" EOL); // ++i; -// ensure_equals_(history[i].which, "out"); -// ensure_equals_(history[i].what, "*eof*"); +// require_equals_(history[i].which, "out"); +// require_equals_(history[i].what, "*eof*"); ++i; UNSCOPED_INFO("blocking I/O on child pipe (1)"); CHECK(history[i].tries); - ensure_equals_(history[i].which, "err"); - ensure_equals_(history[i].what, "stderr after wait" EOL); + require_equals_(history[i].which, "err"); + require_equals_(history[i].what, "stderr after wait" EOL); // ++i; -// ensure_equals_(history[i].which, "err"); -// ensure_equals_(history[i].what, "*eof*"); +// require_equals_(history[i].which, "err"); +// require_equals_(history[i].what, "*eof*"); } catch (const ll_test::Failure&) { @@ -901,20 +890,20 @@ namespace tut boost::split_iterator li(output, boost::first_finder("\n")), lend; UNSCOPED_INFO("didn't get first arg"); - CHECK(li != lend); + REQUIRE(li != lend); std::string arg(li->begin(), li->end()); CHECK(arg == "first arg"); ++li; UNSCOPED_INFO("didn't get second arg"); - CHECK(li != lend); + REQUIRE(li != lend); arg.assign(li->begin(), li->end()); CHECK(arg == "second arg"); ++li; UNSCOPED_INFO("didn't get output filename?!"); - CHECK(li != lend); + REQUIRE(li != lend); arg.assign(li->begin(), li->end()); UNSCOPED_INFO("output filename empty?!"); - REQUIRE_FALSE(arg.empty()); + CHECK_FALSE(arg.empty()); ++li; UNSCOPED_INFO("too many args"); CHECK(li == lend); @@ -926,9 +915,7 @@ namespace tut "import sys\n" "sys.exit(0)\n"); py.run(); - UNSCOPED_INFO("Status.mState"); CHECK(py.mPy->getStatus().mState == LLProcess::EXITED); - UNSCOPED_INFO("Status.mData"); CHECK(py.mPy->getStatus().mData == 0); } @@ -938,9 +925,7 @@ namespace tut "import sys\n" "sys.exit(2)\n"); py.run(); - UNSCOPED_INFO("Status.mState"); CHECK(py.mPy->getStatus().mState == LLProcess::EXITED); - UNSCOPED_INFO("Status.mData"); CHECK(py.mPy->getStatus().mData == 2); } @@ -952,9 +937,7 @@ namespace tut py.mParams.files.add(LLProcess::FileParam()); // inherit stdout py.mParams.files.add(LLProcess::FileParam().type("pipe")); // pipe for stderr py.run(); - UNSCOPED_INFO("Status.mState"); CHECK(py.mPy->getStatus().mState == LLProcess::EXITED); - UNSCOPED_INFO("Status.mData"); CHECK(py.mPy->getStatus().mData == 1); std::istream& rpipe(py.mPy->getReadPipe(LLProcess::STDERR).get_istream()); std::vector buffer(4096); @@ -963,8 +946,7 @@ namespace tut UNSCOPED_INFO("Nothing read from stderr pipe"); CHECK(got); std::string data(&buffer[0], got); - UNSCOPED_INFO("Didn't find 'SyntaxError:'"); - REQUIRE(data.find("\nSyntaxError:") != std::string::npos); + CHECK(data.find("\nSyntaxError:") != std::string::npos); } TEST_CASE_METHOD(llprocess_data, "llprocess: kill() stops a sleeping child before it writes again", "[llprocess]") @@ -998,20 +980,15 @@ namespace tut // wait for the script to terminate... one way or another. waitfor(*py.mPy); #if LL_WINDOWS - UNSCOPED_INFO("Status.mState"); - REQUIRE(py.mPy->getStatus().mState == LLProcess::EXITED); - UNSCOPED_INFO("Status.mData"); - REQUIRE(py.mPy->getStatus().mData == -1); + CHECK(py.mPy->getStatus().mState == LLProcess::EXITED); + CHECK(py.mPy->getStatus().mData == -1); #else - UNSCOPED_INFO("Status.mState"); CHECK(py.mPy->getStatus().mState == LLProcess::KILLED); - UNSCOPED_INFO("Status.mData"); CHECK(py.mPy->getStatus().mData == SIGTERM); #endif // If kill() failed, the script would have woken up on its own and // overwritten the file with 'bad'. But if kill() succeeded, it should // not have had that chance. - UNSCOPED_INFO(ll_test::currentTestName() + " script output"); CHECK(readfile(out.getPath()) == "ok"); } @@ -1053,7 +1030,6 @@ namespace tut // If kill() failed, the script would have woken up on its own and // overwritten the file with 'bad'. But if kill() succeeded, it should // not have had that chance. - UNSCOPED_INFO(ll_test::currentTestName() + " script output"); CHECK(readfile(out.getPath()) == "ok"); } @@ -1113,7 +1089,6 @@ namespace tut waitfor(phandle, "autokill script"); // If the LLProcess destructor implicitly called kill(), the // script could not have written 'ack' as we expect. - UNSCOPED_INFO(ll_test::currentTestName() + " script output"); CHECK(readfile(from.getPath()) == "ack"); } @@ -1176,7 +1151,6 @@ namespace tut waitfor(phandle, "autokill script"); // If the LLProcess destructor implicitly called kill(), the // script could not have written 'ack' as we expect. - UNSCOPED_INFO(ll_test::currentTestName() + " script output"); CHECK(readfile(from.getPath()) == "ack"); } @@ -1188,10 +1162,8 @@ namespace tut "print('Hello world')\n"); py.mParams.files.add(LLProcess::FileParam("bogus")); py.mPy = LLProcess::create(py.mParams); - UNSCOPED_INFO("should have rejected 'bogus'"); - REQUIRE_FALSE(py.mPy); + CHECK_FALSE(py.mPy); std::string message(recorder.messageWith("bogus")); - UNSCOPED_INFO("did not name 'stdin'"); CHECK_THAT(message, Catch::Matchers::ContainsSubstring("stdin")); } @@ -1205,8 +1177,7 @@ namespace tut py.mParams.files.add(LLProcess::FileParam()); py.mParams.files.add(LLProcess::FileParam("file")); py.mPy = LLProcess::create(py.mParams); - UNSCOPED_INFO("should have rejected 'file'"); - REQUIRE_FALSE(py.mPy); + CHECK_FALSE(py.mPy); } TEST_CASE_METHOD(llprocess_data, "llprocess: the unimplemented tpipe type is rejected and its slot named", "[llprocess]") @@ -1220,10 +1191,8 @@ namespace tut py.mParams.files.add(LLProcess::FileParam()); py.mParams.files.add(LLProcess::FileParam("tpipe")); py.mPy = LLProcess::create(py.mParams); - UNSCOPED_INFO("should have rejected 'tpipe'"); - REQUIRE_FALSE(py.mPy); + CHECK_FALSE(py.mPy); std::string message(recorder.messageWith("tpipe")); - UNSCOPED_INFO("did not name 'stdout'"); CHECK_THAT(message, Catch::Matchers::ContainsSubstring("stdout")); } @@ -1239,10 +1208,8 @@ namespace tut py.mParams.files.add(LLProcess::FileParam()); py.mParams.files.add(LLProcess::FileParam("npipe")); py.mPy = LLProcess::create(py.mParams); - UNSCOPED_INFO("should have rejected 'npipe'"); - REQUIRE_FALSE(py.mPy); + CHECK_FALSE(py.mPy); std::string message(recorder.messageWith("npipe")); - UNSCOPED_INFO("did not name 'stderr'"); CHECK_THAT(message, Catch::Matchers::ContainsSubstring("stderr")); } @@ -1254,9 +1221,7 @@ namespace tut "sys.exit(7)\n"); py.mParams.files.add(LLProcess::FileParam("pipe", "somename")); py.run(); // verify that it did launch anyway - UNSCOPED_INFO("Status.mState"); CHECK(py.mPy->getStatus().mState == LLProcess::EXITED); - UNSCOPED_INFO("Status.mData"); CHECK(py.mPy->getStatus().mData == 7); std::string message(recorder.messageWith("not yet supported")); UNSCOPED_INFO("log message did not mention internal pipe name"); @@ -1270,7 +1235,7 @@ namespace tut std::string threw; \ /* Both the following calls should work. */ \ (PROCESS).GETPIPE(VALID); \ - UNSCOPED_INFO((#GETOPTPIPE "(" #VALID ") failed")); REQUIRE((bool((PROCESS).GETOPTPIPE(VALID)))); \ + UNSCOPED_INFO(#GETOPTPIPE "(" #VALID ") failed"); REQUIRE(bool((PROCESS).GETOPTPIPE(VALID))); \ /* pass obviously bogus PIPESLOT */ \ CATCH_IN(threw, LLProcess::NoPipe, (PROCESS).GETPIPE(LLProcess::FILESLOT(4))); \ UNSCOPED_INFO("didn't reject bad slot"); REQUIRE_THAT(threw, Catch::Matchers::ContainsSubstring("no slot")); \ @@ -1296,14 +1261,14 @@ namespace tut (THREW) = catch_what([&](){ \ CODE; \ }); \ - UNSCOPED_INFO(("failed to throw " #EXCEPTION ": " #CODE)); REQUIRE((! (THREW).empty())); \ + UNSCOPED_INFO("failed to throw " #EXCEPTION ": " #CODE); REQUIRE_FALSE((THREW).empty()); \ } while (0) #define EXPECT_FAIL_WITH_LOG(EXPECT, CODE) \ do \ { \ CaptureLog recorder; \ - UNSCOPED_INFO((#CODE " succeeded")); REQUIRE((! (CODE))); \ + UNSCOPED_INFO(#CODE " succeeded"); REQUIRE_FALSE(CODE); \ recorder.messageWith(EXPECT); \ } while (0) @@ -1349,18 +1314,14 @@ namespace tut } UNSCOPED_INFO("script never started"); CHECK(i < timeout); - UNSCOPED_INFO("bad wakeup from stdin/stdout script"); CHECK(childout.getline() == "ok"); // important to get the implicit flush from std::endl py.mPy->getWritePipe().get_ostream() << "go" << std::endl; waitfor(*py.mPy); UNSCOPED_INFO("script never replied"); CHECK(childout.contains("\n")); - UNSCOPED_INFO("child didn't ack"); CHECK(childout.getline() == "ack"); - UNSCOPED_INFO("bad child termination"); CHECK(py.mPy->getStatus().mState == LLProcess::EXITED); - UNSCOPED_INFO("bad child exit code"); CHECK(py.mPy->getStatus().mData == 0); } @@ -1388,7 +1349,7 @@ namespace tut { try { - // we expect this lambda to contain tut::ensure() calls + // we expect this lambda to contain REQUIRE() calls std::forward(code)(mHistory); } catch (const ll_test::Failure&) @@ -1460,23 +1421,17 @@ namespace tut auto li(history.begin()), lend(history.end()); UNSCOPED_INFO("no events"); REQUIRE(li != lend); - UNSCOPED_INFO("history[0]"); REQUIRE((*li)["data"].asString() == "abc"); - UNSCOPED_INFO("history[0] len"); REQUIRE((*li)["len"].asInteger() == 3); ++li; UNSCOPED_INFO("only 1 event"); REQUIRE(li != lend); - UNSCOPED_INFO("history[1]"); REQUIRE((*li)["data"].asString() == "abcdef"); - UNSCOPED_INFO("history[0] len"); REQUIRE((*li)["len"].asInteger() == 6); ++li; UNSCOPED_INFO("only 2 events"); REQUIRE(li != lend); - UNSCOPED_INFO("history[2]"); REQUIRE((*li)["data"].asString() == "abcdefghi" EOL); - UNSCOPED_INFO("history[0] len"); REQUIRE((*li)["len"].asInteger() == 9 + sizeof(EOL) - 1); ++li; // We DO NOT expect a whole new event for the second line because we @@ -1540,7 +1495,6 @@ namespace tut EventListener listener(childout.getPump()); // but set limit childout.setLimit(10); - UNSCOPED_INFO("getLimit() after setlimit(10)"); CHECK(childout.getLimit() == 10); // okay, pump I/O to pick up output from child waitfor(*py.mPy); @@ -1551,9 +1505,7 @@ namespace tut REQUIRE_FALSE(history.empty()); // For all we know, that data could have arrived in several different // bursts... probably not, but anyway, only check the last one. - UNSCOPED_INFO("event[\"len\"]"); REQUIRE(history.back()["len"].asInteger() == abc.length()); - UNSCOPED_INFO("length of setLimit(10) data"); REQUIRE(history.back()["data"].asString().length() == 10); }); } @@ -1572,69 +1524,41 @@ namespace tut // okay, pump I/O to pick up output from child waitfor(*py.mPy); // peek() with substr args - UNSCOPED_INFO("peek()"); CHECK(childout.peek() == abc); - UNSCOPED_INFO("peek(23)"); CHECK(childout.peek(23) == abc.substr(23)); - UNSCOPED_INFO("peek(5, 3)"); CHECK(childout.peek(5, 3) == abc.substr(5, 3)); - UNSCOPED_INFO("peek(27, 2)"); CHECK(childout.peek(27, 2) == ""); - UNSCOPED_INFO("peek(23, 5)"); CHECK(childout.peek(23, 5) == "xyz"); // contains() -- we don't exercise as thoroughly as find() because the // contains() implementation is trivially (and visibly) based on find() - UNSCOPED_INFO("contains(\":\")"); CHECK_FALSE(childout.contains(":")); - UNSCOPED_INFO("contains(':')"); CHECK_FALSE(childout.contains(':')); - UNSCOPED_INFO("contains(\"d\")"); CHECK(childout.contains("d")); - UNSCOPED_INFO("contains('d')"); CHECK(childout.contains('d')); - UNSCOPED_INFO("contains(\"klm\")"); CHECK(childout.contains("klm")); - UNSCOPED_INFO("contains(\"klx\")"); CHECK_FALSE(childout.contains("klx")); // find() - UNSCOPED_INFO("find(\":\")"); - REQUIRE(childout.find(":") == LLProcess::ReadPipe::npos); - UNSCOPED_INFO("find(':')"); - REQUIRE(childout.find(':') == LLProcess::ReadPipe::npos); - UNSCOPED_INFO("find(\"d\")"); + CHECK(childout.find(":") == LLProcess::ReadPipe::npos); + CHECK(childout.find(':') == LLProcess::ReadPipe::npos); CHECK(childout.find("d") == 3); - UNSCOPED_INFO("find('d')"); CHECK(childout.find('d') == 3); - UNSCOPED_INFO("find(\"d\", 3)"); CHECK(childout.find("d", 3) == 3); - UNSCOPED_INFO("find('d', 3)"); CHECK(childout.find('d', 3) == 3); - UNSCOPED_INFO("find(\"d\", 4)"); - REQUIRE(childout.find("d", 4) == LLProcess::ReadPipe::npos); - UNSCOPED_INFO("find('d', 4)"); - REQUIRE(childout.find('d', 4) == LLProcess::ReadPipe::npos); + CHECK(childout.find("d", 4) == LLProcess::ReadPipe::npos); + CHECK(childout.find('d', 4) == LLProcess::ReadPipe::npos); // The case of offset == end and offset > end are different. In the // first case, we can form a valid (albeit empty) iterator range and // search that. In the second, guard logic in the implementation must // realize we can't form a valid iterator range. - UNSCOPED_INFO("find(\"d\", 26)"); - REQUIRE(childout.find("d", 26) == LLProcess::ReadPipe::npos); - UNSCOPED_INFO("find('d', 26)"); - REQUIRE(childout.find('d', 26) == LLProcess::ReadPipe::npos); - UNSCOPED_INFO("find(\"d\", 27)"); - REQUIRE(childout.find("d", 27) == LLProcess::ReadPipe::npos); - UNSCOPED_INFO("find('d', 27)"); - REQUIRE(childout.find('d', 27) == LLProcess::ReadPipe::npos); - UNSCOPED_INFO("find(\"ghi\")"); + CHECK(childout.find("d", 26) == LLProcess::ReadPipe::npos); + CHECK(childout.find('d', 26) == LLProcess::ReadPipe::npos); + CHECK(childout.find("d", 27) == LLProcess::ReadPipe::npos); + CHECK(childout.find('d', 27) == LLProcess::ReadPipe::npos); CHECK(childout.find("ghi") == 6); - UNSCOPED_INFO("find(\"ghi\", 6)"); CHECK(childout.find("ghi") == 6); - UNSCOPED_INFO("find(\"ghi\", 7)"); - REQUIRE(childout.find("ghi", 7) == LLProcess::ReadPipe::npos); - UNSCOPED_INFO("find(\"ghi\", 26)"); - REQUIRE(childout.find("ghi", 26) == LLProcess::ReadPipe::npos); - UNSCOPED_INFO("find(\"ghi\", 27)"); - REQUIRE(childout.find("ghi", 27) == LLProcess::ReadPipe::npos); + CHECK(childout.find("ghi", 7) == LLProcess::ReadPipe::npos); + CHECK(childout.find("ghi", 26) == LLProcess::ReadPipe::npos); + CHECK(childout.find("ghi", 27) == LLProcess::ReadPipe::npos); } TEST_CASE_METHOD(llprocess_data, "llprocess: postend reports a launch that failed validation", "[llprocess]") @@ -1645,28 +1569,21 @@ namespace tut params.desc = ll_test::currentTestName(); params.postend = pumpname; LLProcessPtr child = LLProcess::create(params); - UNSCOPED_INFO("shouldn't have launched"); - REQUIRE_FALSE(child); + CHECK_FALSE(child); listener.checkHistory( [¶ms](const EventListener::Listory& history) { - UNSCOPED_INFO("number of postend events"); REQUIRE(history.size() == 1); LLSD postend(history.front()); - UNSCOPED_INFO("has id"); REQUIRE_FALSE(postend.has("id")); - UNSCOPED_INFO("desc"); REQUIRE(postend["desc"].asString() == std::string(params.desc)); - UNSCOPED_INFO("state"); REQUIRE(postend["state"].asInteger() == LLProcess::UNSTARTED); - UNSCOPED_INFO("has data"); REQUIRE_FALSE(postend.has("data")); std::string error(postend["string"]); // All we get from canned parameter validation is a bool, so the // "validation failed" message we ourselves generate can't mention // "executable" by name. Just check that it's nonempty. //ensure_contains("error", error, "executable"); - UNSCOPED_INFO("string"); REQUIRE_FALSE(error.empty()); }); } @@ -1693,21 +1610,14 @@ namespace tut { UNSCOPED_INFO("no postend event"); REQUIRE(i < timeout); - UNSCOPED_INFO("number of postend events"); REQUIRE(history.size() == 1); LLSD postend(history.front()); - UNSCOPED_INFO("id"); REQUIRE(postend["id"].asInteger() == childid); - UNSCOPED_INFO("desc empty"); REQUIRE_FALSE(postend["desc"].asString().empty()); - UNSCOPED_INFO("state"); REQUIRE(postend["state"].asInteger() == LLProcess::EXITED); - UNSCOPED_INFO("data"); REQUIRE(postend["data"].asInteger() == 35); std::string str(postend["string"]); - UNSCOPED_INFO("string"); REQUIRE_THAT(str, Catch::Matchers::ContainsSubstring("exited")); - UNSCOPED_INFO("string"); REQUIRE_THAT(str, Catch::Matchers::ContainsSubstring("35")); }); } @@ -1732,7 +1642,6 @@ namespace tut bool postend(const LLSD&) { mTriggered = true; - UNSCOPED_INFO("postend listener"); REQUIRE(mReadPipe.read(mReadPipe.size()) == mExpect); return false; } @@ -1759,7 +1668,6 @@ namespace tut pumpname, "partial line"); waitfor(*py.mPy); - UNSCOPED_INFO("postend never triggered"); CHECK(listener.mTriggered); } @@ -1796,12 +1704,9 @@ namespace tut CHECK(i < timeout); UNSCOPED_INFO("child never replied"); CHECK(childout.contains("\n")); - UNSCOPED_INFO("large stdin ack"); CHECK(childout.getline() == "ok"); waitfor(*py.mPy); - UNSCOPED_INFO("bad child termination"); CHECK(py.mPy->getStatus().mState == LLProcess::EXITED); - UNSCOPED_INFO("bad child exit code"); CHECK(py.mPy->getStatus().mData == 0); } @@ -1832,9 +1737,7 @@ namespace tut UNSCOPED_INFO("initial output timeout"); CHECK(i < timeout); - UNSCOPED_INFO("stdout message"); CHECK(childout.getline() == "stdout message"); - UNSCOPED_INFO("stderr message"); CHECK(childerr.getline() == "stderr message"); py.mPy->getWritePipe().get_ostream() << "test input" << std::endl; @@ -1842,7 +1745,6 @@ namespace tut waitfor(*py.mPy); UNSCOPED_INFO("script never replied"); CHECK(childout.contains("\n")); - UNSCOPED_INFO("echo response"); CHECK(childout.getline() == "received: test input"); } @@ -1856,17 +1758,13 @@ namespace tut // Immediately after launch LLProcess::Status status = py.mPy->getStatus(); - UNSCOPED_INFO("post-launch state"); CHECK(status.mState == LLProcess::RUNNING); - UNSCOPED_INFO("process is running"); CHECK(py.mPy->isRunning()); // After completion waitfor(*py.mPy); status = py.mPy->getStatus(); - UNSCOPED_INFO("post-completion state"); CHECK(status.mState == LLProcess::EXITED); - UNSCOPED_INFO("process is not running"); CHECK_FALSE(py.mPy->isRunning()); } @@ -1895,11 +1793,9 @@ namespace tut for (const LLSD& event : history) { const std::string data = event["data"].asString(); - UNSCOPED_INFO("event data within limit"); REQUIRE(data.length() <= 50); saw_data = saw_data || !data.empty(); } - UNSCOPED_INFO("saw at least one data event"); REQUIRE(saw_data); }); } @@ -1914,16 +1810,13 @@ namespace tut EventListener listener(LLEventPumps::instance().obtain(pumpname)); LLProcessPtr child = LLProcess::create(params); - UNSCOPED_INFO("should not create invalid process"); - REQUIRE_FALSE(child); + CHECK_FALSE(child); listener.checkHistory( [](const EventListener::Listory& history) { - UNSCOPED_INFO("got failure event"); REQUIRE(history.size() == 1); LLSD event = history.front(); - UNSCOPED_INFO("state is UNSTARTED"); REQUIRE(event["state"].asInteger() == LLProcess::UNSTARTED); UNSCOPED_INFO("has error string"); REQUIRE_FALSE(event["string"].asString().empty()); @@ -1940,23 +1833,17 @@ namespace tut LLProcess::id pid = py.mPy->getProcessID(); LLProcess::handle handle = py.mPy->getProcessHandle(); - UNSCOPED_INFO("PID is valid"); CHECK(pid != 0); - UNSCOPED_INFO("handle is valid"); CHECK(handle != 0); #if LL_WINDOWS // On Windows, verify handle is a valid process handle DWORD exitCode; - UNSCOPED_INFO("GetExitCodeProcess succeeds"); - REQUIRE((GetExitCodeProcess(handle, &exitCode) != 0)); - UNSCOPED_INFO("process still running"); - REQUIRE(exitCode == STILL_ACTIVE); + REQUIRE(GetExitCodeProcess(handle, &exitCode) != 0); + CHECK(exitCode == STILL_ACTIVE); #else // On POSIX, PID and handle should be the same - UNSCOPED_INFO("PID equals handle"); CHECK(pid == handle); - UNSCOPED_INFO("process still running"); CHECK(py.mPy->isRunning()); #endif @@ -1976,25 +1863,18 @@ namespace tut waitfor(*py.mPy); // Test find at exact end - UNSCOPED_INFO("find at end succeeds"); - REQUIRE(childout.find("xyz", 23) != LLProcess::ReadPipe::npos); - UNSCOPED_INFO("find past end returns npos"); - REQUIRE(childout.find("a", 30) == LLProcess::ReadPipe::npos); + CHECK(childout.find("xyz", 23) != LLProcess::ReadPipe::npos); + CHECK(childout.find("a", 30) == LLProcess::ReadPipe::npos); // Test empty string search - UNSCOPED_INFO("contains empty string"); CHECK(childout.contains("")); // Test single char at boundaries - UNSCOPED_INFO("find 'a' at 0"); CHECK(childout.find('a', 0) == 0); - UNSCOPED_INFO("find 'z' at end"); CHECK(childout.find('z') == 25); // Test peek at boundaries - UNSCOPED_INFO("peek at exact size"); CHECK(childout.peek(26) == ""); - UNSCOPED_INFO("peek past end"); CHECK(childout.peek(30, 10) == ""); } @@ -2008,9 +1888,8 @@ namespace tut "import sys\n" "sys.exit(0)\n"); py.run(); - UNSCOPED_INFO("quick exit status"); + CAPTURE(i); REQUIRE(py.mPy->getStatus().mState == LLProcess::EXITED); - UNSCOPED_INFO("quick exit code"); REQUIRE(py.mPy->getStatus().mData == 0); // Let the process object destroy } @@ -2049,34 +1928,29 @@ namespace tut LLEventPumps::instance().obtain("mainloop").post(LLSD()); } - UNSCOPED_INFO("stdout size"); - REQUIRE(childout.size() == 0); - UNSCOPED_INFO("stderr size"); - REQUIRE(childerr.size() == 0); - UNSCOPED_INFO("process exited"); + CHECK(childout.size() == 0); + CHECK(childerr.size() == 0); CHECK(py.mPy->getStatus().mState == LLProcess::EXITED); - auto check_eof = [](const EventListener::Listory& history, const std::string& which) + auto require_eof = [](const EventListener::Listory& history, const std::string& which) { - UNSCOPED_INFO((STRINGIZE(which << " events"))); + CAPTURE(which); REQUIRE(history.size() == 1); const LLSD& event = history.front(); - UNSCOPED_INFO((STRINGIZE(which << " eof event"))); REQUIRE(event["eof"].asBoolean()); - UNSCOPED_INFO((STRINGIZE(which << " len"))); REQUIRE(event["len"].asInteger() == 0); }; outListener.checkHistory( [&](const EventListener::Listory& history) { - check_eof(history, "stdout"); + require_eof(history, "stdout"); }); errListener.checkHistory( [&](const EventListener::Listory& history) { - check_eof(history, "stderr"); + require_eof(history, "stderr"); }); } @@ -2094,7 +1968,6 @@ namespace tut // Wait for the process to start up yield(); - UNSCOPED_INFO("process started"); CHECK(py.mPy->isRunning()); // Send the kill signal @@ -2111,7 +1984,6 @@ namespace tut auto elapsed_ms = std::chrono::duration_cast( std::chrono::steady_clock::now() - start).count(); - UNSCOPED_INFO((STRINGIZE("tick() took " << elapsed_ms << " ms, expected < 75 ms"))); CHECK(elapsed_ms < 75); // Let the process fully exit so cleanup is orderly @@ -2132,7 +2004,6 @@ namespace tut // Wait for the process to start up yield(); - UNSCOPED_INFO("process started"); CHECK(py.mPy->isRunning()); // Kill the process (sets mKillCalled = true) @@ -2144,7 +2015,6 @@ namespace tut auto elapsed_ms = std::chrono::duration_cast( std::chrono::steady_clock::now() - start).count(); - UNSCOPED_INFO((STRINGIZE("destructor took " << elapsed_ms << " ms, expected < 75 ms"))); CHECK(elapsed_ms < 75); } @@ -2204,7 +2074,6 @@ namespace tut waitfor(phandle, "kill() script"); // Had the destructor detached instead, the script would have woken on // its own and overwritten the file with 'bad'. - UNSCOPED_INFO(ll_test::currentTestName() + " script output"); CHECK(readfile(out.getPath()) == "ok"); } } // namespace tut diff --git a/indra/llcommon/tests/llprocessor_test.cpp b/indra/llcommon/tests/llprocessor_test.cpp index dbc6b73e11..414a71e7a6 100644 --- a/indra/llcommon/tests/llprocessor_test.cpp +++ b/indra/llcommon/tests/llprocessor_test.cpp @@ -43,14 +43,9 @@ namespace std::string brand = pi.getCPUBrandName(); //std::string steam = pi.getCPUFeatureDescription(); - UNSCOPED_INFO("Unknown Brand name"); CHECK(brand != "Unknown"); - UNSCOPED_INFO("Unknown Family name"); CHECK(family != "Unknown"); - { - INFO("Reasonable CPU Frequency > 100 && < 10000"); - CHECK(freq > 100); - CHECK(freq < 10000); - } + CHECK(freq > 100); + CHECK(freq < 10000); } } diff --git a/indra/llcommon/tests/llprocinfo_test.cpp b/indra/llcommon/tests/llprocinfo_test.cpp index b91a14231f..d647e036fd 100644 --- a/indra/llcommon/tests/llprocinfo_test.cpp +++ b/indra/llcommon/tests/llprocinfo_test.cpp @@ -52,9 +52,7 @@ TEST_CASE_METHOD(procinfo_test, "LLProcInfo: getCPUUsage() writes both its argum LLProcInfo::getCPUUsage(user, system); - UNSCOPED_INFO("getCPUUsage() writes to its user argument"); CHECK(user != bad_user); - UNSCOPED_INFO("getCPUUsage() writes to its system argument"); CHECK(system != bad_system); } @@ -74,10 +72,8 @@ TEST_CASE_METHOD(procinfo_test, "LLProcInfo: getCPUUsage() never goes down over LLProcInfo::getCPUUsage(user2, system2); - UNSCOPED_INFO("getCPUUsage() user value doesn't decrease over time"); - CHECK((user2 >= user) == true); - UNSCOPED_INFO("getCPUUsage() system value doesn't decrease over time"); - CHECK((system2 >= system) == true); + CHECK(user2 >= user); + CHECK(system2 >= system); } diff --git a/indra/llcommon/tests/llptrto_test.cpp b/indra/llcommon/tests/llptrto_test.cpp index 35c156e654..525f36b50e 100644 --- a/indra/llcommon/tests/llptrto_test.cpp +++ b/indra/llcommon/tests/llptrto_test.cpp @@ -101,7 +101,7 @@ namespace } /***************************************************************************** -* tut test group +* Tests *****************************************************************************/ namespace { @@ -111,7 +111,6 @@ namespace TEST_CASE("LLPtrTo: LLPtrTo of a refcounted class holds a reference", "[LLPtrTo]") { LLPtrTo::type ptr(new RCFoo); - UNSCOPED_INFO("one reference"); CHECK(ptr->getNumRefs() == 1); } } diff --git a/indra/llcommon/tests/llrand_test.cpp b/indra/llcommon/tests/llrand_test.cpp index 9137dfa6c2..720615d4f7 100644 --- a/indra/llcommon/tests/llrand_test.cpp +++ b/indra/llcommon/tests/llrand_test.cpp @@ -42,18 +42,15 @@ // 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 +// So that we don't need two different versions of require_in_range(), when // testing extent < 0, negate the return value and the extent before passing -// into ensure_in_range(). +// into require_in_range(). template -static void ensure_in_range(const std::string_view& name, - NUMBER value, NUMBER low, NUMBER high) +static void require_in_range(const std::string_view& name, + NUMBER value, NUMBER low, NUMBER high) { - auto failmsg{ stringize(name, " >= ", low, " (", value, ')') }; - UNSCOPED_INFO(failmsg); + INFO(name); REQUIRE(value >= low); - failmsg = stringize(name, " < ", high, " (", value, ')'); - UNSCOPED_INFO(failmsg); REQUIRE(value < high); } @@ -77,7 +74,7 @@ namespace } // That `draws` spread over `counts` as evenly as chance allows. - void ensure_uniform(const std::string& name, const std::vector& counts, U64 draws) + void require_uniform(const std::string& name, const std::vector& counts, U64 draws) { const F64 expected = F64(draws) / F64(counts.size()); F64 stat = 0.0; @@ -87,12 +84,12 @@ namespace stat += off * off / expected; } const F64 limit = chi_square_limit(counts.size() - 1); - UNSCOPED_INFO(stringize(name, ": chi-square ", stat, " under ", limit)); + INFO(name << ": chi-square"); REQUIRE(stat < limit); } // That draws uniform over [0, 1) have its mean, 1/2, and variance, 1/12. - void ensure_unit_moments(const std::string& name, F64 sum, F64 sum_of_squares, U64 draws) + void require_unit_moments(const std::string& name, F64 sum, F64 sum_of_squares, U64 draws) { const F64 n = F64(draws); const F64 mean = sum / n; @@ -101,9 +98,9 @@ namespace // fourth central moment, 1/80, less its square, over n. const F64 mean_bound = ONE_IN_A_MILLION_Z * std::sqrt(1.0 / 12.0 / n); const F64 variance_bound = ONE_IN_A_MILLION_Z * std::sqrt((1.0 / 80.0 - 1.0 / 144.0) / n); - UNSCOPED_INFO(stringize(name, ": mean ", mean)); + INFO(name); + CAPTURE(mean, variance); REQUIRE(std::fabs(mean - 0.5) < mean_bound); - UNSCOPED_INFO(stringize(name, ": variance ", variance)); REQUIRE(std::fabs(variance - 1.0 / 12.0) < variance_bound); } @@ -147,7 +144,7 @@ namespace { for(S32 ii = 0; ii < 100000; ++ii) { - ensure_in_range("frand", ll_frand(), 0.0f, 1.0f); + require_in_range("frand", ll_frand(), 0.0f, 1.0f); } } @@ -155,7 +152,7 @@ namespace { for(S32 ii = 0; ii < 100000; ++ii) { - ensure_in_range("drand", ll_drand(), 0.0, 1.0); + require_in_range("drand", ll_drand(), 0.0, 1.0); } } @@ -163,7 +160,7 @@ namespace { for(S32 ii = 0; ii < 100000; ++ii) { - ensure_in_range("frand(2.0f)", ll_frand(2.0f) - 1.0f, -1.0f, 1.0f); + require_in_range("frand(2.0f)", ll_frand(2.0f) - 1.0f, -1.0f, 1.0f); } } @@ -173,7 +170,7 @@ namespace { // 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); + require_in_range("-frand(-7.0)", -ll_frand(-7.0), 0.0f, 7.0f); } } @@ -181,7 +178,7 @@ namespace { for(S32 ii = 0; ii < 100000; ++ii) { - ensure_in_range("-drand(-2.0)", -ll_drand(-2.0), 0.0, 2.0); + require_in_range("-drand(-2.0)", -ll_drand(-2.0), 0.0, 2.0); } } @@ -189,7 +186,7 @@ namespace { for(S32 ii = 0; ii < 100000; ++ii) { - ensure_in_range("rand(100)", ll_rand(100), 0, 100); + require_in_range("rand(100)", ll_rand(100), 0, 100); } } @@ -197,38 +194,34 @@ namespace { for(S32 ii = 0; ii < 100000; ++ii) { - ensure_in_range("-rand(-127)", -ll_rand(-127), 0, 127); + require_in_range("-rand(-127)", -ll_rand(-127), 0, 127); } } - // Tests 8 to 15 hold for any generator under llrand. Tests 16 to 18 are - // what the generator itself draws, and change when it does. + // The cases from here to the one on scaling unit values hold for any + // generator under llrand. The last three are what the generator itself + // draws, and change when it does. TEST_CASE_METHOD(random, "LLSeedRand: extents of zero or one give zero and the widest extents stay in range", "[LLSeedRand]") { ll_rand_seed(8); for (S32 ii = 0; ii < 10000; ++ii) { - UNSCOPED_INFO("rand(0)"); REQUIRE(ll_rand(0) == 0); - UNSCOPED_INFO("rand(1)"); REQUIRE(ll_rand(1) == 0); - UNSCOPED_INFO("rand(-1)"); REQUIRE(ll_rand(-1) == 0); - UNSCOPED_INFO("frand(0)"); REQUIRE(ll_frand(0.f) == 0.f); - UNSCOPED_INFO("drand(0)"); REQUIRE(ll_drand(0.0) == 0.0); - ensure_in_range("rand(INT_MAX)", ll_rand(INT_MAX), 0, INT_MAX); + require_in_range("rand(INT_MAX)", ll_rand(INT_MAX), 0, INT_MAX); const S32 lowest = ll_rand(INT_MIN); - UNSCOPED_INFO(stringize("rand(INT_MIN) in (INT_MIN, 0] (", lowest, ')')); + CAPTURE(lowest); REQUIRE((lowest > INT_MIN && lowest <= 0)); - ensure_in_range("frand(FLT_MAX)", ll_frand(std::numeric_limits::max()), 0.f, std::numeric_limits::max()); - ensure_in_range("frand(FLT_MIN)", ll_frand(std::numeric_limits::min()), 0.f, std::numeric_limits::min()); - ensure_in_range("drand(DBL_MAX)", ll_drand(std::numeric_limits::max()), 0.0, std::numeric_limits::max()); - ensure_in_range("drand(DBL_MIN)", ll_drand(std::numeric_limits::min()), 0.0, std::numeric_limits::min()); + require_in_range("frand(FLT_MAX)", ll_frand(std::numeric_limits::max()), 0.f, std::numeric_limits::max()); + require_in_range("frand(FLT_MIN)", ll_frand(std::numeric_limits::min()), 0.f, std::numeric_limits::min()); + require_in_range("drand(DBL_MAX)", ll_drand(std::numeric_limits::max()), 0.0, std::numeric_limits::max()); + require_in_range("drand(DBL_MIN)", ll_drand(std::numeric_limits::min()), 0.0, std::numeric_limits::min()); } } @@ -237,7 +230,6 @@ namespace ll_rand_seed(1234); const std::vector first = draw_each(16); ll_rand_seed(1234); - UNSCOPED_INFO("the same seed draws the same"); CHECK(draw_each(16) == first); std::vector elsewhere; @@ -245,11 +237,9 @@ namespace ll_rand_seed(1234); elsewhere = draw_each(16); }).join(); - UNSCOPED_INFO("on another thread too"); CHECK(elsewhere == first); ll_rand_seed(1235); - UNSCOPED_INFO("another seed draws otherwise"); CHECK(draw_each(16) != first); } @@ -264,7 +254,7 @@ namespace { ++counts.at(ll_rand(n)); } - ensure_uniform(stringize("rand(", n, ")"), counts, draws); + require_uniform(stringize("rand(", n, ")"), counts, draws); } std::vector counts(7); @@ -272,7 +262,7 @@ namespace { ++counts.at(-ll_rand(-7)); } - ensure_uniform("rand(-7)", counts, draws); + require_uniform("rand(-7)", counts, draws); } TEST_CASE_METHOD(random, "LLSeedRand: frand() and drand() spread evenly over the unit interval", "[LLSeedRand]") @@ -295,10 +285,10 @@ namespace drand_sum += d; drand_squares += d * d; } - ensure_uniform("frand", frand_bins, draws); - ensure_uniform("drand", drand_bins, draws); - ensure_unit_moments("frand", frand_sum, frand_squares, draws); - ensure_unit_moments("drand", drand_sum, drand_squares, draws); + require_uniform("frand", frand_bins, draws); + require_uniform("drand", drand_bins, draws); + require_unit_moments("frand", frand_sum, frand_squares, draws); + require_unit_moments("drand", drand_sum, drand_squares, draws); } TEST_CASE_METHOD(random, "LLSeedRand: rand_u32() draws each byte evenly and sets each bit half the time", "[LLSeedRand]") @@ -321,11 +311,11 @@ namespace } for (U32 byte = 0; byte < 4; ++byte) { - ensure_uniform(stringize("byte ", byte), bytes[byte], draws); + require_uniform(stringize("byte ", byte), bytes[byte], draws); } for (U32 bit = 0; bit < 32; ++bit) { - ensure_uniform(stringize("bit ", bit), { ones[bit], draws - ones[bit] }, draws); + require_uniform(stringize("bit ", bit), { ones[bit], draws - ones[bit] }, draws); } } @@ -337,13 +327,9 @@ namespace first.join(); second.join(); const std::vector here = draw_drand(8); - UNSCOPED_INFO("two threads draw apart"); CHECK(one != two); - { - INFO("and apart from this one"); - CHECK(one != here); - CHECK(two != here); - } + CHECK(one != here); + CHECK(two != here); } TEST_CASE_METHOD(random, "LLSeedRand: seeding and drawing on another thread leaves this thread's draws alone", "[LLSeedRand]") @@ -358,7 +344,6 @@ namespace drawn.insert(drawn.end(), after.begin(), after.end()); ll_rand_seed(14); - UNSCOPED_INFO("this thread's draws went on as if alone"); CHECK(draw_drand(8) == drawn); } @@ -368,44 +353,29 @@ namespace const F64 top53 = 1.0 - 0x1p-53; const F64 top48 = 1.0 - 0x1p-48; - UNSCOPED_INFO("0 of 100"); CHECK(extentS32(0.0, 100) == 0); - UNSCOPED_INFO("a half of 100"); CHECK(extentS32(0.5, 100) == 50); - UNSCOPED_INFO("the top of 100, to 48 bits"); CHECK(extentS32(top48, 100) == 99); - UNSCOPED_INFO("the top of 100, to 53 bits"); CHECK(extentS32(top53, 100) == 99); - UNSCOPED_INFO("the top of INT_MAX"); CHECK(extentS32(top53, INT_MAX) == INT_MAX - 1); - UNSCOPED_INFO("the top of 2^30"); CHECK(extentS32(top53, 1 << 30) == (1 << 30) - 1); - UNSCOPED_INFO("a half of -7, toward zero"); CHECK(extentS32(0.5, -7) == -3); - UNSCOPED_INFO("the top of -7"); CHECK(extentS32(top53, -7) == -6); - UNSCOPED_INFO("the top of INT_MIN"); CHECK(extentS32(top53, INT_MIN) == INT_MIN + 1); UNSCOPED_INFO("a unit of 1 gives 0, not the extent"); CHECK(extentS32(1.0, 100) == 0); - UNSCOPED_INFO("nor the negative extent"); + UNSCOPED_INFO("a unit of 1 gives 0, not the negative extent"); CHECK(extentS32(1.0, -100) == 0); - UNSCOPED_INFO("a half of 2"); CHECK(extentReal(0.5f, 2.f) == 1.f); - UNSCOPED_INFO("a half of -7"); CHECK(extentReal(0.5f, -7.f) == -3.5f); - UNSCOPED_INFO("0 of 0"); CHECK(extentReal(0.5, 0.0) == 0.0); - UNSCOPED_INFO("the top F32 of 3 stays under 3"); CHECK(extentReal(1.f - 0x1p-24f, 3.f) < 3.f); - UNSCOPED_INFO("the top F64 of 3 stays under 3"); CHECK(extentReal(top53, 3.0) < 3.0); - UNSCOPED_INFO("the top F64 of -3 stays over -3"); CHECK(extentReal(top53, -3.0) > -3.0); UNSCOPED_INFO("a unit of 1 gives 0, not the extent"); CHECK(extentReal(1.f, 2.f) == 0.f); - UNSCOPED_INFO("nor the negative extent"); + UNSCOPED_INFO("a unit of 1 gives 0, not the negative extent"); CHECK(extentReal(1.0, -2.0) == 0.0); } @@ -414,20 +384,15 @@ namespace using namespace ALRandMap; const U64 all = ~U64(0); - UNSCOPED_INFO("0"); CHECK(unitF64(0) == 0.0); - UNSCOPED_INFO("a half"); CHECK(unitF64(U64(1) << 63) == 0.5); - UNSCOPED_INFO("the top"); CHECK(unitF64(all) == 1.0 - 0x1p-53); UNSCOPED_INFO("the low 11 bits are passed over"); CHECK(unitF64(0x7FF) == 0.0); UNSCOPED_INFO("the lowest bit used"); CHECK(unitF64(0x800) == 0x1p-53); - UNSCOPED_INFO("0"); CHECK(unitF32(0) == 0.f); - UNSCOPED_INFO("a half"); CHECK(unitF32(U64(1) << 63) == 0.5f); UNSCOPED_INFO("the top, which does not round up to 1"); CHECK(unitF32(all) == 1.f - 0x1p-24f); @@ -436,11 +401,8 @@ namespace UNSCOPED_INFO("the lowest bit used"); CHECK(unitF32(U64(1) << 40) == 0x1p-24f); - UNSCOPED_INFO("the bits of 0"); CHECK(bitsU32(0) == 0u); - UNSCOPED_INFO("the top 32 bits"); CHECK(bitsU32(0x123456789ABCDEF0) == 0x12345678u); - UNSCOPED_INFO("the bits of the top"); CHECK(bitsU32(all) == 0xFFFFFFFFu); } @@ -456,25 +418,25 @@ namespace ll_rand_seed(42); for (S32 ii = 0; ii < 8; ++ii) { - UNSCOPED_INFO(stringize("drand ", ii)); + CAPTURE(ii); REQUIRE(ll_drand() == drand[ii]); } ll_rand_seed(42); for (S32 ii = 0; ii < 8; ++ii) { - UNSCOPED_INFO(stringize("frand ", ii)); + CAPTURE(ii); REQUIRE(ll_frand() == frand[ii]); } ll_rand_seed(42); for (S32 ii = 0; ii < 8; ++ii) { - UNSCOPED_INFO(stringize("rand(100) ", ii)); + CAPTURE(ii); REQUIRE(ll_rand(100) == rand100[ii]); } ll_rand_seed(42); for (S32 ii = 0; ii < 8; ++ii) { - UNSCOPED_INFO(stringize("rand_u32 ", ii)); + CAPTURE(ii); REQUIRE(ll_rand_u32() == u32[ii]); } } @@ -486,13 +448,14 @@ namespace bool frand_last_bit = false; for (S32 ii = 0; ii < 100000; ++ii) { + CAPTURE(ii); const F64 drawn = std::ldexp(ll_drand(), 53); - UNSCOPED_INFO(stringize("drand ", ii, " is a multiple of 2^-53")); + UNSCOPED_INFO("drand is a multiple of 2^-53"); REQUIRE(drawn == std::floor(drawn)); drand_last_bit = drand_last_bit || std::fmod(drawn, 2.0) != 0.0; const F64 drawn_f = std::ldexp(F64(ll_frand()), 24); - UNSCOPED_INFO(stringize("frand ", ii, " is a multiple of 2^-24")); + UNSCOPED_INFO("frand is a multiple of 2^-24"); REQUIRE(drawn_f == std::floor(drawn_f)); frand_last_bit = frand_last_bit || std::fmod(drawn_f, 2.0) != 0.0; } diff --git a/indra/llcommon/tests/llsd_test.cpp b/indra/llcommon/tests/llsd_test.cpp index 95c0741022..a57a67a413 100644 --- a/indra/llcommon/tests/llsd_test.cpp +++ b/indra/llcommon/tests/llsd_test.cpp @@ -41,28 +41,26 @@ namespace tut { // LLSD's live count is back where it started once what the body made // is gone. Checked here rather than in a destructor, which is - // noexcept: a failed ensure() there would end the process instead of + // noexcept: a failed REQUIRE there would end the process instead of // failing the test. template - void ensure_cleaned_up(BODY&& body) + void require_cleaned_up(BODY&& body) { const U32 outstanding = llsd::outstandingCount(); body(); - UNSCOPED_INFO("SDCleanupCheck"); REQUIRE(llsd::outstandingCount() == outstanding); } // ...and the body allocated exactly as many LLSD values as expected. template - void ensure_allocations(const std::string& message, U32 expectedAllocations, BODY&& body) + void require_allocations(const std::string& what, U32 expectedAllocations, BODY&& body) { const U32 outstanding = llsd::outstandingCount(); const U32 allocations = llsd::allocationCount(); body(); - UNSCOPED_INFO(message + " SDAllocationCheck"); + INFO(what); REQUIRE(llsd::allocationCount() - allocations == expectedAllocations); - UNSCOPED_INFO("SDCleanupCheck"); REQUIRE(llsd::outstandingCount() == outstanding); } } // anonymous namespace @@ -73,30 +71,24 @@ namespace tut // Static in tut rather than in the anonymous namespace, whose TUT // ensure_equals would hide lltut.h's overloads for LLSD's types. template - static void ensureTypeAndValue(const char* msg, const LLSD& actual, + static void require_type_and_value(const char* what, const LLSD& actual, T expectedValue) { LLSDTraits traits; - std::string s(msg); - - UNSCOPED_INFO(s + " type"); + INFO(what); REQUIRE(traits.checkType(actual)); - UNSCOPED_INFO(s + " value"); REQUIRE(traits.get(actual) == expectedValue); } template - static void ensureTypeAndRefValue(const char* msg, const LLSD& actual, + static void require_type_and_ref_value(const char* what, const LLSD& actual, const T& expectedValue) { LLSDTraits traits; - std::string s(msg); - - UNSCOPED_INFO(s + " type"); + INFO(what); REQUIRE(traits.checkType(actual)); - UNSCOPED_INFO(s + " value"); REQUIRE(traits.get(actual) == expectedValue); } @@ -111,129 +103,119 @@ namespace tut // } TEST_CASE("LLSD(new): setting and fetching scalar types", "[LLSD_new]") - // setting and fetching scalar types { - ensure_cleaned_up([&] + require_cleaned_up([&] { LLSD v; - v = true; ensureTypeAndValue("set true", v, true); - v = false; ensureTypeAndValue("set false", v, false); - v = true; ensureTypeAndValue("set true again", v, true); + v = true; require_type_and_value("set true", v, true); + v = false; require_type_and_value("set false", v, false); + v = true; require_type_and_value("set true again", v, true); - v = 42; ensureTypeAndValue("set to 42", v, 42); - v = 0; ensureTypeAndValue("set to zero", v, 0); - v = -12345; ensureTypeAndValue("set to neg", v, -12345); - v = 2000000000; ensureTypeAndValue("set to big", v, 2000000000); + v = 42; require_type_and_value("set to 42", v, 42); + v = 0; require_type_and_value("set to zero", v, 0); + v = -12345; require_type_and_value("set to neg", v, -12345); + v = 2000000000; require_type_and_value("set to big", v, 2000000000); v = 3.14159265359; - ensureTypeAndValue("set to pi", v, 3.14159265359); - UNSCOPED_INFO("isn't float"); + require_type_and_value("set to pi", v, 3.14159265359); REQUIRE(v.asReal() != (float)3.14159265359); - v = 6.7e256; ensureTypeAndValue("set to big", v, 6.7e256); + v = 6.7e256; require_type_and_value("set to big", v, 6.7e256); LLUUID nullUUID; LLUUID newUUID; newUUID.generate(); - v = nullUUID; ensureTypeAndValue("set to null UUID", v, nullUUID); - v = newUUID; ensureTypeAndValue("set to new UUID", v, newUUID); - v = nullUUID; ensureTypeAndValue("set to null again", v, nullUUID); + v = nullUUID; require_type_and_value("set to null UUID", v, nullUUID); + v = newUUID; require_type_and_value("set to new UUID", v, newUUID); + v = nullUUID; require_type_and_value("set to null again", v, nullUUID); // strings must be tested with two types of string objects std::string s = "now is the time"; const char* cs = "for all good zorks"; - v = s; ensureTypeAndValue("set to std::string", v, s); - v = cs; ensureTypeAndValue("set to const char*", v, cs); + v = s; require_type_and_value("set to std::string", v, s); + v = cs; require_type_and_value("set to const char*", v, cs); LLDate epoch; LLDate aDay("2001-10-22T10:11:12.00Z"); - v = epoch; ensureTypeAndValue("set to epoch", v, epoch); - v = aDay; ensureTypeAndValue("set to a day", v, aDay); + v = epoch; require_type_and_value("set to epoch", v, epoch); + v = aDay; require_type_and_value("set to a day", v, aDay); LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/"); - v = path; ensureTypeAndValue("set to a uri", v, path); + v = path; require_type_and_value("set to a uri", v, path); const char source[] = "once in a blue moon"; std::vector data; copy(&source[0], &source[sizeof(source)], back_inserter(data)); - v = data; ensureTypeAndRefValue("set to data", v, data); + v = data; require_type_and_ref_value("set to data", v, data); v.clear(); - UNSCOPED_INFO("reset to undefined"); REQUIRE(v.type() == LLSD::TypeUndefined); }); } TEST_CASE("LLSD(new): construction and initialization from scalar values", "[LLSD_new]") - // construction via scalar values - // tests both constructor and initialize forms { - ensure_cleaned_up([&] + require_cleaned_up([&] { - LLSD b1(true); ensureTypeAndValue("construct boolean", b1, true); - LLSD b2 = true; ensureTypeAndValue("initialize boolean", b2, true); - LLSD i1(42); ensureTypeAndValue("construct int", i1, 42); - LLSD i2 =42; ensureTypeAndValue("initialize int", i2, 42); - LLSD d1(1.2); ensureTypeAndValue("construct double", d1, 1.2); - LLSD d2 = 1.2; ensureTypeAndValue("initialize double", d2, 1.2); + LLSD b1(true); require_type_and_value("construct boolean", b1, true); + LLSD b2 = true; require_type_and_value("initialize boolean", b2, true); + LLSD i1(42); require_type_and_value("construct int", i1, 42); + LLSD i2 =42; require_type_and_value("initialize int", i2, 42); + LLSD d1(1.2); require_type_and_value("construct double", d1, 1.2); + LLSD d2 = 1.2; require_type_and_value("initialize double", d2, 1.2); LLUUID newUUID; newUUID.generate(); LLSD u1(newUUID); - ensureTypeAndValue("construct UUID", u1, newUUID); + require_type_and_value("construct UUID", u1, newUUID); LLSD u2 = newUUID; - ensureTypeAndValue("initialize UUID", u2, newUUID); + require_type_and_value("initialize UUID", u2, newUUID); LLSD ss1(std::string("abc")); - ensureTypeAndValue("construct std::string", ss1, "abc"); + require_type_and_value("construct std::string", ss1, "abc"); LLSD ss2 = std::string("abc"); - ensureTypeAndValue("initialize std::string",ss2, "abc"); + require_type_and_value("initialize std::string",ss2, "abc"); LLSD sl1(std::string("def")); - ensureTypeAndValue("construct std::string", sl1, "def"); + require_type_and_value("construct std::string", sl1, "def"); LLSD sl2 = std::string("def"); - ensureTypeAndValue("initialize std::string", sl2, "def"); + require_type_and_value("initialize std::string", sl2, "def"); LLSD sc1("ghi"); - ensureTypeAndValue("construct const char*", sc1, "ghi"); + require_type_and_value("construct const char*", sc1, "ghi"); LLSD sc2 = "ghi"; - ensureTypeAndValue("initialize const char*",sc2, "ghi"); + require_type_and_value("initialize const char*",sc2, "ghi"); LLDate aDay("2001-10-22T10:11:12.00Z"); - LLSD t1(aDay); ensureTypeAndValue("construct LLDate", t1, aDay); - LLSD t2 = aDay; ensureTypeAndValue("initialize LLDate", t2, aDay); + LLSD t1(aDay); require_type_and_value("construct LLDate", t1, aDay); + LLSD t2 = aDay; require_type_and_value("initialize LLDate", t2, aDay); LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/"); - LLSD p1(path); ensureTypeAndValue("construct LLURI", p1, path); - LLSD p2 = path; ensureTypeAndValue("initialize LLURI", p2, path); + LLSD p1(path); require_type_and_value("construct LLURI", p1, path); + LLSD p2 = path; require_type_and_value("initialize LLURI", p2, path); const char source[] = "once in a blue moon"; std::vector data; copy(&source[0], &source[sizeof(source)], back_inserter(data)); - LLSD x1(data); ensureTypeAndRefValue("construct vector", x1, data); - LLSD x2 = data; ensureTypeAndRefValue("initialize vector", x2, data); + LLSD x1(data); require_type_and_ref_value("construct vector", x1, data); + LLSD x2 = data; require_type_and_ref_value("initialize vector", x2, data); }); } - static void checkConversions(const char* msg, const LLSD& v, + static void require_conversions(const char* what, const LLSD& v, LLSD::Boolean eBoolean, LLSD::Integer eInteger, LLSD::Real eReal, const LLSD::String& eString) { - std::string s(msg); - - UNSCOPED_INFO(s+" to bool"); + INFO(what); REQUIRE(v.asBoolean() == eBoolean); - UNSCOPED_INFO(s+" to int"); REQUIRE(v.asInteger() == eInteger); if (eReal == eReal) { - UNSCOPED_INFO(s+" to real"); REQUIRE(v.asReal() == eReal); - UNSCOPED_INFO(s+" to string"); REQUIRE(v.asString() == eString); } else @@ -241,7 +223,6 @@ namespace tut int left = fpclassify(v.asReal()); int right = fpclassify(eReal); - UNSCOPED_INFO(s+" to real"); REQUIRE(left == right); // ensure_equals(s+" to string", v.asString(), eString); // I've commented this check out, since there doesn't @@ -256,21 +237,21 @@ namespace tut // conversion between undefined and basic scalar types: // boolean, integer, real and string { - ensure_cleaned_up([&] + require_cleaned_up([&] { - LLSD v; checkConversions("untitled", v, false, 0, 0.0, ""); + LLSD v; require_conversions("untitled", v, false, 0, 0.0, ""); - v = false; checkConversions("false", v, false, 0, 0.0, ""); - v = true; checkConversions("true", v, true, 1, 1.0, "true"); + v = false; require_conversions("false", v, false, 0, 0.0, ""); + v = true; require_conversions("true", v, true, 1, 1.0, "true"); - v = 0; checkConversions("zero", v, false, 0, 0.0, "0"); - v = 1; checkConversions("one", v, true, 1, 1.0, "1"); - v = -33; checkConversions("neg33", v, true, -33, -33.0, "-33"); + v = 0; require_conversions("zero", v, false, 0, 0.0, "0"); + v = 1; require_conversions("one", v, true, 1, 1.0, "1"); + v = -33; require_conversions("neg33", v, true, -33, -33.0, "-33"); - v = 0.0; checkConversions("0.0", v, false, 0, 0.0, "0"); - v = 0.5; checkConversions("point5", v, true, 0, 0.5, "0.5"); - v = 0.9; checkConversions("point9", v, true, 0, 0.9, "0.9"); - v = -3.9; checkConversions("neg3dot9", v, true, -3, -3.9, "-3.9"); + v = 0.0; require_conversions("0.0", v, false, 0, 0.0, "0"); + v = 0.5; require_conversions("point5", v, true, 0, 0.5, "0.5"); + v = 0.9; require_conversions("point9", v, true, 0, 0.9, "0.9"); + v = -3.9; require_conversions("neg3dot9", v, true, -3, -3.9, "-3.9"); // Get rid of NaN test. First, some libraries don't reliably return // NaN for sqrt(-1.0) -- meaning that I don't even know how to // portably, reliably produce a NaN value. Second, we observe failures @@ -281,43 +262,41 @@ namespace tut // we're not testing any LLSD code: we're testing the local library's // vsnprintf() function, which (empirically) produces idiosyncratic // results. This is just not a good test case. - // v = sqrt(-1.0); checkConversions("NaN", v, false, 0, sqrt(-1.0), "nan"); + // v = sqrt(-1.0); require_conversions("NaN", v, false, 0, sqrt(-1.0), "nan"); - v = ""; checkConversions("empty", v, false, 0, 0.0, ""); - v = "0"; checkConversions("digit0", v, true, 0, 0.0, "0"); - v = "10"; checkConversions("digit10", v, true, 10, 10.0, "10"); - v = "-2.345"; checkConversions("decdigits", v, + v = ""; require_conversions("empty", v, false, 0, 0.0, ""); + v = "0"; require_conversions("digit0", v, true, 0, 0.0, "0"); + v = "10"; require_conversions("digit10", v, true, 10, 10.0, "10"); + v = "-2.345"; require_conversions("decdigits", v, true, -2, -2.345, "-2.345"); - v = "apple"; checkConversions("apple", v, true, 0, 0.0, "apple"); - v = "33bob"; checkConversions("digialpha", v, true, 0, 0.0, "33bob"); - v = " "; checkConversions("space", v, true, 0, 0.0, " "); - v = "\n"; checkConversions("newline", v, true, 0, 0.0, "\n"); + v = "apple"; require_conversions("apple", v, true, 0, 0.0, "apple"); + v = "33bob"; require_conversions("digialpha", v, true, 0, 0.0, "33bob"); + v = " "; require_conversions("space", v, true, 0, 0.0, " "); + v = "\n"; require_conversions("newline", v, true, 0, 0.0, "\n"); }); } template - static void checkRoundTrip(const std::string& msg, const LLSD& actual, + static void require_round_trip(const std::string& what, const LLSD& actual, const char* sExpected, T vExpected) { + INFO(what); std::string str = actual.asString(); if (sExpected) { - UNSCOPED_INFO(msg + " string"); REQUIRE(str == sExpected); } LLSD u(str); LLSDTraits traits; - UNSCOPED_INFO(msg + " value"); REQUIRE(traits.get(u) == vExpected); } TEST_CASE("LLSD(new): UUID and Date and URI convert to and from String", "[LLSD_new]") - // conversion of String to and from UUID, Date and URI. { - ensure_cleaned_up([&] + require_cleaned_up([&] { LLSD v; @@ -325,29 +304,29 @@ namespace tut LLUUID someUUID; someUUID.generate(); - v = nullUUID; checkRoundTrip("null uuid", v, + v = nullUUID; require_round_trip("null uuid", v, "00000000-0000-0000-0000-000000000000", nullUUID); - v = someUUID; checkRoundTrip("random uuid", v, 0, someUUID); + v = someUUID; require_round_trip("random uuid", v, 0, someUUID); LLDate epoch; LLDate beta("2003-04-30T04:00:00Z"); LLDate oneOh("2003-06-23T04:00:00Z"); - v = epoch; checkRoundTrip("epoch date", v, 0, epoch); - v = beta; checkRoundTrip("beta date", v, + v = epoch; require_round_trip("epoch date", v, 0, epoch); + v = beta; require_round_trip("beta date", v, "2003-04-30T04:00:00Z", beta); - v = oneOh; checkRoundTrip("1.0 date", v, + v = oneOh; require_round_trip("1.0 date", v, "2003-06-23T04:00:00Z", oneOh); LLURI empty; LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/"); LLURI mail("mailto:zero.linden@secondlife.com"); - v = empty; checkRoundTrip("empty URI", v, 0, empty); - v = path; checkRoundTrip("path URI", v, + v = empty; require_round_trip("empty URI", v, 0, empty); + v = path; require_round_trip("path URI", v, "http://slurl.com/secondlife/Ambleside/57/104/26/", path); - v = mail; checkRoundTrip("mail URI", v, + v = mail; require_round_trip("mail URI", v, "mailto:zero.linden@secondlife.com", mail); }); } @@ -357,23 +336,23 @@ namespace tut // checking for shared values after constr. or assignment // checking in both the same type and change of type case { - ensure_cleaned_up([&] + require_cleaned_up([&] { { LLSD v = 42; LLSD w0(v); - ensureTypeAndValue("int constr.", w0, 42); + require_type_and_value("int constr.", w0, 42); LLSD w1(v); w1 = 13; - ensureTypeAndValue("int constr. change case 1", w1, 13); - ensureTypeAndValue("int constr. change case 2", v, 42); + require_type_and_value("int constr. change case 1", w1, 13); + require_type_and_value("int constr. change case 2", v, 42); LLSD w2(v); v = 7; - ensureTypeAndValue("int constr. change case 3", w2, 42); - ensureTypeAndValue("int constr. change case 4", v, 7); + require_type_and_value("int constr. change case 3", w2, 42); + require_type_and_value("int constr. change case 4", v, 7); } { @@ -381,13 +360,13 @@ namespace tut LLSD w1(v); w1 = "bob"; - ensureTypeAndValue("string constr. change case 1", w1, "bob"); - ensureTypeAndValue("string constr. change case 2", v, 42); + require_type_and_value("string constr. change case 1", w1, "bob"); + require_type_and_value("string constr. change case 2", v, 42); LLSD w2(v); v = "amy"; - ensureTypeAndValue("string constr. change case 3", w2, 42); - ensureTypeAndValue("string constr. change case 4", v, "amy"); + require_type_and_value("string constr. change case 3", w2, 42); + require_type_and_value("string constr. change case 4", v, "amy"); } { @@ -395,19 +374,19 @@ namespace tut LLSD w0; w0 = v; - ensureTypeAndValue("int assign", w0, 42); + require_type_and_value("int assign", w0, 42); LLSD w1; w1 = v; w1 = 13; - ensureTypeAndValue("int assign change case 1", w1, 13); - ensureTypeAndValue("int assign change case 2", v, 42); + require_type_and_value("int assign change case 1", w1, 13); + require_type_and_value("int assign change case 2", v, 42); LLSD w2; w2 = v; v = 7; - ensureTypeAndValue("int assign change case 3", w2, 42); - ensureTypeAndValue("int assign change case 4", v, 7); + require_type_and_value("int assign change case 3", w2, 42); + require_type_and_value("int assign change case 4", v, 7); } { @@ -416,14 +395,14 @@ namespace tut LLSD w1; w1 = v; w1 = "bob"; - ensureTypeAndValue("string assign change case 1", w1, "bob"); - ensureTypeAndValue("string assign change case 2", v, 42); + require_type_and_value("string assign change case 1", w1, "bob"); + require_type_and_value("string assign change case 2", v, 42); LLSD w2; w2 = v; v = "amy"; - ensureTypeAndValue("string assign change case 3", w2, 42); - ensureTypeAndValue("string assign change case 4", v, "amy"); + require_type_and_value("string assign change case 3", w2, 42); + require_type_and_value("string assign change case 4", v, "amy"); } }); } @@ -434,13 +413,13 @@ namespace tut // assignments should invoke the right conversion without it being // mentioned explicitly. The few exceptions are marked SAD. { - ensure_cleaned_up([&] + require_cleaned_up([&] { LLSD v(" 42.375"); bool b = false; - b = v; UNSCOPED_INFO("assign to bool"); REQUIRE(b == true); - b = (bool)v; UNSCOPED_INFO("cast to bool"); REQUIRE(b == true); + b = v; UNSCOPED_INFO("assign to bool"); REQUIRE(b); + b = (bool)v; UNSCOPED_INFO("cast to bool"); REQUIRE(b); int i = 99; i = v; UNSCOPED_INFO("assign to int"); REQUIRE(i == 42); @@ -489,29 +468,29 @@ namespace tut // These should invoke the right conversion without it being // mentioned explicitly. The few exceptions are marked SAD. { - ensure_cleaned_up([&] + require_cleaned_up([&] { LLSD v(" 42.375"); - bool b1(v); UNSCOPED_INFO("contruct bool"); REQUIRE(b1 == true); - bool b2 = v; UNSCOPED_INFO("initialize bool"); REQUIRE(b2 == true); + bool b1(v); UNSCOPED_INFO("construct bool"); REQUIRE(b1); + bool b2 = v; UNSCOPED_INFO("initialize bool"); REQUIRE(b2); - int i1(v); UNSCOPED_INFO("contruct int"); REQUIRE(i1 == 42); + int i1(v); UNSCOPED_INFO("construct int"); REQUIRE(i1 == 42); int i2 = v; UNSCOPED_INFO("initialize int"); REQUIRE(i2 == 42); - double d1(v); UNSCOPED_INFO("contruct double"); REQUIRE(d1 == 42.375); + double d1(v); UNSCOPED_INFO("construct double"); REQUIRE(d1 == 42.375); double d2 = v; UNSCOPED_INFO("initialize double"); REQUIRE(d2 == 42.375); std::string s1(v); std::string s2 = v; - UNSCOPED_INFO("contruct string"); + UNSCOPED_INFO("construct string"); REQUIRE(s1 == " 42.375"); UNSCOPED_INFO("initialize string"); REQUIRE(s2 == " 42.375"); std::string t1(v); std::string t2 = v.asString(); // SAD - UNSCOPED_INFO("contruct std::string"); + UNSCOPED_INFO("construct std::string"); REQUIRE(t1 == " 42.375"); UNSCOPED_INFO("initialize std::string"); REQUIRE(t2 == " 42.375"); @@ -520,7 +499,7 @@ namespace tut v = uuidStr; LLUUID uuid1(v.asUUID()); // SAD LLUUID uuid2 = v; - UNSCOPED_INFO("contruct LLUUID"); + UNSCOPED_INFO("construct LLUUID"); REQUIRE(uuid1 == LLUUID(uuidStr)); UNSCOPED_INFO("initialize LLUUID"); REQUIRE(uuid2 == LLUUID(uuidStr)); @@ -529,7 +508,7 @@ namespace tut v = dateStr; LLDate date1(v.asDate()); // SAD LLDate date2 = v; - UNSCOPED_INFO("contruct LLDate"); + UNSCOPED_INFO("construct LLDate"); REQUIRE(date1.asString() == dateStr); UNSCOPED_INFO("initialize LLDate"); REQUIRE(date2.asString() == dateStr); @@ -538,7 +517,7 @@ namespace tut v = uriStr; LLURI uri1(v.asURI()); // SAD LLURI uri2 = v; - UNSCOPED_INFO("contruct LLURI"); + UNSCOPED_INFO("construct LLURI"); REQUIRE(uri1.asString() == uriStr); UNSCOPED_INFO("initialize LLURI"); REQUIRE(uri2.asString() == uriStr); @@ -550,7 +529,7 @@ namespace tut // test to make sure v is interpreted as a bool in a various // scenarios. { - ensure_cleaned_up([&] + require_cleaned_up([&] { LLSD v = "0"; // magic value that is interpreted as boolean true, but integer false! @@ -562,7 +541,6 @@ namespace tut if(v) { - UNSCOPED_INFO("if converted to bool"); REQUIRE(true); } else @@ -578,9 +556,8 @@ namespace tut } TEST_CASE("LLSD(new): map has/get/insert/erase and copy", "[LLSD_new]") - // map operations { - ensure_cleaned_up([&] + require_cleaned_up([&] { LLSD v; UNSCOPED_INFO("undefined has no members"); @@ -589,7 +566,6 @@ namespace tut REQUIRE(v.get("bob").isUndefined()); v = LLSD::emptyMap(); - UNSCOPED_INFO("empty map is a map"); REQUIRE(v.isMap()); UNSCOPED_INFO("empty map has no members"); REQUIRE_FALSE(v.has("cam")); @@ -600,66 +576,56 @@ namespace tut v.insert("eli", 43); UNSCOPED_INFO("insert converts to map"); REQUIRE(v.isMap()); - UNSCOPED_INFO("inserted key is present"); REQUIRE(v.has("eli")); - ensureTypeAndValue("inserted value", v.get("eli"), 43); + require_type_and_value("inserted value", v.get("eli"), 43); v.insert("fra", false); UNSCOPED_INFO("first key still present"); REQUIRE(v.has("eli")); - UNSCOPED_INFO("second key is present"); REQUIRE(v.has("fra")); - ensureTypeAndValue("first value", v.get("eli"), 43); - ensureTypeAndValue("second value", v.get("fra"), false); + require_type_and_value("first value", v.get("eli"), 43); + require_type_and_value("second value", v.get("fra"), false); v.erase("eli"); - UNSCOPED_INFO("first key now gone"); REQUIRE_FALSE(v.has("eli")); UNSCOPED_INFO("second key still present"); REQUIRE(v.has("fra")); - UNSCOPED_INFO("first value gone"); REQUIRE(v.get("eli").isUndefined()); - ensureTypeAndValue("second value sill there", v.get("fra"), false); + require_type_and_value("second value still there", v.get("fra"), false); v.erase("fra"); - UNSCOPED_INFO("second key now gone"); REQUIRE_FALSE(v.has("fra")); - UNSCOPED_INFO("second value gone"); REQUIRE(v.get("fra").isUndefined()); v["gil"] = (std::string)"good morning"; - UNSCOPED_INFO("third key present"); REQUIRE(v.has("gil")); - ensureTypeAndValue("third key value", v.get("gil"), "good morning"); + require_type_and_value("third key value", v.get("gil"), "good morning"); const LLSD& cv = v; // FIX ME IF POSSIBLE - UNSCOPED_INFO("missing key"); REQUIRE(cv["ham"].isUndefined()); - UNSCOPED_INFO("key not present"); + UNSCOPED_INFO("const operator[] does not insert"); REQUIRE_FALSE(v.has("ham")); LLSD w = 43; const LLSD& cw = w; // FIX ME IF POSSIBLE int i = cw["ian"]; - ensureTypeAndValue("other missing value", i, 0); - UNSCOPED_INFO("other missing key"); + require_type_and_value("other missing value", i, 0); + UNSCOPED_INFO("const operator[] does not insert"); REQUIRE_FALSE(w.has("ian")); - UNSCOPED_INFO("no conversion"); + UNSCOPED_INFO("const operator[] does not convert"); REQUIRE(w.isInteger()); LLSD x; x = v; - UNSCOPED_INFO("copy map type"); REQUIRE(x.isMap()); - ensureTypeAndValue("copy map value gil", x.get("gil"), "good morning"); + require_type_and_value("copy map value gil", x.get("gil"), "good morning"); }); } TEST_CASE("LLSD(new): array append/insert/set/erase and copy", "[LLSD_new]") - // array operations { - ensure_cleaned_up([&] + require_cleaned_up([&] { LLSD v; UNSCOPED_INFO("undefined has no size"); @@ -668,36 +634,32 @@ namespace tut REQUIRE(v.get(0).isUndefined()); v = LLSD::emptyArray(); - UNSCOPED_INFO("empty array is an array"); REQUIRE(v.isArray()); UNSCOPED_INFO("empty array has no size"); REQUIRE(v.size() == 0); - UNSCOPED_INFO("empty map get() is undefined"); + UNSCOPED_INFO("empty array get() is undefined"); REQUIRE(v.get(0).isUndefined()); v.clear(); v.append(88); v.append("noodle"); v.append(true); - UNSCOPED_INFO("appened array size"); REQUIRE(v.size() == 3); - UNSCOPED_INFO("append array is an array"); REQUIRE(v.isArray()); - ensureTypeAndValue("append 0", v[0], 88); - ensureTypeAndValue("append 1", v[1], "noodle"); - ensureTypeAndValue("append 2", v[2], true); + require_type_and_value("append 0", v[0], 88); + require_type_and_value("append 1", v[1], "noodle"); + require_type_and_value("append 2", v[2], true); v.insert(0, 77); v.insert(2, "soba"); v.insert(4, false); - UNSCOPED_INFO("inserted array size"); REQUIRE(v.size() == 6); - ensureTypeAndValue("post insert 0", v[0], 77); - ensureTypeAndValue("post insert 1", v[1], 88); - ensureTypeAndValue("post insert 2", v[2], "soba"); - ensureTypeAndValue("post insert 3", v[3], "noodle"); - ensureTypeAndValue("post insert 4", v[4], false); - ensureTypeAndValue("post insert 5", v[5], true); + require_type_and_value("post insert 0", v[0], 77); + require_type_and_value("post insert 1", v[1], 88); + require_type_and_value("post insert 2", v[2], "soba"); + require_type_and_value("post insert 3", v[3], "noodle"); + require_type_and_value("post insert 4", v[4], false); + require_type_and_value("post insert 5", v[5], true); LLSD beyond; beyond.insert(2, "far"); @@ -707,95 +669,89 @@ namespace tut REQUIRE(beyond[0].isUndefined()); UNSCOPED_INFO("beyond-end insert pad 1"); REQUIRE(beyond[1].isUndefined()); - ensureTypeAndValue("beyond-end insert value", beyond[2], "far"); + require_type_and_value("beyond-end insert value", beyond[2], "far"); - ensureTypeAndValue("get 1", v.get(1), 88); + require_type_and_value("get 1", v.get(1), 88); v.set(1, "hot"); - ensureTypeAndValue("set 1", v.get(1), "hot"); + require_type_and_value("set 1", v.get(1), "hot"); v.erase(3); - UNSCOPED_INFO("post erase array size"); REQUIRE(v.size() == 5); - ensureTypeAndValue("post erase 0", v[0], 77); - ensureTypeAndValue("post erase 1", v[1], "hot"); - ensureTypeAndValue("post erase 2", v[2], "soba"); - ensureTypeAndValue("post erase 3", v[3], false); - ensureTypeAndValue("post erase 4", v[4], true); + require_type_and_value("post erase 0", v[0], 77); + require_type_and_value("post erase 1", v[1], "hot"); + require_type_and_value("post erase 2", v[2], "soba"); + require_type_and_value("post erase 3", v[3], false); + require_type_and_value("post erase 4", v[4], true); v.append(34); - UNSCOPED_INFO("size after append"); REQUIRE(v.size() == 6); - ensureTypeAndValue("post append 5", v[5], 34); + require_type_and_value("post append 5", v[5], 34); LLSD w; w = v; - UNSCOPED_INFO("copy array type"); REQUIRE(w.isArray()); - UNSCOPED_INFO("copy array size"); REQUIRE(w.size() == 6); - ensureTypeAndValue("copy array 0", w[0], 77); - ensureTypeAndValue("copy array 1", w[1], "hot"); - ensureTypeAndValue("copy array 2", w[2], "soba"); - ensureTypeAndValue("copy array 3", w[3], false); - ensureTypeAndValue("copy array 4", w[4], true); - ensureTypeAndValue("copy array 5", w[5], 34); + require_type_and_value("copy array 0", w[0], 77); + require_type_and_value("copy array 1", w[1], "hot"); + require_type_and_value("copy array 2", w[2], "soba"); + require_type_and_value("copy array 3", w[3], false); + require_type_and_value("copy array 4", w[4], true); + require_type_and_value("copy array 5", w[5], 34); }); } TEST_CASE("LLSD(new): a copy shares no values with its original", "[LLSD_new]") - // no sharing { - ensure_cleaned_up([&] + require_cleaned_up([&] { LLSD a = 99; LLSD b = a; a = 34; - ensureTypeAndValue("top level original changed", a, 34); - ensureTypeAndValue("top level copy unaltered", b, 99); + require_type_and_value("top level original changed", a, 34); + require_type_and_value("top level copy unaltered", b, 99); b = a; b = 66; - ensureTypeAndValue("top level original unaltered", a, 34); - ensureTypeAndValue("top level copy changed", b, 66); + require_type_and_value("top level original unaltered", a, 34); + require_type_and_value("top level copy changed", b, 66); a[0] = "uno"; a[1] = 99; a[2] = 1.414; b = a; a[1] = 34; - ensureTypeAndValue("array member original changed", a[1], 34); - ensureTypeAndValue("array member copy unaltered", b[1], 99); + require_type_and_value("array member original changed", a[1], 34); + require_type_and_value("array member copy unaltered", b[1], 99); b = a; b[1] = 66; - ensureTypeAndValue("array member original unaltered", a[1], 34); - ensureTypeAndValue("array member copy changed", b[1], 66); + require_type_and_value("array member original unaltered", a[1], 34); + require_type_and_value("array member copy changed", b[1], 66); a["alpha"] = "uno"; a["beta"] = 99; a["gamma"] = 1.414; b = a; a["beta"] = 34; - ensureTypeAndValue("map member original changed", a["beta"], 34); - ensureTypeAndValue("map member copy unaltered", b["beta"], 99); + require_type_and_value("map member original changed", a["beta"], 34); + require_type_and_value("map member copy unaltered", b["beta"], 99); b = a; b["beta"] = 66; - ensureTypeAndValue("map member original unaltered", a["beta"], 34); - ensureTypeAndValue("map member copy changed", b["beta"], 66); + require_type_and_value("map member original unaltered", a["beta"], 34); + require_type_and_value("map member copy changed", b["beta"], 66); }); } TEST_CASE("LLSD(new): copies share their implementation and scalars never allocate", "[LLSD_new]") - // sharing implementation { - ensure_cleaned_up([&] + require_cleaned_up([&] { - ensure_allocations("copy construct undefinded", 0, [] + require_allocations("copy construct undefined", 0, [] { LLSD v; LLSD w = v; }); - ensure_allocations("assign undefined", 0, [] + require_allocations("assign undefined", 0, [] { LLSD v; LLSD w; @@ -803,27 +759,27 @@ namespace tut }); // scalar values are stored inline and never allocate - ensure_allocations("assign integer value", 0, [] + require_allocations("assign integer value", 0, [] { LLSD v = 45; v = 33; v = 0; }); - ensure_allocations("copy construct integer", 0, [] + require_allocations("copy construct integer", 0, [] { LLSD v = 45; LLSD w = v; }); - ensure_allocations("assign integer", 0, [] + require_allocations("assign integer", 0, [] { LLSD v = 45; LLSD w; w = v; }); - ensure_allocations("avoids extra clone", 1, [] + require_allocations("avoids extra clone", 1, [] { LLSD v = 45; LLSD w = v; @@ -831,7 +787,7 @@ namespace tut }); // map + array + two strings; integers, reals and undefs are inline - ensure_allocations("shared values test for threaded work", 4, [] + require_allocations("shared values test for threaded work", 4, [] { //U32 start_llsd_count = LLSD::outstandingCount(); @@ -869,39 +825,30 @@ namespace tut } TEST_CASE("LLSD(new): assigning a null char pointer makes a string without crashing", "[LLSD_new]") - // make sure that assignment of char* NULL in a string does not crash. { LLSD v; v = (const char*)NULL; - UNSCOPED_INFO("type is a string"); CHECK(v.isString()); } TEST_CASE("LLSD(new): asXMLRPCValue serializes each type", "[LLSD_new]") - // XMLRPC value serialization { - ensure_cleaned_up([&] + require_cleaned_up([&] { - UNSCOPED_INFO("xmlrpc undef"); REQUIRE(LLSD().asXMLRPCValue() == ""); - UNSCOPED_INFO("xmlrpc bool"); REQUIRE(LLSD(true).asXMLRPCValue() == "1"); - UNSCOPED_INFO("xmlrpc int"); REQUIRE(LLSD(3).asXMLRPCValue() == "3"); - UNSCOPED_INFO("xmlrpc real"); REQUIRE(LLSD(1.5).asXMLRPCValue() == "1.5"); - UNSCOPED_INFO("xmlrpc string"); REQUIRE(LLSD("a" "1" @@ -910,7 +857,6 @@ namespace tut LLSD map = LLSD::emptyMap(); map["a"] = 1; - UNSCOPED_INFO("xmlrpc map"); REQUIRE(map.asXMLRPCValue() == "" "a1" diff --git a/indra/llcommon/tests/llsdjson_test.cpp b/indra/llcommon/tests/llsdjson_test.cpp index 13a0ae30c8..5e8888c2ec 100644 --- a/indra/llcommon/tests/llsdjson_test.cpp +++ b/indra/llcommon/tests/llsdjson_test.cpp @@ -41,155 +41,107 @@ namespace struct llsdjson_data { // parse json text expecting success - LLSD parse(const std::string& json) + LLSD require_parse(const std::string& json) { LLSD result; std::string errmsg; bool ok = LlsdFromJsonString(json, result, &errmsg); - UNSCOPED_INFO("parse failed for " + json + ": " + errmsg); + CAPTURE(json, errmsg); REQUIRE(ok); return result; } }; - // scalar parsing: types and values TEST_CASE_METHOD(llsdjson_data, "LlsdJson: scalars parse to the right type and value", "[LlsdJson]") { - LLSD val = parse("null"); - UNSCOPED_INFO("null type"); + LLSD val = require_parse("null"); CHECK(val.type() == LLSD::TypeUndefined); - val = parse("true"); - UNSCOPED_INFO("bool type"); + val = require_parse("true"); CHECK(val.type() == LLSD::TypeBoolean); - UNSCOPED_INFO("bool value"); CHECK(val.asBoolean()); - val = parse("false"); - UNSCOPED_INFO("false value"); + val = require_parse("false"); CHECK_FALSE(val.asBoolean()); - val = parse("42"); - UNSCOPED_INFO("int type"); + val = require_parse("42"); CHECK(val.type() == LLSD::TypeInteger); - UNSCOPED_INFO("int value"); CHECK(val.asInteger() == 42); - val = parse("-7"); - UNSCOPED_INFO("negative int"); + val = require_parse("-7"); CHECK(val.asInteger() == -7); - val = parse("3.25"); - UNSCOPED_INFO("real type"); + val = require_parse("3.25"); CHECK(val.type() == LLSD::TypeReal); - UNSCOPED_INFO("real value"); CHECK(val.asReal() == 3.25); - val = parse("\"hello world\""); - UNSCOPED_INFO("string type"); + val = require_parse("\"hello world\""); CHECK(val.type() == LLSD::TypeString); - UNSCOPED_INFO("string value"); CHECK(val.asString() == "hello world"); } - // integer vs real distinction is preserved by the parse TEST_CASE_METHOD(llsdjson_data, "LlsdJson: integer vs real distinction is preserved by the parse", "[LlsdJson]") { - UNSCOPED_INFO("1 is integer"); - CHECK(parse("1").type() == LLSD::TypeInteger); - UNSCOPED_INFO("1.0 is real"); - CHECK(parse("1.0").type() == LLSD::TypeReal); - UNSCOPED_INFO("1e0 is real"); - CHECK(parse("1e0").type() == LLSD::TypeReal); - UNSCOPED_INFO("0 is integer"); - CHECK(parse("0").type() == LLSD::TypeInteger); + CHECK(require_parse("1").type() == LLSD::TypeInteger); + CHECK(require_parse("1.0").type() == LLSD::TypeReal); + CHECK(require_parse("1e0").type() == LLSD::TypeReal); + CHECK(require_parse("0").type() == LLSD::TypeInteger); } - // arrays: order, nesting, empties TEST_CASE_METHOD(llsdjson_data, "LlsdJson: arrays parse in order with nesting and empty arrays", "[LlsdJson]") { - LLSD val = parse("[1, \"two\", 3.0, null, [true]]"); - UNSCOPED_INFO("array type"); + LLSD val = require_parse("[1, \"two\", 3.0, null, [true]]"); CHECK(val.type() == LLSD::TypeArray); - UNSCOPED_INFO("array size"); REQUIRE(val.size() == 5); - UNSCOPED_INFO("elt 0"); CHECK(val[0].asInteger() == 1); - UNSCOPED_INFO("elt 1"); CHECK(val[1].asString() == "two"); - UNSCOPED_INFO("elt 2"); CHECK(val[2].asReal() == 3.0); - UNSCOPED_INFO("elt 3 undefined"); CHECK(val[3].type() == LLSD::TypeUndefined); - UNSCOPED_INFO("nested array"); - CHECK(val[4][0].asBoolean() == true); + CHECK(val[4][0].asBoolean()); - val = parse("[]"); - UNSCOPED_INFO("empty array type"); + val = require_parse("[]"); CHECK(val.type() == LLSD::TypeArray); - UNSCOPED_INFO("empty array size"); - REQUIRE(val.size() == 0); + CHECK(val.size() == 0); } - // objects: keys, nesting, empties TEST_CASE_METHOD(llsdjson_data, "LlsdJson: objects parse by key with nesting and empty objects", "[LlsdJson]") { - LLSD val = parse("{\"a\": 1, \"b\": {\"c\": [2, 3]}}"); - UNSCOPED_INFO("map type"); + LLSD val = require_parse("{\"a\": 1, \"b\": {\"c\": [2, 3]}}"); CHECK(val.type() == LLSD::TypeMap); - UNSCOPED_INFO("map size"); REQUIRE(val.size() == 2); - UNSCOPED_INFO("member a"); CHECK(val["a"].asInteger() == 1); - UNSCOPED_INFO("nested member"); CHECK(val["b"]["c"][1].asInteger() == 3); - val = parse("{}"); - UNSCOPED_INFO("empty map type"); + val = require_parse("{}"); CHECK(val.type() == LLSD::TypeMap); - UNSCOPED_INFO("empty map size"); - REQUIRE(val.size() == 0); + CHECK(val.size() == 0); } - // serialization golden strings TEST_CASE_METHOD(llsdjson_data, "LlsdJson: serialization writes the golden strings", "[LlsdJson]") { - UNSCOPED_INFO("undefined"); CHECK(LlsdToJson(LLSD()) == "null"); - UNSCOPED_INFO("true"); CHECK(LlsdToJson(LLSD(true)) == "true"); - UNSCOPED_INFO("false"); CHECK(LlsdToJson(LLSD(false)) == "false"); - UNSCOPED_INFO("integer"); CHECK(LlsdToJson(LLSD(17)) == "17"); - UNSCOPED_INFO("negative"); CHECK(LlsdToJson(LLSD(-17)) == "-17"); - UNSCOPED_INFO("real"); CHECK(LlsdToJson(LLSD(2.5)) == "2.5"); - UNSCOPED_INFO("string"); CHECK(LlsdToJson(LLSD("simple")) == "\"simple\""); - UNSCOPED_INFO("empty string"); CHECK(LlsdToJson(LLSD("")) == "\"\""); - UNSCOPED_INFO("empty map"); CHECK(LlsdToJson(LLSD::emptyMap()) == "{}"); - UNSCOPED_INFO("empty array"); CHECK(LlsdToJson(LLSD::emptyArray()) == "[]"); LLSD map; map["b"] = 2; map["a"] = 1; // LLSD maps iterate in sorted key order - UNSCOPED_INFO("map"); CHECK(LlsdToJson(map) == "{\"a\":1,\"b\":2}"); LLSD array; array.append(1); array.append("x"); - UNSCOPED_INFO("array"); CHECK(LlsdToJson(array) == "[1,\"x\"]"); } - // string escaping in serialization TEST_CASE_METHOD(llsdjson_data, "LlsdJson: string escaping in serialization", "[LlsdJson]") { UNSCOPED_INFO("quote"); @@ -203,55 +155,41 @@ namespace // control character must be escaped one way or another; round-trip it LLSD ctrl; - UNSCOPED_INFO("control char parses"); REQUIRE(LlsdFromJsonString(LlsdToJson(LLSD(std::string("a\x01z"))), ctrl)); - UNSCOPED_INFO("control char round-trip"); CHECK(ctrl.asString() == std::string("a\x01z")); // UTF-8 passes through unescaped or escaped, but must round-trip std::string utf8("\xE3\x81\x93\xE3\x82\x93 caf\xC3\xA9"); LLSD uni; - UNSCOPED_INFO("utf8 parses"); REQUIRE(LlsdFromJsonString(LlsdToJson(LLSD(utf8)), uni)); - UNSCOPED_INFO("utf8 round-trip"); CHECK(uni.asString() == utf8); } - // special scalar types serialize as strings TEST_CASE_METHOD(llsdjson_data, "LlsdJson: special scalar types serialize as strings", "[LlsdJson]") { LLUUID id("c96f9b8e-f5ad-4b72-b6f7-a2b30d1444d2"); - UNSCOPED_INFO("uuid"); CHECK(LlsdToJson(LLSD(id)) == "\"c96f9b8e-f5ad-4b72-b6f7-a2b30d1444d2\""); LLDate date("2026-06-11T12:34:56Z"); - UNSCOPED_INFO("date"); CHECK(LlsdToJson(LLSD(date)) == "\"2026-06-11T12:34:56Z\""); LLURI uri("http://example.com/path"); - UNSCOPED_INFO("uri"); CHECK(LlsdToJson(LLSD(uri)) == "\"http://example.com/path\""); } - // non-finite reals degrade to null (JSON has no NaN/Infinity) TEST_CASE_METHOD(llsdjson_data, "LlsdJson: non-finite reals degrade to null (JSON has no NaN/Infinity)", "[LlsdJson]") { - UNSCOPED_INFO("nan"); CHECK(LlsdToJson(LLSD(std::numeric_limits::quiet_NaN())) == "null"); - UNSCOPED_INFO("inf"); CHECK(LlsdToJson(LLSD(std::numeric_limits::infinity())) == "null"); - UNSCOPED_INFO("-inf"); CHECK(LlsdToJson(LLSD(-std::numeric_limits::infinity())) == "null"); // and stay valid inside containers LLSD map; map["bad"] = std::numeric_limits::quiet_NaN(); - UNSCOPED_INFO("nan in map"); CHECK(LlsdToJson(map) == "{\"bad\":null}"); } - // structured round trip preserves everything TEST_CASE_METHOD(llsdjson_data, "LlsdJson: structured round trip preserves everything", "[LlsdJson]") { LLSD src; @@ -266,27 +204,23 @@ namespace LLSD dst; std::string json = LlsdToJson(src); - UNSCOPED_INFO("round-trip parses: " + json); + CAPTURE(json); REQUIRE(LlsdFromJsonString(json, dst)); - UNSCOPED_INFO("round-trip equality: " + json); CHECK(llsd_equals(src, dst)); } - // full-precision reals survive the round trip TEST_CASE_METHOD(llsdjson_data, "LlsdJson: full-precision reals survive the round trip", "[LlsdJson]") { F64 vals[] = { 0.1, 1.0/3.0, 2.718281828459045, 1e-300, 1.7976931348623157e308 }; for (F64 v : vals) { LLSD parsed; - UNSCOPED_INFO("real parses"); + CAPTURE(v); REQUIRE(LlsdFromJsonString(LlsdToJson(LLSD(v)), parsed)); - UNSCOPED_INFO("real precision"); REQUIRE(parsed.asReal() == v); } } - // parse failures report errors and clear the output TEST_CASE_METHOD(llsdjson_data, "LlsdJson: parse failures report errors and clear the output", "[LlsdJson]") { const char* bad[] = @@ -309,49 +243,37 @@ namespace LLSD out = LLSD::emptyMap(); std::string errmsg; bool ok = LlsdFromJsonString(json, out, &errmsg); - UNSCOPED_INFO(std::string("rejects: ") + json); + CAPTURE(json); REQUIRE_FALSE(ok); - UNSCOPED_INFO(std::string("errmsg set for: ") + json); REQUIRE_FALSE(errmsg.empty()); - UNSCOPED_INFO(std::string("output cleared for: ") + json); REQUIRE(out.type() == LLSD::TypeUndefined); } // errmsg pointer is optional LLSD out; - UNSCOPED_INFO("no errmsg crash"); + UNSCOPED_INFO("without an errmsg pointer"); CHECK_FALSE(LlsdFromJsonString("{", out)); } - // unicode object keys TEST_CASE_METHOD(llsdjson_data, "LlsdJson: unicode object keys round-trip", "[LlsdJson]") { LLSD src; src["caf\xC3\xA9"] = 1; LLSD dst; - UNSCOPED_INFO("unicode key parses"); REQUIRE(LlsdFromJsonString(LlsdToJson(src), dst)); - UNSCOPED_INFO("unicode key value"); CHECK(dst["caf\xC3\xA9"].asInteger() == 1); } - // padded_string overload parses in place TEST_CASE_METHOD(llsdjson_data, "LlsdJson: padded_string overload parses in place", "[LlsdJson]") { LLSD out; - UNSCOPED_INFO("padded parse"); REQUIRE(LlsdFromJsonString(simdjson::padded_string(std::string("{\"a\": [1, 2.5, \"three\"]}")), out)); - UNSCOPED_INFO("padded int"); CHECK(out["a"][0].asInteger() == 1); - UNSCOPED_INFO("padded real"); CHECK(out["a"][1].asReal() == 2.5); - UNSCOPED_INFO("padded string"); CHECK(out["a"][2].asString() == "three"); std::string errmsg; - UNSCOPED_INFO("padded parse fails"); REQUIRE_FALSE(LlsdFromJsonString(simdjson::padded_string(std::string("{")), out, &errmsg)); - UNSCOPED_INFO("padded errmsg set"); - REQUIRE_FALSE(errmsg.empty()); + CHECK_FALSE(errmsg.empty()); } } diff --git a/indra/llcommon/tests/llsdserialize_python_test.cpp b/indra/llcommon/tests/llsdserialize_python_test.cpp index 4b86c4e607..0949395270 100644 --- a/indra/llcommon/tests/llsdserialize_python_test.cpp +++ b/indra/llcommon/tests/llsdserialize_python_test.cpp @@ -50,7 +50,6 @@ #include "altestfailure.h" #include "altestmatchers.h" #include "../test/namedtempfile.h" -#include "stringize.h" #include "StringVec.h" #include @@ -73,6 +72,7 @@ namespace tut UNSCOPED_INFO("Set $PYTHON to the Python interpreter"); REQUIRE_FALSE(PYTHON.empty()); + INFO(desc << " script"); NamedTempFile scriptfile("py", script); #if LL_WINDOWS @@ -85,12 +85,10 @@ namespace tut { char buffer[256]; strerror_s(buffer, errno); // C++ can infer the buffer size! :-O - UNSCOPED_INFO((STRINGIZE("Couldn't run Python " << desc << "script: " << buffer))); - REQUIRE(false); + FAIL("couldn't run Python: " << buffer); } else { - UNSCOPED_INFO(STRINGIZE(desc << " script terminated with rc " << rc)); REQUIRE(rc == expect); } @@ -103,7 +101,7 @@ namespace tut params.args.add(arg); } LLProcessPtr py(LLProcess::create(params)); - UNSCOPED_INFO((STRINGIZE("Couldn't launch " << desc << " script"))); + UNSCOPED_INFO("couldn't launch"); REQUIRE(bool(py)); // Implementing timeout would mean messing with alarm() and // catching SIGALRM... later maybe... @@ -111,8 +109,7 @@ namespace tut if (waitpid(py->getProcessID(), &status, 0) == -1) { int waitpid_errno(errno); - UNSCOPED_INFO((STRINGIZE("Couldn't retrieve rc from " << desc << " script: " - "waitpid() errno " << waitpid_errno))); + UNSCOPED_INFO("couldn't retrieve rc: waitpid() failed"); REQUIRE(waitpid_errno == ECHILD); } else @@ -120,18 +117,15 @@ namespace tut if (WIFEXITED(status)) { int rc(WEXITSTATUS(status)); - UNSCOPED_INFO((STRINGIZE(desc << " script terminated with rc " << rc))); REQUIRE(rc == expect); } else if (WIFSIGNALED(status)) { - UNSCOPED_INFO((STRINGIZE(desc << " script terminated by signal " << WTERMSIG(status)))); - REQUIRE(false); + FAIL("terminated by signal " << WTERMSIG(status)); } else { - UNSCOPED_INFO((STRINGIZE(desc << " script produced impossible status " << status))); - REQUIRE(false); + FAIL("produced impossible status " << status); } } #endif @@ -376,14 +370,13 @@ namespace tut LLSD item; try { - UNSCOPED_INFO("Failed to read LLSD::Integer from Python"); + UNSCOPED_INFO("reading the Integer"); REQUIRE(itemFromStream(inf, item, parse)); REQUIRE(item.asInteger() == 17); - UNSCOPED_INFO("Failed to read LLSD::Real from Python"); + UNSCOPED_INFO("reading the Real"); REQUIRE(itemFromStream(inf, item, parse)); - UNSCOPED_INFO("Bad LLSD::Real value from Python"); REQUIRE_THAT(item.asReal(), ll_test::WithinBits(3.14, 7)); // 7 bits ~= 0.01 - UNSCOPED_INFO("Failed to read LLSD::String from Python"); + UNSCOPED_INFO("reading the String"); REQUIRE(itemFromStream(inf, item, parse)); REQUIRE(item.asString() == "This string\n" diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp index 5ab32a7e53..f39a2e224c 100644 --- a/indra/llcommon/tests/llsdserialize_test.cpp +++ b/indra/llcommon/tests/llsdserialize_test.cpp @@ -47,7 +47,6 @@ typedef U32 uint32_t; #include #include "altestmatchers.h" -#include "stringize.h" #include "wrapllerrs.h" #include @@ -69,11 +68,11 @@ namespace } LLSD mSD; LLPointer mFormatter; - void xml_test(const char* name, const std::string& expected) + void require_xml(const char* what, const std::string& expected) { std::ostringstream ostr; mFormatter->format(mSD, ostr); - UNSCOPED_INFO(name); + INFO(what); REQUIRE(ostr.str() == expected); } }; @@ -84,35 +83,35 @@ namespace std::string expected; expected = "\n"; - xml_test("undef", expected); + require_xml("undef", expected); mSD = 3463; expected = "3463\n"; - xml_test("integer", expected); + require_xml("integer", expected); mSD = ""; expected = "\n"; - xml_test("empty string", expected); + require_xml("empty string", expected); mSD = "foobar"; expected = "foobar\n"; - xml_test("string", expected); + require_xml("string", expected); mSD = LLUUID::null; expected = "\n"; - xml_test("null uuid", expected); + require_xml("null uuid", expected); mSD = LLUUID("c96f9b1e-f589-4100-9774-d98643ce0bed"); expected = "c96f9b1e-f589-4100-9774-d98643ce0bed\n"; - xml_test("uuid", expected); + require_xml("uuid", expected); mSD = LLURI("https://secondlife.com/login"); expected = "https://secondlife.com/login\n"; - xml_test("uri", expected); + require_xml("uri", expected); mSD = LLDate("2006-04-24T16:11:33Z"); expected = "2006-04-24T16:11:33Z\n"; - xml_test("date", expected); + require_xml("date", expected); // Generated by: echo -n 'hello' | openssl enc -e -base64 std::vector hello; @@ -123,7 +122,7 @@ namespace hello.push_back('o'); mSD = hello; expected = "aGVsbG8=\n"; - xml_test("binary", expected); + require_xml("binary", expected); } TEST_CASE_METHOD(sd_xml_data, "LLSDXMLFormatter: booleans format as words or digits by boolalpha", "[LLSDXMLFormatter]") @@ -134,18 +133,18 @@ namespace mFormatter->boolalpha(true); mSD = true; expected = "true\n"; - xml_test("bool alpha true", expected); + require_xml("bool alpha true", expected); mSD = false; expected = "false\n"; - xml_test("bool alpha false", expected); + require_xml("bool alpha false", expected); mFormatter->boolalpha(false); mSD = true; expected = "1\n"; - xml_test("bool true", expected); + require_xml("bool true", expected); mSD = false; expected = "0\n"; - xml_test("bool false", expected); + require_xml("bool false", expected); } @@ -157,30 +156,30 @@ namespace // default format: shortest representation that round-trips exactly mSD = 0.1; expected = "0.1\n"; - xml_test("real default shortest", expected); + require_xml("real default shortest", expected); mSD = 1.0; expected = "1\n"; - xml_test("real default integral", expected); + require_xml("real default integral", expected); mFormatter->realFormat("%.2f"); mSD = 1.0; expected = "1.00\n"; - xml_test("real 1", expected); + require_xml("real 1", expected); mSD = -34379.0438; expected = "-34379.04\n"; - xml_test("real reduced precision", expected); + require_xml("real reduced precision", expected); mFormatter->realFormat("%.4f"); expected = "-34379.0438\n"; - xml_test("higher precision", expected); + require_xml("higher precision", expected); mFormatter->realFormat("%.0f"); mSD = 0.0; expected = "0\n"; - xml_test("no decimal 0", expected); + require_xml("no decimal 0", expected); mSD = 3287.4387; expected = "3287\n"; - xml_test("no decimal real number", expected); + require_xml("no decimal real number", expected); } TEST_CASE_METHOD(sd_xml_data, "LLSDXMLFormatter: arrays format empty and with elements", "[LLSDXMLFormatter]") @@ -190,15 +189,15 @@ namespace mSD = LLSD::emptyArray(); expected = "\n"; - xml_test("empty array", expected); + require_xml("empty array", expected); mSD.append(LLSD()); expected = "\n"; - xml_test("1 element array", expected); + require_xml("1 element array", expected); mSD.append(1); expected = "1\n"; - xml_test("2 element array", expected); + require_xml("2 element array", expected); } TEST_CASE_METHOD(sd_xml_data, "LLSDXMLFormatter: maps format empty and with their keys sorted", "[LLSDXMLFormatter]") @@ -208,15 +207,15 @@ namespace mSD = LLSD::emptyMap(); expected = "\n"; - xml_test("empty map", expected); + require_xml("empty map", expected); mSD["foo"] = "bar"; expected = "foobar\n"; - xml_test("1 element map", expected); + require_xml("1 element map", expected); mSD["baz"] = LLSD(); expected = "bazfoobar\n"; - xml_test("2 element map", expected); + require_xml("2 element map", expected); } TEST_CASE_METHOD(sd_xml_data, "LLSDXMLFormatter: binary formats as base64", "[LLSDXMLFormatter]") @@ -227,11 +226,11 @@ namespace // Generated by: echo -n 'hello' | openssl enc -e -base64 mSD = string_to_vector("hello"); expected = "aGVsbG8=\n"; - xml_test("binary", expected); + require_xml("binary", expected); mSD = string_to_vector("6|6|asdfhappybox|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|00000000-0000-0000-0000-000000000000|7fffffff|7fffffff|0|0|82000|450fe394-2904-c9ad-214c-a07eb7feec29|(No Description)|0|10|0"); expected = "Nnw2fGFzZGZoYXBweWJveHw2MGU0NGVjNS0zMDVjLTQzYzItOWExOS1iNGI4OWIxYWUyYTZ8NjBlNDRlYzUtMzA1Yy00M2MyLTlhMTktYjRiODliMWFlMmE2fDYwZTQ0ZWM1LTMwNWMtNDNjMi05YTE5LWI0Yjg5YjFhZTJhNnwwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDB8N2ZmZmZmZmZ8N2ZmZmZmZmZ8MHwwfDgyMDAwfDQ1MGZlMzk0LTI5MDQtYzlhZC0yMTRjLWEwN2ViN2ZlZWMyOXwoTm8gRGVzY3JpcHRpb24pfDB8MTB8MA==\n"; - xml_test("binary", expected); + require_xml("binary", expected); } TEST_CASE_METHOD(sd_xml_data, "LLSDXMLFormatter: a single-line document deserializes without a spurious error", "[LLSDXMLFormatter]") @@ -242,23 +241,21 @@ namespace // first chunk with nothing left for the stream to supply. That is a // successful parse and must not be reported as an error. Empty // containers are the common single-line form. - auto deserialize_no_spurious_error = + auto require_no_spurious_error = [](const std::string& xml, const LLSD& expected) { CaptureLog capture(LLError::LEVEL_INFO); std::istringstream input(xml); LLSD parsed; bool ok = LLSDSerialize::deserialize(parsed, input, xml.size()); - UNSCOPED_INFO((STRINGIZE("deserialize " << xml << " succeeded"))); + CAPTURE(xml); REQUIRE(ok); - UNSCOPED_INFO((STRINGIZE("deserialize " << xml << " value"))); REQUIRE_THAT(parsed, ll_test::LLSDEquals(expected)); - UNSCOPED_INFO((STRINGIZE("no spurious parse error for " << xml))); REQUIRE(capture.messageWith("Unexpected XML parsing error", false).empty()); }; - deserialize_no_spurious_error("\n", LLSD::emptyMap()); - deserialize_no_spurious_error("\n", LLSD::emptyArray()); + require_no_spurious_error("\n", LLSD::emptyMap()); + require_no_spurious_error("\n", LLSD::emptyArray()); } TEST_CASE_METHOD(sd_xml_data, "LLSDXMLFormatter: format restores the stream's precision and exception bits", "[LLSDXMLFormatter]") @@ -279,15 +276,12 @@ namespace S32 result = mFormatter->format(mSD, ostr); // Verify formatting succeeded - UNSCOPED_INFO("format should succeed"); CHECK(result >= 0); // Verify precision was restored - UNSCOPED_INFO("precision should be restored"); CHECK(ostr.precision() == 10); // Verify exceptions were restored - UNSCOPED_INFO("exception bits should be restored"); CHECK(ostr.exceptions() == original_exceptions); } @@ -301,15 +295,12 @@ namespace S32 result = mFormatter->format(mSD, ostr); // Verify formatting succeeded - UNSCOPED_INFO("format should succeed"); CHECK(result >= 0); // Verify exceptions were removed - UNSCOPED_INFO("exception bits should remain unchanged"); CHECK(ostr.exceptions() == original_exceptions); // Verify precision was still restored even on failure - UNSCOPED_INFO("precision should be restored even on stream failure"); CHECK(ostr.precision() == (std::streamsize)5); } } @@ -321,7 +312,7 @@ namespace ~TestLLSDSerializeData(); void doRoundTripTests(const std::string&); - void checkRoundTrip(const std::string&, const LLSD& v); + void require_round_trip(const std::string& what, const LLSD& v); void setFormatterParser(LLPointer formatter, LLPointer parser) { @@ -360,7 +351,7 @@ namespace { } - void TestLLSDSerializeData::checkRoundTrip(const std::string& msg, const LLSD& v) + void TestLLSDSerializeData::require_round_trip(const std::string& what, const LLSD& v) { std::stringstream stream; mFormatter(v, stream); @@ -370,7 +361,7 @@ namespace try { - UNSCOPED_INFO(msg); + INFO(what); REQUIRE_THAT(w, ll_test::LLSDEquals(v)); } catch (...) @@ -400,58 +391,58 @@ namespace void TestLLSDSerializeData::doRoundTripTests(const std::string& msg) { LLSD v; - checkRoundTrip(msg + " undefined", v); + require_round_trip(msg + " undefined", v); v = true; - checkRoundTrip(msg + " true bool", v); + require_round_trip(msg + " true bool", v); v = false; - checkRoundTrip(msg + " false bool", v); + require_round_trip(msg + " false bool", v); v = 1; - checkRoundTrip(msg + " positive int", v); + require_round_trip(msg + " positive int", v); v = 0; - checkRoundTrip(msg + " zero int", v); + require_round_trip(msg + " zero int", v); v = -1; - checkRoundTrip(msg + " negative int", v); + require_round_trip(msg + " negative int", v); v = 1234.5f; - checkRoundTrip(msg + " positive float", v); + require_round_trip(msg + " positive float", v); v = 0.0f; - checkRoundTrip(msg + " zero float", v); + require_round_trip(msg + " zero float", v); v = -1234.5f; - checkRoundTrip(msg + " negative float", v); + require_round_trip(msg + " negative float", v); // reals whose decimal expansion exceeds the default 6-digit stream // precision must still round-trip exactly v = 3.141592653589793; - checkRoundTrip(msg + " full precision real", v); + require_round_trip(msg + " full precision real", v); v = 1.0e300; - checkRoundTrip(msg + " huge real", v); + require_round_trip(msg + " huge real", v); v = -2.718281828459045e-12; - checkRoundTrip(msg + " tiny negative real", v); + require_round_trip(msg + " tiny negative real", v); // FIXME: need a NaN test v = LLUUID::null; - checkRoundTrip(msg + " null uuid", v); + require_round_trip(msg + " null uuid", v); LLUUID newUUID; newUUID.generate(); v = newUUID; - checkRoundTrip(msg + " new uuid", v); + require_round_trip(msg + " new uuid", v); v = ""; - checkRoundTrip(msg + " empty string", v); + require_round_trip(msg + " empty string", v); v = "some string"; - checkRoundTrip(msg + " non-empty string", v); + require_round_trip(msg + " non-empty string", v); v = "Second Life is a 3-D virtual world entirely built and owned by its residents. " @@ -474,7 +465,7 @@ namespace "\n" "Welcome to Second Life. We look forward to seeing you in-world!\n" ; - checkRoundTrip(msg + " long string", v); + require_round_trip(msg + " long string", v); static const U32 block_size = 0x000020; for (U32 block = 0x000000; block <= 0x10ffff; block += block_size) @@ -523,20 +514,20 @@ namespace std::ostringstream blockmsg; blockmsg << msg << " unicode string block 0x" << std::hex << block; - checkRoundTrip(blockmsg.str(), v); + require_round_trip(blockmsg.str(), v); } LLDate epoch; v = epoch; - checkRoundTrip(msg + " epoch date", v); + require_round_trip(msg + " epoch date", v); LLDate aDay("2002-12-07T05:07:15.00Z"); v = aDay; - checkRoundTrip(msg + " date", v); + require_round_trip(msg + " date", v); LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/"); v = path; - checkRoundTrip(msg + " url", v); + require_round_trip(msg + " url", v); const char source[] = "it must be a blue moon again"; std::vector data; @@ -544,37 +535,37 @@ namespace copy(&source[0], &source[sizeof(source)], back_inserter(data)); v = data; - checkRoundTrip(msg + " binary", v); + require_round_trip(msg + " binary", v); v = LLSD::emptyMap(); - checkRoundTrip(msg + " empty map", v); + require_round_trip(msg + " empty map", v); v = LLSD::emptyMap(); v["name"] = "luke"; //v.insert("name", "luke"); v["age"] = 3; //v.insert("age", 3); - checkRoundTrip(msg + " map", v); + require_round_trip(msg + " map", v); v.clear(); v["a"]["1"] = true; v["b"]["0"] = false; - checkRoundTrip(msg + " nested maps", v); + require_round_trip(msg + " nested maps", v); v = LLSD::emptyArray(); - checkRoundTrip(msg + " empty array", v); + require_round_trip(msg + " empty array", v); v = LLSD::emptyArray(); v.append("ali"); v.append(28); - checkRoundTrip(msg + " array", v); + require_round_trip(msg + " array", v); v.clear(); v[0][0] = true; v[1][0] = false; - checkRoundTrip(msg + " nested arrays", v); + require_round_trip(msg + " nested arrays", v); v = LLSD::emptyMap(); fillmap(v, 10, 3); // 10^6 maps - checkRoundTrip(msg + " many nested maps", v); + require_round_trip(msg + " many nested maps", v); } /*==========================================================================*| @@ -607,8 +598,8 @@ namespace mParser = new parser_t; } - void ensureParse( - const std::string& msg, + void require_parse( + const std::string& what, const std::string& in, const LLSD& expected_value, S32 expected_count, @@ -620,15 +611,12 @@ namespace LLSD parsed_result; mParser->reset(); // reset() call is needed since test code re-uses mParser S32 parsed_count = mParser->parse(input, parsed_result, in.size(), depth_limit); - UNSCOPED_INFO(msg.c_str()); + INFO(what); REQUIRE_THAT(parsed_result, ll_test::LLSDEquals(expected_value)); // This count check is really only useful for expected // parse failures, since the ensures equal will already // require equality. - std::string count_msg(msg); - count_msg += " (count)"; - UNSCOPED_INFO(count_msg); REQUIRE(parsed_count == expected_count); } @@ -650,22 +638,22 @@ namespace { // test handling of xml not recognized as llsd results in an // LLSD Undefined - ensureParse( + require_parse( "malformed xml", "ha ha", LLSD(), LLSDParser::PARSE_FAILURE); - ensureParse( + require_parse( "not llsd", "

ha ha

", LLSD(), LLSDParser::PARSE_FAILURE); - ensureParse( + require_parse( "value without llsd", "ha ha", LLSD(), LLSDParser::PARSE_FAILURE); - ensureParse( + require_parse( "key without llsd", "ha ha", LLSD(), @@ -681,7 +669,7 @@ namespace v["bob"] = LLSD(); v["cam"] = 1.23; - ensureParse( + require_parse( "unknown data type", "" "amy23" @@ -700,7 +688,7 @@ namespace v["amy"] = 23; v["cam"] = 1.23; - ensureParse( + require_parse( "map with html", "" "amy23" @@ -713,7 +701,7 @@ namespace v.clear(); v["amy"] = 23; v["cam"] = 1.23; - ensureParse( + require_parse( "map with value for key", "" "amy23" @@ -727,7 +715,7 @@ namespace v["amy"] = 23; v["bob"] = LLSD::emptyMap(); v["cam"] = 1.23; - ensureParse( + require_parse( "map with map of html", "" "amy23" @@ -745,7 +733,7 @@ namespace v[1] = LLSD(); v[2] = 1.23; - ensureParse( + require_parse( "array value of html", "" "23" @@ -759,7 +747,7 @@ namespace v[0] = 23; v[1] = LLSD::emptyMap(); v[2] = 1.23; - ensureParse( + require_parse( "array with map of html", "" "23" @@ -781,7 +769,7 @@ namespace // Generated by: echo -n 'hello' | openssl enc -e -base64 expected = string_to_vector("hello"); xml = "aGVsbG8=\n"; - ensureParse( + require_parse( "the word 'hello' packed in binary encoded base64", xml, expected, @@ -789,7 +777,7 @@ namespace expected = string_to_vector("6|6|asdfhappybox|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|00000000-0000-0000-0000-000000000000|7fffffff|7fffffff|0|0|82000|450fe394-2904-c9ad-214c-a07eb7feec29|(No Description)|0|10|0"); xml = "Nnw2fGFzZGZoYXBweWJveHw2MGU0NGVjNS0zMDVjLTQzYzItOWExOS1iNGI4OWIxYWUyYTZ8NjBlNDRlYzUtMzA1Yy00M2MyLTlhMTktYjRiODliMWFlMmE2fDYwZTQ0ZWM1LTMwNWMtNDNjMi05YTE5LWI0Yjg5YjFhZTJhNnwwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDB8N2ZmZmZmZmZ8N2ZmZmZmZmZ8MHwwfDgyMDAwfDQ1MGZlMzk0LTI5MDQtYzlhZC0yMTRjLWEwN2ViN2ZlZWMyOXwoTm8gRGVzY3JpcHRpb24pfDB8MTB8MA==\n"; - ensureParse( + require_parse( "a common binary blob for object -> agent offline inv transfer", xml, expected, @@ -801,8 +789,8 @@ namespace xml += "LWI0Yjg5YjFhZTJhNnwwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDB8N2ZmZmZm\n"; xml += "ZmZ8N2ZmZmZmZmZ8MHwwfDgyMDAwfDQ1MGZlMzk0LTI5MDQtYzlhZC0yMTRjLWEwN2ViN2ZlZWMy\n"; xml += "OXwoTm8gRGVzY3JpcHRpb24pfDB8MTB8MA==\n"; - ensureParse( - "a common binary blob for object -> agent offline inv transfer", + require_parse( + "the same blob in base64 broken into lines", xml, expected, 1); @@ -821,7 +809,7 @@ namespace LLSD v; v["deep"] = level_0; - ensureParse( + require_parse( "deep llsd xml map", "" "deep" @@ -846,17 +834,17 @@ namespace TEST_CASE_METHOD(TestLLSDXMLParsing, "llsd XML parsing: empty binary parses as empty Binary not undefined", "[llsd_XML_parsing]") { // empty binary must parse to an empty LLSD::Binary, not undef - ensureParse( + require_parse( "empty binary self-closed", "", LLSD(LLSD::Binary()), 1); - ensureParse( + require_parse( "empty binary open/close", "", LLSD(LLSD::Binary()), 1); - ensureParse( + require_parse( "whitespace-only binary", " \n ", LLSD(LLSD::Binary()), @@ -909,13 +897,13 @@ namespace uint32_t size = htonl(6); memcpy(&vec[1], &size, sizeof(uint32_t)); std::string str_good((char*)&vec[0], vec.size()); - ensureParse("correct string parse", str_good, value, 1); + require_parse("correct string parse", str_good, value, 1); size = htonl(7); memcpy(&vec[1], &size, sizeof(uint32_t)); std::string str_bad_1((char*)&vec[0], vec.size()); - ensureParse( - "incorrect size string parse", + require_parse( + "incorrect size string parse 1", str_bad_1, LLSD(), LLSDParser::PARSE_FAILURE); @@ -923,8 +911,8 @@ namespace size = htonl(100000); memcpy(&vec[1], &size, sizeof(uint32_t)); std::string str_bad_2((char*)&vec[0], vec.size()); - ensureParse( - "incorrect size string parse", + require_parse( + "incorrect size string parse 2", str_bad_2, LLSD(), LLSDParser::PARSE_FAILURE); @@ -946,12 +934,12 @@ namespace uint32_t size = htonl(6); memcpy(&vec[1], &size, sizeof(uint32_t)); std::string str_good((char*)&vec[0], vec.size()); - ensureParse("correct binary parse", str_good, value, 1); + require_parse("correct binary parse", str_good, value, 1); size = htonl(7); memcpy(&vec[1], &size, sizeof(uint32_t)); std::string str_bad_1((char*)&vec[0], vec.size()); - ensureParse( + require_parse( "incorrect size binary parse 1", str_bad_1, LLSD(), @@ -960,7 +948,7 @@ namespace size = htonl(100000); memcpy(&vec[1], &size, sizeof(uint32_t)); std::string str_bad_2((char*)&vec[0], vec.size()); - ensureParse( + require_parse( "incorrect size binary parse 2", str_bad_2, LLSD(), @@ -971,22 +959,22 @@ namespace { // test handling of xml not recognized as llsd results in an // LLSD Undefined - ensureParse( + require_parse( "malformed binary map", "{'ha ha'", LLSD(), LLSDParser::PARSE_FAILURE); - ensureParse( + require_parse( "malformed binary array", "['ha ha'", LLSD(), LLSDParser::PARSE_FAILURE); - ensureParse( + require_parse( "malformed binary string", "'ha ha", LLSD(), LLSDParser::PARSE_FAILURE); - ensureParse( + require_parse( "bad noise", "g48ejlnfr", LLSD(), @@ -994,19 +982,19 @@ namespace } TEST_CASE_METHOD(TestLLSDBinaryParsing, "llsd binary parsing: undef parses", "[llsd_binary_parsing]") { - ensureParse("valid undef", "!", LLSD(), 1); + require_parse("valid undef", "!", LLSD(), 1); } TEST_CASE_METHOD(TestLLSDBinaryParsing, "llsd binary parsing: booleans parse from 0 and 1 only", "[llsd_binary_parsing]") { LLSD val = false; - ensureParse("valid boolean false 2", "0", val, 1); + require_parse("valid boolean false 2", "0", val, 1); val = true; - ensureParse("valid boolean true 2", "1", val, 1); + require_parse("valid boolean true 2", "1", val, 1); val.clear(); - ensureParse("invalid true", "t", val, LLSDParser::PARSE_FAILURE); - ensureParse("invalid false", "f", val, LLSDParser::PARSE_FAILURE); + require_parse("invalid true", "t", val, LLSDParser::PARSE_FAILURE); + require_parse("invalid false", "f", val, LLSDParser::PARSE_FAILURE); } TEST_CASE_METHOD(TestLLSDBinaryParsing, "llsd binary parsing: a map parses only with the right sizes and terminator", "[llsd_binary_parsing]") @@ -1028,7 +1016,7 @@ namespace uint32_t val_int = htonl(23); memcpy(&vec[integer_loc], &val_int, sizeof(uint32_t)); std::string str_bad_1((char*)&vec[0], vec.size()); - ensureParse( + require_parse( "invalid key size", str_bad_1, LLSD(), @@ -1038,7 +1026,7 @@ namespace size = htonl(3); // correct size memcpy(&vec[key_size_loc], &size, sizeof(uint32_t)); std::string str_bad_2((char*)&vec[0], vec.size()); - ensureParse( + require_parse( "valid key size, unterminated map", str_bad_2, LLSD(), @@ -1049,7 +1037,7 @@ namespace val["amy"] = 23; vec.push_back('}'); std::string str_good((char*)&vec[0], vec.size()); - ensureParse( + require_parse( "valid map", str_good, val, @@ -1059,7 +1047,7 @@ namespace size = htonl(0); // 1 too few (for the map entry) memcpy(&vec[1], &size, sizeof(uint32_t)); std::string str_bad_3((char*)&vec[0], vec.size()); - ensureParse( + require_parse( "invalid map too long", str_bad_3, LLSD(), @@ -1068,7 +1056,7 @@ namespace size = htonl(2); // 1 too many memcpy(&vec[1], &size, sizeof(uint32_t)); std::string str_bad_4((char*)&vec[0], vec.size()); - ensureParse( + require_parse( "invalid map too short", str_bad_4, LLSD(), @@ -1090,7 +1078,7 @@ namespace memcpy(&vec[integer_loc], &val_int, sizeof(uint32_t)); std::string str_bad_1((char*)&vec[0], vec.size()); - ensureParse( + require_parse( "invalid array size", str_bad_1, LLSD(), @@ -1100,7 +1088,7 @@ namespace size = htonl(2); // correct size memcpy(&vec[1], &size, sizeof(uint32_t)); std::string str_bad_2((char*)&vec[0], vec.size()); - ensureParse( + require_parse( "unterminated array", str_bad_2, LLSD(), @@ -1112,7 +1100,7 @@ namespace val.append(23); vec.push_back(']'); std::string str_good((char*)&vec[0], vec.size()); - ensureParse( + require_parse( "valid array", str_good, val, @@ -1122,7 +1110,7 @@ namespace size = htonl(3); // 1 too long memcpy(&vec[1], &size, sizeof(uint32_t)); std::string str_bad_3((char*)&vec[0], vec.size()); - ensureParse( + require_parse( "array too short", str_bad_3, LLSD(), @@ -1138,7 +1126,7 @@ namespace vec.push_back('}'); std::string str_good((char*)&vec[0], vec.size()); LLSD val = LLSD::emptyMap(); - ensureParse( + require_parse( "empty map", str_good, val, @@ -1154,7 +1142,7 @@ namespace vec.push_back(']'); std::string str_good((char*)&vec[0], vec.size()); LLSD val = LLSD::emptyArray(); - ensureParse( + require_parse( "empty array", str_good, val, @@ -1174,7 +1162,7 @@ namespace vec.push_back('.'); vec.push_back('c'); vec.push_back('o'); vec.push_back('m'); std::string str_bad((char*)&vec[0], vec.size()); - ensureParse( + require_parse( "invalid uri length size", str_bad, LLSD(), @@ -1185,8 +1173,8 @@ namespace size = htonl(13); // correct length memcpy(&vec[1], &size, sizeof(uint32_t)); std::string str_good((char*)&vec[0], vec.size()); - ensureParse( - "valid key size", + require_parse( + "valid uri length", str_good, val, 1); @@ -1207,7 +1195,7 @@ namespace memcpy(&vec[integer_loc], &val_int, sizeof(uint32_t)); vec.push_back(']'); std::string str_huge((char*)&vec[0], vec.size()); - ensureParse( + require_parse( "array size lies huge", str_huge, LLSD(), @@ -1218,7 +1206,7 @@ namespace size = htonl(0xFFFFFFFF); memcpy(&vec[1], &size, sizeof(uint32_t)); std::string str_neg_array((char*)&vec[0], vec.size()); - ensureParse( + require_parse( "negative array size", str_neg_array, LLSD(), @@ -1227,7 +1215,7 @@ namespace vec[0] = '{'; vec.back() = '}'; std::string str_neg_map((char*)&vec[0], vec.size()); - ensureParse( + require_parse( "negative map size", str_neg_map, LLSD(), @@ -1238,7 +1226,7 @@ namespace bin.resize(bin.size() + 4); memcpy(&bin[1], &size, sizeof(uint32_t)); std::string str_neg_bin((char*)&bin[0], bin.size()); - ensureParse( + require_parse( "negative binary size", str_neg_bin, LLSD(), @@ -1248,17 +1236,17 @@ namespace TEST_CASE_METHOD(TestLLSDBinaryParsing, "llsd binary parsing: truncated fixed-width payloads fail", "[llsd_binary_parsing]") { // truncated fixed-width payloads must fail, not parse as zero - ensureParse( + require_parse( "truncated integer", std::string("i\x00\x01", 3), LLSD(), LLSDParser::PARSE_FAILURE); - ensureParse( + require_parse( "truncated real", std::string("r\x3f\xf0\x00\x00", 5), LLSD(), LLSDParser::PARSE_FAILURE); - ensureParse( + require_parse( "truncated uuid", std::string("u\x01\x02\x03\x04\x05\x06\x07\x08", 9), LLSD(), @@ -1282,7 +1270,7 @@ namespace memcpy(&vec[integer_loc], &val_int, sizeof(uint32_t)); vec.push_back('}'); std::string str_bad((char*)&vec[0], vec.size()); - ensureParse( + require_parse( "bogus map key marker", str_bad, LLSD(), @@ -1293,22 +1281,22 @@ namespace { // test handling of xml not recognized as llsd results in an // LLSD Undefined - ensureParse( + require_parse( "malformed notation map", "{'ha ha'", LLSD(), LLSDParser::PARSE_FAILURE); - ensureParse( + require_parse( "malformed notation array", "['ha ha'", LLSD(), LLSDParser::PARSE_FAILURE); - ensureParse( + require_parse( "malformed notation string", "'ha ha", LLSD(), LLSDParser::PARSE_FAILURE); - ensureParse( + require_parse( "bad notation noise", "g48ejlnfr", LLSD(), @@ -1317,63 +1305,63 @@ namespace TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: undef parses", "[llsd_notation_parsing]") { - ensureParse("valid undef", "!", LLSD(), 1); + require_parse("valid undef", "!", LLSD(), 1); } TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: booleans parse in every spelling and fail when cut short", "[llsd_notation_parsing]") { LLSD val = false; - ensureParse("valid boolean false 0", "false", val, 1); - ensureParse("valid boolean false 1", "f", val, 1); - ensureParse("valid boolean false 2", "0", val, 1); - ensureParse("valid boolean false 3", "F", val, 1); - ensureParse("valid boolean false 4", "FALSE", val, 1); + require_parse("valid boolean false 0", "false", val, 1); + require_parse("valid boolean false 1", "f", val, 1); + require_parse("valid boolean false 2", "0", val, 1); + require_parse("valid boolean false 3", "F", val, 1); + require_parse("valid boolean false 4", "FALSE", val, 1); val = true; - ensureParse("valid boolean true 0", "true", val, 1); - ensureParse("valid boolean true 1", "t", val, 1); - ensureParse("valid boolean true 2", "1", val, 1); - ensureParse("valid boolean true 3", "T", val, 1); - ensureParse("valid boolean true 4", "TRUE", val, 1); + require_parse("valid boolean true 0", "true", val, 1); + require_parse("valid boolean true 1", "t", val, 1); + require_parse("valid boolean true 2", "1", val, 1); + require_parse("valid boolean true 3", "T", val, 1); + require_parse("valid boolean true 4", "TRUE", val, 1); val.clear(); - ensureParse("invalid true", "TR", val, LLSDParser::PARSE_FAILURE); - ensureParse("invalid false", "FAL", val, LLSDParser::PARSE_FAILURE); + require_parse("invalid true", "TR", val, LLSDParser::PARSE_FAILURE); + require_parse("invalid false", "FAL", val, LLSDParser::PARSE_FAILURE); } TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: integers parse with a sign and fail when malformed", "[llsd_notation_parsing]") { LLSD val = 123; - ensureParse("valid integer", "i123", val, 1); - ensureParse("explicit plus integer", "i+123", val, 1); + require_parse("valid integer", "i123", val, 1); + require_parse("explicit plus integer", "i+123", val, 1); val = -123; - ensureParse("negative integer", "i-123", val, 1); + require_parse("negative integer", "i-123", val, 1); val.clear(); - ensureParse("invalid integer", "421", val, LLSDParser::PARSE_FAILURE); - ensureParse("empty integer", "i,", val, LLSDParser::PARSE_FAILURE); + require_parse("invalid integer", "421", val, LLSDParser::PARSE_FAILURE); + require_parse("empty integer", "i,", val, LLSDParser::PARSE_FAILURE); // an oversized token must fail whole, not parse as a buffered prefix - ensureParse("oversized integer token", + require_parse("oversized integer token", "i" + std::string(100, '1'), val, LLSDParser::PARSE_FAILURE); } TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: reals parse signed and scientific and infinite", "[llsd_notation_parsing]") { LLSD val = 456.7; - ensureParse("valid real", "r456.7", val, 1); - ensureParse("explicit plus real", "r+456.7", val, 1); + require_parse("valid real", "r456.7", val, 1); + require_parse("explicit plus real", "r+456.7", val, 1); val = -456.7; - ensureParse("negative real", "r-456.7", val, 1); + require_parse("negative real", "r-456.7", val, 1); val = 1500.0; - ensureParse("scientific real", "r1.5e3", val, 1); - ensureParse("scientific real explicit plus exponent", "r1.5E+3", val, 1); + require_parse("scientific real", "r1.5e3", val, 1); + require_parse("scientific real explicit plus exponent", "r1.5E+3", val, 1); val = std::numeric_limits::infinity(); - ensureParse("infinity real", "rinf", val, 1); + require_parse("infinity real", "rinf", val, 1); val = -std::numeric_limits::infinity(); - ensureParse("negative infinity real", "r-inf", val, 1); + require_parse("negative infinity real", "r-inf", val, 1); val.clear(); - ensureParse("invalid real", "456.7", val, LLSDParser::PARSE_FAILURE); - ensureParse("empty real", "r,", val, LLSDParser::PARSE_FAILURE); + require_parse("invalid real", "456.7", val, LLSDParser::PARSE_FAILURE); + require_parse("empty real", "r,", val, LLSDParser::PARSE_FAILURE); // an oversized token must fail whole, not parse as a buffered prefix - ensureParse("oversized real token", + require_parse("oversized real token", "r0." + std::string(600, '3'), val, LLSDParser::PARSE_FAILURE); } @@ -1381,7 +1369,7 @@ namespace { LLUUID id; LLSD val = id; - ensureParse( + require_parse( "unparseable uuid", "u123", LLSD(), @@ -1390,29 +1378,29 @@ namespace val = id; std::string uuid_str("u"); uuid_str += id.asString(); - ensureParse("valid uuid", uuid_str.c_str(), val, 1); + require_parse("valid uuid", uuid_str.c_str(), val, 1); } TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: strings parse quoted or sized", "[llsd_notation_parsing]") { LLSD val = std::string("foolish"); - ensureParse("valid string 1", "\"foolish\"", val, 1); + require_parse("valid string 1", "\"foolish\"", val, 1); val = std::string("g'day"); - ensureParse("valid string 2", "\"g'day\"", val, 1); + require_parse("valid string 2", "\"g'day\"", val, 1); val = std::string("have a \"nice\" day"); - ensureParse("valid string 3", "'have a \"nice\" day'", val, 1); + require_parse("valid string 3", "'have a \"nice\" day'", val, 1); val = std::string("whatever"); - ensureParse("valid string 4", "s(8)\"whatever\"", val, 1); + require_parse("valid string 4", "s(8)\"whatever\"", val, 1); } TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: a sized string with the wrong size fails", "[llsd_notation_parsing]") { - ensureParse( + require_parse( "invalid string 1", "s(7)\"whatever\"", LLSD(), LLSDParser::PARSE_FAILURE); - ensureParse( + require_parse( "invalid string 2", "s(9)\"whatever\"", LLSD(), @@ -1422,13 +1410,13 @@ namespace TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: a uri parses", "[llsd_notation_parsing]") { LLSD val = LLURI("http://www.google.com"); - ensureParse("valid uri", "l\"http://www.google.com\"", val, 1); + require_parse("valid uri", "l\"http://www.google.com\"", val, 1); } TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: a date parses", "[llsd_notation_parsing]") { LLSD val = LLDate("2007-12-28T09:22:53.10Z"); - ensureParse("valid date", "d\"2007-12-28T09:22:53.10Z\"", val, 1); + require_parse("valid date", "d\"2007-12-28T09:22:53.10Z\"", val, 1); } TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: binary parses as b64 or b16 or raw", "[llsd_notation_parsing]") @@ -1437,19 +1425,19 @@ namespace vec.push_back((U8)'a'); vec.push_back((U8)'b'); vec.push_back((U8)'c'); vec.push_back((U8)'3'); vec.push_back((U8)'2'); vec.push_back((U8)'1'); LLSD val = vec; - ensureParse("valid binary b64", "b64\"YWJjMzIx\"", val, 1); - ensureParse("valid bainry b16", "b16\"616263333231\"", val, 1); - ensureParse("valid bainry raw", "b(6)\"abc321\"", val, 1); + require_parse("valid binary b64", "b64\"YWJjMzIx\"", val, 1); + require_parse("valid binary b16", "b16\"616263333231\"", val, 1); + require_parse("valid binary raw", "b(6)\"abc321\"", val, 1); } TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: binary with too long a length fails", "[llsd_notation_parsing]") { - ensureParse( + require_parse( "invalid -- binary length specified too long", "b(7)\"abc321\"", LLSD(), LLSDParser::PARSE_FAILURE); - ensureParse( + require_parse( "invalid -- binary length specified way too long", "b(1000000)\"abc321\"", LLSD(), @@ -1462,11 +1450,11 @@ namespace val["amy"] = 23; val["bob"] = LLSD(); val["cam"] = 1.23; - ensureParse("simple map", "{'amy':i23,'bob':!,'cam':r1.23}", val, 4); + require_parse("simple map", "{'amy':i23,'bob':!,'cam':r1.23}", val, 4); val["bob"] = LLSD::emptyMap(); val["bob"]["vehicle"] = std::string("bicycle"); - ensureParse( + require_parse( "nested map", "{'amy':i23,'bob':{'vehicle':'bicycle'},'cam':r1.23}", val, @@ -1479,10 +1467,10 @@ namespace val.append(23); val.append(LLSD()); val.append(1.23); - ensureParse("simple array", "[i23,!,r1.23]", val, 4); + require_parse("simple array", "[i23,!,r1.23]", val, 4); val[1] = LLSD::emptyArray(); val[1].append("bicycle"); - ensureParse("nested array", "[i23,['bicycle'],r1.23]", val, 5); + require_parse("nested array", "[i23,['bicycle'],r1.23]", val, 5); } TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: maps and arrays nest in each other", "[llsd_notation_parsing]") @@ -1497,7 +1485,7 @@ namespace val["bob"]["dogs"][1]["name"] = std::string("greyley"); val["bob"]["dogs"][1]["breed"] = std::string("chow/husky"); val["cam"] = 1.23; - ensureParse( + require_parse( "nested notation", "{'amy':i23," " 'bob':{'dogs':[" @@ -1512,7 +1500,7 @@ namespace { // text to make sure that incorrect sizes bail because std::string bad_str("s(5)\"hi\""); - ensureParse( + require_parse( "size longer than bytes left", bad_str, LLSD(), @@ -1523,7 +1511,7 @@ namespace { // text to make sure that incorrect sizes bail because std::string bad_bin("b(5)\"hi\""); - ensureParse( + require_parse( "size longer than bytes left", bad_bin, LLSD(), @@ -1541,7 +1529,7 @@ namespace LLSD root = LLSD::emptyMap(); root["deep"] = deep; - ensureParse( + require_parse( "nested notation 3 deep", "{'deep' : {'level_0':{'level_1':{'level_2': i99} } } }", root, @@ -1565,7 +1553,7 @@ namespace LLSD deep = LLSD::emptyMap(); deep["deep"] = level_0; - ensureParse( + require_parse( "nested notation 10 deep", "{'deep' : {'level_0':{'level_1':{'level_2':{'level_3':{'level_4':{'level_5':{'level_6':{'level_7':{'level_8':{'level_9':i99}" "} } } } } } } } } }", @@ -1636,7 +1624,7 @@ namespace LLSD deep = LLSD::emptyMap(); deep["deep"] = level_0; - ensureParse( + require_parse( "nested notation deep", "{'deep':" "{'level_0' :{'level_1' :{'level_2' :{'level_3' :{'level_4' :{'level_5' :{'level_6' :{'level_7' :{'level_8' :{'level_9' :" @@ -1657,8 +1645,8 @@ namespace TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: nesting past the depth limit fails", "[llsd_notation_parsing]") { - ensureParse( - "nested notation 10 deep", + require_parse( + "nested notation 10 deep past a depth limit of 9", "{'deep' : {'level_0':{'level_1':{'level_2':{'level_3':{'level_4':{'level_5':{'level_6':{'level_7':{'level_8':{'level_9':i99}" "} } } } } } } } } }", LLSD(), @@ -1669,15 +1657,15 @@ namespace TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: b16 that is empty parses and odd or unterminated fails", "[llsd_notation_parsing]") { // b16 edge cases - ensureParse("empty b16", "b16\"\"", LLSD(LLSD::Binary()), 1); + require_parse("empty b16", "b16\"\"", LLSD(LLSD::Binary()), 1); // an odd number of hex digits cannot form whole bytes - ensureParse( + require_parse( "odd-length b16", "b16\"616\"", LLSD(), LLSDParser::PARSE_FAILURE); // unterminated b16 data must fail rather than spin forever - ensureParse( + require_parse( "unterminated b16", "b16\"6162", LLSD(), @@ -1687,20 +1675,20 @@ namespace TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: b64 empty or with whitespace parses and invalid or unterminated fails", "[llsd_notation_parsing]") { // b64 edge cases - ensureParse("empty b64", "b64\"\"", LLSD(LLSD::Binary()), 1); + require_parse("empty b64", "b64\"\"", LLSD(LLSD::Binary()), 1); std::vector vec; vec.push_back((U8)'a'); vec.push_back((U8)'b'); vec.push_back((U8)'c'); vec.push_back((U8)'3'); vec.push_back((U8)'2'); vec.push_back((U8)'1'); LLSD val = vec; // forgiving base64: ASCII whitespace inside the payload is ignored - ensureParse("b64 with whitespace", "b64\"YWJj\nMzIx\"", val, 1); - ensureParse( + require_parse("b64 with whitespace", "b64\"YWJj\nMzIx\"", val, 1); + require_parse( "invalid b64 data", "b64\"Y!WJjMzIx\"", LLSD(), LLSDParser::PARSE_FAILURE); - ensureParse( + require_parse( "unterminated b64", "b64\"YWJj", LLSD(), @@ -1710,12 +1698,12 @@ namespace TEST_CASE_METHOD(TestLLSDNotationParsing, "llsd notation parsing: negative raw sizes fail", "[llsd_notation_parsing]") { // negative sizes must not be fed to resize() - ensureParse( + require_parse( "negative raw binary size", "b(-5)\"hi\"", LLSD(), LLSDParser::PARSE_FAILURE); - ensureParse( + require_parse( "negative raw string size", "s(-5)\"hi\"", LLSD(), @@ -1756,7 +1744,7 @@ namespace }; setParser(LLSDSerialize::deserialize); doRoundTripTests("serialize(LLSD_BINARY)"); - }; + } TEST_CASE_METHOD(TestLLSDSerializeData, "llsd serialization: serialize(LLSD_XML) round-trips through deserialize", "[llsd_serialization]") { @@ -1766,7 +1754,7 @@ namespace }; setParser(LLSDSerialize::deserialize); doRoundTripTests("serialize(LLSD_XML)"); - }; + } TEST_CASE_METHOD(TestLLSDSerializeData, "llsd serialization: serialize(LLSD_NOTATION) round-trips through deserialize", "[llsd_serialization]") { @@ -1778,7 +1766,7 @@ namespace // In this test, serialize(LLSD_NOTATION) emits a header recognized by // deserialize(). doRoundTripTests("serialize(LLSD_NOTATION)"); - }; + } TEST_CASE_METHOD(TestLLSDSerializeData, "llsd serialization: LLSDNotationFormatter output without a header deserializes", "[llsd_serialization]") { @@ -1788,7 +1776,7 @@ namespace // This is an interesting test because LLSDNotationFormatter does not // emit an llsd/notation header. doRoundTripTests("LLSDNotationFormatter -> deserialize"); - }; + } TEST_CASE_METHOD(TestLLSDSerializeData, "llsd serialization: LLSDXMLFormatter output without a header deserializes", "[llsd_serialization]") { @@ -1798,7 +1786,7 @@ namespace // This is an interesting test because LLSDXMLFormatter does not // emit an LLSD/XML header. doRoundTripTests("LLSDXMLFormatter -> deserialize"); - }; + } TEST_CASE_METHOD(TestLLSDSerializeData, "llsd serialization: deserialize with SIZE_UNLIMITED reads every format", "[llsd_serialization]") { @@ -1817,9 +1805,8 @@ namespace std::stringstream stream; LLSDSerialize::serialize(sd, stream, fmt); LLSD parsed; - UNSCOPED_INFO((STRINGIZE("deserialize unlimited fmt " << int(fmt)))); + CAPTURE(fmt); REQUIRE(LLSDSerialize::deserialize(parsed, stream, LLSDSerialize::SIZE_UNLIMITED)); - UNSCOPED_INFO((STRINGIZE("deserialize unlimited fmt " << int(fmt) << " value"))); REQUIRE_THAT(parsed, ll_test::LLSDEquals(sd)); } } @@ -1844,15 +1831,12 @@ namespace llssize cur_size = (llssize)buf.size(); llssize header_size = 0; char* p = strip_deprecated_header(buf.data(), cur_size, &header_size); - UNSCOPED_INFO("stripped header+newline"); + UNSCOPED_INFO("the header and its newline"); CHECK(header_size == llssize(18)); - UNSCOPED_INFO("stripped size"); REQUIRE(cur_size == llssize(body.size())); LLMemoryStream mstr((U8*)p, (S32)cur_size); LLSD parsed; - UNSCOPED_INFO("parse after strip"); CHECK(LLSDSerialize::fromBinary(parsed, mstr, cur_size) > 0); - UNSCOPED_INFO("value after strip"); CHECK_THAT(parsed, ll_test::LLSDEquals(sd)); } @@ -1863,15 +1847,12 @@ namespace llssize cur_size = (llssize)buf.size(); llssize header_size = 0; char* p = strip_deprecated_header(buf.data(), cur_size, &header_size); - UNSCOPED_INFO("stripped bare header"); + UNSCOPED_INFO("the bare header"); CHECK(header_size == llssize(17)); - UNSCOPED_INFO("stripped bare size"); REQUIRE(cur_size == llssize(body.size())); LLMemoryStream mstr((U8*)p, (S32)cur_size); LLSD parsed; - UNSCOPED_INFO("parse after bare strip"); CHECK(LLSDSerialize::fromBinary(parsed, mstr, cur_size) > 0); - UNSCOPED_INFO("value after bare strip"); CHECK_THAT(parsed, ll_test::LLSDEquals(sd)); } } @@ -1891,16 +1872,13 @@ namespace sd["samples"] = arr; std::string compressed = zip_llsd(sd); - UNSCOPED_INFO("zip_llsd produced data"); REQUIRE_FALSE(compressed.empty()); LLSD out; - UNSCOPED_INFO("unzip_llsd ok"); REQUIRE(LLUZipHelper::unzip_llsd(out, (const U8*)compressed.data(), (S32)compressed.size()) == LLUZipHelper::ZR_OK); - UNSCOPED_INFO("unzip_llsd round trip"); CHECK_THAT(out, ll_test::LLSDEquals(sd)); std::string corrupt(compressed); @@ -1909,10 +1887,9 @@ namespace corrupt[i] ^= 0x5A; } LLSD out2; - UNSCOPED_INFO("corrupt unzip_llsd rejected"); - REQUIRE(LLUZipHelper::unzip_llsd(out2, - (const U8*)corrupt.data(), - (S32)corrupt.size()) != LLUZipHelper::ZR_OK); + CHECK(LLUZipHelper::unzip_llsd(out2, + (const U8*)corrupt.data(), + (S32)corrupt.size()) != LLUZipHelper::ZR_OK); } TEST_CASE_METHOD(TestLLSDSerializeData, "llsd serialization: unzip_llsdNavMesh round-trips and rejects corruption", "[llsd_serialization]") @@ -1931,7 +1908,6 @@ namespace int ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, // gzip wrapper 8, Z_DEFAULT_STRATEGY); - UNSCOPED_INFO("deflateInit2"); CHECK(ret == Z_OK); std::string compressed(deflateBound(&strm, (uLong)payload.size()) + 32, '\0'); strm.next_in = (Bytef*)payload.data(); @@ -1939,7 +1915,6 @@ namespace strm.next_out = (Bytef*)&compressed[0]; strm.avail_out = (uInt)compressed.size(); ret = deflate(&strm, Z_FINISH); - UNSCOPED_INFO("deflate finish"); CHECK(ret == Z_STREAM_END); compressed.resize(compressed.size() - strm.avail_out); deflateEnd(&strm); @@ -1949,15 +1924,11 @@ namespace bool valid = false; size_t outsize = 0; U8* out = unzip_llsdNavMesh(valid, outsize, in, (S32)compressed.size()); - UNSCOPED_INFO("navmesh unzip valid"); CHECK(valid); - UNSCOPED_INFO("navmesh unzip buffer"); REQUIRE(out != NULL); - UNSCOPED_INFO("navmesh unzip size"); REQUIRE(outsize == payload.size()); - UNSCOPED_INFO("navmesh unzip content"); - REQUIRE(payload.compare(0, payload.size(), - (const char*)out, outsize) == 0); + CHECK(payload.compare(0, payload.size(), + (const char*)out, outsize) == 0); free(out); } @@ -1972,10 +1943,8 @@ namespace bool valid = true; size_t outsize = 0; U8* out = unzip_llsdNavMesh(valid, outsize, in, (S32)corrupt.size()); - UNSCOPED_INFO("corrupt navmesh rejected"); CHECK_FALSE(valid); - UNSCOPED_INFO("corrupt navmesh returns null"); - REQUIRE(out == NULL); + CHECK(out == NULL); } } @@ -1988,10 +1957,11 @@ namespace public: TestLLSDCrossCompatible() {} - void ensureBinaryAndNotation( - const std::string& msg, + void require_via_binary_and_notation( + const std::string& what, const LLSD& input) { + INFO(what); // to binary, and back again std::stringstream str1; S32 count1 = LLSDSerialize::toBinary(input, str1); @@ -2000,14 +1970,12 @@ namespace actual_value_bin, str1, LLSDSerialize::SIZE_UNLIMITED); - UNSCOPED_INFO("ensureBinaryAndNotation binary count"); REQUIRE(count2 == count1); // to notation and back again std::stringstream str2; S32 count3 = LLSDSerialize::toNotation(actual_value_bin, str2); - UNSCOPED_INFO("ensureBinaryAndNotation notation count1"); REQUIRE(count3 == count2); LLSD actual_value_notation; @@ -2015,18 +1983,17 @@ namespace actual_value_notation, str2, LLSDSerialize::SIZE_UNLIMITED); - UNSCOPED_INFO("ensureBinaryAndNotation notation count2"); REQUIRE(count4 == count3); - UNSCOPED_INFO((msg + " (binaryandnotation)").c_str()); REQUIRE_THAT(actual_value_notation, ll_test::LLSDEquals(input)); } - void ensureBinaryAndXML( - const std::string& msg, + void require_via_binary_and_xml( + const std::string& what, const LLSD& input) { + INFO(what); // to binary, and back again std::stringstream str1; S32 count1 = LLSDSerialize::toBinary(input, str1); @@ -2035,22 +2002,18 @@ namespace actual_value_bin, str1, LLSDSerialize::SIZE_UNLIMITED); - UNSCOPED_INFO("ensureBinaryAndXML binary count"); REQUIRE(count2 == count1); // to xml and back again std::stringstream str2; S32 count3 = LLSDSerialize::toXML(actual_value_bin, str2); - UNSCOPED_INFO("ensureBinaryAndXML xml count1"); REQUIRE(count3 == count2); LLSD actual_value_xml; S32 count4 = LLSDSerialize::fromXML(actual_value_xml, str2); - UNSCOPED_INFO("ensureBinaryAndXML xml count2"); REQUIRE(count4 == count3); - UNSCOPED_INFO((msg + " (binaryandxml)").c_str()); REQUIRE_THAT(actual_value_xml, ll_test::LLSDEquals(input)); } }; @@ -2058,40 +2021,40 @@ namespace TEST_CASE_METHOD(TestLLSDCrossCompatible, "llsd serialize compatible: scalars round-trip via binary into notation and XML", "[llsd_serialize_compatible]") { LLSD test; - ensureBinaryAndNotation("undef", test); - ensureBinaryAndXML("undef", test); + require_via_binary_and_notation("undef", test); + require_via_binary_and_xml("undef", test); test = true; - ensureBinaryAndNotation("boolean true", test); - ensureBinaryAndXML("boolean true", test); + require_via_binary_and_notation("boolean true", test); + require_via_binary_and_xml("boolean true", test); test = false; - ensureBinaryAndNotation("boolean false", test); - ensureBinaryAndXML("boolean false", test); + require_via_binary_and_notation("boolean false", test); + require_via_binary_and_xml("boolean false", test); test = 0; - ensureBinaryAndNotation("integer zero", test); - ensureBinaryAndXML("integer zero", test); + require_via_binary_and_notation("integer zero", test); + require_via_binary_and_xml("integer zero", test); test = 1; - ensureBinaryAndNotation("integer positive", test); - ensureBinaryAndXML("integer positive", test); + require_via_binary_and_notation("integer positive", test); + require_via_binary_and_xml("integer positive", test); test = -234567; - ensureBinaryAndNotation("integer negative", test); - ensureBinaryAndXML("integer negative", test); + require_via_binary_and_notation("integer negative", test); + require_via_binary_and_xml("integer negative", test); test = 0.0; - ensureBinaryAndNotation("real zero", test); - ensureBinaryAndXML("real zero", test); + require_via_binary_and_notation("real zero", test); + require_via_binary_and_xml("real zero", test); test = 1.0; - ensureBinaryAndNotation("real positive", test); - ensureBinaryAndXML("real positive", test); + require_via_binary_and_notation("real positive", test); + require_via_binary_and_xml("real positive", test); test = -1.0; - ensureBinaryAndNotation("real negative", test); - ensureBinaryAndXML("real negative", test); + require_via_binary_and_notation("real negative", test); + require_via_binary_and_xml("real negative", test); } TEST_CASE_METHOD(TestLLSDCrossCompatible, "llsd serialize compatible: a string round-trips via binary into notation and XML", "[llsd_serialize_compatible]") { LLSD test; test = "foobar"; - ensureBinaryAndNotation("string", test); - ensureBinaryAndXML("string", test); + require_via_binary_and_notation("string", test); + require_via_binary_and_xml("string", test); } TEST_CASE_METHOD(TestLLSDCrossCompatible, "llsd serialize compatible: a uuid round-trips via binary into notation and XML", "[llsd_serialize_compatible]") @@ -2100,24 +2063,24 @@ namespace LLUUID id; id.generate(); test = id; - ensureBinaryAndNotation("uuid", test); - ensureBinaryAndXML("uuid", test); + require_via_binary_and_notation("uuid", test); + require_via_binary_and_xml("uuid", test); } TEST_CASE_METHOD(TestLLSDCrossCompatible, "llsd serialize compatible: a date round-trips via binary into notation and XML", "[llsd_serialize_compatible]") { LLSD test; test = LLDate(12345.0); - ensureBinaryAndNotation("date", test); - ensureBinaryAndXML("date", test); + require_via_binary_and_notation("date", test); + require_via_binary_and_xml("date", test); } TEST_CASE_METHOD(TestLLSDCrossCompatible, "llsd serialize compatible: a uri round-trips via binary into notation and XML", "[llsd_serialize_compatible]") { LLSD test; test = LLURI("http://www.secondlife.com/"); - ensureBinaryAndNotation("uri", test); - ensureBinaryAndXML("uri", test); + require_via_binary_and_notation("uri", test); + require_via_binary_and_xml("uri", test); } TEST_CASE_METHOD(TestLLSDCrossCompatible, "llsd serialize compatible: random binary round-trips via binary into notation and XML", "[llsd_serialize_compatible]") @@ -2135,8 +2098,8 @@ namespace rand); } test = val; - ensureBinaryAndNotation("binary", test); - ensureBinaryAndXML("binary", test); + require_via_binary_and_notation("binary", test); + require_via_binary_and_xml("binary", test); } TEST_CASE_METHOD(TestLLSDCrossCompatible, "llsd serialize compatible: an array round-trips via binary into notation and XML", "[llsd_serialize_compatible]") @@ -2145,8 +2108,8 @@ namespace test = LLSD::emptyArray(); test.append(1); test.append("hello"); - ensureBinaryAndNotation("array", test); - ensureBinaryAndXML("array", test); + require_via_binary_and_notation("array", test); + require_via_binary_and_xml("array", test); } TEST_CASE_METHOD(TestLLSDCrossCompatible, "llsd serialize compatible: a map round-trips via binary into notation and XML", "[llsd_serialize_compatible]") @@ -2155,7 +2118,7 @@ namespace test = LLSD::emptyArray(); test["foo"] = "bar"; test["baz"] = 100; - ensureBinaryAndNotation("map", test); - ensureBinaryAndXML("map", test); + require_via_binary_and_notation("map", test); + require_via_binary_and_xml("map", test); } } diff --git a/indra/llcommon/tests/llsdutil_test.cpp b/indra/llcommon/tests/llsdutil_test.cpp index ee78f07763..59dc08f688 100644 --- a/indra/llcommon/tests/llsdutil_test.cpp +++ b/indra/llcommon/tests/llsdutil_test.cpp @@ -40,7 +40,6 @@ #include "llquaternion.h" #include "llsdutil.h" #include "llsdutil_math.h" -#include "stringize.h" #include #include @@ -48,8 +47,8 @@ namespace { struct llsdutil_data { - void test_matches(const std::string& proto_key, const LLSD& possibles, - const char** begin, const char** end) + void require_matches_only(const std::string& proto_key, const LLSD& possibles, + const char** begin, const char** end) { std::set succeed(begin, end); LLSD prototype(possibles[proto_key]); @@ -58,12 +57,12 @@ namespace { std::string match(llsd_matches(prototype, pi->second)); std::set::const_iterator found = succeed.find(pi->first); + CAPTURE(proto_key, pi->first); if (found != succeed.end()) { // This test is supposed to succeed. Comparing to the // empty string ensures that if the test fails, it will // display the string received so we can tell what failed. - UNSCOPED_INFO("match"); REQUIRE(match == ""); } else @@ -72,13 +71,11 @@ namespace // the string 'match' will be empty, which doesn't tell us // much about which case went awry. So construct a more // detailed description string. - UNSCOPED_INFO(proto_key + " shouldn't match " + pi->first); REQUIRE_FALSE(match.empty()); } } } }; - ; TEST_CASE_METHOD(llsdutil_data, "llsdutil: ll_sd_from_U64 round-trips a U64", "[llsdutil]") { @@ -87,7 +84,6 @@ namespace valueIn = U64L(0xFEDCBA9876543210); sd = ll_sd_from_U64(valueIn); valueOut = ll_U64_from_sd(sd); - UNSCOPED_INFO("U64 valueIn->sd->valueOut"); CHECK(valueIn == valueOut); } @@ -98,7 +94,6 @@ namespace valueIn = 0x87654321; sd = ll_sd_from_U32(valueIn); valueOut = ll_U32_from_sd(sd); - UNSCOPED_INFO("U32 valueIn->sd->valueOut"); CHECK(valueIn == valueOut); } @@ -109,7 +104,6 @@ namespace LLSD sd; sd = ll_sd_from_ipaddr(valueIn); valueOut = ll_ipaddr_from_sd(sd); - UNSCOPED_INFO("valueIn->sd->valueOut"); CHECK(valueIn == valueOut); } @@ -142,7 +136,7 @@ namespace // empty prototype matches anything for (mi = map.beginMap(); mi != mend; ++mi) { - UNSCOPED_INFO(std::string("empty matches ") + mi->first); + CAPTURE(mi->first); REQUIRE(llsd_matches(LLSD(), mi->second) == ""); } @@ -156,13 +150,12 @@ namespace // prototype array matches only array for (mi = map.beginMap(); mi != mend; ++mi) { - UNSCOPED_INFO(std::string("array doesn't match ") + mi->first); + CAPTURE(mi->first); REQUIRE_FALSE(llsd_matches(proto_array, mi->second).empty()); } // data array must be at least as long as prototype array proto_array.append(LLSD()); - UNSCOPED_INFO("data array too short"); CHECK(llsd_matches(proto_array, data_array) == "Array size 4 required instead of Array size 3"); data_array.append(LLSD()); @@ -177,7 +170,6 @@ namespace UNSCOPED_INFO("undefined prototype array entry"); CHECK(llsd_matches(proto_array, data_array) == ""); proto_array[0] = LLSD::Binary(); - UNSCOPED_INFO("scalar prototype array entry"); CHECK(llsd_matches(proto_array, data_array) == "[0]: Binary required instead of String"); data_array[0] = LLSD::Binary(); @@ -199,27 +191,20 @@ namespace // prototype map matches only map for (mi = map.beginMap(); mi != mend; ++mi) { - UNSCOPED_INFO(std::string("map doesn't match ") + mi->first); + CAPTURE(mi->first); REQUIRE_FALSE(llsd_matches(proto_map, mi->second).empty()); } // data map must contain all keys in prototype map std::string error(llsd_matches(proto_map, data_map)); - UNSCOPED_INFO("missing keys"); CHECK_THAT(error, Catch::Matchers::ContainsSubstring("missing keys")); - UNSCOPED_INFO("missing foo"); CHECK_THAT(error, Catch::Matchers::ContainsSubstring("foo")); - UNSCOPED_INFO("missing bar"); CHECK_THAT(error, Catch::Matchers::ContainsSubstring("bar")); - UNSCOPED_INFO("found found"); CHECK_THAT(error, !Catch::Matchers::ContainsSubstring("found")); - UNSCOPED_INFO("got got"); CHECK_THAT(error, !Catch::Matchers::ContainsSubstring("got")); data_map["bar"] = LLSD(); error = llsd_matches(proto_map, data_map); - UNSCOPED_INFO("missing foo"); CHECK_THAT(error, Catch::Matchers::ContainsSubstring("foo")); - UNSCOPED_INFO("got bar"); CHECK_THAT(error, !Catch::Matchers::ContainsSubstring("bar")); data_map["foo"] = LLSD(); UNSCOPED_INFO("data map just right"); @@ -233,7 +218,6 @@ namespace UNSCOPED_INFO("undefined prototype map entry"); CHECK(llsd_matches(proto_map, data_map) == ""); proto_map["foo"] = LLSD::Binary(); - UNSCOPED_INFO("scalar prototype map entry"); CHECK(llsd_matches(proto_map, data_map) == "['foo']: Binary required instead of String"); data_map["foo"] = LLSD::Binary(); @@ -244,7 +228,7 @@ namespace { static const char* matches[] = { "String", "NumString", "Boolean", "Integer", "Real", "UUID", "Date", "URI" }; - test_matches("String", map, boost::begin(matches), boost::end(matches)); + require_matches_only("String", map, boost::begin(matches), boost::end(matches)); } // Boolean, Integer, Real @@ -253,31 +237,31 @@ namespace ni != nend; ++ni) { static const char* matches[] = { "Boolean", "Integer", "Real", "String", "NumString" }; - test_matches(*ni, map, boost::begin(matches), boost::end(matches)); + require_matches_only(*ni, map, boost::begin(matches), boost::end(matches)); } // UUID { static const char* matches[] = { "UUID", "String", "NumString" }; - test_matches("UUID", map, boost::begin(matches), boost::end(matches)); + require_matches_only("UUID", map, boost::begin(matches), boost::end(matches)); } // Date { static const char* matches[] = { "Date", "String", "NumString" }; - test_matches("Date", map, boost::begin(matches), boost::end(matches)); + require_matches_only("Date", map, boost::begin(matches), boost::end(matches)); } // URI { static const char* matches[] = { "URI", "String", "NumString" }; - test_matches("URI", map, boost::begin(matches), boost::end(matches)); + require_matches_only("URI", map, boost::begin(matches), boost::end(matches)); } // Binary { static const char* matches[] = { "Binary" }; - test_matches("Binary", map, boost::begin(matches), boost::end(matches)); + require_matches_only("Binary", map, boost::begin(matches), boost::end(matches)); } /*-------------------------- llsd_equals ---------------------------*/ @@ -293,7 +277,7 @@ namespace // interest, e.g "String::Integer". // We expect the values (xmi->second) to be equal if and only // if the type names (xmi->first) are equal. - UNSCOPED_INFO((STRINGIZE(lmi->first << "::" << rmi->first))); + CAPTURE(lmi->first, rmi->first); REQUIRE(bool(lmi->first == rmi->first) == bool(llsd_equals(lmi->second, rmi->second))); } @@ -305,18 +289,18 @@ namespace rarray.append(2); rarray.append("3"); LLSD larray(rarray); - UNSCOPED_INFO("llsd_equals(equal arrays)"); + UNSCOPED_INFO("equal arrays"); CHECK(llsd_equals(larray, rarray)); rarray[2] = "4"; - UNSCOPED_INFO("llsd_equals(different [2])"); + UNSCOPED_INFO("different [2]"); CHECK_FALSE(llsd_equals(larray, rarray)); rarray = larray; rarray.append(LLSD::Date()); - UNSCOPED_INFO("llsd_equals(longer right array)"); + UNSCOPED_INFO("longer right array"); CHECK_FALSE(llsd_equals(larray, rarray)); rarray = larray; rarray.erase(2); - UNSCOPED_INFO("llsd_equals(shorter right array)"); + UNSCOPED_INFO("shorter right array"); CHECK_FALSE(llsd_equals(larray, rarray)); // Map cases @@ -325,18 +309,18 @@ namespace rmap["Phoenix"] = 92; rmap["Boston"] = 77; LLSD lmap(rmap); - UNSCOPED_INFO("llsd_equals(equal maps)"); + UNSCOPED_INFO("equal maps"); CHECK(llsd_equals(lmap, rmap)); rmap["Boston"] = 80; - UNSCOPED_INFO("llsd_equals(different [\"Boston\"])"); + UNSCOPED_INFO("different Boston"); CHECK_FALSE(llsd_equals(lmap, rmap)); rmap = lmap; rmap["Atlanta"] = 95; - UNSCOPED_INFO("llsd_equals(superset right map)"); + UNSCOPED_INFO("superset right map"); CHECK_FALSE(llsd_equals(lmap, rmap)); rmap = lmap; lmap["Seattle"] = 72; - UNSCOPED_INFO("llsd_equals(superset left map)"); + UNSCOPED_INFO("superset left map"); CHECK_FALSE(llsd_equals(lmap, rmap)); } @@ -346,44 +330,44 @@ namespace LLSD data_s1 = LLSD::String("The quick brown aardvark jumped over the lazy lemming."); LLSD data_s2 = LLSD::String("The quick brown aardvark jumped over the lazy lemming."); - UNSCOPED_INFO("hash: Identical string hashes match."); + UNSCOPED_INFO("identical strings"); CHECK(boost::hash{}(data_s1) == boost::hash{}(data_s2)); } { LLSD data_r1 = LLSD::Real(3.0f); LLSD data_i1 = LLSD::Integer(3); - UNSCOPED_INFO("hash: equivalent values but different types do not match."); + UNSCOPED_INFO("equal values of different types"); CHECK(boost::hash{}(data_r1) != boost::hash{}(data_i1)); } { LLSD data_a1 = llsd::array("A", "B", "C"); LLSD data_a2 = llsd::array("A", "B", "C"); - UNSCOPED_INFO("hash: identical arrays produce identical results"); + UNSCOPED_INFO("identical arrays"); CHECK(boost::hash{}(data_a1) == boost::hash{}(data_a2)); data_a2.append(llsd::array(1, 2)); - UNSCOPED_INFO("hash: changing the array changes the hash."); + UNSCOPED_INFO("one array appended to"); CHECK(boost::hash{}(data_a1) != boost::hash{}(data_a2)); data_a1.append(llsd::array(1, 2)); - UNSCOPED_INFO("hash: identical arrays produce identical results with nested arrays"); + UNSCOPED_INFO("identical arrays with nested arrays"); CHECK(boost::hash{}(data_a1) == boost::hash{}(data_a2)); } { LLSD data_m1 = LLSDMap("key1", LLSD::Real(3.0))("key2", "value2")("key3", llsd::array(1, 2, 3)); LLSD data_m2 = LLSDMap("key1", LLSD::Real(3.0))("key2", "value2")("key3", llsd::array(1, 2, 3)); - UNSCOPED_INFO("hash: identical maps produce identical results"); + UNSCOPED_INFO("identical maps"); CHECK(boost::hash{}(data_m1) == boost::hash{}(data_m2)); LLSD data_m3 = LLSDMap("key1", LLSD::Real(5.0))("key2", "value2")("key3", llsd::array(1, 2, 3)); - UNSCOPED_INFO("hash: Different values in the map produce different hashes."); + UNSCOPED_INFO("maps with a different value"); CHECK(boost::hash{}(data_m1) != boost::hash{}(data_m3)); LLSD data_m4 = LLSDMap("keyA", LLSD::Real(3.0))("key2", "value2")("key3", llsd::array(1, 2, 3)); - UNSCOPED_INFO("hash: Different keys in the map produce different hashes."); + UNSCOPED_INFO("maps with a different key"); CHECK(boost::hash{}(data_m1) != boost::hash{}(data_m4)); } diff --git a/indra/llcommon/tests/llsingleton_test.cpp b/indra/llcommon/tests/llsingleton_test.cpp index d73ab0d6e5..1e95e1f18b 100644 --- a/indra/llcommon/tests/llsingleton_test.cpp +++ b/indra/llcommon/tests/llsingleton_test.cpp @@ -112,7 +112,7 @@ namespace TEST_CASE_METHOD(singleton, "LLSingleton: getInstance() makes an instance", "[LLSingleton]") { LLSingletonTest* singleton_test = LLSingletonTest::getInstance(); - REQUIRE(singleton_test); + CHECK(singleton_test); } TEST_CASE_METHOD(singleton, "LLSingleton: deleteSingleton() removes the instance and getInstance() remakes it", "[LLSingleton]") @@ -127,7 +127,7 @@ namespace //Construct it again. LLSingletonTest* singleton_test = LLSingletonTest::getInstance(); - REQUIRE(singleton_test); + CHECK(singleton_test); CHECK(LLSingletonTest::instanceExists()); } @@ -135,8 +135,8 @@ namespace // checks the order they were made in, then deletes every singleton and // checks the order they were cleaned up and destroyed in. template - static void check_order(typename CLS::dep_flag cls_dep, typename OTHER::dep_flag other_dep, - const std::string& made, const std::string& deleted) + static void require_order(typename CLS::dep_flag cls_dep, typename OTHER::dep_flag other_dep, + const std::string& made, const std::string& deleted) { CLS::sDepFlag = cls_dep; OTHER::sDepFlag = other_dep; @@ -150,42 +150,42 @@ namespace TEST_CASE_METHOD(singleton, "LLSingleton: A alone is made and deleted in order", "[LLSingleton]") { - check_order(A::DEP_NONE, B::DEP_NONE, "AiA", "xA~A"); + require_order(A::DEP_NONE, B::DEP_NONE, "AiA", "xA~A"); } TEST_CASE_METHOD(singleton, "LLSingleton: A needing B in its constructor is deleted before B", "[LLSingleton]") { - check_order(A::DEP_CTOR, B::DEP_NONE, "ABiBiA", "xA~AxB~B"); + require_order(A::DEP_CTOR, B::DEP_NONE, "ABiBiA", "xA~AxB~B"); } TEST_CASE_METHOD(singleton, "LLSingleton: A needing B in initSingleton is deleted before B", "[LLSingleton]") { - check_order(A::DEP_INIT, B::DEP_NONE, "AiABiB", "xA~AxB~B"); + require_order(A::DEP_INIT, B::DEP_NONE, "AiABiB", "xA~AxB~B"); } TEST_CASE_METHOD(singleton, "LLSingleton: A needing B in initSingleton and B needing A is deleted before B", "[LLSingleton]") { - check_order(A::DEP_INIT, B::DEP_CTOR, "AiABiB", "xA~AxB~B"); + require_order(A::DEP_INIT, B::DEP_CTOR, "AiABiB", "xA~AxB~B"); } TEST_CASE_METHOD(singleton, "LLSingleton: B alone is made and deleted in order", "[LLSingleton]") { - check_order(B::DEP_NONE, A::DEP_NONE, "BiB", "xB~B"); + require_order(B::DEP_NONE, A::DEP_NONE, "BiB", "xB~B"); } TEST_CASE_METHOD(singleton, "LLSingleton: B needing A in its constructor is deleted before A", "[LLSingleton]") { - check_order(B::DEP_CTOR, A::DEP_NONE, "BAiAiB", "xB~BxA~A"); + require_order(B::DEP_CTOR, A::DEP_NONE, "BAiAiB", "xB~BxA~A"); } TEST_CASE_METHOD(singleton, "LLSingleton: B needing A in initSingleton is deleted before A", "[LLSingleton]") { - check_order(B::DEP_INIT, A::DEP_NONE, "BiBAiA", "xB~BxA~A"); + require_order(B::DEP_INIT, A::DEP_NONE, "BiBAiA", "xB~BxA~A"); } TEST_CASE_METHOD(singleton, "LLSingleton: B needing A in initSingleton and A needing B is deleted before A", "[LLSingleton]") { - check_order(B::DEP_INIT, A::DEP_CTOR, "BiBAiA", "xB~BxA~A"); + require_order(B::DEP_INIT, A::DEP_CTOR, "BiBAiA", "xB~BxA~A"); } #define PARAMSINGLETON(cls) \ @@ -205,16 +205,14 @@ namespace // validly initialize each using two different constructors. If we tried // to test that with a single LLParamSingleton class within the same test // program, we'd get 'trying to use deleted LLParamSingleton' errors. - PARAMSINGLETON(PSing1); - PARAMSINGLETON(PSing2); + PARAMSINGLETON(PSing1); + PARAMSINGLETON(PSing2); TEST_CASE_METHOD(singleton, "LLSingleton: LLParamSingleton must be initialized once and not used after deletion", "[LLSingleton]") { WrapLLErrs catcherr; // query methods - UNSCOPED_INFO("false positive on instanceExists()"); CHECK_FALSE(PSing1::instanceExists()); - UNSCOPED_INFO("false positive on wasDeleted()"); CHECK_FALSE(PSing1::wasDeleted()); // try to reference before initializing std::string threw = catcherr.catch_llerrs([](){ @@ -231,7 +229,6 @@ namespace // initialize using LLSD::String constructor PSing1::initParamSingleton("string"); CHECK(PSing1::instance().desc() == "string"); - UNSCOPED_INFO("false negative on instanceExists()"); CHECK(PSing1::instanceExists()); // try to initialize again threw = catcherr.catch_llerrs([](){ @@ -247,7 +244,6 @@ namespace UNSCOPED_INFO("other ctor(int) didn't throw"); CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("twice")); PSing1::deleteSingleton(); - UNSCOPED_INFO("false negative on wasDeleted()"); CHECK(PSing1::wasDeleted()); threw = catcherr.catch_llerrs([](){ (void)PSing1::instance(); @@ -277,14 +273,14 @@ namespace CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("twice")); } - class CircularPCtor: public LLParamSingleton + class CircularPCtor: public LLParamSingleton + { + LLSINGLETON(CircularPCtor) { - LLSINGLETON(CircularPCtor) - { - // never mind indirection, just go straight for the circularity - (void)instance(); - } - }; + // never mind indirection, just go straight for the circularity + (void)instance(); + } + }; TEST_CASE_METHOD(singleton, "LLSingleton: a circular LLParamSingleton constructor errs", "[LLSingleton]") { @@ -292,24 +288,23 @@ namespace std::string threw = catcherr.catch_llerrs([](){ CircularPCtor::initParamSingleton(); }); - UNSCOPED_INFO("constructor circularity didn't throw"); CHECK_THAT(threw, Catch::Matchers::ContainsSubstring("constructor")); } - class CircularPInit: public LLParamSingleton + class CircularPInit: public LLParamSingleton + { + LLSINGLETON_EMPTY_CTOR(CircularPInit); + public: + virtual void initSingleton() override { - LLSINGLETON_EMPTY_CTOR(CircularPInit); - public: - virtual void initSingleton() override + // never mind indirection, just go straight for the circularity + CircularPInit *pt = getInstance(); + if (!pt) { - // never mind indirection, just go straight for the circularity - CircularPInit *pt = getInstance(); - if (!pt) - { - throw; - } + throw; } - }; + } + }; TEST_CASE_METHOD(singleton, "LLSingleton: a circular LLParamSingleton initSingleton() is allowed", "[LLSingleton]") { @@ -317,7 +312,6 @@ namespace std::string threw = catcherr.catch_llerrs([](){ CircularPInit::initParamSingleton(); }); - UNSCOPED_INFO("initSingleton() circularity threw"); - REQUIRE(threw.empty()); + CHECK(threw.empty()); } } diff --git a/indra/llcommon/tests/llstreamtools_test.cpp b/indra/llcommon/tests/llstreamtools_test.cpp index b2d6c422bc..76d6cb06f6 100644 --- a/indra/llcommon/tests/llstreamtools_test.cpp +++ b/indra/llcommon/tests/llstreamtools_test.cpp @@ -35,7 +35,6 @@ namespace { - //test cases for skip_whitespace() TEST_CASE("streamtools: skip_whitespace skips spaces and tabs but not line ends", "[streamtools]") { char arr[255]; @@ -45,7 +44,7 @@ namespace std::istringstream is; is.str(str = ""); - UNSCOPED_INFO("skip_whitespace: empty string"); + UNSCOPED_INFO("empty string"); CHECK_FALSE(skip_whitespace(is)); is.clear(); @@ -53,7 +52,7 @@ namespace skip_whitespace(is); is.get(arr, 255, '\0'); expected_result = "SecondLife is a 3D World"; - UNSCOPED_INFO("skip_whitespace: space"); + UNSCOPED_INFO("a leading space"); CHECK(arr == expected_result); is.clear(); @@ -61,7 +60,7 @@ namespace skip_whitespace(is); is.get(arr, 255, '\0'); expected_result = "SecondLife is a 3D World"; - UNSCOPED_INFO("skip_whitespace: space and tabs"); + UNSCOPED_INFO("spaces and tabs"); CHECK(arr == expected_result); is.clear(); @@ -69,7 +68,7 @@ namespace skip_whitespace(is); is.get(arr, 255, '\0'); expected_result = "SecondLife is a 3D World "; - UNSCOPED_INFO("skip_whitespace: space at end"); + UNSCOPED_INFO("trailing spaces are kept"); CHECK(arr == expected_result); is.clear(); @@ -77,11 +76,9 @@ namespace skip_whitespace(is); is.get(arr, 255, '\0'); expected_result = "\r\nSecondLife is a 3D World"; - UNSCOPED_INFO("skip_whitespace: space at end"); CHECK(arr == expected_result); } - //testcases for skip_emptyspaces() TEST_CASE("streamtools: skip_emptyspace skips whitespace and line ends", "[streamtools]") { char arr[255]; @@ -96,7 +93,7 @@ namespace skip_emptyspace(is); is.get(arr, 255, '\0'); expected_result = "SecondLife is a 3D World.\n"; - UNSCOPED_INFO("skip_emptyspace: space and tabs"); + UNSCOPED_INFO("spaces and tabs"); CHECK(arr == expected_result); is.clear(); @@ -104,25 +101,24 @@ namespace skip_emptyspace(is); is.get(arr, 255, '\0'); expected_result = "SecondLife is a 3D World.\n"; - UNSCOPED_INFO("skip_emptyspace: space, tabs, carriage return, newline"); + UNSCOPED_INFO("spaces, tabs, carriage returns and newlines"); CHECK(arr == expected_result); is.clear(); is.str(str = ""); ret = skip_emptyspace(is); is.get(arr, 255, '\0'); - UNSCOPED_INFO("skip_emptyspace: empty string"); + UNSCOPED_INFO("empty string"); CHECK_FALSE(ret); is.clear(); is.str(str = " \r\n \t "); ret = skip_emptyspace(is); is.get(arr, 255, '\0'); - UNSCOPED_INFO("skip_emptyspace: space newline empty"); + UNSCOPED_INFO("only empty space"); CHECK_FALSE(ret); } - //testcases for skip_comments_and_emptyspace() TEST_CASE("streamtools: skip_comments_and_emptyspace skips comment lines and empty space", "[streamtools]") { char arr[255]; @@ -137,7 +133,7 @@ namespace skip_comments_and_emptyspace(is); is.get(arr, 255, '\0'); expected_result = "SecondLife is a 3D World.\n"; - UNSCOPED_INFO("skip_comments_and_emptyspace: space, tabs, carriage return, newline"); + UNSCOPED_INFO("spaces, tabs, carriage returns and newlines"); CHECK(arr == expected_result); is.clear(); @@ -145,14 +141,12 @@ namespace skip_comments_and_emptyspace(is); is.get(arr, 255, '\0'); expected_result = "SecondLife is a 3D World."; - UNSCOPED_INFO("skip_comments_and_emptyspace: skip comment - 1"); + UNSCOPED_INFO("a comment line"); CHECK(arr == expected_result); is.clear(); is.str(str = "# \r\n # SecondLife is a 3D World. ##"); - UNSCOPED_INFO("should not be good()"); CHECK_FALSE(skip_comments_and_emptyspace(is)); - UNSCOPED_INFO("should be at eof()"); CHECK(is.eof()); // don't get(): given bad state, we can't rely on results @@ -161,23 +155,21 @@ namespace skip_comments_and_emptyspace(is); is.get(arr, 255, '\0'); expected_result = "SecondLife is a 3D World. ##"; - UNSCOPED_INFO("skip_comments_and_emptyspace: skip comment - 3"); CHECK(arr == expected_result); is.clear(); is.str(str = ""); ret = skip_comments_and_emptyspace(is); - UNSCOPED_INFO("skip_comments_and_emptyspace: empty string"); + UNSCOPED_INFO("empty string"); CHECK_FALSE(ret); is.clear(); is.str(str = " \r\n \t # SecondLife is a 3D World"); ret = skip_comments_and_emptyspace(is); - UNSCOPED_INFO("skip_comments_and_emptyspace: space newline comment empty"); + UNSCOPED_INFO("only empty space and a comment"); CHECK_FALSE(ret); } - //testcases for skip_line() TEST_CASE("streamtools: skip_line skips past the next newline", "[streamtools]") { char arr[255]; @@ -192,7 +184,6 @@ namespace skip_line(is); is.get(arr, 255, '\0'); expected_result = "\n It provides an opportunity to the site \nuser to perform real life activities in virtual world."; - UNSCOPED_INFO("skip_line: 1 newline"); CHECK(arr == expected_result); is.clear(); @@ -200,7 +191,6 @@ namespace skip_line(is); is.get(arr, 255, '\0'); expected_result = " It provides an opportunity to the site \nuser to perform real life activities in virtual world."; - UNSCOPED_INFO("skip_line: 2 newline"); CHECK(arr == expected_result); is.clear(); @@ -208,18 +198,16 @@ namespace skip_line(is); is.get(arr, 255, '\0'); expected_result = "user to perform real life activities in virtual world."; - UNSCOPED_INFO("skip_line: 3 newline"); CHECK(arr == expected_result); is.clear(); is.str(str = ""); ret = skip_line(is); - UNSCOPED_INFO("skip_line: empty string"); + UNSCOPED_INFO("empty string"); CHECK_FALSE(ret); } - // testcases for skip_to_next_word() TEST_CASE("streamtools: skip_to_next_word skips the word and the delimiters after it", "[streamtools]") { char arr[255]; @@ -234,7 +222,6 @@ namespace skip_to_next_word(is); // get past SecondLife is.get(arr, 255, '\0'); expected_result = "is a 3D_World.\n\n It-provides an opportunity to the site \nuser to perform real life activities in virtual world."; - UNSCOPED_INFO("skip_to_next_word: 1"); CHECK(arr == expected_result); is.clear(); @@ -244,7 +231,7 @@ namespace skip_to_next_word(is); // get past 3D_World.\n\n is.get(arr, 255, '\0'); expected_result = "It-provides an opportunity to the site \nuser to perform real life activities in virtual world."; - UNSCOPED_INFO("skip_to_next_word: get past .\n\n 2"); + UNSCOPED_INFO("past a full stop and newlines"); CHECK(arr == expected_result); is.clear(); @@ -252,24 +239,23 @@ namespace skip_to_next_word(is); // get past It- expected_result = "provides an opportunity to the site \nuser to perform real life activities in virtual world."; is.get(arr, 255, '\0'); - UNSCOPED_INFO("skip_to_next_word: get past -"); + UNSCOPED_INFO("past a hyphen"); CHECK(arr == expected_result); is.clear(); is.str(str = ""); ret = skip_to_next_word(is); - UNSCOPED_INFO("skip_line: empty string"); + UNSCOPED_INFO("empty string"); CHECK_FALSE(ret); is.clear(); is.str(str = " \r\n\r\n"); ret = skip_to_next_word(is); - UNSCOPED_INFO("skip_line: space new lines"); + UNSCOPED_INFO("only spaces and newlines"); CHECK_FALSE(ret); } - //testcases for skip_to_end_of_next_keyword() TEST_CASE("streamtools: skip_to_end_of_next_keyword finds a keyword that starts a line", "[streamtools]") { char arr[255]; @@ -284,7 +270,6 @@ namespace ret = skip_to_end_of_next_keyword("FIRSTKEY", is); is.get(arr, 255, '\0'); expected_result = " followed by second delimiter\nSECONDKEY\t SecondValue followed by third delimiter \nSECONDKEY\nFOURTHKEY FOURTHVALUEis a 3DWorld."; - UNSCOPED_INFO("skip_to_end_of_next_keyword: 1"); CHECK(arr == expected_result); is.clear(); @@ -292,7 +277,6 @@ namespace ret = skip_to_end_of_next_keyword("SECONDKEY", is); is.get(arr, 255, '\0'); expected_result = "\t SecondValue followed by third delimiter \nSECONDKEY\nFOURTHKEY FOURTHVALUEis a 3DWorld."; - UNSCOPED_INFO("skip_to_end_of_next_keyword: 2"); CHECK(arr == expected_result); is.clear(); @@ -300,7 +284,6 @@ namespace ret = skip_to_end_of_next_keyword("SECONDKEY", is); is.get(arr, 255, '\0'); expected_result = "\nFOURTHKEY FOURTHVALUEis a 3DWorld."; - UNSCOPED_INFO("skip_to_end_of_next_keyword: 3"); CHECK(arr == expected_result); is.clear(); @@ -308,7 +291,6 @@ namespace ret = skip_to_end_of_next_keyword("FOURTHKEY", is); is.get(arr, 255, '\0'); expected_result = " FOURTHVALUEis a 3DWorld."; - UNSCOPED_INFO("skip_to_end_of_next_keyword: 4"); CHECK(arr == expected_result); is.clear(); @@ -316,29 +298,28 @@ namespace ret = skip_to_end_of_next_keyword("{", is); is.get(arr, 255, '\0'); expected_result = " Does it?\n"; - UNSCOPED_INFO("skip_to_end_of_next_keyword: multiple delim matches on same line"); + UNSCOPED_INFO("several matches on the same line"); CHECK(arr == expected_result); is.clear(); is.str(str = "Delim { could not be found at start"); ret = skip_to_end_of_next_keyword("{", is); - UNSCOPED_INFO("skip_to_end_of_next_keyword: delim should not be present"); + UNSCOPED_INFO("keyword not at the start of a line"); CHECK_FALSE(ret); is.clear(); is.str(str = "Empty Delim"); ret = skip_to_end_of_next_keyword("", is); - UNSCOPED_INFO("skip_to_end_of_next_keyword: empty delim should not be valid"); + UNSCOPED_INFO("an empty keyword is not valid"); CHECK_FALSE(ret); is.clear(); is.str(str = ""); ret = skip_to_end_of_next_keyword("}", is); - UNSCOPED_INFO("skip_to_end_of_next_keyword: empty string"); + UNSCOPED_INFO("empty string"); CHECK_FALSE(ret); } - //testcases for get_word(std::string& output_string, std::istream& input_stream) TEST_CASE("streamtools: get_word reads words split by whitespace", "[streamtools]") { std::string str; @@ -352,19 +333,16 @@ namespace actual_result = ""; ret = get_word(actual_result, is); expected_result = "First"; - UNSCOPED_INFO("get_word: 1"); CHECK(actual_result == expected_result); actual_result = ""; ret = get_word(actual_result, is); expected_result = "Second"; - UNSCOPED_INFO("get_word: 2"); CHECK(actual_result == expected_result); actual_result = ""; ret = get_word(actual_result, is); expected_result = "Third"; - UNSCOPED_INFO("get_word: 3"); CHECK(actual_result == expected_result); // the current implementation of get_word seems inconsistent with @@ -376,31 +354,28 @@ namespace actual_result = ""; ret = get_word(actual_result, is); // will copy Fourth-ShouldThisBePartOfFourth expected_result = "Fourth-ShouldThisBePartOfFourth"; // as per current impl. - UNSCOPED_INFO("get_word: 4"); CHECK(actual_result == expected_result); actual_result = ""; ret = get_word(actual_result, is); // will copy Fifth expected_result = "Fifth"; // as per current impl. - UNSCOPED_INFO("get_word: 5"); CHECK(actual_result == expected_result); is.clear(); is.str(str = " \t \r \n "); actual_result = ""; ret = get_word(actual_result, is); - UNSCOPED_INFO("get_word: empty all spaces, newline tabs"); + UNSCOPED_INFO("only whitespace"); CHECK_FALSE(ret); is.clear(); is.str(str = ""); actual_result = ""; ret = get_word(actual_result, is); - UNSCOPED_INFO("get_word: empty string"); + UNSCOPED_INFO("empty string"); CHECK_FALSE(ret); } - // testcase for get_word and skip_to_next_word compatibility TEST_CASE("streamtools: get_word and skip_to_next_word reach the same fifth word", "[streamtools]") { std::string str; @@ -440,11 +415,9 @@ namespace // will copy ShouldThisBePartOfFourth, the fifth word, // while using get_word above five times result in getting "Fifth" get_word(expected_result, is); - UNSCOPED_INFO("get_word: skip_to_next_word compatibility"); CHECK(actual_result == expected_result); } - //testcases for get_word(std::string& output_string, std::istream& input_stream, int n) TEST_CASE("streamtools: get_word with a limit stops after n characters", "[streamtools]") { std::string str; @@ -458,49 +431,44 @@ namespace actual_result = ""; ret = get_word(actual_result, is, 255); expected_result = "First"; - UNSCOPED_INFO("get_word: 1"); CHECK(actual_result == expected_result); actual_result = ""; ret = get_word(actual_result, is, 4); expected_result = "Seco"; // should be cut short - UNSCOPED_INFO("get_word: 2"); CHECK(actual_result == expected_result); actual_result = ""; ret = get_word(actual_result, is, 255); expected_result = "nd"; // get remainder of Second - UNSCOPED_INFO("get_word: 3"); CHECK(actual_result == expected_result); actual_result = ""; ret = get_word(actual_result, is, 0); // 0 size string expected_result = ""; // get remainder of Second - UNSCOPED_INFO("get_word: 0 sized output"); + UNSCOPED_INFO("a limit of zero"); CHECK(actual_result == expected_result); actual_result = ""; ret = get_word(actual_result, is, 255); expected_result = "Third"; - UNSCOPED_INFO("get_word: 4"); CHECK(actual_result == expected_result); is.clear(); is.str(str = " \t \r \n "); actual_result = ""; ret = get_word(actual_result, is, 255); - UNSCOPED_INFO("get_word: empty all spaces, newline tabs"); + UNSCOPED_INFO("only whitespace"); CHECK_FALSE(ret); is.clear(); is.str(str = ""); actual_result = ""; ret = get_word(actual_result, is, 255); - UNSCOPED_INFO("get_word: empty string"); + UNSCOPED_INFO("empty string"); CHECK_FALSE(ret); } - //test cases for get_line(std::string& output_string, std::istream& input_stream) TEST_CASE("streamtools: get_line reads up to and including each newline", "[streamtools]") { std::string str; @@ -513,13 +481,11 @@ namespace actual_result = ""; get_line(actual_result, is); expected_result = "First Second \t \r\n"; - UNSCOPED_INFO("get_line: 1"); CHECK(actual_result == expected_result); actual_result = ""; get_line(actual_result, is); expected_result = " Third Fourth-ShouldThisBePartOfFourth IsThisFifth\n"; - UNSCOPED_INFO("get_line: 2"); CHECK(actual_result == expected_result); is.clear(); @@ -527,29 +493,25 @@ namespace actual_result = ""; get_line(actual_result, is); expected_result = "\n"; - UNSCOPED_INFO("get_line: First char as newline"); + UNSCOPED_INFO("a newline first"); CHECK(actual_result == expected_result); actual_result = ""; get_line(actual_result, is); expected_result = "First Line.\n"; - UNSCOPED_INFO("get_line: 3"); CHECK(actual_result == expected_result); actual_result = ""; get_line(actual_result, is); expected_result = "\n"; - UNSCOPED_INFO("get_line: 4"); CHECK(actual_result == expected_result); actual_result = ""; get_line(actual_result, is); expected_result = "Second Line.\n"; - UNSCOPED_INFO("get_line: 5"); CHECK(actual_result == expected_result); } - //test cases for get_line(std::string& output_string, std::istream& input_stream) TEST_CASE("streamtools: get_line reads a line with no newline and returns good()", "[streamtools]") { std::string str; @@ -563,13 +525,10 @@ namespace actual_result = ""; ret = get_line(actual_result, is); expected_result = "One Line only with no newline"; - UNSCOPED_INFO("get_line: No newline"); CHECK(actual_result == expected_result); - UNSCOPED_INFO("return value is good state of stream"); CHECK(ret == is.good()); } - //test cases for get_line(std::string& output_string, std::istream& input_stream) TEST_CASE("streamtools: get_line keeps a lone carriage return", "[streamtools]") { std::string str; @@ -583,11 +542,9 @@ namespace actual_result = ""; get_line(actual_result, is); expected_result = "Should not skip lone \r.\r\n"; - UNSCOPED_INFO("get_line: carriage return skipped even though not followed by newline"); CHECK(actual_result == expected_result); } - //test cases for get_line(std::string& output_string, std::istream& input_stream) TEST_CASE("streamtools: get_line reads a line that is just a newline", "[streamtools]") { std::string str; @@ -600,12 +557,10 @@ namespace actual_result = ""; get_line(actual_result, is); expected_result = "\n"; - UNSCOPED_INFO("get_line: Just newline"); CHECK(actual_result == expected_result); } - //testcases for get_line(std::string& output_string, std::istream& input_stream, int n) TEST_CASE("streamtools: get_line with a limit cuts the line and adds a newline", "[streamtools]") { std::string str; @@ -618,19 +573,18 @@ namespace actual_result = ""; get_line(actual_result, is, 255); expected_result = "First Line.\n"; - UNSCOPED_INFO("get_line: Basic Operation"); CHECK(actual_result == expected_result); actual_result = ""; get_line(actual_result, is, sizeof("Second")-1); expected_result = "Second\n"; - UNSCOPED_INFO("get_line: Insufficient length 1"); + UNSCOPED_INFO("cut short"); CHECK(actual_result == expected_result); actual_result = ""; get_line(actual_result, is, 255); expected_result = " Line.\n"; - UNSCOPED_INFO("get_line: Remainder after earlier insufficient length"); + UNSCOPED_INFO("the rest after a cut"); CHECK(actual_result == expected_result); is.clear(); @@ -638,7 +592,7 @@ namespace actual_result = ""; get_line(actual_result, is, sizeof("One Line only with no newline with limited length")-1); expected_result = "One Line only with no newline with limited length\n"; - UNSCOPED_INFO("get_line: No newline with limited length"); + UNSCOPED_INFO("no newline and a limit of its length"); CHECK(actual_result == expected_result); is.clear(); @@ -646,11 +600,10 @@ namespace actual_result = ""; get_line(actual_result, is, 255); expected_result = "One Line only with no newline"; - UNSCOPED_INFO("get_line: No newline"); + UNSCOPED_INFO("no newline"); CHECK(actual_result == expected_result); } - //testcases for get_line(std::string& output_string, std::istream& input_stream, int n) TEST_CASE("streamtools: get_line with a limit reads a line with no newline and returns good()", "[streamtools]") { std::string str; @@ -664,13 +617,10 @@ namespace actual_result = ""; ret = get_line(actual_result, is, 255); expected_result = "One Line only with no newline"; - UNSCOPED_INFO("get_line: No newline"); CHECK(actual_result == expected_result); - UNSCOPED_INFO("return value is good state of stream"); CHECK(ret == is.good()); } - //testcases for remove_last_char() TEST_CASE("streamtools: remove_last_char removes only a matching last character", "[streamtools]") { std::string str; @@ -681,28 +631,24 @@ namespace ret = remove_last_char('d',str); expected_result = "SecondLife is a 3D Worl"; - UNSCOPED_INFO("remove_last_char: should remove last char"); CHECK(str == expected_result); str = "SecondLife is a 3D World"; ret = remove_last_char('W',str); expected_result = "SecondLife is a 3D World"; - UNSCOPED_INFO("remove_last_char: should not remove as it is not last char"); + UNSCOPED_INFO("not the last char"); CHECK(str == expected_result); - UNSCOPED_INFO("remove_last_char: should return false"); CHECK_FALSE(ret); str = "SecondLife is a 3D World\n"; ret = remove_last_char('\n',str); expected_result = "SecondLife is a 3D World"; - UNSCOPED_INFO("remove_last_char: should remove last newline"); + UNSCOPED_INFO("a trailing newline"); CHECK(str == expected_result); - UNSCOPED_INFO("remove_last_char: should remove newline and return true"); CHECK(ret); } - //testcases for unescaped_string() TEST_CASE("streamtools: unescape_string unescapes newlines and backslashes only", "[streamtools]") { std::string str; @@ -711,41 +657,40 @@ namespace str = "SecondLife is a 3D world \\n"; unescape_string(str); expected_result = "SecondLife is a 3D world \n"; - UNSCOPED_INFO("unescape_string: newline"); + UNSCOPED_INFO("newline"); CHECK(str == expected_result); str = "SecondLife is a 3D world \\\\t \\n"; unescape_string(str); expected_result = "SecondLife is a 3D world \\t \n"; - UNSCOPED_INFO("unescape_string: backslash and newline"); + UNSCOPED_INFO("backslash and newline"); CHECK(str == expected_result); str = "SecondLife is a 3D world \\ "; unescape_string(str); expected_result = "SecondLife is a 3D world \\ "; - UNSCOPED_INFO("unescape_string: insufficient to unescape"); + UNSCOPED_INFO("a lone trailing backslash"); CHECK(str == expected_result); str = "SecondLife is a 3D world \\n \\n \\n \\\\\\n"; unescape_string(str); expected_result = "SecondLife is a 3D world \n \n \n \\\n"; - UNSCOPED_INFO("unescape_string: multipel newline and backslash"); + UNSCOPED_INFO("several newlines and a backslash"); CHECK(str == expected_result); str = "SecondLife is a 3D world \\t"; unescape_string(str); expected_result = "SecondLife is a 3D world \\t"; - UNSCOPED_INFO("unescape_string: leaves tab as is"); + UNSCOPED_INFO("leaves a tab as is"); CHECK(str == expected_result); str = "\\n"; unescape_string(str); expected_result = "\n"; - UNSCOPED_INFO("unescape_string: only a newline"); + UNSCOPED_INFO("only a newline"); CHECK(str == expected_result); } - //testcases for escape_string() TEST_CASE("streamtools: escape_string escapes newlines and backslashes and round-trips", "[streamtools]") { std::string str; @@ -754,31 +699,31 @@ namespace str = "SecondLife is a 3D world \n"; escape_string(str); expected_result = "SecondLife is a 3D world \\n"; - UNSCOPED_INFO("escape_string: newline"); + UNSCOPED_INFO("newline"); CHECK(str == expected_result); str = "SecondLife is a 3D world \\t \n"; escape_string(str); expected_result = "SecondLife is a 3D world \\\\t \\n"; - UNSCOPED_INFO("escape_string: backslash and newline"); + UNSCOPED_INFO("backslash and newline"); CHECK(str == expected_result); str = "SecondLife is a 3D world \n \n \n \\\n"; escape_string(str); expected_result = "SecondLife is a 3D world \\n \\n \\n \\\\\\n"; - UNSCOPED_INFO("escape_string: multipel newline and backslash"); + UNSCOPED_INFO("several newlines and a backslash"); CHECK(str == expected_result); str = "SecondLife is a 3D world \t"; escape_string(str); expected_result = "SecondLife is a 3D world \t"; - UNSCOPED_INFO("unescape_string: leaves tab as is"); + UNSCOPED_INFO("leaves a tab as is"); CHECK(str == expected_result); str = "\n"; escape_string(str); expected_result = "\\n"; - UNSCOPED_INFO("unescape_string: only a newline"); + UNSCOPED_INFO("only a newline"); CHECK(str == expected_result); // serialization/deserialization escape->unescape @@ -786,7 +731,6 @@ namespace escape_string(str); unescape_string(str); expected_result = "SecondLife is a 3D world \n \n \n \\\n"; - UNSCOPED_INFO("escape_string: should preserve with escape/unescape"); CHECK(str == expected_result); // serialization/deserialization unescape->escape @@ -794,11 +738,9 @@ namespace unescape_string(str); escape_string(str); expected_result = "SecondLife is a 3D world \\n \\n \\n \\\\"; - UNSCOPED_INFO("escape_string: should preserve with unescape/escape"); CHECK(str == expected_result); } - // testcases for replace_newlines_with_whitespace() TEST_CASE("streamtools: replace_newlines_with_whitespace replaces only newlines", "[streamtools]") { std::string str; @@ -807,29 +749,28 @@ namespace str = "SecondLife is a 3D \n\nworld\n"; replace_newlines_with_whitespace(str); expected_result = "SecondLife is a 3D world "; - UNSCOPED_INFO("replace_newlines_with_whitespace: replace all newline"); + UNSCOPED_INFO("every newline"); CHECK(str == expected_result); str = "\nSecondLife is a 3D world\n"; replace_newlines_with_whitespace(str); expected_result = " SecondLife is a 3D world "; - UNSCOPED_INFO("replace_newlines_with_whitespace: begin and newline"); + UNSCOPED_INFO("newlines at both ends"); CHECK(str == expected_result); str = "SecondLife is a 3D world\r\t"; replace_newlines_with_whitespace(str); expected_result = "SecondLife is a 3D world\r\t"; - UNSCOPED_INFO("replace_newlines_with_whitespace: should only replace newline"); + UNSCOPED_INFO("carriage returns and tabs are kept"); CHECK(str == expected_result); str = ""; replace_newlines_with_whitespace(str); expected_result = ""; - UNSCOPED_INFO("replace_newlines_with_whitespace: empty string"); + UNSCOPED_INFO("empty string"); CHECK(str == expected_result); } - //testcases for remove_double_quotes() TEST_CASE("streamtools: remove_double_quotes removes paired and lone double quotes", "[streamtools]") { std::string str; @@ -838,52 +779,42 @@ namespace str = "SecondLife is a \"\"3D world"; remove_double_quotes(str); expected_result = "SecondLife is a 3D world"; - UNSCOPED_INFO("remove_double_quotes: replace empty doube quotes"); + UNSCOPED_INFO("an empty pair of quotes"); CHECK(str == expected_result); str = "SecondLife is a \"3D world"; remove_double_quotes(str); expected_result = "SecondLife is a 3D world"; - UNSCOPED_INFO("remove_double_quotes: keep as is it matching quote not found"); CHECK(str == expected_result); } - // testcases for get_brace_count() TEST_CASE("streamtools: get_brace_count has no checks yet", "[streamtools]") { } - //testcases for get_keyword_and_value() TEST_CASE("streamtools: get_keyword_and_value splits off the first word as the keyword", "[streamtools]") { std::string s = "SecondLife is a 3D World"; std::string keyword; std::string value; get_keyword_and_value(keyword, value, s); - { - INFO("get_keyword_and_value: Unable to get Keyword and Value"); - CHECK(keyword == "SecondLife"); - CHECK(value == "is a 3D World"); - } + CHECK(keyword == "SecondLife"); + CHECK(value == "is a 3D World"); s = "SecondLife"; get_keyword_and_value(keyword, value, s); - { - INFO("get_keyword_and_value: value should be empty"); - CHECK(keyword == "SecondLife"); - CHECK(value == ""); - } + CHECK(keyword == "SecondLife"); + CHECK(value == ""); s = "SecondLife \t is cool! \n"; get_keyword_and_value(keyword, value, s); { - INFO("get_keyword_and_value: remove space before value but not after"); + INFO("space before the value is dropped but not after"); CHECK(keyword == "SecondLife"); CHECK(value == "is cool! "); } } - //testcases for get_keyword_and_value() TEST_CASE("streamtools: get_keyword_and_value gives no value after a keyword and newline", "[streamtools]") { std::string s; @@ -892,14 +823,10 @@ namespace s = "SecondLife\n"; get_keyword_and_value(keyword, value, s); - { - INFO("get_keyword_and_value: terminated with newline. value should be empty"); - CHECK(keyword == "SecondLife"); - CHECK(value == ""); - } + CHECK(keyword == "SecondLife"); + CHECK(value == ""); } - //testcases for get_keyword_and_value() TEST_CASE("streamtools: get_keyword_and_value of an empty string clears both", "[streamtools]") { std::string s; @@ -908,14 +835,10 @@ namespace s = ""; get_keyword_and_value(keyword, value, s); - { - INFO("get_keyword_and_value: empty string. keyword value should empty"); - CHECK(keyword == ""); - CHECK(value == ""); - } + CHECK(keyword == ""); + CHECK(value == ""); } - //testcase for fullread() TEST_CASE("streamtools: fullread reads across newlines up to the requested count", "[streamtools]") { std::string str = "First Line.\nSecond Line\n"; @@ -923,8 +846,7 @@ namespace char buf[255] = {0}; fullread(is, buf, 255); - UNSCOPED_INFO("fullread: read with newlines"); - REQUIRE_THAT(ll_test::Memory((void*) buf, static_cast(str.size())-1), ll_test::MemoryMatches((void*) str.c_str(), static_cast(str.size())-1)); + CHECK_THAT(ll_test::Memory(buf, str.size()-1), ll_test::MemoryMatches(str.c_str(), str.size()-1)); is.clear(); is.str(str = "First Line.\nSecond Line\n"); @@ -933,13 +855,10 @@ namespace char expected_string[] = "First Line.\nSecond"; int len = sizeof(expected_string)-1; fullread(is, buf, len); - UNSCOPED_INFO("fullread: read with newlines"); - REQUIRE_THAT(ll_test::Memory((void*) buf, len), ll_test::MemoryMatches((void*) &expected_string, len)); + CHECK_THAT(ll_test::Memory(buf, len), ll_test::MemoryMatches(expected_string, len)); } -// testcases for operator >> - TEST_CASE("streamtools: operator>> consumes a matching C string and fails on a mismatch", "[streamtools]") { char arr[255]; @@ -950,14 +869,12 @@ namespace stream >> toCheck.c_str(); stream.get(arr, 255, '\0'); expected_result = " is a 3D World"; - UNSCOPED_INFO("istream << operator"); CHECK(arr == expected_result); stream.clear(); stream.str(str = "SecondLife is a 3D World"); toCheck = "is"; stream >> toCheck.c_str(); - UNSCOPED_INFO("istream << operator should have failed"); CHECK_FALSE(stream.good()); } } diff --git a/indra/llcommon/tests/llstring_test.cpp b/indra/llcommon/tests/llstring_test.cpp index f3ba3cc1cf..f8538b2ef6 100644 --- a/indra/llcommon/tests/llstring_test.cpp +++ b/indra/llcommon/tests/llstring_test.cpp @@ -40,87 +40,84 @@ namespace { std::string llstr1; { - INFO("Empty std::string"); - REQUIRE(llstr1.size() == 0); - REQUIRE(llstr1.empty()); + INFO("default-constructed"); + CHECK(llstr1.size() == 0); + CHECK(llstr1.empty()); } std::string llstr2("Hello"); { - INFO("std::string = Hello"); + INFO("from a C string"); CHECK(std::string_view(llstr2.c_str()) == "Hello"); - REQUIRE(llstr2.size() == 5); - REQUIRE_FALSE(llstr2.empty()); + CHECK(llstr2.size() == 5); + CHECK_FALSE(llstr2.empty()); } std::string llstr3(llstr2); { - INFO("std::string = std::string(std::string)"); + INFO("copy"); CHECK(std::string_view(llstr3.c_str()) == "Hello"); - REQUIRE(llstr3.size() == 5); - REQUIRE_FALSE(llstr3.empty()); + CHECK(llstr3.size() == 5); + CHECK_FALSE(llstr3.empty()); } std::string str("Hello World"); std::string llstr4(str, 6); { - INFO("std::string = std::string(s, size_type pos, size_type n = npos)"); + INFO("from a position"); CHECK(std::string_view(llstr4.c_str()) == "World"); - REQUIRE(llstr4.size() == 5); - REQUIRE_FALSE(llstr4.empty()); + CHECK(llstr4.size() == 5); + CHECK_FALSE(llstr4.empty()); } std::string llstr5(str, str.size()); { - INFO("std::string = std::string(s, size_type pos, size_type n = npos)"); - REQUIRE(llstr5.size() == 0); - REQUIRE(llstr5.empty()); + INFO("from the end position"); + CHECK(llstr5.size() == 0); + CHECK(llstr5.empty()); } std::string llstr6(5, 'A'); { - INFO("std::string = std::string(count, c)"); + INFO("count copies of a character"); CHECK(std::string_view(llstr6.c_str()) == "AAAAA"); - REQUIRE(llstr6.size() == 5); - REQUIRE_FALSE(llstr6.empty()); + CHECK(llstr6.size() == 5); + CHECK_FALSE(llstr6.empty()); } std::string llstr7("Hello World", 5); { - INFO("std::string(s, n)"); + INFO("the first n characters"); CHECK(std::string_view(llstr7.c_str()) == "Hello"); - REQUIRE(llstr7.size() == 5); - REQUIRE_FALSE(llstr7.empty()); + CHECK(llstr7.size() == 5); + CHECK_FALSE(llstr7.empty()); } std::string llstr8("Hello World", 6, 5); { - INFO("std::string(s, n, count)"); + INFO("n characters from a position"); CHECK(std::string_view(llstr8.c_str()) == "World"); - REQUIRE(llstr8.size() == 5); - REQUIRE_FALSE(llstr8.empty()); + CHECK(llstr8.size() == 5); + CHECK_FALSE(llstr8.empty()); } std::string llstr9("Hello World", sizeof("Hello World")-1, 5); // go past end { - INFO("std::string(s, n, count) goes past end"); - REQUIRE(llstr9.size() == 0); - REQUIRE(llstr9.empty()); + INFO("from the end position, asking for more"); + CHECK(llstr9.size() == 0); + CHECK(llstr9.empty()); } } TEST_CASE("LLString: isValidIndex accepts indexes up to and including the length", "[LLString]") { std::string str("Len=5"); - { - INFO("isValidIndex failed"); - CHECK(LLStringUtil::isValidIndex(str, 0)); - CHECK(LLStringUtil::isValidIndex(str, 5)); - CHECK_FALSE(LLStringUtil::isValidIndex(str, 6)); - } + CHECK(LLStringUtil::isValidIndex(str, 0)); + CHECK(LLStringUtil::isValidIndex(str, 5)); + CHECK_FALSE(LLStringUtil::isValidIndex(str, 6)); std::string str1; - UNSCOPED_INFO("isValidIndex failed fo rempty string"); + UNSCOPED_INFO("an empty string"); CHECK_FALSE(LLStringUtil::isValidIndex(str1, 0)); } @@ -128,12 +125,10 @@ namespace { std::string str_val(" Testing the extra whitespaces "); LLStringUtil::trimHead(str_val); - UNSCOPED_INFO("1: trimHead failed"); CHECK(str_val == "Testing the extra whitespaces "); std::string str_val1("\n\t\r\n Testing the extra whitespaces "); LLStringUtil::trimHead(str_val1); - UNSCOPED_INFO("2: trimHead failed"); CHECK(str_val1 == "Testing the extra whitespaces "); } @@ -141,12 +136,10 @@ namespace { std::string str_val(" Testing the extra whitespaces "); LLStringUtil::trimTail(str_val); - UNSCOPED_INFO("1: trimTail failed"); CHECK(str_val == " Testing the extra whitespaces"); std::string str_val1("\n Testing the extra whitespaces \n\t\r\n "); LLStringUtil::trimTail(str_val1); - UNSCOPED_INFO("2: trimTail failed"); CHECK(str_val1 == "\n Testing the extra whitespaces"); } @@ -155,7 +148,6 @@ namespace { std::string str_val(" \t \r Testing the extra \r\n whitespaces \n \t "); LLStringUtil::trim(str_val); - UNSCOPED_INFO("1: trim failed"); CHECK(str_val == "Testing the extra \r\n whitespaces"); } @@ -163,12 +155,10 @@ namespace { std::string str("Second LindenLabs"); LLStringUtil::truncate(str, 6); - UNSCOPED_INFO("1: truncate"); CHECK(str == "Second"); // further truncate more than the length LLStringUtil::truncate(str, 0); - UNSCOPED_INFO("2: truncate"); CHECK(str == ""); } @@ -176,7 +166,6 @@ namespace { std::string str_val("SecondLife Source"); LLStringUtil::toUpper(str_val); - UNSCOPED_INFO("toUpper failed"); CHECK(str_val == "SECONDLIFE SOURCE"); } @@ -184,19 +173,15 @@ namespace { std::string str_val("SecondLife Source"); LLStringUtil::toLower(str_val); - UNSCOPED_INFO("toLower failed"); CHECK(str_val == "secondlife source"); } TEST_CASE("LLString: isHead matches a prefix and never an empty string", "[LLString]") { std::string str_val("Second"); - UNSCOPED_INFO("1. isHead failed"); CHECK(LLStringUtil::isHead(str_val, "SecondLife Source")); - UNSCOPED_INFO("2. isHead failed"); CHECK_FALSE(LLStringUtil::isHead(str_val, " SecondLife Source")); std::string str_val2(""); - UNSCOPED_INFO("3. isHead failed"); CHECK_FALSE(LLStringUtil::isHead(str_val2, "")); } @@ -205,10 +190,8 @@ namespace std::string str_val("Hello.\n\n Lindenlabs. \n This is \na simple test.\n"); std::string orig_str_val(str_val); LLStringUtil::addCRLF(str_val); - UNSCOPED_INFO("addCRLF failed"); CHECK(str_val == "Hello.\r\n\r\n Lindenlabs. \r\n This is \r\na simple test.\r\n"); LLStringUtil::removeCRLF(str_val); - UNSCOPED_INFO("removeCRLF failed"); CHECK(str_val == orig_str_val); } @@ -217,15 +200,14 @@ namespace std::string str_val("Hello.\n\n\t \t Lindenlabs. \t\t"); std::string orig_str_val(str_val); LLStringUtil::replaceTabsWithSpaces(str_val, 1); - UNSCOPED_INFO("replaceTabsWithSpaces failed"); CHECK(str_val == "Hello.\n\n Lindenlabs. "); LLStringUtil::replaceTabsWithSpaces(orig_str_val, 0); - UNSCOPED_INFO("replaceTabsWithSpaces failed for 0"); + UNSCOPED_INFO("a width of zero"); CHECK(orig_str_val == "Hello.\n\n Lindenlabs. "); str_val = "\t\t\t\t"; LLStringUtil::replaceTabsWithSpaces(str_val, 0); - UNSCOPED_INFO("replaceTabsWithSpaces failed for all tabs"); + UNSCOPED_INFO("only tabs"); CHECK(str_val == ""); } @@ -233,7 +215,6 @@ namespace { std::string str_val("Hello.\n\n\t\t\r\nLindenlabsX."); LLStringUtil::replaceNonstandardASCII(str_val, 'X'); - UNSCOPED_INFO("replaceNonstandardASCII failed"); CHECK(str_val == "Hello.\n\nXXX\nLindenlabsX."); } @@ -241,7 +222,6 @@ namespace { std::string str_val("Hello.\n\t\r\nABCDEFGHIABABAB"); LLStringUtil::replaceChar(str_val, 'A', 'X'); - UNSCOPED_INFO("1: replaceChar failed"); CHECK(str_val == "Hello.\n\t\r\nXBCDEFGHIXBXBXB"); std::string str_val1("Hello.\n\t\r\nABCDEFGHIABABAB"); } @@ -249,11 +229,9 @@ namespace TEST_CASE("LLString: containsNonprintable finds controls and separators but not text", "[LLString]") { std::string str_val("Hello.\n\r\t"); - UNSCOPED_INFO("containsNonprintable failed"); CHECK(LLStringUtil::containsNonprintable(str_val)); str_val = "ABC "; - UNSCOPED_INFO("containsNonprintable failed"); CHECK_FALSE(LLStringUtil::containsNonprintable(str_val)); // Controls above the ASCII range are two bytes, and neither of them @@ -284,17 +262,16 @@ namespace { std::string str_val("Hello.\n\r\t Again!"); LLStringUtil::stripNonprintable(str_val); - UNSCOPED_INFO("stripNonprintable failed"); CHECK(str_val == "Hello. Again!"); str_val = "\r\n\t\t"; LLStringUtil::stripNonprintable(str_val); - UNSCOPED_INFO("stripNonprintable resulting in empty string failed"); + UNSCOPED_INFO("only controls"); CHECK(str_val == ""); str_val = ""; LLStringUtil::stripNonprintable(str_val); - UNSCOPED_INFO("stripNonprintable of empty string resulting in empty string failed"); + UNSCOPED_INFO("empty string"); CHECK(str_val == ""); // A control above the ASCII range goes whole, taking both its bytes. @@ -314,81 +291,44 @@ namespace { bool value; std::string str_val("1"); - { - INFO("convertToBOOL 1 failed"); - REQUIRE(LLStringUtil::convertToBOOL(str_val, value)); - CHECK(value); - } + REQUIRE(LLStringUtil::convertToBOOL(str_val, value)); + CHECK(value); str_val = "T"; - { - INFO("convertToBOOL T failed"); - CHECK(LLStringUtil::convertToBOOL(str_val, value)); - CHECK(value); - } + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK(value); str_val = "t"; - { - INFO("convertToBOOL t failed"); - CHECK(LLStringUtil::convertToBOOL(str_val, value)); - CHECK(value); - } + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK(value); str_val = "TRUE"; - { - INFO("convertToBOOL TRUE failed"); - CHECK(LLStringUtil::convertToBOOL(str_val, value)); - CHECK(value); - } + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK(value); str_val = "True"; - { - INFO("convertToBOOL True failed"); - CHECK(LLStringUtil::convertToBOOL(str_val, value)); - CHECK(value); - } + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK(value); str_val = "true"; - { - INFO("convertToBOOL true failed"); - CHECK(LLStringUtil::convertToBOOL(str_val, value)); - CHECK(value); - } + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK(value); str_val = "0"; - { - INFO("convertToBOOL 0 failed"); - CHECK(LLStringUtil::convertToBOOL(str_val, value)); - CHECK_FALSE(value); - } + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK_FALSE(value); str_val = "F"; - { - INFO("convertToBOOL F failed"); - CHECK(LLStringUtil::convertToBOOL(str_val, value)); - CHECK_FALSE(value); - } + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK_FALSE(value); str_val = "f"; - { - INFO("convertToBOOL f failed"); - CHECK(LLStringUtil::convertToBOOL(str_val, value)); - CHECK_FALSE(value); - } + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK_FALSE(value); str_val = "FALSE"; - { - INFO("convertToBOOL FASLE failed"); - CHECK(LLStringUtil::convertToBOOL(str_val, value)); - CHECK_FALSE(value); - } + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK_FALSE(value); str_val = "False"; - { - INFO("convertToBOOL False failed"); - CHECK(LLStringUtil::convertToBOOL(str_val, value)); - CHECK_FALSE(value); - } + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK_FALSE(value); str_val = "false"; - { - INFO("convertToBOOL false failed"); - CHECK(LLStringUtil::convertToBOOL(str_val, value)); - CHECK_FALSE(value); - } + CHECK(LLStringUtil::convertToBOOL(str_val, value)); + CHECK_FALSE(value); str_val = "Tblah"; - UNSCOPED_INFO("convertToBOOL false failed"); CHECK_FALSE(LLStringUtil::convertToBOOL(str_val, value)); } @@ -396,25 +336,17 @@ namespace { U8 value; std::string str_val("255"); - { - INFO("1: convertToU8 failed"); - REQUIRE(LLStringUtil::convertToU8(str_val, value)); - CHECK(value == 255); - } + REQUIRE(LLStringUtil::convertToU8(str_val, value)); + CHECK(value == 255); str_val = "0"; - { - INFO("2: convertToU8 failed"); - CHECK(LLStringUtil::convertToU8(str_val, value)); - CHECK(value == 0); - } + CHECK(LLStringUtil::convertToU8(str_val, value)); + CHECK(value == 0); str_val = "-1"; - UNSCOPED_INFO("3: convertToU8 failed"); CHECK_FALSE(LLStringUtil::convertToU8(str_val, value)); str_val = "256"; // bigger than MAX_U8 - UNSCOPED_INFO("4: convertToU8 failed"); CHECK_FALSE(LLStringUtil::convertToU8(str_val, value)); } @@ -422,32 +354,21 @@ namespace { S8 value; std::string str_val("127"); - { - INFO("1: convertToS8 failed"); - REQUIRE(LLStringUtil::convertToS8(str_val, value)); - CHECK(value == 127); - } + REQUIRE(LLStringUtil::convertToS8(str_val, value)); + CHECK(value == 127); str_val = "0"; - { - INFO("2: convertToS8 failed"); - CHECK(LLStringUtil::convertToS8(str_val, value)); - CHECK(value == 0); - } + CHECK(LLStringUtil::convertToS8(str_val, value)); + CHECK(value == 0); str_val = "-128"; - { - INFO("3: convertToS8 failed"); - CHECK(LLStringUtil::convertToS8(str_val, value)); - CHECK(value == -128); - } + CHECK(LLStringUtil::convertToS8(str_val, value)); + CHECK(value == -128); str_val = "128"; // bigger than MAX_S8 - UNSCOPED_INFO("4: convertToS8 failed"); CHECK_FALSE(LLStringUtil::convertToS8(str_val, value)); str_val = "-129"; - UNSCOPED_INFO("5: convertToS8 failed"); CHECK_FALSE(LLStringUtil::convertToS8(str_val, value)); } @@ -455,32 +376,21 @@ namespace { S16 value; std::string str_val("32767"); - { - INFO("1: convertToS16 failed"); - REQUIRE(LLStringUtil::convertToS16(str_val, value)); - CHECK(value == 32767); - } + REQUIRE(LLStringUtil::convertToS16(str_val, value)); + CHECK(value == 32767); str_val = "0"; - { - INFO("2: convertToS16 failed"); - CHECK(LLStringUtil::convertToS16(str_val, value)); - CHECK(value == 0); - } + CHECK(LLStringUtil::convertToS16(str_val, value)); + CHECK(value == 0); str_val = "-32768"; - { - INFO("3: convertToS16 failed"); - CHECK(LLStringUtil::convertToS16(str_val, value)); - CHECK(value == -32768); - } + CHECK(LLStringUtil::convertToS16(str_val, value)); + CHECK(value == -32768); str_val = "32768"; - UNSCOPED_INFO("4: convertToS16 failed"); CHECK_FALSE(LLStringUtil::convertToS16(str_val, value)); str_val = "-32769"; - UNSCOPED_INFO("5: convertToS16 failed"); CHECK_FALSE(LLStringUtil::convertToS16(str_val, value)); } @@ -488,25 +398,17 @@ namespace { U16 value; std::string str_val("65535"); //0xFFFF - { - INFO("1: convertToU16 failed"); - REQUIRE(LLStringUtil::convertToU16(str_val, value)); - CHECK(value == 65535); - } + REQUIRE(LLStringUtil::convertToU16(str_val, value)); + CHECK(value == 65535); str_val = "0"; - { - INFO("2: convertToU16 failed"); - CHECK(LLStringUtil::convertToU16(str_val, value)); - CHECK(value == 0); - } + CHECK(LLStringUtil::convertToU16(str_val, value)); + CHECK(value == 0); str_val = "-1"; - UNSCOPED_INFO("3: convertToU16 failed"); CHECK_FALSE(LLStringUtil::convertToU16(str_val, value)); str_val = "65536"; - UNSCOPED_INFO("4: convertToU16 failed"); CHECK_FALSE(LLStringUtil::convertToU16(str_val, value)); } @@ -514,21 +416,14 @@ namespace { U32 value; std::string str_val("4294967295"); //0xFFFFFFFF - { - INFO("1: convertToU32 failed"); - REQUIRE(LLStringUtil::convertToU32(str_val, value)); - CHECK(value == 4294967295UL); - } + REQUIRE(LLStringUtil::convertToU32(str_val, value)); + CHECK(value == 4294967295UL); str_val = "0"; - { - INFO("2: convertToU32 failed"); - CHECK(LLStringUtil::convertToU32(str_val, value)); - CHECK(value == 0); - } + CHECK(LLStringUtil::convertToU32(str_val, value)); + CHECK(value == 0); str_val = "4294967296"; - UNSCOPED_INFO("3: convertToU32 failed"); CHECK_FALSE(LLStringUtil::convertToU32(str_val, value)); // A negative is rejected rather than wrapped. convertToU8 and @@ -536,79 +431,54 @@ namespace // after parsing as signed -- so this is the unsigned form agreeing with // its own siblings. Stream extraction let num_get negate it instead. str_val = "-1"; - UNSCOPED_INFO("4: convertToU32 failed"); CHECK_FALSE(LLStringUtil::convertToU32(str_val, value)); // A leading '+' is still accepted, as it was through the stream. str_val = "+7"; - { - INFO("5: convertToU32 failed"); - CHECK(LLStringUtil::convertToU32(str_val, value)); - CHECK(value == 7); - } + CHECK(LLStringUtil::convertToU32(str_val, value)); + CHECK(value == 7); } TEST_CASE("LLString: convertToS32 reads the S32 range with a sign or spaces or trailing text", "[LLString]") { S32 value; std::string str_val("2147483647"); //0x7FFFFFFF - { - INFO("1: convertToS32 failed"); - REQUIRE(LLStringUtil::convertToS32(str_val, value)); - CHECK(value == 2147483647); - } + REQUIRE(LLStringUtil::convertToS32(str_val, value)); + CHECK(value == 2147483647); str_val = "0"; - { - INFO("2: convertToS32 failed"); - CHECK(LLStringUtil::convertToS32(str_val, value)); - CHECK(value == 0); - } + CHECK(LLStringUtil::convertToS32(str_val, value)); + CHECK(value == 0); // Avoid "unary minus operator applied to unsigned type" warning on VC++. JC S32 min_val = -2147483647 - 1; str_val = "-2147483648"; - { - INFO("3: convertToS32 failed"); - CHECK(LLStringUtil::convertToS32(str_val, value)); - CHECK(value == min_val); - } + CHECK(LLStringUtil::convertToS32(str_val, value)); + CHECK(value == min_val); str_val = "2147483648"; - UNSCOPED_INFO("4: convertToS32 failed"); CHECK_FALSE(LLStringUtil::convertToS32(str_val, value)); str_val = "-2147483649"; - UNSCOPED_INFO("5: convertToS32 failed"); CHECK_FALSE(LLStringUtil::convertToS32(str_val, value)); // from_chars does not take a leading '+' on its own; the conversions // strip it so they keep accepting what stream extraction accepted. str_val = "+42"; - { - INFO("6: convertToS32 failed"); - CHECK(LLStringUtil::convertToS32(str_val, value)); - CHECK(value == 42); - } + CHECK(LLStringUtil::convertToS32(str_val, value)); + CHECK(value == 42); // Surrounding space is trimmed, and a parse that stops early still // reports what it read -- both as the stream did. str_val = " 13 "; - { - INFO("7: convertToS32 failed"); - CHECK(LLStringUtil::convertToS32(str_val, value)); - CHECK(value == 13); - } + CHECK(LLStringUtil::convertToS32(str_val, value)); + CHECK(value == 13); str_val = "12abc"; - { - INFO("8: convertToS32 failed"); - CHECK(LLStringUtil::convertToS32(str_val, value)); - CHECK(value == 12); - } + CHECK(LLStringUtil::convertToS32(str_val, value)); + CHECK(value == 12); str_val = "abc"; - UNSCOPED_INFO("9: convertToS32 failed"); CHECK_FALSE(LLStringUtil::convertToS32(str_val, value)); } @@ -616,18 +486,12 @@ namespace { F32 value; std::string str_val("2147483647"); //0x7FFFFFFF - { - INFO("1: convertToF32 failed"); - REQUIRE(LLStringUtil::convertToF32(str_val, value)); - CHECK(value == 2147483647.f); - } + REQUIRE(LLStringUtil::convertToF32(str_val, value)); + CHECK(value == 2147483647.f); str_val = "0"; - { - INFO("2: convertToF32 failed"); - CHECK(LLStringUtil::convertToF32(str_val, value)); - CHECK(value == 0); - } + CHECK(LLStringUtil::convertToF32(str_val, value)); + CHECK(value == 0); /* Need to find max/min F32 values str_val = "-2147483648"; @@ -644,42 +508,26 @@ namespace // a leading '+' all read the same as they did through the stream, and // the decimal point stays a point whatever the C locale is set to. str_val = "1.5"; - { - INFO("6: convertToF32 failed"); - CHECK(LLStringUtil::convertToF32(str_val, value)); - CHECK(value == 1.5f); - } + CHECK(LLStringUtil::convertToF32(str_val, value)); + CHECK(value == 1.5f); str_val = "-2.25"; - { - INFO("7: convertToF32 failed"); - CHECK(LLStringUtil::convertToF32(str_val, value)); - CHECK(value == -2.25f); - } + CHECK(LLStringUtil::convertToF32(str_val, value)); + CHECK(value == -2.25f); str_val = "+0.5"; - { - INFO("8: convertToF32 failed"); - CHECK(LLStringUtil::convertToF32(str_val, value)); - CHECK(value == 0.5f); - } + CHECK(LLStringUtil::convertToF32(str_val, value)); + CHECK(value == 0.5f); str_val = "1.5e2"; - { - INFO("9: convertToF32 failed"); - CHECK(LLStringUtil::convertToF32(str_val, value)); - CHECK(value == 150.f); - } + CHECK(LLStringUtil::convertToF32(str_val, value)); + CHECK(value == 150.f); str_val = " 0.25 "; - { - INFO("10: convertToF32 failed"); - CHECK(LLStringUtil::convertToF32(str_val, value)); - CHECK(value == 0.25f); - } + CHECK(LLStringUtil::convertToF32(str_val, value)); + CHECK(value == 0.25f); str_val = "nonsense"; - UNSCOPED_INFO("11: convertToF32 failed"); CHECK_FALSE(LLStringUtil::convertToF32(str_val, value)); } @@ -687,18 +535,12 @@ namespace { F64 value; std::string str_val("9223372036854775807"); //0x7FFFFFFFFFFFFFFF - { - INFO("1: convertToF64 failed"); - REQUIRE(LLStringUtil::convertToF64(str_val, value)); - CHECK(value == 9223372036854775807.0); - } + REQUIRE(LLStringUtil::convertToF64(str_val, value)); + CHECK(value == 9223372036854775807.0); str_val = "0"; - { - INFO("2: convertToF64 failed"); - CHECK(LLStringUtil::convertToF64(str_val, value)); - CHECK(value == 0.0F); - } + CHECK(LLStringUtil::convertToF64(str_val, value)); + CHECK(value == 0.0F); /* Need to find max/min F64 values str_val = "-2147483648"; @@ -717,21 +559,16 @@ namespace const char* str1 = NULL; const char* str2 = NULL; - UNSCOPED_INFO("1: compareStrings failed"); CHECK(LLStringUtil::compareStrings(str1, str2) == 0); str2 = "A"; - UNSCOPED_INFO("2: compareStrings failed"); CHECK(LLStringUtil::compareStrings(str1, str2) > 0); - UNSCOPED_INFO("3: compareStrings failed"); CHECK(LLStringUtil::compareStrings(str2, str1) < 0); str1 = "A is smaller than B"; str2 = "B is greater than A"; - UNSCOPED_INFO("4: compareStrings failed"); CHECK(LLStringUtil::compareStrings(str1, str2) < 0); str2 = "A is smaller than B"; - UNSCOPED_INFO("5: compareStrings failed"); CHECK(LLStringUtil::compareStrings(str1, str2) == 0); } @@ -740,17 +577,13 @@ namespace const char* str1 = NULL; const char* str2 = NULL; - UNSCOPED_INFO("1: compareInsensitive failed"); CHECK(LLStringUtil::compareInsensitive(str1, str2) == 0); str2 = "A"; - UNSCOPED_INFO("2: compareInsensitive failed"); CHECK(LLStringUtil::compareInsensitive(str1, str2) > 0); - UNSCOPED_INFO("3: compareInsensitive failed"); CHECK(LLStringUtil::compareInsensitive(str2, str1) < 0); str1 = "A is equal to a"; str2 = "a is EQUAL to A"; - UNSCOPED_INFO("4: compareInsensitive failed"); CHECK(LLStringUtil::compareInsensitive(str1, str2) == 0); // Ignoring case is not the same as uppercasing both sides, which is @@ -777,32 +610,23 @@ namespace { std::string lhs_str("PROgraM12files"); std::string rhs_str("PROgram12Files"); - UNSCOPED_INFO("compareDict 1 failed"); CHECK(LLStringUtil::compareDict(lhs_str, rhs_str) < 0); - UNSCOPED_INFO("precedesDict 1 failed"); CHECK(LLStringUtil::precedesDict(lhs_str, rhs_str)); lhs_str = "PROgram12Files"; rhs_str = "PROgram12Files"; - UNSCOPED_INFO("compareDict 2 failed"); CHECK(LLStringUtil::compareDict(lhs_str, rhs_str) == 0); - UNSCOPED_INFO("precedesDict 2 failed"); CHECK_FALSE(LLStringUtil::precedesDict(lhs_str, rhs_str)); lhs_str = "PROgram12Files"; rhs_str = "PROgRAM12FILES"; - UNSCOPED_INFO("compareDict 3 failed"); CHECK(LLStringUtil::compareDict(lhs_str, rhs_str) > 0); - UNSCOPED_INFO("precedesDict 3 failed"); CHECK_FALSE(LLStringUtil::precedesDict(lhs_str, rhs_str)); // Uppercase sorts first, which is a tertiary-level ordering. The // caseless form drops that level and so sees no difference at all. - UNSCOPED_INFO("compareDictInsensitive case"); CHECK(LLStringUtil::compareDictInsensitive(lhs_str, rhs_str) == 0); - UNSCOPED_INFO("compareDict case"); CHECK(LLStringUtil::compareDict(std::string("abc"), std::string("ABC")) > 0); - UNSCOPED_INFO("compareDict case 2"); CHECK(LLStringUtil::compareDict(std::string("Apple"), std::string("apple")) < 0); // Digit runs compare as the numbers they spell. @@ -831,10 +655,10 @@ namespace char str2[100]; LLStringUtil::copy(str2, str1, 100); - UNSCOPED_INFO("LLStringUtil::copy with enough dest length failed"); + UNSCOPED_INFO("enough room"); CHECK(std::string_view(str2) == std::string_view(str1)); LLStringUtil::copy(str2, str1, sizeof("First")); - UNSCOPED_INFO("LLStringUtil::copy with less dest length failed"); + UNSCOPED_INFO("too little room"); CHECK(std::string_view(str2) == "First"); } @@ -849,12 +673,10 @@ namespace dest = str1; LLStringUtil::copyInto(dest, str3, str2.length()); - UNSCOPED_INFO("LLStringUtil::copyInto insert failed"); CHECK(dest == str4); dest = str1; LLStringUtil::copyInto(dest, str3, dest.length()); - UNSCOPED_INFO("LLStringUtil::copyInto append failed"); CHECK(dest == str5); } @@ -866,7 +688,7 @@ namespace // should not be altered. std::string simple_text = "Hello, world!"; stripped = LLStringFn::strip_invalid_xml(simple_text); - UNSCOPED_INFO("Simple text passed unchanged"); + UNSCOPED_INFO("plain text unchanged"); CHECK(stripped == simple_text); // Control characters should be removed @@ -882,7 +704,7 @@ namespace allowed_control_chars.push_back( (char)0x0d ); stripped = LLStringFn::strip_invalid_xml(control_chars); - UNSCOPED_INFO("Only tab, LF, CR control characters allowed"); + UNSCOPED_INFO("only tab, LF and CR kept"); CHECK(stripped == allowed_control_chars); // UTF-8 should be passed intact, including high byte @@ -892,7 +714,7 @@ namespace french.push_back( (char)0xA7 ); french += "ais"; stripped = LLStringFn::strip_invalid_xml( french ); - UNSCOPED_INFO("UTF-8 high byte text is allowed"); + UNSCOPED_INFO("UTF-8 kept"); CHECK(french == stripped); } @@ -921,17 +743,15 @@ namespace // Test when source string is entirely one key std::string s1 = (std::string)iter->first; std::string s2 = (std::string)iter->second; + CAPTURE(s1); subcount = LLStringUtil::format(s1, fmt_map); - UNSCOPED_INFO("LLStringUtil::format: Raw interpolation result"); REQUIRE(s1 == s2); if (s1 == "?" || s1 == "[]") // no interp expected { - UNSCOPED_INFO("LLStringUtil::format: Raw interpolation result count"); REQUIRE(0 == subcount); } else { - UNSCOPED_INFO("LLStringUtil::format: Raw interpolation result count"); REQUIRE(1 == subcount); } } @@ -941,18 +761,16 @@ namespace // Test when source string is one key, duplicated std::string s1 = (std::string)iter->first; std::string s2 = (std::string)iter->second; + CAPTURE(s1); s = s1 + s1 + s1 + s1; subcount = LLStringUtil::format(s, fmt_map); - UNSCOPED_INFO("LLStringUtil::format: Rawx4 interpolation result"); REQUIRE(s == s2 + s2 + s2 + s2); if (s1 == "?" || s1 == "[]") // no interp expected { - UNSCOPED_INFO("LLStringUtil::format: Rawx4 interpolation result count"); REQUIRE(0 == subcount); } else { - UNSCOPED_INFO("LLStringUtil::format: Rawx4 interpolation result count"); REQUIRE(4 == subcount); } } @@ -961,54 +779,42 @@ namespace std::string srcs = "!!!!!!!!!!!!!!!!"; s = srcs; subcount = LLStringUtil::format(s, fmt_map); - UNSCOPED_INFO("LLStringUtil::format: No key test result"); CHECK(s == srcs); - UNSCOPED_INFO("LLStringUtil::format: No key test result count"); CHECK(0 == subcount); // Test when source string has no keys and is empty std::string srcs3; s = srcs3; subcount = LLStringUtil::format(s, fmt_map); - UNSCOPED_INFO("LLStringUtil::format: No key test3 result"); - REQUIRE(s.empty()); - UNSCOPED_INFO("LLStringUtil::format: No key test3 result count"); + CHECK(s.empty()); CHECK(0 == subcount); // Test a substitution where a key is substituted with blankness std::string srcs2 = "[DELETE]"; s = srcs2; subcount = LLStringUtil::format(s, fmt_map); - UNSCOPED_INFO("LLStringUtil::format: Delete key test2 result"); - REQUIRE(s.empty()); - UNSCOPED_INFO("LLStringUtil::format: Delete key test2 result count"); + CHECK(s.empty()); CHECK(1 == subcount); // Test an assorted substitution std::string srcs4 = "[TRICK1][A][B][AAA][BBB][TRICK2][KEYLONGER][KEYSHORTER]?[DELETE]"; s = srcs4; subcount = LLStringUtil::format(s, fmt_map); - UNSCOPED_INFO("LLStringUtil::format: Assorted Test1 result"); CHECK(s == "[A]abaaabbb[A]shortAm I not a long string??"); - UNSCOPED_INFO("LLStringUtil::format: Assorted Test1 result count"); CHECK(9 == subcount); // Test an assorted substitution std::string srcs5 = "[DELETE]?[KEYSHORTER][KEYLONGER][TRICK2][BBB][AAA][B][A][TRICK1]"; s = srcs5; subcount = LLStringUtil::format(s, fmt_map); - UNSCOPED_INFO("LLStringUtil::format: Assorted Test2 result"); CHECK(s == "?Am I not a long string?short[A]bbbaaaba[A]"); - UNSCOPED_INFO("LLStringUtil::format: Assorted Test2 result count"); CHECK(9 == subcount); // Test on nested brackets std::string srcs6 = "[[TRICK1]][[A]][[B]][[AAA]][[BBB]][[TRICK2]][[KEYLONGER]][[KEYSHORTER]]?[[DELETE]]"; s = srcs6; subcount = LLStringUtil::format(s, fmt_map); - UNSCOPED_INFO("LLStringUtil::format: Assorted Test2 result"); CHECK(s == "[[A]][a][b][aaa][bbb][[A]][short][Am I not a long string?]?[]"); - UNSCOPED_INFO("LLStringUtil::format: Assorted Test2 result count"); CHECK(9 == subcount); @@ -1016,9 +822,7 @@ namespace std::string srcs8 = "foo[DELETE]bar?"; s = srcs8; subcount = LLStringUtil::format(s, fmt_map); - UNSCOPED_INFO("LLStringUtil::format: Assorted Test3 result"); CHECK(s == "foobar?"); - UNSCOPED_INFO("LLStringUtil::format: Assorted Test3 result count"); CHECK(1 == subcount); } @@ -1033,18 +837,14 @@ namespace std::string srcs6 = "12345"; s = srcs6; subcount = LLStringUtil::format(s, blank_fmt_map); - UNSCOPED_INFO("LLStringUtil::format: Blankfmt Test1 result"); CHECK(s == "12345"); - UNSCOPED_INFO("LLStringUtil::format: Blankfmt Test1 result count"); CHECK(0 == subcount); // Test substituting a blank string out of a blank format_map std::string srcs7; s = srcs7; subcount = LLStringUtil::format(s, blank_fmt_map); - UNSCOPED_INFO("LLStringUtil::format: Blankfmt Test2 result"); - REQUIRE(s.empty()); - UNSCOPED_INFO("LLStringUtil::format: Blankfmt Test2 result count"); + CHECK(s.empty()); CHECK(0 == subcount); } @@ -1061,18 +861,14 @@ namespace std::string srcs6 = "12345"; s = srcs6; subcount = LLStringUtil::format(s, nasty_fmt_map); - UNSCOPED_INFO("LLStringUtil::format: Nastyfmt Test1 result"); CHECK(s == "12345"); - UNSCOPED_INFO("LLStringUtil::format: Nastyfmt Test1 result count"); CHECK(0 == subcount); // Test substituting a blank string out of a nasty format_map std::string srcs7; s = srcs7; subcount = LLStringUtil::format(s, nasty_fmt_map); - UNSCOPED_INFO("LLStringUtil::format: Nastyfmt Test2 result"); - REQUIRE(s.empty()); - UNSCOPED_INFO("LLStringUtil::format: Nastyfmt Test2 result count"); + CHECK(s.empty()); CHECK(0 == subcount); } @@ -1081,7 +877,6 @@ namespace // Make sure startsWith works std::string string("anybody in there?"); std::string substr("anybody"); - UNSCOPED_INFO("startsWith works."); CHECK(LLStringUtil::startsWith(string, substr)); } @@ -1090,7 +885,6 @@ namespace // Make sure startsWith correctly fails std::string string("anybody in there?"); std::string substr("there"); - UNSCOPED_INFO("startsWith fails."); CHECK_FALSE(LLStringUtil::startsWith(string, substr)); } @@ -1099,11 +893,8 @@ namespace // startsWith fails on empty strings std::string value("anybody in there?"); std::string empty; - UNSCOPED_INFO("empty string."); CHECK_FALSE(LLStringUtil::startsWith(value, empty)); - UNSCOPED_INFO("empty substr."); CHECK_FALSE(LLStringUtil::startsWith(empty, value)); - UNSCOPED_INFO("empty everything."); CHECK_FALSE(LLStringUtil::startsWith(empty, empty)); } @@ -1112,7 +903,6 @@ namespace // Make sure endsWith works correctly std::string string("anybody in there?"); std::string substr("there?"); - UNSCOPED_INFO("endsWith works."); CHECK(LLStringUtil::endsWith(string, substr)); } @@ -1121,13 +911,10 @@ namespace // Make sure endsWith correctly fails std::string string("anybody in there?"); std::string substr("anybody"); - UNSCOPED_INFO("endsWith fails."); CHECK_FALSE(LLStringUtil::endsWith(string, substr)); substr = "there"; - UNSCOPED_INFO("endsWith fails."); CHECK_FALSE(LLStringUtil::endsWith(string, substr)); substr = "ther?"; - UNSCOPED_INFO("endsWith fails."); CHECK_FALSE(LLStringUtil::endsWith(string, substr)); } @@ -1136,11 +923,8 @@ namespace // endsWith fails on empty strings std::string value("anybody in there?"); std::string empty; - UNSCOPED_INFO("empty string."); CHECK_FALSE(LLStringUtil::endsWith(value, empty)); - UNSCOPED_INFO("empty substr."); CHECK_FALSE(LLStringUtil::endsWith(empty, value)); - UNSCOPED_INFO("empty everything."); CHECK_FALSE(LLStringUtil::endsWith(empty, empty)); } @@ -1166,31 +950,29 @@ namespace // don't pass a string of escape characters, when you pass an empty string // (different overloads), and when you pass a string of characters that // aren't actually present. - static void ensure_getTokens(const std::string& desc, - const std::string& string, - const std::string& drop_delims, - const std::string& keep_delims, - const std::string& quotes, - const std::vector& expect) + static void require_tokens(const std::string& desc, + const std::string& string, + const std::string& drop_delims, + const std::string& keep_delims, + const std::string& quotes, + const std::vector& expect) { - UNSCOPED_INFO(desc + " - no esc"); + INFO(desc); REQUIRE(LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes) == expect); - UNSCOPED_INFO(desc + " - empty esc"); REQUIRE(LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes, "") == expect); - UNSCOPED_INFO(desc + " - unused esc"); REQUIRE(LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes, "!") == expect); } - static void ensure_getTokens(const std::string& desc, - const std::string& string, - const std::string& drop_delims, - const std::string& keep_delims, - const std::vector& expect) + static void require_tokens(const std::string& desc, + const std::string& string, + const std::string& drop_delims, + const std::string& keep_delims, + const std::vector& expect) { - ensure_getTokens(desc, string, drop_delims, keep_delims, "", expect); + require_tokens(desc, string, drop_delims, keep_delims, "", expect); } TEST_CASE("LLString: getTokens keeps delimiters and handles quotes and escapes", "[LLString]") @@ -1200,37 +982,37 @@ namespace // If you omit keep_delims, you get the older function (test above). // cases like the getTokens(string, delims) tests above - ensure_getTokens("empty string", "", " ", "", StringVec()); - ensure_getTokens("only delims", - " \r\n ", " \r\n", "", StringVec()); - ensure_getTokens("sequence of delims", - ",,, one ,,,", ", ", "", StringVec{"one"}); + require_tokens("empty string", "", " ", "", StringVec()); + require_tokens("only delims", + " \r\n ", " \r\n", "", StringVec()); + require_tokens("sequence of delims", + ",,, one ,,,", ", ", "", StringVec{"one"}); // Note contrast with the case in the previous method - ensure_getTokens("noncontiguous tokens", - ", ,, , one ,,,", ", ", "", StringVec{"one"}); - ensure_getTokens("space-padded tokens", - ", one , two ,", ", ", "", - StringVec{"one", "two"}); - ensure_getTokens("no delims", "one", ",", "", StringVec{ "one" }); + require_tokens("noncontiguous tokens", + ", ,, , one ,,,", ", ", "", StringVec{"one"}); + require_tokens("space-padded tokens", + ", one , two ,", ", ", "", + StringVec{"one", "two"}); + require_tokens("no delims", "one", ",", "", StringVec{ "one" }); // drop_delims vs. keep_delims - ensure_getTokens("arithmetic", - " ab+def / xx* yy ", " ", "+-*/", { "ab", "+", "def", "/", "xx", "*", "yy" }); + require_tokens("arithmetic", + " ab+def / xx* yy ", " ", "+-*/", { "ab", "+", "def", "/", "xx", "*", "yy" }); // quotes - ensure_getTokens("no quotes", - "She said, \"Don't go.\"", " ", ",", "", { "She", "said", ",", "\"Don't", "go.\"" }); - ensure_getTokens("quotes", - "She said, \"Don't go.\"", " ", ",", "\"", { "She", "said", ",", "Don't go." }); - ensure_getTokens("quotes and delims", - "run c:/'Documents and Settings'/someone", " ", "", "'", - { "run", "c:/Documents and Settings/someone" }); - ensure_getTokens("unmatched quote", - "baby don't leave", " ", "", "'", { "baby", "don't", "leave" }); - ensure_getTokens("adjacent quoted", - "abc'def \"ghi'\"jkl' mno\"pqr", " ", "", "\"'", { "abcdef \"ghijkl' mnopqr" }); - ensure_getTokens("quoted empty string", - "--set SomeVar ''", " ", "", "'", { "--set", "SomeVar", "" }); + require_tokens("no quotes", + "She said, \"Don't go.\"", " ", ",", "", { "She", "said", ",", "\"Don't", "go.\"" }); + require_tokens("quotes", + "She said, \"Don't go.\"", " ", ",", "\"", { "She", "said", ",", "Don't go." }); + require_tokens("quotes and delims", + "run c:/'Documents and Settings'/someone", " ", "", "'", + { "run", "c:/Documents and Settings/someone" }); + require_tokens("unmatched quote", + "baby don't leave", " ", "", "'", { "baby", "don't", "leave" }); + require_tokens("adjacent quoted", + "abc'def \"ghi'\"jkl' mno\"pqr", " ", "", "\"'", { "abcdef \"ghijkl' mnopqr" }); + require_tokens("quoted empty string", + "--set SomeVar ''", " ", "", "'", { "--set", "SomeVar", "" }); // escapes // Don't use backslash as an escape for these tests -- you'll go nuts @@ -1301,25 +1083,20 @@ namespace for (const char* src : untouched) { + CAPTURE(src); std::string s(src); - UNSCOPED_INFO(std::string("format_map count: ") + src); REQUIRE(0 == LLStringUtil::format(s, fmt_map)); - UNSCOPED_INFO(std::string("format_map result: ") + src); REQUIRE(s == std::string(src)); std::string s2(src); - UNSCOPED_INFO(std::string("LLSD count: ") + src); REQUIRE(0 == LLStringUtil::format(s2, sd_map)); - UNSCOPED_INFO(std::string("LLSD result: ") + src); REQUIRE(s2 == std::string(src)); } // A real substitution still fires, and the surrounding text — including // brackets that are not a key — survives it. std::string s("no [FOO] here [MISSING] -=[Stylized Name]=-"); - UNSCOPED_INFO("mixed count"); CHECK(1 == LLStringUtil::format(s, fmt_map)); - UNSCOPED_INFO("mixed result"); CHECK(s == std::string("no bar here [MISSING] -=[Stylized Name]=-")); } diff --git a/indra/llcommon/tests/llstring_utf_test.cpp b/indra/llcommon/tests/llstring_utf_test.cpp index 3f2e9b73b7..f5b9da603c 100644 --- a/indra/llcommon/tests/llstring_utf_test.cpp +++ b/indra/llcommon/tests/llstring_utf_test.cpp @@ -181,17 +181,17 @@ namespace return removed; } - // llutf16string element types have no std::ostream operator<<, so - // ensure_equals fails to instantiate its failure formatter. Compare by - // value and format diagnostics ourselves. - void ensure_wstring_equals(const std::string& msg, - const std::u32string& actual, - const std::u32string& expected) + // char32_t has no std::ostream operator<<, so a failed comparison of + // two std::u32strings could not show them. Compare by value and format + // diagnostics ourselves. + void require_same_u32(const std::string& what, + const std::u32string& actual, + const std::u32string& expected) { if (actual == expected) return; std::ostringstream oss; - oss << msg << ": std::u32string mismatch (actual.size=" << actual.size() + oss << what << ": std::u32string mismatch (actual.size=" << actual.size() << " expected.size=" << expected.size() << ")\n actual: "; oss << std::hex << std::uppercase; for (llwchar c : actual) oss << "U+" << (U32)c << ' '; @@ -202,10 +202,10 @@ namespace } -namespace tut +namespace { struct llstring_utf_data {}; - // Tests are numbered by category: + // The cases come in categories, which TUT numbered: // 1x conversion (utf8 <-> wstring/utf16, basic round-trips) // 2x validation (well-formed input pins) // 3x case (ICU-backed casing) @@ -219,9 +219,7 @@ namespace tut // 11x utf8str helpers (substChar/makeASCII/removeCRLF/split/preview) // 12x utf8 walkers (byte-offset segmentation against its wide twin) // 14x trim (Unicode whitespace, and what is not whitespace) - // The TUT default registers only test<1>..test<50>, but the explicit - // test_group<..., 150> below raises that ceiling. Keep this index in - // sync with categories used below. + // Keep this index in sync with the categories used below. // --------------------------------------------------------------- // round-trip coverage @@ -234,10 +232,8 @@ namespace tut const std::u32string wascii (ascii.begin(), ascii.end()); const llutf16string u16ascii(ascii.begin(), ascii.end()); - ensure_wstring_equals ("utf8->wstring ASCII", to_u32 (ascii), wascii); - UNSCOPED_INFO("wstring->utf8 ASCII"); + require_same_u32("utf8->wstring ASCII", to_u32 (ascii), wascii); CHECK(to_utf8 (wascii) == ascii); - UNSCOPED_INFO("utf16->utf8 ASCII"); CHECK(utf16str_to_utf8str(u16ascii) == ascii); } @@ -248,10 +244,8 @@ namespace tut const std::u32string w = { (llwchar)'H', (llwchar)0x00E9, (llwchar)'l', (llwchar)'l', (llwchar)'o' }; const llutf16string u16 = { (char16_t)'H', (char16_t)0x00E9, (char16_t)'l', (char16_t)'l', (char16_t)'o' }; - ensure_wstring_equals ("utf8->wstring 2-byte", to_u32 (utf8), w); - UNSCOPED_INFO("wstring->utf8 2-byte"); + require_same_u32("utf8->wstring 2-byte", to_u32 (utf8), w); CHECK(to_utf8 (w) == utf8); - UNSCOPED_INFO("utf16->utf8 2-byte"); CHECK(utf16str_to_utf8str(u16) == utf8); } @@ -262,10 +256,8 @@ namespace tut const std::u32string w = { (llwchar)0x65E5, (llwchar)0x672C }; const llutf16string u16 = { (char16_t)0x65E5, (char16_t)0x672C }; - ensure_wstring_equals ("utf8->wstring 3-byte", to_u32 (utf8), w); - UNSCOPED_INFO("wstring->utf8 3-byte"); + require_same_u32("utf8->wstring 3-byte", to_u32 (utf8), w); CHECK(to_utf8 (w) == utf8); - UNSCOPED_INFO("utf16->utf8 3-byte"); CHECK(utf16str_to_utf8str(u16) == utf8); } @@ -276,10 +268,8 @@ namespace tut const std::u32string w = { (llwchar)0x1F680 }; const llutf16string u16 = { (char16_t)0xD83D, (char16_t)0xDE80 }; - ensure_wstring_equals ("utf8->wstring astral", to_u32 (utf8), w); - UNSCOPED_INFO("wstring->utf8 astral"); + require_same_u32("utf8->wstring astral", to_u32 (utf8), w); CHECK(to_utf8 (w) == utf8); - UNSCOPED_INFO("utf16->utf8 astral"); CHECK(utf16str_to_utf8str(u16) == utf8); } @@ -292,21 +282,16 @@ namespace tut const std::u32string w = { (llwchar)'A', (llwchar)0x65E5, (llwchar)0x1F680, (llwchar)'B' }; const llutf16string u16 = { (char16_t)'A', (char16_t)0x65E5, (char16_t)0xD83D, (char16_t)0xDE80, (char16_t)'B' }; - ensure_wstring_equals ("utf8->wstring mixed", to_u32 (utf8), w); - UNSCOPED_INFO("wstring->utf8 mixed"); + require_same_u32("utf8->wstring mixed", to_u32 (utf8), w); CHECK(to_utf8 (w) == utf8); - UNSCOPED_INFO("utf16->utf8 mixed"); CHECK(utf16str_to_utf8str(u16) == utf8); } // Empty inputs produce empty outputs in every direction. TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: empty input converts to empty output in every direction", "[LLStringUTF]") { - UNSCOPED_INFO("utf8->wstring empty"); - REQUIRE(to_u32 (std::string()).size() == size_t(0)); - UNSCOPED_INFO("wstring->utf8 empty"); + CHECK(to_u32 (std::string()).size() == size_t(0)); CHECK(to_utf8 (std::u32string()) == std::string()); - UNSCOPED_INFO("utf16->utf8 empty"); CHECK(utf16str_to_utf8str(llutf16string()) == std::string()); } @@ -314,7 +299,6 @@ namespace tut TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: rawstr_to_utf8 leaves valid UTF-8 as it is", "[LLStringUTF]") { const std::string mixed = "A\xE6\x97\xA5\xF0\x9F\x9A\x80" "B"; - UNSCOPED_INFO("rawstr_to_utf8 preserves valid utf8"); CHECK(rawstr_to_utf8(mixed) == mixed); } @@ -323,9 +307,7 @@ namespace tut { const std::string bytes = "\xF0\x9F\x9A\x80"; const std::u8string u8 = str_to_u8str(bytes); - UNSCOPED_INFO("str_to_u8str size"); - REQUIRE(u8.size() == bytes.size()); - UNSCOPED_INFO("u8 round-trip"); + CHECK(u8.size() == bytes.size()); CHECK(u8str_to_str(u8) == bytes); } @@ -339,36 +321,22 @@ namespace tut char buf[8]; std::memset(buf, 0xAA, sizeof(buf)); - UNSCOPED_INFO("'A' len"); CHECK(wchar_to_utf8chars(0x41, buf) == std::ptrdiff_t(1)); - UNSCOPED_INFO("'A' b0"); CHECK((U32)(U8)buf[0] == U32(0x41)); - UNSCOPED_INFO("é len"); CHECK(wchar_to_utf8chars(0x00E9, buf) == std::ptrdiff_t(2)); - UNSCOPED_INFO("é b0"); CHECK((U32)(U8)buf[0] == U32(0xC3)); - UNSCOPED_INFO("é b1"); CHECK((U32)(U8)buf[1] == U32(0xA9)); - UNSCOPED_INFO("日 len"); CHECK(wchar_to_utf8chars(0x65E5, buf) == std::ptrdiff_t(3)); - UNSCOPED_INFO("日 b0"); CHECK((U32)(U8)buf[0] == U32(0xE6)); - UNSCOPED_INFO("日 b1"); CHECK((U32)(U8)buf[1] == U32(0x97)); - UNSCOPED_INFO("日 b2"); CHECK((U32)(U8)buf[2] == U32(0xA5)); - UNSCOPED_INFO("🚀 len"); CHECK(wchar_to_utf8chars(0x1F680, buf) == std::ptrdiff_t(4)); - UNSCOPED_INFO("🚀 b0"); CHECK((U32)(U8)buf[0] == U32(0xF0)); - UNSCOPED_INFO("🚀 b1"); CHECK((U32)(U8)buf[1] == U32(0x9F)); - UNSCOPED_INFO("🚀 b2"); CHECK((U32)(U8)buf[2] == U32(0x9A)); - UNSCOPED_INFO("🚀 b3"); CHECK((U32)(U8)buf[3] == U32(0x80)); } @@ -380,15 +348,11 @@ namespace tut char buf[8]; std::memset(buf, 0xAA, sizeof(buf)); - UNSCOPED_INFO("5-byte len"); CHECK(wchar_to_utf8chars(0x00200000, buf) == std::ptrdiff_t(5)); - UNSCOPED_INFO("5-byte lead"); CHECK((U32)((U8)buf[0] & 0xF8) == U32(0xF8)); std::memset(buf, 0xAA, sizeof(buf)); - UNSCOPED_INFO("6-byte len"); CHECK(wchar_to_utf8chars(0x04000000, buf) == std::ptrdiff_t(6)); - UNSCOPED_INFO("6-byte lead"); CHECK((U32)((U8)buf[0] & 0xFC) == U32(0xFC)); } @@ -398,17 +362,11 @@ namespace tut TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: wchar_utf8_length counts one to six bytes", "[LLStringUTF]") { - UNSCOPED_INFO("wchar_utf8_length ASCII"); CHECK(wchar_utf8_length(0x00000041) == S32(1)); - UNSCOPED_INFO("wchar_utf8_length 2-byte"); CHECK(wchar_utf8_length(0x000000E9) == S32(2)); - UNSCOPED_INFO("wchar_utf8_length 3-byte"); CHECK(wchar_utf8_length(0x000065E5) == S32(3)); - UNSCOPED_INFO("wchar_utf8_length 4-byte"); CHECK(wchar_utf8_length(0x0001F680) == S32(4)); - UNSCOPED_INFO("wchar_utf8_length 5-byte"); CHECK(wchar_utf8_length(0x00200000) == S32(5)); - UNSCOPED_INFO("wchar_utf8_length 6-byte"); CHECK(wchar_utf8_length(0x04000000) == S32(6)); } @@ -422,10 +380,8 @@ namespace tut w.push_back((wchar_t)0xDE80); w.push_back(L'B'); // 4 UTF-16 units = 3 codepoints. - UNSCOPED_INFO("wide_wstring_length 4u -> 3cp"); - REQUIRE(wide_wstring_length(w, 4) == S32(3)); - UNSCOPED_INFO("wide_wstring_length 0"); - REQUIRE(wide_wstring_length(w, 0) == S32(0)); + CHECK(wide_wstring_length(w, 4) == S32(3)); + CHECK(wide_wstring_length(w, 0) == S32(0)); } #endif @@ -436,13 +392,9 @@ namespace tut TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_truncate cuts ASCII at the length given", "[LLStringUTF]") { const std::string s = "Hello"; - UNSCOPED_INFO("truncate noop"); CHECK(utf8str_truncate(s, 10) == s); - UNSCOPED_INFO("truncate exact"); CHECK(utf8str_truncate(s, 5) == s); - UNSCOPED_INFO("truncate to 3"); CHECK(utf8str_truncate(s, 3) == std::string("Hel")); - UNSCOPED_INFO("truncate to 0"); CHECK(utf8str_truncate(s, 0) == std::string()); } @@ -454,24 +406,17 @@ namespace tut TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_truncate backs off only from inside a character", "[LLStringUTF]") { const std::string s = "A\xE6\x97\xA5" "B"; - UNSCOPED_INFO("trunc 2 inside 日"); CHECK(utf8str_truncate(s, 2) == std::string("A")); - UNSCOPED_INFO("trunc 3 inside 日"); CHECK(utf8str_truncate(s, 3) == std::string("A")); - UNSCOPED_INFO("trunc 4 on 'B'"); CHECK(utf8str_truncate(s, 4) == std::string("A\xE6\x97\xA5")); - UNSCOPED_INFO("trunc 5 whole"); CHECK(utf8str_truncate(s, 5) == s); } TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_substr takes a start and a length", "[LLStringUTF]") { const std::string s = "ABCDE"; - UNSCOPED_INFO("substr 1,3"); CHECK(utf8str_substr(s, 1, 3) == std::string("BCD")); - UNSCOPED_INFO("substr 3,2"); CHECK(utf8str_substr(s, 3, 2) == std::string("DE")); - UNSCOPED_INFO("substr 0,0"); CHECK(utf8str_substr(s, 0, 0) == std::string()); } @@ -480,15 +425,10 @@ namespace tut TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_symbol_truncate returns the first N complete UTF-8 symbols", "[LLStringUTF]") { const std::string s = "A\xE6\x97\xA5\xF0\x9F\x9A\x80" "B"; - UNSCOPED_INFO("sym 4 whole"); CHECK(utf8str_symbol_truncate(s, 4) == s); - UNSCOPED_INFO("sym 3"); CHECK(utf8str_symbol_truncate(s, 3) == std::string("A\xE6\x97\xA5\xF0\x9F\x9A\x80")); - UNSCOPED_INFO("sym 2"); CHECK(utf8str_symbol_truncate(s, 2) == std::string("A\xE6\x97\xA5")); - UNSCOPED_INFO("sym 1"); CHECK(utf8str_symbol_truncate(s, 1) == std::string("A")); - UNSCOPED_INFO("sym 0"); CHECK(utf8str_symbol_truncate(s, 0) == std::string()); } @@ -502,22 +442,17 @@ namespace tut { for (const std::string& bad : { std::string("\x80"), std::string("\xFF") }) { - UNSCOPED_INFO("rejected outright"); REQUIRE_FALSE(utf8str_is_valid(bad)); // The decoder gives one replacement character per bad byte, so a // walk driven off it always advances. const std::u32string w = to_u32(bad); - UNSCOPED_INFO("one bad byte, one character"); REQUIRE(w.size() == size_t(1)); - UNSCOPED_INFO("decodes to U+FFFD"); REQUIRE((U32)w[0] == U32(0xFFFD)); // Repair substitutes LL_UNKNOWN_CHAR, and the result is valid. const std::string fixed = utf8str_sanitize(bad); - UNSCOPED_INFO("repaired to one byte"); REQUIRE(fixed == std::string(1, LL_UNKNOWN_CHAR)); - UNSCOPED_INFO("repair is valid"); REQUIRE(utf8str_is_valid(fixed)); } } @@ -530,18 +465,13 @@ namespace tut TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: an overlong two-byte NUL is two bad bytes", "[LLStringUTF]") { const std::string overlong("\xC0\x80"); - UNSCOPED_INFO("overlong NUL rejected"); CHECK_FALSE(utf8str_is_valid(overlong)); const std::u32string w = to_u32(overlong); - UNSCOPED_INFO("overlong NUL size"); REQUIRE(w.size() == size_t(2)); - UNSCOPED_INFO("overlong NUL[0]"); CHECK((U32)w[0] == U32(0xFFFD)); - UNSCOPED_INFO("overlong NUL[1]"); CHECK((U32)w[1] == U32(0xFFFD)); - UNSCOPED_INFO("repaired byte for byte"); CHECK(utf8str_sanitize(overlong) == std::string(2, LL_UNKNOWN_CHAR)); } @@ -552,20 +482,14 @@ namespace tut TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a truncated three-byte sequence is two bad bytes", "[LLStringUTF]") { const std::string truncated("A\xE6\x97"); - UNSCOPED_INFO("truncated rejected"); CHECK_FALSE(utf8str_is_valid(truncated)); const std::u32string w = to_u32(truncated); - UNSCOPED_INFO("truncated size"); REQUIRE(w.size() == size_t(3)); - UNSCOPED_INFO("truncated[0]='A'"); CHECK((U32)w[0] == U32('A')); - UNSCOPED_INFO("truncated[1]"); CHECK((U32)w[1] == U32(0xFFFD)); - UNSCOPED_INFO("truncated[2]"); CHECK((U32)w[2] == U32(0xFFFD)); - UNSCOPED_INFO("the good byte survives the repair"); CHECK(utf8str_sanitize(truncated) == std::string("A??")); } @@ -576,19 +500,16 @@ namespace tut TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a legacy five-byte sequence is five bad bytes", "[LLStringUTF]") { const std::string legacy("\xF8\x88\x80\x80\x80"); - UNSCOPED_INFO("5-byte legacy rejected"); CHECK_FALSE(utf8str_is_valid(legacy)); const std::u32string w = to_u32(legacy); - UNSCOPED_INFO("5-byte legacy size"); - REQUIRE(w.size() == size_t(5)); + CHECK(w.size() == size_t(5)); for (size_t i = 0; i < w.size(); ++i) { - UNSCOPED_INFO("5-byte legacy[i]"); + CAPTURE(i); REQUIRE((U32)w[i] == U32(0xFFFD)); } - UNSCOPED_INFO("every byte repaired"); CHECK(utf8str_sanitize(legacy) == std::string(5, LL_UNKNOWN_CHAR)); } @@ -600,11 +521,8 @@ namespace tut { const llutf16string bad = { (char16_t)0xD83D, (char16_t)0x0041 }; const std::string out = utf16str_to_utf8str(bad); - UNSCOPED_INFO("lone surrogate + 'A' size"); REQUIRE(out.size() == size_t(2)); - UNSCOPED_INFO("lone surrogate -> '?'"); CHECK((U32)(U8)out[0] == U32((U8)LL_UNKNOWN_CHAR)); - UNSCOPED_INFO("'A' preserved"); CHECK((U32)(U8)out[1] == U32('A')); } @@ -618,19 +536,14 @@ namespace tut // so composition regressions surface before we diagnose them through // callers. const std::string mixed = "H\xC3\xA9LLO \xE6\x97\xA5"; - UNSCOPED_INFO("utf8str_trim passthrough"); CHECK(utf8str_trim(mixed) == mixed); - UNSCOPED_INFO("utf8str_tolower preserves BMP non-ASCII"); CHECK(utf8str_tolower(mixed) == std::string("h\xC3\xA9llo \xE6\x97\xA5")); } TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_compare_insensitive orders regardless of case", "[LLStringUTF]") { - UNSCOPED_INFO("compare_insensitive eq"); CHECK(utf8str_compare_insensitive("Hello", "hello") == S32(0)); - UNSCOPED_INFO("compare_insensitive lt"); CHECK(utf8str_compare_insensitive("abc", "abd") < 0); - UNSCOPED_INFO("compare_insensitive gt"); CHECK(utf8str_compare_insensitive("abd", "abc") > 0); } @@ -655,7 +568,6 @@ namespace tut // A BMP emoji, which the old astral-only range could never see. UNSCOPED_INFO("isEmoji watch"); CHECK(LLStringOps::isEmoji((llwchar)0x231A)); - UNSCOPED_INFO("not isEmoji 'A'"); CHECK_FALSE(LLStringOps::isEmoji((llwchar)'A')); UNSCOPED_INFO("not isEmoji ©"); CHECK_FALSE(LLStringOps::isEmoji((llwchar)0x00A9)); @@ -672,28 +584,22 @@ namespace tut { std::u32string ws = { (llwchar)'H', (llwchar)0x1F680, (llwchar)0x1F681, (llwchar)'i' }; const std::u32string expected = { (llwchar)'H', (llwchar)'i' }; - UNSCOPED_INFO("remove_emojis returned true"); CHECK(wstring_remove_emojis(ws)); - ensure_wstring_equals("consecutive emojis stripped", ws, expected); + require_same_u32("consecutive emojis stripped", ws, expected); std::u32string none = { (llwchar)'H', (llwchar)'i' }; - UNSCOPED_INFO("remove_emojis returned false"); CHECK_FALSE(wstring_remove_emojis(none)); - ensure_wstring_equals("unchanged when no emojis", none, expected); + require_same_u32("unchanged when no emojis", none, expected); } TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_remove_emojis strips whole clusters from UTF-8", "[LLStringUTF]") { std::string s = "Hi\xF0\x9F\x9A\x80" "!"; - UNSCOPED_INFO("utf8str_remove_emojis found"); CHECK(utf8str_remove_emojis(s)); - UNSCOPED_INFO("utf8 stripped"); CHECK(s == std::string("Hi!")); std::string clean = "Hello"; - UNSCOPED_INFO("utf8str_remove_emojis clean"); CHECK_FALSE(utf8str_remove_emojis(clean)); - UNSCOPED_INFO("clean unchanged"); CHECK(clean == std::string("Hello")); // Heart-on-fire ❤️‍🔥 round-trips through utf8 encoding without @@ -701,9 +607,7 @@ namespace tut // bounds. UTF-8 byte sequence: U+2764 (E2 9D A4) U+FE0F (EF B8 8F) // U+200D (E2 80 8D) U+1F525 (F0 9F 94 A5). std::string fire = "A" "\xE2\x9D\xA4" "\xEF\xB8\x8F" "\xE2\x80\x8D" "\xF0\x9F\x94\xA5" "B"; - UNSCOPED_INFO("utf8 heart-on-fire found"); CHECK(utf8str_remove_emojis(fire)); - UNSCOPED_INFO("utf8 heart-on-fire stripped"); CHECK(fire == std::string("AB")); // Subdivision flag 🏴󠁧󠁢󠁥󠁮󠁧󠁿 — base + 5 tag chars + CANCEL TAG, all @@ -713,9 +617,7 @@ namespace tut "\xF3\xA0\x81\xA7" "\xF3\xA0\x81\xA2" "\xF3\xA0\x81\xB3" "\xF3\xA0\x81\xA3" "\xF3\xA0\x81\xB4" "\xF3\xA0\x81\xBF" "]"; - UNSCOPED_INFO("utf8 subdiv flag found"); CHECK(utf8str_remove_emojis(flag)); - UNSCOPED_INFO("utf8 subdiv flag stripped"); CHECK(flag == std::string("[]")); } @@ -730,9 +632,8 @@ namespace tut (llwchar)0x1F467, (llwchar)'i' }; const std::u32string expected = { (llwchar)'H', (llwchar)'i' }; - UNSCOPED_INFO("ZWJ family found"); CHECK(wstring_remove_emojis(ws)); - ensure_wstring_equals("ZWJ family stripped", ws, expected); + require_same_u32("ZWJ family stripped", ws, expected); } // Skin-tone modifier (U+1F3FB..FF) must strip together with its base emoji. @@ -742,9 +643,8 @@ namespace tut (llwchar)0x1F468, (llwchar)0x1F3FB, (llwchar)'X' }; const std::u32string expected = { (llwchar)'M', (llwchar)'X' }; - UNSCOPED_INFO("skin tone found"); CHECK(wstring_remove_emojis(ws)); - ensure_wstring_equals("skin tone stripped", ws, expected); + require_same_u32("skin tone stripped", ws, expected); } // Regional indicator flag (🇺🇸 = U+1F1FA U+1F1F8). Both code points are in @@ -754,9 +654,8 @@ namespace tut { std::u32string ws = { (llwchar)0x1F1FA, (llwchar)0x1F1F8, (llwchar)'!' }; const std::u32string expected = { (llwchar)'!' }; - UNSCOPED_INFO("flag found"); CHECK(wstring_remove_emojis(ws)); - ensure_wstring_equals("flag stripped", ws, expected); + require_same_u32("flag stripped", ws, expected); } // Trailing VS16 (U+FE0F) after an astral emoji must be consumed together @@ -765,9 +664,8 @@ namespace tut { std::u32string ws = { (llwchar)0x1F680, (llwchar)0xFE0F, (llwchar)'!' }; const std::u32string expected = { (llwchar)'!' }; - UNSCOPED_INFO("VS16 found"); CHECK(wstring_remove_emojis(ws)); - ensure_wstring_equals("VS16 stripped with base", ws, expected); + require_same_u32("VS16 stripped with base", ws, expected); } // Bare ZWJ or VS16 (no adjacent emoji) are not emoji themselves and must @@ -777,9 +675,8 @@ namespace tut { std::u32string ws = { (llwchar)'a', (llwchar)0x200D, (llwchar)'b' }; const std::u32string expected = ws; - UNSCOPED_INFO("bare ZWJ not found"); CHECK_FALSE(wstring_remove_emojis(ws)); - ensure_wstring_equals("bare ZWJ preserved", ws, expected); + require_same_u32("bare ZWJ preserved", ws, expected); } // Broad-contract rewrite: wstring_remove_emojis now drives off the @@ -792,18 +689,16 @@ namespace tut TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: a heart with VS16 strips but a bare heart stays", "[LLStringUTF]") { std::u32string ws = { (llwchar)0x2764, (llwchar)0xFE0F }; - UNSCOPED_INFO("BMP heart+VS16 stripped"); CHECK(wstring_remove_emojis(ws)); - ensure_wstring_equals("BMP heart+VS16 cleared", ws, std::u32string()); + require_same_u32("BMP heart+VS16 cleared", ws, std::u32string()); // Bare BMP heart (no VS-16) is not a cluster — single-codepoint // pictograph, shaped via the 1:1 path. Outside isEmoji's narrow // range too. Must pass through. std::u32string bare_heart = { (llwchar)0x2764 }; const std::u32string bare_expected = bare_heart; - UNSCOPED_INFO("bare heart not stripped"); CHECK_FALSE(wstring_remove_emojis(bare_heart)); - ensure_wstring_equals("bare heart preserved", bare_heart, bare_expected); + require_same_u32("bare heart preserved", bare_heart, bare_expected); } // --------------------------------------------------------------- @@ -822,9 +717,8 @@ namespace tut (llwchar)0x200D, (llwchar)0x1F525, (llwchar)'B' }; const std::u32string expected = { (llwchar)'A', (llwchar)'B' }; - UNSCOPED_INFO("heart-on-fire found"); CHECK(wstring_remove_emojis(ws)); - ensure_wstring_equals("heart-on-fire cleared", ws, expected); + require_same_u32("heart-on-fire cleared", ws, expected); } // Keycap `1️⃣` (`'1' + VS-16 + U+20E3`): no codepoint inside the cluster @@ -836,27 +730,23 @@ namespace tut { std::u32string digit_kc = { (llwchar)'1', (llwchar)0xFE0F, (llwchar)0x20E3, (llwchar)'!' }; const std::u32string expected = { (llwchar)'!' }; - UNSCOPED_INFO("digit keycap found"); CHECK(wstring_remove_emojis(digit_kc)); - ensure_wstring_equals("digit keycap cleared", digit_kc, expected); + require_same_u32("digit keycap cleared", digit_kc, expected); std::u32string hash_kc = { (llwchar)'#', (llwchar)0xFE0F, (llwchar)0x20E3 }; - UNSCOPED_INFO("hash keycap found"); CHECK(wstring_remove_emojis(hash_kc)); - ensure_wstring_equals("hash keycap cleared", hash_kc, std::u32string()); + require_same_u32("hash keycap cleared", hash_kc, std::u32string()); std::u32string star_kc = { (llwchar)'*', (llwchar)0xFE0F, (llwchar)0x20E3 }; - UNSCOPED_INFO("star keycap found"); CHECK(wstring_remove_emojis(star_kc)); - ensure_wstring_equals("star keycap cleared", star_kc, std::u32string()); + require_same_u32("star keycap cleared", star_kc, std::u32string()); // Bare digit (no VS-16 + combiner) must pass through. The cluster // walker rejects it as a starter; nothing to strip. std::u32string bare_digit = { (llwchar)'9' }; const std::u32string bare_expected = bare_digit; - UNSCOPED_INFO("bare digit not stripped"); CHECK_FALSE(wstring_remove_emojis(bare_digit)); - ensure_wstring_equals("bare digit preserved", bare_digit, bare_expected); + require_same_u32("bare digit preserved", bare_digit, bare_expected); } // Subdivision flag 🏴󠁧󠁢󠁥󠁮󠁧󠁿 (U+1F3F4 + tag bytes + U+E007F CANCEL TAG). @@ -872,9 +762,8 @@ namespace tut (llwchar)0xE007F, (llwchar)'>' }; const std::u32string expected = { (llwchar)'<', (llwchar)'>' }; - UNSCOPED_INFO("subdivision flag found"); CHECK(wstring_remove_emojis(ws)); - ensure_wstring_equals("subdivision flag cleared", ws, expected); + require_same_u32("subdivision flag cleared", ws, expected); } // Astral emoji + VS-15 (U+FE0E text-presentation selector) — VS-15 is @@ -886,9 +775,8 @@ namespace tut { std::u32string ws = { (llwchar)0x1F680, (llwchar)0xFE0E, (llwchar)'!' }; const std::u32string expected = { (llwchar)'!' }; - UNSCOPED_INFO("VS-15 cluster found"); CHECK(wstring_remove_emojis(ws)); - ensure_wstring_equals("VS-15 cluster cleared", ws, expected); + require_same_u32("VS-15 cluster cleared", ws, expected); } // Two flags back-to-back 🇺🇸🇯🇵 — distinct clusters [0,2) and [2,4). @@ -899,9 +787,8 @@ namespace tut (llwchar)0x1F1EF, (llwchar)0x1F1F5, (llwchar)'!' }; const std::u32string expected = { (llwchar)'!' }; - UNSCOPED_INFO("two flags found"); CHECK(wstring_remove_emojis(ws)); - ensure_wstring_equals("two flags cleared", ws, expected); + require_same_u32("two flags cleared", ws, expected); } // ZWSP (U+200B) is not an emoji extender — it must pass through even @@ -912,9 +799,8 @@ namespace tut { std::u32string ws = { (llwchar)0x1F680, (llwchar)0x200B, (llwchar)'X' }; const std::u32string expected = { (llwchar)0x200B, (llwchar)'X' }; - UNSCOPED_INFO("rocket+ZWSP found"); CHECK(wstring_remove_emojis(ws)); - ensure_wstring_equals("rocket stripped, ZWSP kept", ws, expected); + require_same_u32("rocket stripped, ZWSP kept", ws, expected); } // --------------------------------------------------------------- @@ -929,33 +815,23 @@ namespace tut { for (char c : std::string("0123456789abcdefABCDEF")) { - UNSCOPED_INFO("is_char_hex yes"); + CAPTURE(c); REQUIRE(is_char_hex(c)); } - UNSCOPED_INFO("is_char_hex 'g'"); CHECK_FALSE(is_char_hex('g')); - UNSCOPED_INFO("is_char_hex 'Z'"); CHECK_FALSE(is_char_hex('Z')); - UNSCOPED_INFO("is_char_hex ' '"); CHECK_FALSE(is_char_hex(' ')); } TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: hex_as_nybble reads either case and gives 0 otherwise", "[LLStringUTF]") { - UNSCOPED_INFO("nybble '0'"); CHECK((U32)hex_as_nybble('0') == U32(0)); - UNSCOPED_INFO("nybble '9'"); CHECK((U32)hex_as_nybble('9') == U32(9)); - UNSCOPED_INFO("nybble 'a'"); CHECK((U32)hex_as_nybble('a') == U32(10)); - UNSCOPED_INFO("nybble 'f'"); CHECK((U32)hex_as_nybble('f') == U32(15)); - UNSCOPED_INFO("nybble 'A'"); CHECK((U32)hex_as_nybble('A') == U32(10)); - UNSCOPED_INFO("nybble 'F'"); CHECK((U32)hex_as_nybble('F') == U32(15)); // Non-hex inputs fall through to 0 (undefined-but-documented behavior). - UNSCOPED_INFO("nybble 'g' -> 0"); CHECK((U32)hex_as_nybble('g') == U32(0)); } @@ -968,20 +844,15 @@ namespace tut // the 1:1 codepoint path, so the detector must return no runs. TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: text that needs no shaping has no emoji clusters", "[LLStringUTF]") { - UNSCOPED_INFO("empty"); - REQUIRE(wstring_find_emoji_clusters(std::u32string()).size() == size_t(0)); + CHECK(wstring_find_emoji_clusters(std::u32string()).size() == size_t(0)); std::u32string ascii = { (llwchar)'H', (llwchar)'i', (llwchar)'!' }; - UNSCOPED_INFO("ascii"); - REQUIRE(wstring_find_emoji_clusters(ascii).size() == size_t(0)); + CHECK(wstring_find_emoji_clusters(ascii).size() == size_t(0)); std::u32string cjk = { (llwchar)0x65E5, (llwchar)0x672C }; - UNSCOPED_INFO("cjk"); - REQUIRE(wstring_find_emoji_clusters(cjk).size() == size_t(0)); + CHECK(wstring_find_emoji_clusters(cjk).size() == size_t(0)); std::u32string lone = { (llwchar)0x1F680 }; - UNSCOPED_INFO("lone emoji"); - REQUIRE(wstring_find_emoji_clusters(lone).size() == size_t(0)); + CHECK(wstring_find_emoji_clusters(lone).size() == size_t(0)); std::u32string pair = { (llwchar)0x1F680, (llwchar)0x1F681 }; - UNSCOPED_INFO("two adj em"); - REQUIRE(wstring_find_emoji_clusters(pair).size() == size_t(0)); + CHECK(wstring_find_emoji_clusters(pair).size() == size_t(0)); } // ZWJ family 👨‍👩‍👧 = U+1F468 U+200D U+1F469 U+200D U+1F467 — must emerge @@ -992,11 +863,8 @@ namespace tut (llwchar)0x1F469, (llwchar)0x200D, (llwchar)0x1F467 }; auto runs = wstring_find_emoji_clusters(ws); - UNSCOPED_INFO("one run"); REQUIRE(runs.size() == size_t(1)); - UNSCOPED_INFO("run begin"); CHECK(runs[0].first == size_t(0)); - UNSCOPED_INFO("run end"); CHECK(runs[0].second == size_t(5)); } @@ -1005,9 +873,7 @@ namespace tut { std::u32string ws = { (llwchar)0x1F468, (llwchar)0x1F3FB }; auto runs = wstring_find_emoji_clusters(ws); - UNSCOPED_INFO("skintone runs"); REQUIRE(runs.size() == size_t(1)); - UNSCOPED_INFO("skintone end"); CHECK(runs[0].second == size_t(2)); } @@ -1017,9 +883,7 @@ namespace tut { std::u32string ws = { (llwchar)0x1F680, (llwchar)0xFE0F }; auto runs = wstring_find_emoji_clusters(ws); - UNSCOPED_INFO("vs16 runs"); REQUIRE(runs.size() == size_t(1)); - UNSCOPED_INFO("vs16 end"); CHECK(runs[0].second == size_t(2)); } @@ -1029,21 +893,15 @@ namespace tut { std::u32string ws = { (llwchar)0x1F1FA, (llwchar)0x1F1F8 }; auto runs = wstring_find_emoji_clusters(ws); - UNSCOPED_INFO("flag runs"); REQUIRE(runs.size() == size_t(1)); - UNSCOPED_INFO("flag end"); CHECK(runs[0].second == size_t(2)); // Four RIs in a row form two separate flags. std::u32string four = { (llwchar)0x1F1FA, (llwchar)0x1F1F8, (llwchar)0x1F1EB, (llwchar)0x1F1F7 }; auto more = wstring_find_emoji_clusters(four); - UNSCOPED_INFO("two flags"); REQUIRE(more.size() == size_t(2)); - UNSCOPED_INFO("flag0 end"); CHECK(more[0].second == size_t(2)); - UNSCOPED_INFO("flag1 begin"); CHECK(more[1].first == size_t(2)); - UNSCOPED_INFO("flag1 end"); CHECK(more[1].second == size_t(4)); } @@ -1055,16 +913,12 @@ namespace tut { std::u32string digit_kc = { (llwchar)'9', (llwchar)0xFE0F, (llwchar)0x20E3 }; auto runs = wstring_find_emoji_clusters(digit_kc); - UNSCOPED_INFO("keycap runs"); REQUIRE(runs.size() == size_t(1)); - UNSCOPED_INFO("keycap end"); CHECK(runs[0].second == size_t(3)); std::u32string hash_kc = { (llwchar)'#', (llwchar)0xFE0F, (llwchar)0x20E3 }; - UNSCOPED_INFO("hash keycap"); - REQUIRE(wstring_find_emoji_clusters(hash_kc).size() == size_t(1)); + CHECK(wstring_find_emoji_clusters(hash_kc).size() == size_t(1)); std::u32string bare = { (llwchar)'5' }; - UNSCOPED_INFO("bare digit"); - REQUIRE(wstring_find_emoji_clusters(bare).size() == size_t(0)); + CHECK(wstring_find_emoji_clusters(bare).size() == size_t(0)); } // Subdivision flag — base 🏴 (U+1F3F4) + tag characters (U+E0020..U+E007F) @@ -1076,11 +930,8 @@ namespace tut (llwchar)0xE0063, (llwchar)0xE0074, (llwchar)0xE007F }; auto runs = wstring_find_emoji_clusters(ws); - UNSCOPED_INFO("tag runs"); REQUIRE(runs.size() == size_t(1)); - UNSCOPED_INFO("tag begin"); CHECK(runs[0].first == size_t(0)); - UNSCOPED_INFO("tag end"); CHECK(runs[0].second == size_t(7)); } @@ -1096,15 +947,10 @@ namespace tut (llwchar)0x1F467, (llwchar)'!' }; auto runs = wstring_find_emoji_clusters(ws); - UNSCOPED_INFO("two runs"); REQUIRE(runs.size() == size_t(2)); - UNSCOPED_INFO("run0 begin"); CHECK(runs[0].first == size_t(1)); - UNSCOPED_INFO("run0 end"); CHECK(runs[0].second == size_t(3)); - UNSCOPED_INFO("run1 begin"); CHECK(runs[1].first == size_t(4)); - UNSCOPED_INFO("run1 end"); CHECK(runs[1].second == size_t(9)); } @@ -1114,18 +960,15 @@ namespace tut TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: bare extenders and malformed sequences cluster as pinned", "[LLStringUTF]") { std::u32string zwj = { (llwchar)'a', (llwchar)0x200D, (llwchar)'b' }; - UNSCOPED_INFO("bare zwj"); - REQUIRE(wstring_find_emoji_clusters(zwj).size() == size_t(0)); + CHECK(wstring_find_emoji_clusters(zwj).size() == size_t(0)); std::u32string vs16 = { (llwchar)'a', (llwchar)0xFE0F, (llwchar)'b' }; - UNSCOPED_INFO("bare vs16"); - REQUIRE(wstring_find_emoji_clusters(vs16).size() == size_t(0)); + CHECK(wstring_find_emoji_clusters(vs16).size() == size_t(0)); // Malformed inputs: leading/trailing extenders, lone modifiers, and // singletons that need a partner. Pin current behavior so future // refactors don't silently change cluster boundaries on broken input. std::u32string lead_zwj = { (llwchar)0x200D, (llwchar)0x1F468 }; - UNSCOPED_INFO("leading zwj"); - REQUIRE(wstring_find_emoji_clusters(lead_zwj).size() == size_t(0)); + CHECK(wstring_find_emoji_clusters(lead_zwj).size() == size_t(0)); std::u32string trail_zwj = { (llwchar)0x1F468, (llwchar)0x200D }; // Trailing ZWJ: is_shaping_starter accepts the base because the // next codepoint is a ZWJ, but advance_shaping_run hits the @@ -1133,34 +976,26 @@ namespace tut // the resulting length-1 "run" so the result matches the doc // contract (isolated single-codepoint emoji are intentionally // absent from the cluster list). - UNSCOPED_INFO("trailing zwj"); - REQUIRE(wstring_find_emoji_clusters(trail_zwj).size() == size_t(0)); + CHECK(wstring_find_emoji_clusters(trail_zwj).size() == size_t(0)); // Same shape but with VS-16 instead of ZWJ: 'is_shaping_starter' // accepts because VS-16 is a valid extender, advance consumes it, // run length is 2 → real 2-codepoint cluster. Confirm we didn't // over-trim and accidentally drop legitimate 2-codepoint clusters. std::u32string base_vs16 = { (llwchar)0x1F680, (llwchar)0xFE0F }; auto vs_runs = wstring_find_emoji_clusters(base_vs16); - UNSCOPED_INFO("base+VS16 count"); REQUIRE(vs_runs.size() == size_t(1)); - UNSCOPED_INFO("base+VS16 begin"); CHECK(vs_runs[0].first == size_t(0)); - UNSCOPED_INFO("base+VS16 end"); CHECK(vs_runs[0].second == size_t(2)); std::u32string lone_skin = { (llwchar)0x1F3FB }; - UNSCOPED_INFO("lone skintone"); - REQUIRE(wstring_find_emoji_clusters(lone_skin).size() == size_t(0)); + CHECK(wstring_find_emoji_clusters(lone_skin).size() == size_t(0)); std::u32string lone_ri = { (llwchar)0x1F1FA }; - UNSCOPED_INFO("lone RI"); - REQUIRE(wstring_find_emoji_clusters(lone_ri).size() == size_t(0)); + CHECK(wstring_find_emoji_clusters(lone_ri).size() == size_t(0)); // Range boundaries on isPictographBase's astral / BMP windows. std::u32string just_below_astral = { (llwchar)0x1FFF }; - UNSCOPED_INFO("U+1FFF no run"); - REQUIRE(wstring_find_emoji_clusters(just_below_astral).size() == size_t(0)); + CHECK(wstring_find_emoji_clusters(just_below_astral).size() == size_t(0)); std::u32string just_above_bmp = { (llwchar)0x3300 }; - UNSCOPED_INFO("U+3300 no run"); - REQUIRE(wstring_find_emoji_clusters(just_above_bmp).size() == size_t(0)); + CHECK(wstring_find_emoji_clusters(just_above_bmp).size() == size_t(0)); // Back-to-back ZWJ families with no separator. Pin current behavior: // each MAN-ZWJ-WOMAN sub-sequence registers as its own cluster, so we @@ -1168,15 +1003,10 @@ namespace tut std::u32string two_fam = { (llwchar)0x1F468, (llwchar)0x200D, (llwchar)0x1F469, (llwchar)0x1F468, (llwchar)0x200D, (llwchar)0x1F469 }; auto two_fam_runs = wstring_find_emoji_clusters(two_fam); - UNSCOPED_INFO("two_fam count"); REQUIRE(two_fam_runs.size() == size_t(2)); - UNSCOPED_INFO("two_fam[0].first"); CHECK(two_fam_runs[0].first == size_t(0)); - UNSCOPED_INFO("two_fam[0].second"); CHECK(two_fam_runs[0].second == size_t(3)); - UNSCOPED_INFO("two_fam[1].first"); CHECK(two_fam_runs[1].first == size_t(3)); - UNSCOPED_INFO("two_fam[1].second"); CHECK(two_fam_runs[1].second == size_t(6)); // ZWJ followed by a non-pictograph: starter accepts the base @@ -1184,45 +1014,35 @@ namespace tut // non-pictograph 'A' afterwards, breaks orphan-ZWJ. Length-1 run // gets discarded by the cluster walker. No cluster emitted. std::u32string zwj_plus_nonemoji = { (llwchar)0x1F468, (llwchar)0x200D, (llwchar)'A' }; - UNSCOPED_INFO("ZWJ+non-pictograph"); - REQUIRE(wstring_find_emoji_clusters(zwj_plus_nonemoji).size() == size_t(0)); + CHECK(wstring_find_emoji_clusters(zwj_plus_nonemoji).size() == size_t(0)); // Double ZWJ (ZWJ + ZWJ between bases): advance hits the first // ZWJ, looks at the next codepoint, finds another ZWJ which is // not a pictograph base. Orphan-ZWJ break. Length 1, discarded. std::u32string double_zwj = { (llwchar)0x1F468, (llwchar)0x200D, (llwchar)0x200D, (llwchar)0x1F469 }; - UNSCOPED_INFO("double ZWJ"); - REQUIRE(wstring_find_emoji_clusters(double_zwj).size() == size_t(0)); + CHECK(wstring_find_emoji_clusters(double_zwj).size() == size_t(0)); // Truncated subdivision flag — base 🏴 + a couple of tag bytes, // no U+E007F terminator. Pin: walker greedily eats every tag // codepoint to end-of-string. Real implementation behavior. std::u32string trunc_tag = { (llwchar)0x1F3F4, (llwchar)0xE0067, (llwchar)0xE0062 }; auto trunc_runs = wstring_find_emoji_clusters(trunc_tag); - UNSCOPED_INFO("trunc tag count"); REQUIRE(trunc_runs.size() == size_t(1)); - UNSCOPED_INFO("trunc tag begin"); CHECK(trunc_runs[0].first == size_t(0)); - UNSCOPED_INFO("trunc tag end"); CHECK(trunc_runs[0].second == size_t(3)); // Three RIs — first two form a flag at [0,2), third RI alone is // not a starter (no partner). Pin: one cluster, length 2. std::u32string three_ri = { (llwchar)0x1F1FA, (llwchar)0x1F1F8, (llwchar)0x1F1F8 }; auto three_ri_runs = wstring_find_emoji_clusters(three_ri); - UNSCOPED_INFO("three RI count"); REQUIRE(three_ri_runs.size() == size_t(1)); - UNSCOPED_INFO("three RI begin"); CHECK(three_ri_runs[0].first == size_t(0)); - UNSCOPED_INFO("three RI end"); CHECK(three_ri_runs[0].second == size_t(2)); // Star keycap '*' + VS-16 + U+20E3. Pin parity with digit/hash. std::u32string star_kc = { (llwchar)'*', (llwchar)0xFE0F, (llwchar)0x20E3 }; auto star_runs = wstring_find_emoji_clusters(star_kc); - UNSCOPED_INFO("star kc count"); REQUIRE(star_runs.size() == size_t(1)); - UNSCOPED_INFO("star kc end"); CHECK(star_runs[0].second == size_t(3)); } @@ -1236,11 +1056,8 @@ namespace tut std::u32string ws = { (llwchar)0x2764, (llwchar)0xFE0F, (llwchar)0x200D, (llwchar)0x1F525 }; auto runs = wstring_find_emoji_clusters(ws); - UNSCOPED_INFO("bmp zwj runs"); REQUIRE(runs.size() == size_t(1)); - UNSCOPED_INFO("bmp zwj begin"); CHECK(runs[0].first == size_t(0)); - UNSCOPED_INFO("bmp zwj end"); CHECK(runs[0].second == size_t(4)); } @@ -1255,11 +1072,8 @@ namespace tut { // Pure ASCII — one codepoint per step, clamped at size. std::u32string ascii = { (llwchar)'a', (llwchar)'b', (llwchar)'c' }; - UNSCOPED_INFO("ascii 0->1"); CHECK(wstring_step_grapheme_forward(ascii, 0) == size_t(1)); - UNSCOPED_INFO("ascii 2->3"); CHECK(wstring_step_grapheme_forward(ascii, 2) == size_t(3)); - UNSCOPED_INFO("ascii at end stays"); CHECK(wstring_step_grapheme_forward(ascii, 3) == size_t(3)); // ZWJ family in the middle: H, 👨, ZWJ, 👩, ZWJ, 👧, i @@ -1269,18 +1083,15 @@ namespace tut (llwchar)0x1F469, (llwchar)0x200D, (llwchar)0x1F467, (llwchar)'i' }; - UNSCOPED_INFO("H->fam start"); CHECK(wstring_step_grapheme_forward(fam, 0) == size_t(1)); UNSCOPED_INFO("fam start jumps past cluster"); CHECK(wstring_step_grapheme_forward(fam, 1) == size_t(6)); UNSCOPED_INFO("from mid-cluster snaps past"); CHECK(wstring_step_grapheme_forward(fam, 3) == size_t(6)); - UNSCOPED_INFO("fam end -> 'i'"); CHECK(wstring_step_grapheme_forward(fam, 6) == size_t(7)); // Regional indicator flag: US = U+1F1FA U+1F1F8. One step covers both. std::u32string flag = { (llwchar)0x1F1FA, (llwchar)0x1F1F8 }; - UNSCOPED_INFO("flag 0->past both"); CHECK(wstring_step_grapheme_forward(flag, 0) == size_t(2)); } @@ -1289,11 +1100,8 @@ namespace tut TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: stepping back crosses one character or one whole cluster", "[LLStringUTF]") { std::u32string ascii = { (llwchar)'a', (llwchar)'b', (llwchar)'c' }; - UNSCOPED_INFO("ascii 3->2"); CHECK(wstring_step_grapheme_backward(ascii, 3) == size_t(2)); - UNSCOPED_INFO("ascii 1->0"); CHECK(wstring_step_grapheme_backward(ascii, 1) == size_t(0)); - UNSCOPED_INFO("ascii at 0 stays"); CHECK(wstring_step_grapheme_backward(ascii, 0) == size_t(0)); std::u32string fam = { (llwchar)'H', @@ -1311,7 +1119,7 @@ namespace tut CHECK(wstring_step_grapheme_backward(fam, 1) == size_t(0)); std::u32string flag = { (llwchar)0x1F1FA, (llwchar)0x1F1F8 }; - UNSCOPED_INFO("flag end -> 0 (whole flag is one cluster)"); + UNSCOPED_INFO("the whole flag is one cluster"); CHECK(wstring_step_grapheme_backward(flag, 2) == size_t(0)); } @@ -1336,26 +1144,18 @@ namespace tut UNSCOPED_INFO("isPictographBase registered"); CHECK(LLStringOps::isPictographBase((llwchar)0x00AE)); // Plain ASCII / digits never qualify. - UNSCOPED_INFO("not isPictographBase 'A'"); CHECK_FALSE(LLStringOps::isPictographBase((llwchar)'A')); - UNSCOPED_INFO("not isPictographBase '0'"); CHECK_FALSE(LLStringOps::isPictographBase((llwchar)'0')); // ZWJ and VS-16 are extenders, not bases. - UNSCOPED_INFO("not isPictographBase ZWJ"); CHECK_FALSE(LLStringOps::isPictographBase((llwchar)0x200D)); - UNSCOPED_INFO("not isPictographBase VS-16"); CHECK_FALSE(LLStringOps::isPictographBase((llwchar)0xFE0F)); - UNSCOPED_INFO("not isPictographBase U+20000"); CHECK_FALSE(LLStringOps::isPictographBase((llwchar)0x20000)); - UNSCOPED_INFO("not isPictographBase U+1FFF"); CHECK_FALSE(LLStringOps::isPictographBase((llwchar)0x1FFF)); - UNSCOPED_INFO("not isPictographBase U+3300 boundary"); CHECK_FALSE(LLStringOps::isPictographBase((llwchar)0x3300)); // A flag is two regional indicators and nothing else. Unicode does // not call them pictographic, so they are named here separately; // reading Extended_Pictographic alone would stop flags clustering. - UNSCOPED_INFO("isPictographBase regional indicator"); CHECK(LLStringOps::isPictographBase((llwchar)0x1F1FA)); // The old range ran from U+2000 to U+3300, which swept up punctuation, @@ -1390,98 +1190,81 @@ namespace tut auto on_lead = wstring_emoji_range_at(fam, 1); auto inside = wstring_emoji_range_at(fam, 3); auto on_end = wstring_emoji_range_at(fam, 6); - UNSCOPED_INFO("fam pos==first.first"); CHECK(on_lead.first == size_t(1)); - UNSCOPED_INFO("fam pos==first.second"); CHECK(on_lead.second == size_t(6)); - UNSCOPED_INFO("fam mid.first"); CHECK(inside.first == size_t(1)); - UNSCOPED_INFO("fam mid.second"); CHECK(inside.second == size_t(6)); // pos == run.second is past the cluster — falls through to the // codepoint there ('i', non-emoji), so empty range. - UNSCOPED_INFO("fam past-end empty"); CHECK(on_end.first == on_end.second); // Plain ASCII: empty range everywhere. std::u32string ascii = { (llwchar)'a', (llwchar)'b', (llwchar)'c' }; auto a0 = wstring_emoji_range_at(ascii, 0); auto a2 = wstring_emoji_range_at(ascii, 2); - UNSCOPED_INFO("ascii 0 empty"); CHECK(a0.first == a0.second); - UNSCOPED_INFO("ascii 2 empty"); CHECK(a2.first == a2.second); // Single-codepoint emoji 😀 (U+1F600): not in cluster list, but // the helper synthesises [pos, pos+1). std::u32string lone = { (llwchar)0x1F600 }; auto le = wstring_emoji_range_at(lone, 0); - UNSCOPED_INFO("lone emoji.first"); CHECK(le.first == size_t(0)); - UNSCOPED_INFO("lone emoji.second"); CHECK(le.second == size_t(1)); // Out-of-bounds and exact-end positions are empty. auto at_end = wstring_emoji_range_at(ascii, 3); auto past_end = wstring_emoji_range_at(ascii, 99); - UNSCOPED_INFO("ascii at-end empty"); CHECK(at_end.first == at_end.second); - UNSCOPED_INFO("ascii oob empty"); CHECK(past_end.first == past_end.second); // Empty wstring: empty range. auto empty = wstring_emoji_range_at(std::u32string(), 0); - UNSCOPED_INFO("empty wstr empty"); CHECK(empty.first == empty.second); // Flag pair 🇺🇸 — one cluster spanning [0, 2). std::u32string flag = { (llwchar)0x1F1FA, (llwchar)0x1F1F8 }; auto f0 = wstring_emoji_range_at(flag, 0); auto f1 = wstring_emoji_range_at(flag, 1); - UNSCOPED_INFO("flag at lead.first"); CHECK(f0.first == size_t(0)); - UNSCOPED_INFO("flag at lead.second"); CHECK(f0.second == size_t(2)); - UNSCOPED_INFO("flag mid.first"); CHECK(f1.first == size_t(0)); - UNSCOPED_INFO("flag mid.second"); CHECK(f1.second == size_t(2)); // BMP pictographs — the orthodox cross bug. isPictographBase accepts // U+2000..U+33FF plus © U+00A9 and ® U+00AE; isEmoji rejects them. // Tooltip lookup must still find them. - auto check_bmp = [](llwchar cp, const char* tag) { + auto require_single_range = [](llwchar cp, const char* tag) { std::u32string ws { cp }; auto r = wstring_emoji_range_at(ws, 0); - UNSCOPED_INFO(std::string(tag) + ".first"); + INFO(tag); REQUIRE(r.first == size_t(0)); - UNSCOPED_INFO(std::string(tag) + ".second"); REQUIRE(r.second == size_t(1)); }; - check_bmp((llwchar)0x2626, "orthodox cross"); - check_bmp((llwchar)0x00A9, "copyright"); - check_bmp((llwchar)0x00AE, "registered"); - check_bmp((llwchar)0x2764, "heart"); - check_bmp((llwchar)0x2693, "anchor"); + require_single_range((llwchar)0x2626, "orthodox cross"); + require_single_range((llwchar)0x00A9, "copyright"); + require_single_range((llwchar)0x00AE, "registered"); + require_single_range((llwchar)0x2764, "heart"); + require_single_range((llwchar)0x2693, "anchor"); // Bare extenders / out-of-range codepoints get an empty range — // isPictographBase explicitly rejects these so they don't trigger // a tooltip lookup on their own. - auto check_no_range = [](llwchar cp, const char* tag) { + auto require_no_range = [](llwchar cp, const char* tag) { std::u32string ws { cp }; auto r = wstring_emoji_range_at(ws, 0); - UNSCOPED_INFO(std::string(tag) + " empty"); + INFO(tag); REQUIRE(r.first == r.second); }; - check_no_range((llwchar)0x200D, "bare ZWJ"); - check_no_range((llwchar)0xFE0F, "bare VS-16"); - check_no_range((llwchar)0xFE0E, "bare VS-15"); - check_no_range((llwchar)0x20E3, "bare keycap combiner"); - check_no_range((llwchar)0x1F3FB, "bare skintone"); - check_no_range((llwchar)0xE0067, "bare tag char"); - check_no_range((llwchar)0x1FFF, "U+1FFF below astral"); - check_no_range((llwchar)0x3300, "U+3300 above BMP"); - check_no_range((llwchar)0x20000, "U+20000 above astral"); + require_no_range((llwchar)0x200D, "bare ZWJ"); + require_no_range((llwchar)0xFE0F, "bare VS-16"); + require_no_range((llwchar)0xFE0E, "bare VS-15"); + require_no_range((llwchar)0x20E3, "bare keycap combiner"); + require_no_range((llwchar)0x1F3FB, "bare skintone"); + require_no_range((llwchar)0xE0067, "bare tag char"); + require_no_range((llwchar)0x1FFF, "U+1FFF below astral"); + require_no_range((llwchar)0x3300, "U+3300 above BMP"); + require_no_range((llwchar)0x20000, "U+20000 above astral"); // Back-to-back flags 🇺🇸🇯🇵 — pos lookup must find the *second* run // when pos has passed the first. Exercises the ordered-iteration @@ -1491,17 +1274,11 @@ namespace tut auto tf0 = wstring_emoji_range_at(two_flags, 0); auto tf2 = wstring_emoji_range_at(two_flags, 2); auto tf3 = wstring_emoji_range_at(two_flags, 3); - UNSCOPED_INFO("two_flags[0].first"); CHECK(tf0.first == size_t(0)); - UNSCOPED_INFO("two_flags[0].second"); CHECK(tf0.second == size_t(2)); - UNSCOPED_INFO("two_flags[2].first"); CHECK(tf2.first == size_t(2)); - UNSCOPED_INFO("two_flags[2].second"); CHECK(tf2.second == size_t(4)); - UNSCOPED_INFO("two_flags mid 2nd flag"); CHECK(tf3.first == size_t(2)); - UNSCOPED_INFO("two_flags mid 2nd second"); CHECK(tf3.second == size_t(4)); // Mixed "A 🚀 B": ASCII positions return empty, the rocket position @@ -1510,13 +1287,9 @@ namespace tut auto m0 = wstring_emoji_range_at(mixed, 0); auto m1 = wstring_emoji_range_at(mixed, 1); auto m2 = wstring_emoji_range_at(mixed, 2); - UNSCOPED_INFO("mixed A empty"); CHECK(m0.first == m0.second); - UNSCOPED_INFO("mixed rocket.first"); CHECK(m1.first == size_t(1)); - UNSCOPED_INFO("mixed rocket.second"); CHECK(m1.second == size_t(2)); - UNSCOPED_INFO("mixed B empty"); CHECK(m2.first == m2.second); } @@ -1536,9 +1309,8 @@ namespace tut std::u32string ascii = { (llwchar)'a', (llwchar)'b', (llwchar)'c' }; for (size_t p = 0; p <= ascii.size(); ++p) { - UNSCOPED_INFO("ascii align_backward"); + CAPTURE(p); REQUIRE(wstring_grapheme_align_backward(ascii, p) == p); - UNSCOPED_INFO("ascii align_forward"); REQUIRE(wstring_grapheme_align_forward(ascii, p) == p); } @@ -1551,42 +1323,31 @@ namespace tut // Endpoint short-circuits: pos == 0 stays 0 (backward); pos >= size // stays size (forward) regardless of trailing emoji. - UNSCOPED_INFO("fam align_backward(0)"); CHECK(wstring_grapheme_align_backward(fam, 0) == size_t(0)); - UNSCOPED_INFO("fam align_backward(size)"); - REQUIRE(wstring_grapheme_align_backward(fam, fam.size()) == fam.size()); - UNSCOPED_INFO("fam align_forward(size)"); - REQUIRE(wstring_grapheme_align_forward(fam, fam.size()) == fam.size()); - UNSCOPED_INFO("fam align_forward(99)"); - REQUIRE(wstring_grapheme_align_forward(fam, 99) == fam.size()); + CHECK(wstring_grapheme_align_backward(fam, fam.size()) == fam.size()); + CHECK(wstring_grapheme_align_forward(fam, fam.size()) == fam.size()); + CHECK(wstring_grapheme_align_forward(fam, 99) == fam.size()); // pos == run.first — strictly outside, returned unchanged. This is // the contract LLTextEditor::prevWordPos relies on: a word-walk // that already landed on a cluster start is fine and must not be // pulled backward into something else. - UNSCOPED_INFO("fam align_backward(1)"); CHECK(wstring_grapheme_align_backward(fam, 1) == size_t(1)); - UNSCOPED_INFO("fam align_forward(1)"); CHECK(wstring_grapheme_align_forward(fam, 1) == size_t(1)); // pos == run.second — also strictly outside, unchanged. - UNSCOPED_INFO("fam align_backward(6)"); CHECK(wstring_grapheme_align_backward(fam, 6) == size_t(6)); - UNSCOPED_INFO("fam align_forward(6)"); CHECK(wstring_grapheme_align_forward(fam, 6) == size_t(6)); // pos strictly inside [2..5] — backward snaps to 1, forward to 6. for (size_t p = 2; p <= 5; ++p) { - UNSCOPED_INFO("fam mid align_backward"); + CAPTURE(p); REQUIRE(wstring_grapheme_align_backward(fam, p) == size_t(1)); - UNSCOPED_INFO("fam mid align_forward"); REQUIRE(wstring_grapheme_align_forward(fam, p) == size_t(6)); } // Outside the cluster — surrounding ASCII positions unchanged. - UNSCOPED_INFO("fam align_backward(7)"); CHECK(wstring_grapheme_align_backward(fam, 7) == size_t(7)); - UNSCOPED_INFO("fam align_forward(7)"); CHECK(wstring_grapheme_align_forward(fam, 7) == size_t(7)); // Two disjoint clusters in one string — flag at [1,3), family at @@ -1602,25 +1363,19 @@ namespace tut (llwchar)0x1F467, (llwchar)'!' }; // Flag interior (pos == 2) → flag.first 1. - UNSCOPED_INFO("two flag mid backward"); CHECK(wstring_grapheme_align_backward(two, 2) == size_t(1)); - UNSCOPED_INFO("two flag mid forward"); CHECK(wstring_grapheme_align_forward(two, 2) == size_t(3)); // Family interior (pos == 6) → family bounds [4, 9). - UNSCOPED_INFO("two fam mid backward"); CHECK(wstring_grapheme_align_backward(two, 6) == size_t(4)); - UNSCOPED_INFO("two fam mid forward"); CHECK(wstring_grapheme_align_forward(two, 6) == size_t(9)); // Between the two clusters (the space at pos 3) — unchanged. - UNSCOPED_INFO("two between backward"); CHECK(wstring_grapheme_align_backward(two, 3) == size_t(3)); - UNSCOPED_INFO("two between forward"); CHECK(wstring_grapheme_align_forward(two, 3) == size_t(3)); } // --------------------------------------------------------------- // step_grapheme + emoji_range_at: malformed-input pins. The simple - // ZWJ-family / flag cases are covered above (tests 100/101/103/104); + // ZWJ-family / flag cases are covered by the cases above; // here we cover the rougher edges that show up in user-pasted text: // empty strings, out-of-bounds positions, clusters at the very ends // of the buffer, two clusters back-to-back with no separator, and @@ -1633,47 +1388,35 @@ namespace tut { // Empty wstring — no grapheme moves possible. std::u32string empty; - UNSCOPED_INFO("empty step_forward"); CHECK(wstring_step_grapheme_forward(empty, 0) == size_t(0)); - UNSCOPED_INFO("empty step_backward"); CHECK(wstring_step_grapheme_backward(empty, 0) == size_t(0)); // Out-of-bounds position — both directions clamp into [0, size], as the // header says. The old walker returned pos-1 here, handing a caller an // index past the end of its own string. std::u32string ascii = { (llwchar)'a', (llwchar)'b' }; - UNSCOPED_INFO("oob step_forward"); CHECK(wstring_step_grapheme_forward(ascii, 99) == size_t(2)); - UNSCOPED_INFO("oob step_backward"); CHECK(wstring_step_grapheme_backward(ascii, 99) == size_t(2)); // Cluster at start of string — backward from inside or just-past // snaps to 0 (cluster.first). std::u32string lead = { (llwchar)0x1F1FA, (llwchar)0x1F1F8, (llwchar)'X' }; - UNSCOPED_INFO("lead flag step_forward(0)"); CHECK(wstring_step_grapheme_forward(lead, 0) == size_t(2)); - UNSCOPED_INFO("lead flag step_backward(2)"); CHECK(wstring_step_grapheme_backward(lead, 2) == size_t(0)); // Cluster at end of string — forward from inside snaps to size. std::u32string trail = { (llwchar)'X', (llwchar)0x1F1FA, (llwchar)0x1F1F8 }; - UNSCOPED_INFO("trail flag step_forward(1)"); CHECK(wstring_step_grapheme_forward(trail, 1) == size_t(3)); - UNSCOPED_INFO("trail flag step_backward(size)"); - REQUIRE(wstring_step_grapheme_backward(trail, trail.size()) == size_t(1)); + CHECK(wstring_step_grapheme_backward(trail, trail.size()) == size_t(1)); // Two clusters back-to-back with no separator: step_forward from // inside cluster A must land at A.end (which is also B.first), not // skip into cluster B. std::u32string two_flags = { (llwchar)0x1F1FA, (llwchar)0x1F1F8, (llwchar)0x1F1EF, (llwchar)0x1F1F5 }; - UNSCOPED_INFO("two flags forward"); CHECK(wstring_step_grapheme_forward(two_flags, 0) == size_t(2)); - UNSCOPED_INFO("two flags forward 2nd"); CHECK(wstring_step_grapheme_forward(two_flags, 2) == size_t(4)); - UNSCOPED_INFO("two flags backward"); CHECK(wstring_step_grapheme_backward(two_flags, 4) == size_t(2)); - UNSCOPED_INFO("two flags backward 1st"); CHECK(wstring_step_grapheme_backward(two_flags, 2) == size_t(0)); // emoji_range_at on a tag char inside a subdivision flag — the @@ -1690,17 +1433,14 @@ namespace tut for (size_t p = 1; p < 8; ++p) { auto r = wstring_emoji_range_at(subdiv, p); - UNSCOPED_INFO("subdiv begin"); + CAPTURE(p); REQUIRE(r.first == size_t(1)); - UNSCOPED_INFO("subdiv end"); REQUIRE(r.second == size_t(8)); } // pos on the surrounding ASCII falls outside the cluster. auto pre = wstring_emoji_range_at(subdiv, 0); auto post = wstring_emoji_range_at(subdiv, 8); - UNSCOPED_INFO("subdiv pre empty"); CHECK(pre.first == pre.second); - UNSCOPED_INFO("subdiv post empty"); CHECK(post.first == post.second); } @@ -1715,7 +1455,7 @@ namespace tut TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_emoji_range_at answers the same given the cluster list", "[LLStringUTF]") { - // Same mixed-content string used in test<104>: ASCII + flag + + // Same mixed-content string as the align case above: ASCII + flag + // space + ZWJ family + ASCII. Two disjoint clusters at [1,3) and // [4,9), so the loop bodies exercise both early-out and middle- // of-vector iteration. @@ -1728,8 +1468,7 @@ namespace tut (llwchar)'!' }; const std::string utf8 = to_utf8(two); const auto clusters = utf8str_find_emoji_clusters(utf8); - UNSCOPED_INFO("two clusters"); - REQUIRE(clusters.size() == size_t(2)); + CHECK(clusters.size() == size_t(2)); // Handing the list in has to give the same answer as building it // inside, at every position including the ones inside a character. @@ -1737,16 +1476,14 @@ namespace tut { auto r1 = utf8str_emoji_range_at(utf8, p); auto r2 = utf8str_emoji_range_at(utf8, p, clusters); - UNSCOPED_INFO("range_at first"); + CAPTURE(p); REQUIRE(r1.first == r2.first); - UNSCOPED_INFO("range_at second"); REQUIRE(r1.second == r2.second); } // Empty string -- pinning that the with-clusters overload handles a // degenerate empty cluster vector identically to the no-clusters form. const EmojiClusterList no_clusters; - UNSCOPED_INFO("empty range_at first overload"); CHECK(utf8str_emoji_range_at(std::string_view(), 0).first == utf8str_emoji_range_at(std::string_view(), 0, no_clusters).first); } @@ -1758,12 +1495,10 @@ namespace tut { const std::string in = "A\xF0\x9F\x9A\x80" "B\xF0\x9F\x9A\x80" "C"; const std::string out = utf8str_substChar(in, (llwchar)0x1F680, (llwchar)'X'); - UNSCOPED_INFO("substChar astral -> ASCII"); CHECK(out == std::string("AXBXC")); const std::string in2 = "A\xE6\x97\xA5" "B"; const std::string out2 = utf8str_substChar(in2, (llwchar)0x65E5, (llwchar)'.'); - UNSCOPED_INFO("substChar BMP -> ASCII"); CHECK(out2 == std::string("A.B")); } @@ -1773,9 +1508,7 @@ namespace tut { const std::string mixed = "A\xE6\x97\xA5" "B\xF0\x9F\x9A\x80" "C"; const std::string expected = "A?B?C"; - UNSCOPED_INFO("utf8str_makeASCII"); CHECK(utf8str_makeASCII(mixed) == expected); - UNSCOPED_INFO("utf8str_makeASCII ascii passthrough"); CHECK(utf8str_makeASCII("Hello") == std::string("Hello")); } @@ -1785,20 +1518,15 @@ namespace tut { const std::string mixed = "A\xE6\x97\xA5" "B"; const std::string expected = "A???B"; - UNSCOPED_INFO("mbcsstring_makeASCII"); CHECK(mbcsstring_makeASCII(mixed) == expected); } // utf8str_removeCRLF strips CR (0x0D) only — LF is preserved. TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_removeCRLF strips CR and keeps LF", "[LLStringUTF]") { - UNSCOPED_INFO("removeCRLF crlf"); CHECK(utf8str_removeCRLF("a\r\nb") == std::string("a\nb")); - UNSCOPED_INFO("removeCRLF cr"); CHECK(utf8str_removeCRLF("a\rb") == std::string("ab")); - UNSCOPED_INFO("removeCRLF lf"); CHECK(utf8str_removeCRLF("a\nb") == std::string("a\nb")); - UNSCOPED_INFO("removeCRLF empty"); CHECK(utf8str_removeCRLF("") == std::string()); } @@ -1810,22 +1538,16 @@ namespace tut { std::list out; utf8str_split(out, "Hello there world", 10, ' '); - UNSCOPED_INFO("split 3 parts"); REQUIRE(out.size() == size_t(3)); auto it = out.begin(); - UNSCOPED_INFO("split[0]"); CHECK(*it++ == std::string("Hello")); - UNSCOPED_INFO("split[1]"); CHECK(*it++ == std::string("there ")); - UNSCOPED_INFO("split[2]"); CHECK(*it == std::string("world")); } { std::list out; utf8str_split(out, "short", 10, ' '); - UNSCOPED_INFO("split single"); REQUIRE(out.size() == size_t(1)); - UNSCOPED_INFO("split single val"); CHECK(out.front() == std::string("short")); } } @@ -1834,14 +1556,11 @@ namespace tut // the decoded UTF-8 byte sequence also in hex. TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: wchar_utf8_preview gives the code point and its UTF-8 bytes in hex", "[LLStringUTF]") { - UNSCOPED_INFO("preview ASCII"); CHECK(wchar_utf8_preview((llwchar)'A') == std::string("41")); // é = U+00E9 -> 0xC3 0xA9 - UNSCOPED_INFO("preview 2-byte"); CHECK(wchar_utf8_preview((llwchar)0x00E9) == std::string("E9 [C3, A9]")); // Rocket = U+1F680 -> 0xF0 0x9F 0x9A 0x80 - UNSCOPED_INFO("preview astral"); CHECK(wchar_utf8_preview((llwchar)0x1F680) == std::string("1F680 [F0, 9F, 9A, 80]")); } @@ -1854,66 +1573,51 @@ namespace tut // "e" + COMBINING ACUTE, then "x". The base and its mark are one // cluster; the old walker split them. const std::u32string combining = { (llwchar)'e', (llwchar)0x0301, (llwchar)'x' }; - UNSCOPED_INFO("combining fwd from 0"); CHECK(wstring_step_grapheme_forward(combining, 0) == size_t(2)); - UNSCOPED_INFO("combining back from 2"); CHECK(wstring_step_grapheme_backward(combining, 2) == size_t(0)); - UNSCOPED_INFO("combining align back mid"); CHECK(wstring_grapheme_align_backward(combining, 1) == size_t(0)); - UNSCOPED_INFO("combining align fwd mid"); CHECK(wstring_grapheme_align_forward(combining, 1) == size_t(2)); // Hangul LVT: HANGUL SYLLABLE inputs as jamo L + V + T are one cluster. const std::u32string hangul = { (llwchar)0x1100, (llwchar)0x1161, (llwchar)0x11A8, (llwchar)'!' }; - UNSCOPED_INFO("hangul fwd from 0"); CHECK(wstring_step_grapheme_forward(hangul, 0) == size_t(3)); - UNSCOPED_INFO("hangul back from 3"); CHECK(wstring_step_grapheme_backward(hangul, 3) == size_t(0)); // Regional indicator pairs still pair up, and a third RI starts a new // cluster rather than joining the first two. const std::u32string flags = { (llwchar)0x1F1FA, (llwchar)0x1F1F8, (llwchar)0x1F1FA }; - UNSCOPED_INFO("flag pair fwd"); CHECK(wstring_step_grapheme_forward(flags, 0) == size_t(2)); - UNSCOPED_INFO("third RI is its own"); CHECK(wstring_step_grapheme_forward(flags, 2) == size_t(3)); // ZWJ family stays one cluster, as before. const std::u32string family = { (llwchar)0x1F468, (llwchar)0x200D, (llwchar)0x1F469, (llwchar)0x200D, (llwchar)0x1F467, (llwchar)'!' }; - UNSCOPED_INFO("zwj family fwd"); CHECK(wstring_step_grapheme_forward(family, 0) == size_t(5)); - UNSCOPED_INFO("zwj family back"); CHECK(wstring_step_grapheme_backward(family, 5) == size_t(0)); // GB4 breaks after LF, which is also what bounds the backward scan. const std::u32string lines = { (llwchar)'a', (llwchar)'\n', (llwchar)'e', (llwchar)0x0301 }; - UNSCOPED_INFO("break after lf"); CHECK(wstring_step_grapheme_backward(lines, 2) == size_t(1)); UNSCOPED_INFO("cluster after lf"); CHECK(wstring_step_grapheme_backward(lines, 4) == size_t(2)); // Degenerate input: empty, and positions past the end clamp. const std::u32string empty; - UNSCOPED_INFO("empty fwd"); CHECK(wstring_step_grapheme_forward(empty, 0) == size_t(0)); - UNSCOPED_INFO("empty back"); CHECK(wstring_step_grapheme_backward(empty, 0) == size_t(0)); - UNSCOPED_INFO("past end fwd"); - REQUIRE(wstring_step_grapheme_forward(combining, 99) == combining.size()); - UNSCOPED_INFO("past end align"); - REQUIRE(wstring_grapheme_align_forward(combining, 99) == combining.size()); + CHECK(wstring_step_grapheme_forward(combining, 99) == combining.size()); + CHECK(wstring_grapheme_align_forward(combining, 99) == combining.size()); // Stepping forward from every position must strictly advance, and // backward must strictly retreat, or a caret can wedge. for (size_t p = 0; p < family.size(); ++p) { - UNSCOPED_INFO("fwd advances"); + CAPTURE(p); REQUIRE(wstring_step_grapheme_forward(family, p) > p); } for (size_t p = 1; p <= family.size(); ++p) { - UNSCOPED_INFO("back retreats"); + CAPTURE(p); REQUIRE(wstring_step_grapheme_backward(family, p) < p); } } @@ -1923,13 +1627,9 @@ namespace tut TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: word stepping lands on the start of each word", "[LLStringUTF]") { const std::u32string two_words = to_u32("foo bar"); - UNSCOPED_INFO("fwd to next word"); CHECK(wstring_step_word_forward(two_words, 0) == size_t(4)); - UNSCOPED_INFO("fwd from next word"); CHECK(wstring_step_word_forward(two_words, 4) == size_t(7)); - UNSCOPED_INFO("back from end"); CHECK(wstring_step_word_backward(two_words, 7) == size_t(4)); - UNSCOPED_INFO("back over the space"); CHECK(wstring_step_word_backward(two_words, 4) == size_t(0)); UNSCOPED_INFO("back from mid-word"); CHECK(wstring_step_word_backward(two_words, 5) == size_t(4)); @@ -1938,7 +1638,6 @@ namespace tut // example.com and 3.14 to hold together (WB6/WB7, FULL STOP is // MidNumLet). So the word here is "foo.bar" and the next one is "baz". const std::u32string dotted = to_u32("foo.bar baz"); - UNSCOPED_INFO("dot does not split"); CHECK(wstring_step_word_forward(dotted, 0) == size_t(8)); UNSCOPED_INFO("fwd from inside the dot word"); CHECK(wstring_step_word_forward(dotted, 3) == size_t(8)); @@ -1958,14 +1657,12 @@ namespace tut // An apostrophe is inside the word, not a break in it. The old walk // stopped at index 3, mid-word. const std::u32string contraction = to_u32("don't stop"); - UNSCOPED_INFO("contraction is one word"); CHECK(wstring_step_word_forward(contraction, 0) == size_t(6)); UNSCOPED_INFO("back over a contraction"); CHECK(wstring_step_word_backward(contraction, 10) == size_t(6)); // Tabs are whitespace to step over; the old test was == ' ' only. const std::u32string tabbed = to_u32("foo\tbar"); - UNSCOPED_INFO("fwd over a tab"); CHECK(wstring_step_word_forward(tabbed, 0) == size_t(4)); // Neither direction crosses a newline, matching what the old walk did @@ -1983,43 +1680,32 @@ namespace tut // Trailing whitespace has no word after it, so the line's end is where // forward stops. const std::u32string trailing = to_u32("foo "); - UNSCOPED_INFO("fwd into trailing space"); CHECK(wstring_step_word_forward(trailing, 0) == size_t(6)); // Degenerate input. const std::u32string empty; - UNSCOPED_INFO("empty fwd"); CHECK(wstring_step_word_forward(empty, 0) == size_t(0)); - UNSCOPED_INFO("empty back"); CHECK(wstring_step_word_backward(empty, 0) == size_t(0)); - UNSCOPED_INFO("past end fwd"); - REQUIRE(wstring_step_word_forward(two_words, 99) == two_words.size()); - UNSCOPED_INFO("past end back"); + CHECK(wstring_step_word_forward(two_words, 99) == two_words.size()); CHECK(wstring_step_word_backward(two_words, 99) == size_t(4)); // Scripts without spaces still segment, rather than swallowing the // whole run the way an alnum test would. const std::u32string cjk = to_u32("\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E ok"); - UNSCOPED_INFO("cjk advances"); CHECK(wstring_step_word_forward(cjk, 0) > size_t(0)); - UNSCOPED_INFO("cjk terminates"); - REQUIRE(wstring_step_word_forward(cjk, 0) <= cjk.size()); + CHECK(wstring_step_word_forward(cjk, 0) <= cjk.size()); // Adjacent emoji are separate words, so a word step crosses exactly // one. Callers must not pre-step a grapheme and then ask for the next // word -- that lands two emoji away. const std::u32string two_emoji = { (llwchar)0x1F436, (llwchar)0x1F431, (llwchar)'x' }; - UNSCOPED_INFO("one emoji forward"); CHECK(wstring_step_word_forward(two_emoji, 0) == size_t(1)); - UNSCOPED_INFO("one emoji back"); CHECK(wstring_step_word_backward(two_emoji, 2) == size_t(1)); // A ZWJ sequence is a single word, however many codepoints it spans. const std::u32string flag_then_dog = { (llwchar)0x1F3F3, (llwchar)0xFE0F, (llwchar)0x200D, (llwchar)0x26A7, (llwchar)0xFE0F, (llwchar)0x1F436 }; - UNSCOPED_INFO("zwj sequence is one word"); CHECK(wstring_step_word_forward(flag_then_dog, 0) == size_t(5)); - UNSCOPED_INFO("back over the zwj sequence"); CHECK(wstring_step_word_backward(flag_then_dog, 6) == size_t(5)); } @@ -2033,69 +1719,54 @@ namespace tut // space and the next one starts on the word. This is what the old // start_of_last_word tracking computed, and it has to stay that way. wstring_line_break_opportunities(to_u32("hello world"), breaks); - UNSCOPED_INFO("two opportunities"); REQUIRE(breaks.size() == size_t(2)); - UNSCOPED_INFO("after the space"); CHECK(breaks[0] == size_t(6)); - UNSCOPED_INFO("and the end"); CHECK(breaks[1] == size_t(11)); // The string's end is always an opportunity; 0 never is. wstring_line_break_opportunities(to_u32("unbroken"), breaks); - UNSCOPED_INFO("single word, one opportunity"); REQUIRE(breaks.size() == size_t(1)); - UNSCOPED_INFO("at the end"); CHECK(breaks[0] == size_t(8)); // A non-breaking space is glue. The old code special-cased U+00A0 by // hand; here it simply produces no opportunity. wstring_line_break_opportunities(to_u32("a\xC2\xA0" "b"), breaks); - UNSCOPED_INFO("nbsp does not break"); REQUIRE(breaks.size() == size_t(1)); - UNSCOPED_INFO("only the end"); CHECK(breaks[0] == size_t(3)); // CJK ideographs may be split between characters... const std::u32string cjk = to_u32("\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E"); wstring_line_break_opportunities(cjk, breaks); - UNSCOPED_INFO("cjk splits between characters"); - REQUIRE(breaks.size() > size_t(1)); + CHECK(breaks.size() > size_t(1)); // ...but a line may not begin with closing punctuation, so there is no // opportunity immediately before U+3002 IDEOGRAPHIC FULL STOP. The old // ideograph-range test would have broken there happily. const std::u32string cjk_stop = to_u32("\xE6\x97\xA5\xE6\x9C\xAC\xE3\x80\x82"); wstring_line_break_opportunities(cjk_stop, breaks); - UNSCOPED_INFO("no break before the full stop"); - REQUIRE(std::find(breaks.begin(), breaks.end(), size_t(2)) == breaks.end()); + CHECK(std::find(breaks.begin(), breaks.end(), size_t(2)) == breaks.end()); // Every opportunity is in range and strictly ascending, since the // consumer walks them with a single forward cursor. const std::u32string mixed = to_u32("one two\xC2\xA0three, four"); wstring_line_break_opportunities(mixed, breaks); - UNSCOPED_INFO("mixed has opportunities"); REQUIRE_FALSE(breaks.empty()); for (size_t k = 0; k < breaks.size(); ++k) { - { - INFO("in range"); - REQUIRE(breaks[k] > 0); - REQUIRE(breaks[k] <= mixed.size()); - } + CAPTURE(k); + REQUIRE(breaks[k] > 0); + REQUIRE(breaks[k] <= mixed.size()); if (k > 0) { - UNSCOPED_INFO("ascending"); REQUIRE(breaks[k] > breaks[k - 1]); } } - UNSCOPED_INFO("ends at the end"); - REQUIRE(breaks.back() == mixed.size()); + CHECK(breaks.back() == mixed.size()); // Empty input clears the caller's buffer rather than leaving it alone, // since the buffer is reused across lines. wstring_line_break_opportunities(std::u32string(), breaks); - UNSCOPED_INFO("empty clears"); - REQUIRE(breaks.empty()); + CHECK(breaks.empty()); } // Case conversion, and the index map that lets a search fold its haystack @@ -2105,37 +1776,29 @@ namespace tut // Uppercasing sharp s produces two characters. towupper could not do // this at all -- it maps one codepoint to one codepoint. std::string sharp_s = "stra\xC3\x9F" "e"; - UNSCOPED_INFO("sharp s starts at 7 bytes"); - REQUIRE(sharp_s.size() == size_t(7)); + CHECK(sharp_s.size() == size_t(7)); LLStringUtil::toUpper(sharp_s); - UNSCOPED_INFO("uppercased grows"); CHECK(sharp_s == std::string("STRASSE")); std::string plain = "Hello"; LLStringUtil::toLower(plain); - UNSCOPED_INFO("ordinary lowercase"); CHECK(plain == std::string("hello")); // U+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE lowercases to two // characters, so every offset after it shifts. This is the case that // breaks an offset taken from a folded copy. const std::string dotted_i = "a\xC4\xB0" "b"; - UNSCOPED_INFO("four bytes in"); REQUIRE(dotted_i.size() == size_t(4)); std::string folded = dotted_i; LLStringUtil::toLower(folded); - UNSCOPED_INFO("fold grew"); - REQUIRE(folded.size() > dotted_i.size()); + CHECK(folded.size() > dotted_i.size()); // The point of all this: find 'b' in the folded copy and land on the // right byte in the original. Taken raw, the offset would be past it. const size_t folded_at = folded.find('b'); - UNSCOPED_INFO("found in fold"); REQUIRE(folded_at != std::string::npos); - UNSCOPED_INFO("raw offset would be wrong"); CHECK(folded_at != size_t(3)); - UNSCOPED_INFO("mapped offset is right"); CHECK(utf8str_bytes_from_cased_bytes(dotted_i, folded_at, false) == size_t(3)); // Every prefix of the fold maps back onto a character boundary of the @@ -2143,9 +1806,8 @@ namespace tut for (size_t k = 0; k <= folded.size(); ++k) { const size_t back = utf8str_bytes_from_cased_bytes(dotted_i, k, false); - UNSCOPED_INFO("never past the end"); + CAPTURE(k); REQUIRE(back <= dotted_i.size()); - UNSCOPED_INFO("lands on a character start"); REQUIRE((back == dotted_i.size() || (((unsigned char)dotted_i[back]) & 0xC0) != 0x80)); } @@ -2154,7 +1816,7 @@ namespace tut const std::string ascii = "ABC"; for (size_t k = 0; k <= ascii.size(); ++k) { - UNSCOPED_INFO("ascii maps to itself"); + CAPTURE(k); REQUIRE(utf8str_bytes_from_cased_bytes(ascii, k, false) == k); } @@ -2166,15 +1828,13 @@ namespace tut const std::string strasse = "stra\xC3\x9F" "e"; std::string upper = strasse; LLStringUtil::toUpper(upper); - UNSCOPED_INFO("same byte length"); REQUIRE(upper.size() == strasse.size()); - UNSCOPED_INFO("but one more character"); CHECK(utf8str_codepoint_count(upper) == utf8str_codepoint_count(strasse) + 1); const size_t back_from[] = { 0, 1, 2, 3, 4, 4, 6, 7 }; for (size_t k = 0; k <= upper.size(); ++k) { - UNSCOPED_INFO("cased offset " + std::to_string(k) + " maps back"); + CAPTURE(k); REQUIRE(utf8str_bytes_from_cased_bytes(strasse, k, true) == back_from[k]); } } @@ -2185,24 +1845,20 @@ namespace tut { std::string s = "stra\xC3\x9F" "e"; LLStringUtil::toUpper(s); - UNSCOPED_INFO("narrow toUpper is unicode"); CHECK(s == std::string("STRASSE")); std::string acc = "\xC3\xA9" "cole"; LLStringUtil::toUpper(acc); - UNSCOPED_INFO("narrow toUpper accents"); CHECK(acc == std::string("\xC3\x89" "COLE")); std::string upper = "\xC3\x89" "COLE"; LLStringUtil::toLower(upper); - UNSCOPED_INFO("narrow toLower accents"); CHECK(upper == std::string("\xC3\xA9" "cole")); // ASCII is untouched, which is what the identifier and protocol callers // rely on. std::string ascii = "Image.PNG"; LLStringUtil::toLower(ascii); - UNSCOPED_INFO("ascii unchanged"); CHECK(ascii == std::string("image.png")); // The ff ligature uppercases to "FF": three bytes become two, so an @@ -2213,15 +1869,11 @@ namespace tut std::string key = label; LLStringUtil::toUpper(key); const size_t at = key.find("XYZ"); - UNSCOPED_INFO("found in the key"); REQUIRE(at != std::string::npos); - UNSCOPED_INFO("the ligature expanded, so the key is shorter"); - REQUIRE(key.size() < label.size()); + CHECK(key.size() < label.size()); // xyz begins at byte 6 of the label and byte 5 of the key. - UNSCOPED_INFO("offset maps onto the label's own bytes"); CHECK(utf8str_bytes_from_cased_bytes(label, at, true) == size_t(6)); - UNSCOPED_INFO("the raw offset would have been wrong"); CHECK(at != size_t(6)); } @@ -2232,33 +1884,25 @@ namespace tut std::string key = label; LLStringUtil::toUpper(key); const size_t at = key.find("XYZ"); - UNSCOPED_INFO("found in the key"); REQUIRE(at != std::string::npos); - UNSCOPED_INFO("same byte length either cased or not"); - REQUIRE(key.size() == label.size()); + CHECK(key.size() == label.size()); const size_t mapped = utf8str_bytes_from_cased_bytes(label, at, true); - UNSCOPED_INFO("mapped to the label's own byte offset"); CHECK(mapped == size_t(8)); // And the matched span's own length, which is not the key's. const size_t span_begin = utf8str_bytes_from_cased_bytes(label, 0, true); const size_t span_end = utf8str_bytes_from_cased_bytes(label, key.find(' '), true); - UNSCOPED_INFO("word before the space is seven bytes"); CHECK(span_end - span_begin == size_t(7)); // Offset 0 and a whole-string offset are the degenerate ends. - UNSCOPED_INFO("zero maps to zero"); CHECK(utf8str_bytes_from_cased_bytes(label, 0, true) == size_t(0)); - UNSCOPED_INFO("whole string maps to its byte count"); - REQUIRE(utf8str_bytes_from_cased_bytes(label, key.size(), true) == size_t(11)); + CHECK(utf8str_bytes_from_cased_bytes(label, key.size(), true) == size_t(11)); // ASCII text maps one-to-one, so existing offsets are unaffected. const std::string plain = "hello world"; - UNSCOPED_INFO("ascii maps straight through"); CHECK(utf8str_bytes_from_cased_bytes(plain, 6, true) == size_t(6)); - UNSCOPED_INFO("empty"); CHECK(utf8str_bytes_from_cased_bytes(std::string(), 4, true) == size_t(0)); } @@ -2271,27 +1915,22 @@ namespace tut // A contraction is one word. The alnum-or-underscore walk this replaced // stopped at the apostrophe and offered "don" or "t". auto word = wstring_word_range_at(text, 0); - UNSCOPED_INFO("contraction begin"); CHECK(word.first == size_t(0)); - UNSCOPED_INFO("contraction end"); CHECK(word.second == size_t(5)); // Anywhere inside it gives the same word. for (size_t p = 0; p < 5; ++p) { const auto at = wstring_word_range_at(text, p); - UNSCOPED_INFO("same word from inside"); + CAPTURE(p); REQUIRE(at.first == size_t(0)); - UNSCOPED_INFO("same word end"); REQUIRE(at.second == size_t(5)); } // Whitespace and punctuation are segments too, and neither is a word. const auto space = wstring_word_range_at(text, 5); - UNSCOPED_INFO("space is not a word"); CHECK(space.first == space.second); const auto comma = wstring_word_range_at(text, 10); - UNSCOPED_INFO("comma is not a word"); CHECK(comma.first == comma.second); // Iterating: each call yields the next word, skipping what is between. @@ -2305,52 +1944,38 @@ namespace tut found.push_back(to_utf8(text.substr(next.first, next.second - next.first))); at = next.second; } - UNSCOPED_INFO("three words"); REQUIRE(found.size() == size_t(3)); - UNSCOPED_INFO("first"); CHECK(found[0] == std::string("don't")); - UNSCOPED_INFO("second"); CHECK(found[1] == std::string("stop")); - UNSCOPED_INFO("third"); CHECK(found[2] == std::string("ok")); // Iteration crosses lines, which the spell checker relies on. const std::u32string lines = to_u32("one\ntwo"); const auto first = wstring_next_word_range(lines, 0); - UNSCOPED_INFO("first line word end"); CHECK(first.second == size_t(3)); const auto second = wstring_next_word_range(lines, first.second); - UNSCOPED_INFO("second line word begin"); CHECK(second.first == size_t(4)); - UNSCOPED_INFO("second line word end"); CHECK(second.second == size_t(7)); // Past the end, and past the last word, both terminate. const auto none = wstring_next_word_range(lines, 7); - UNSCOPED_INFO("no more words"); CHECK(none.first == none.second); const auto oob = wstring_word_range_at(lines, 99); - UNSCOPED_INFO("past end is empty"); CHECK(oob.first == oob.second); const std::u32string empty; const auto on_empty = wstring_word_range_at(empty, 0); - UNSCOPED_INFO("empty string"); CHECK(on_empty.first == on_empty.second); // A whole string that is one word -- what autoreplace validates a // keyword with. const std::u32string keyword = to_u32("don't"); const auto whole = wstring_word_range_at(keyword, 0); - { - INFO("keyword is one word"); - CHECK(whole.first == size_t(0)); - REQUIRE(whole.second == keyword.size()); - } + CHECK(whole.first == size_t(0)); + CHECK(whole.second == keyword.size()); const std::u32string two_words = to_u32("not one"); const auto partial = wstring_word_range_at(two_words, 0); - UNSCOPED_INFO("two words is not one"); - REQUIRE(partial.second != two_words.size()); + CHECK(partial.second != two_words.size()); } // Classification above the BMP. Every one of these codepoints used to be @@ -2368,21 +1993,13 @@ namespace tut const llwchar MATH_BOLD_A = 0x1D400; // -> U+D400, a lone surrogate const llwchar GRINNING_FACE = 0x1F600; - UNSCOPED_INFO("CJK ideograph is not whitespace"); CHECK_FALSE(LLStringOps::isSpace(CJK_EXT_B_A)); - UNSCOPED_INFO("CJK ideograph is alphanumeric"); CHECK(LLStringOps::isAlnum(CJK_EXT_B_A)); - UNSCOPED_INFO("CJK ideograph is not a digit"); CHECK_FALSE(LLStringOps::isDigit(CJK_EXT_B_ONE)); - UNSCOPED_INFO("CJK ideograph is alphanumeric"); CHECK(LLStringOps::isAlnum(CJK_EXT_B_ONE)); - UNSCOPED_INFO("Linear B is not whitespace"); CHECK_FALSE(LLStringOps::isSpace(LINEAR_B)); - UNSCOPED_INFO("math capital is a letter"); CHECK(LLStringOps::isAlpha(MATH_BOLD_A)); - UNSCOPED_INFO("emoji is not alphanumeric"); CHECK_FALSE(LLStringOps::isAlnum(GRINNING_FACE)); - UNSCOPED_INFO("emoji is not whitespace"); CHECK_FALSE(LLStringOps::isSpace(GRINNING_FACE)); // Case above the BMP: this used to return an unrelated codepoint @@ -2391,11 +2008,8 @@ namespace tut // failure message rather than compared as they stand. const auto cased = [](llwchar c) { return (U32)c; }; - UNSCOPED_INFO("Deseret capital is upper"); CHECK(LLStringOps::isUpper(DESERET_CAP)); - UNSCOPED_INFO("Deseret capital lowercases within Deseret"); CHECK(cased(LLStringOps::toLower(DESERET_CAP)) == cased(DESERET_SMALL)); - UNSCOPED_INFO("Deseret small uppercases back"); CHECK(cased(LLStringOps::toUpper(DESERET_SMALL)) == cased(DESERET_CAP)); // Case inside the BMP, which towlower left alone entirely. @@ -2421,17 +2035,11 @@ namespace tut CHECK(LLStringOps::isAlnum(llwchar(0x0660))); // ASCII keeps answering as it always did. - UNSCOPED_INFO("A is upper"); CHECK(LLStringOps::isUpper(llwchar('A'))); - UNSCOPED_INFO("a is lower"); CHECK(LLStringOps::isLower(llwchar('a'))); - UNSCOPED_INFO("5 is a digit"); CHECK(LLStringOps::isDigit(llwchar('5'))); - UNSCOPED_INFO("space is space"); CHECK(LLStringOps::isSpace(llwchar(' '))); - UNSCOPED_INFO("dot is punct"); CHECK(LLStringOps::isPunct(llwchar('.'))); - UNSCOPED_INFO("A lowercases"); CHECK(cased(LLStringOps::toLower(llwchar('A'))) == cased(llwchar('a'))); } @@ -2448,11 +2056,9 @@ namespace tut // so the cursor stops at the first. Reading their low halves as line // feeds made all three of the ideographs and the space look like one // gap, and the cursor jumped clear to "cd" at 5. - UNSCOPED_INFO("cursor stops at the ideograph"); CHECK(wstring_step_word_forward(text, 0) == size_t(2)); const auto range = wstring_word_range_at(text, 2); - UNSCOPED_INFO("an ideograph is a word"); CHECK(range.first != range.second); } @@ -2465,7 +2071,7 @@ namespace tut const std::string ascii = "abcd"; for (size_t i = 0; i <= ascii.size(); ++i) { - UNSCOPED_INFO("ascii is all boundaries"); + CAPTURE(i); REQUIRE(utf8str_grapheme_align_backward(ascii, i) == i); } @@ -2495,18 +2101,14 @@ namespace tut const std::string flag = "\xF0\x9F\x87\xBA\xF0\x9F\x87\xB8"; for (size_t i = 1; i < flag.size(); ++i) { - UNSCOPED_INFO("no half flags"); + CAPTURE(i); REQUIRE(utf8str_grapheme_align_backward(flag, i) == size_t(0)); } - UNSCOPED_INFO("whole flag survives"); - REQUIRE(utf8str_grapheme_align_backward(flag, flag.size()) == flag.size()); + CHECK(utf8str_grapheme_align_backward(flag, flag.size()) == flag.size()); // Degenerate input. - UNSCOPED_INFO("zero"); CHECK(utf8str_grapheme_align_backward(cjk, 0) == size_t(0)); - UNSCOPED_INFO("past end"); - REQUIRE(utf8str_grapheme_align_backward(cjk, 99) == cjk.size()); - UNSCOPED_INFO("empty"); + CHECK(utf8str_grapheme_align_backward(cjk, 99) == cjk.size()); CHECK(utf8str_grapheme_align_backward(std::string(), 0) == size_t(0)); } @@ -2523,7 +2125,6 @@ namespace tut "\xF0\x9F\x87\xBA\xF0\x9F\x87\xB8" // flag: two codepoints, one cluster " end"; // a=0 b=1 sp=2 CJK=3,6 sp=9 e=10 acute=11 sp=13 flag=14 sp=22 end=23. - UNSCOPED_INFO("layout"); REQUIRE(utf8.size() == size_t(26)); // Every grapheme boundary, written out. The combining acute and the @@ -2535,19 +2136,17 @@ namespace tut for (size_t i = 1; i < count; ++i) { at = utf8str_step_grapheme_forward(utf8, at); - UNSCOPED_INFO("forward boundary " + std::to_string(i)); + CAPTURE(i); REQUIRE(at == boundaries[i]); } - UNSCOPED_INFO("forward walk ended at the end"); - REQUIRE(at == utf8.size()); + CHECK(at == utf8.size()); for (size_t i = count - 1; i > 0; --i) { at = utf8str_step_grapheme_backward(utf8, at); - UNSCOPED_INFO("backward boundary " + std::to_string(i - 1)); + CAPTURE(i); REQUIRE(at == boundaries[i - 1]); } - UNSCOPED_INFO("backward walk came back to nothing"); CHECK(at == size_t(0)); // Every offset inside a cluster snaps to that cluster's edges. @@ -2555,10 +2154,9 @@ namespace tut { for (size_t inside = boundaries[i - 1] + 1; inside < boundaries[i]; ++inside) { - UNSCOPED_INFO("aligns back"); + CAPTURE(i, inside); REQUIRE(utf8str_grapheme_align_backward(utf8, inside) == boundaries[i - 1]); - UNSCOPED_INFO("aligns forward"); REQUIRE(utf8str_grapheme_align_forward(utf8, inside) == boundaries[i]); } @@ -2570,47 +2168,37 @@ namespace tut for (size_t i = 0; i < sizeof(word_starts) / sizeof(word_starts[0]); ++i) { word_at = utf8str_step_word_forward(utf8, word_at); - UNSCOPED_INFO("word start " + std::to_string(i)); + CAPTURE(i); REQUIRE(word_at == word_starts[i]); } // The word under the first ideograph is the ideograph pair. const auto cjk_word = utf8str_word_range_at(utf8, 3); - UNSCOPED_INFO("CJK word begins"); CHECK(cjk_word.first == size_t(3)); - UNSCOPED_INFO("CJK word ends"); CHECK(cjk_word.second == size_t(9)); // Line break opportunities: after each space, between the ideographs, // and at the end. Never inside a character or a flag. std::vector breaks; utf8str_line_break_opportunities(utf8, breaks); - { - INFO("the end is always an opportunity"); - REQUIRE_FALSE(breaks.empty()); - REQUIRE(breaks.back() == utf8.size()); - } + REQUIRE_FALSE(breaks.empty()); + CHECK(breaks.back() == utf8.size()); for (size_t where : breaks) { - UNSCOPED_INFO("never zero"); + CAPTURE(where); REQUIRE(where != 0); - UNSCOPED_INFO("always on a grapheme boundary"); REQUIRE(std::find(boundaries, boundaries + count, where) != boundaries + count); } - UNSCOPED_INFO("CJK may be split between the ideographs"); - REQUIRE(std::find(breaks.begin(), breaks.end(), size_t(6)) != breaks.end()); - UNSCOPED_INFO("a flag may not be split"); - REQUIRE(std::find(breaks.begin(), breaks.end(), size_t(18)) == breaks.end()); + CHECK(std::find(breaks.begin(), breaks.end(), size_t(6)) != breaks.end()); + CHECK(std::find(breaks.begin(), breaks.end(), size_t(18)) == breaks.end()); // A flag is one cluster of eight bytes, and nothing cuts it in half. const size_t flag = utf8.find("\xF0\x9F\x87\xBA"); - UNSCOPED_INFO("flag steps whole"); CHECK(utf8str_step_grapheme_forward(utf8, flag) == flag + 8); for (size_t inside = 1; inside < 8; ++inside) { - UNSCOPED_INFO("inside the flag aligns back to its start"); + CAPTURE(inside); REQUIRE(utf8str_grapheme_align_backward(utf8, flag + inside) == flag); - UNSCOPED_INFO("and forward to its end"); REQUIRE(utf8str_grapheme_align_forward(utf8, flag + inside) == flag + 8); } } @@ -2658,24 +2246,19 @@ namespace tut {13, true, 0, 13}, // heart on fire, 3+3+3+4 {16, true, 4, 15}, // a + ideograph + ZWJ pair + z }; - UNSCOPED_INFO("every corpus entry is accounted for"); REQUIRE(corpus.size() == sizeof(expected) / sizeof(expected[0])); for (size_t c = 0; c < corpus.size(); ++c) { const std::string utf8 = to_utf8(corpus[c]); - const std::string tag = "corpus " + std::to_string(c); - UNSCOPED_INFO(tag + ": byte length"); + CAPTURE(c); REQUIRE(utf8.size() == expected[c].bytes); const auto runs = utf8str_find_emoji_clusters(utf8); - UNSCOPED_INFO(tag + ": run count"); REQUIRE(runs.size() == (expected[c].has_run ? size_t(1) : size_t(0))); if (expected[c].has_run) { - UNSCOPED_INFO(tag + ": run begin"); REQUIRE(runs[0].first == expected[c].begin); - UNSCOPED_INFO(tag + ": run end"); REQUIRE(runs[0].second == expected[c].end); } } @@ -2690,15 +2273,11 @@ namespace tut (llwchar)0x1F468, (llwchar)0x200D, (llwchar)0x1F469, (llwchar)'z' }; const std::string utf8 = to_utf8(wide); - UNSCOPED_INFO("byte length"); REQUIRE(utf8.size() == size_t(1 + 3 + 4 + 3 + 4 + 1)); const auto runs = utf8str_find_emoji_clusters(utf8); - UNSCOPED_INFO("one run"); REQUIRE(runs.size() == size_t(1)); - UNSCOPED_INFO("begins past 'a' and the ideograph"); CHECK(runs[0].first == size_t(4)); - UNSCOPED_INFO("ends before 'z'"); CHECK(runs[0].second == size_t(15)); } @@ -2708,11 +2287,11 @@ namespace tut TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: malformed UTF-8 neither stalls the walk nor invents a cluster", "[LLStringUTF]") { UNSCOPED_INFO("lone continuation"); - REQUIRE(utf8str_find_emoji_clusters("\x80\x80\x80").size() == size_t(0)); + CHECK(utf8str_find_emoji_clusters("\x80\x80\x80").size() == size_t(0)); UNSCOPED_INFO("truncated four-byte"); - REQUIRE(utf8str_find_emoji_clusters("\xF0\x9F\x91").size() == size_t(0)); + CHECK(utf8str_find_emoji_clusters("\xF0\x9F\x91").size() == size_t(0)); UNSCOPED_INFO("bad lead"); - REQUIRE(utf8str_find_emoji_clusters("\xFF\xFE").size() == size_t(0)); + CHECK(utf8str_find_emoji_clusters("\xFF\xFE").size() == size_t(0)); // A bad byte in front of a real cluster still leaves the cluster // findable, at the offset that byte pushed it to. @@ -2720,12 +2299,9 @@ namespace tut text += to_utf8(std::u32string{ (llwchar)0x1F468, (llwchar)0x200D, (llwchar)0x1F469 }); const auto runs = utf8str_find_emoji_clusters(text); - UNSCOPED_INFO("one run past the bad byte"); REQUIRE(runs.size() == size_t(1)); - UNSCOPED_INFO("begins at byte 1"); CHECK(runs[0].first == size_t(1)); - UNSCOPED_INFO("ends at the end"); - REQUIRE(runs[0].second == text.size()); + CHECK(runs[0].second == text.size()); } // utf8str_emoji_range_at answered at every position in the string -- @@ -2744,7 +2320,6 @@ namespace tut const std::string utf8 = to_utf8(wide); // a=0 heart=1 VS16=4 ideograph=7 man=10 ZWJ=14 woman=17 (c)=21 z=23. - UNSCOPED_INFO("layout"); REQUIRE(utf8.size() == size_t(24)); // The range every byte offset belongs to, written out. Inside a @@ -2766,39 +2341,32 @@ namespace tut for (const Expect& e : expected) { const auto got = utf8str_emoji_range_at(utf8, e.at); - UNSCOPED_INFO("range begin at " + std::to_string(e.at)); + CAPTURE(e.at); REQUIRE(got.first == e.begin); - UNSCOPED_INFO("range end at " + std::to_string(e.at)); REQUIRE(got.second == e.end); } // The heart carries a presentation selector, so it is a cluster and // its range spans both codepoints -- three bytes plus three. const auto heart = utf8str_emoji_range_at(utf8, 1); - UNSCOPED_INFO("heart begins at 1"); CHECK(heart.first == size_t(1)); - UNSCOPED_INFO("heart spans VS16"); CHECK(heart.second == size_t(7)); // The lone copyright sign is no cluster, but it is a pictograph base, // so it reports its own two bytes rather than an empty range. const size_t copy_at = 1 + 6 + 3 + 11; const auto copyright = utf8str_emoji_range_at(utf8, copy_at); - UNSCOPED_INFO("(c) begins"); CHECK(copyright.first == copy_at); - UNSCOPED_INFO("(c) is two bytes"); CHECK(copyright.second == copy_at + 2); // A position inside the ideograph is not a character start and so // belongs to nothing. const auto split = utf8str_emoji_range_at(utf8, 8); - UNSCOPED_INFO("mid-character is empty"); CHECK(split.first == split.second); // Past the end reports an empty range at the position asked about, // not at the end of the string. const auto past = utf8str_emoji_range_at(utf8, utf8.size() + 4); - UNSCOPED_INFO("past the end is empty"); CHECK(past.first == past.second); } @@ -2806,18 +2374,14 @@ namespace tut // compared against, so it has to agree with the UTF-32 form's size(). TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_codepoint_count agrees with the UTF-32 size", "[LLStringUTF]") { - UNSCOPED_INFO("empty"); CHECK(utf8str_codepoint_count("") == size_t(0)); - UNSCOPED_INFO("ascii"); CHECK(utf8str_codepoint_count("abc") == size_t(3)); const std::u32string wide = { (llwchar)'a', (llwchar)0x00E9, (llwchar)0x65E5, (llwchar)0x1F600, (llwchar)0x200D, (llwchar)0x1F600 }; const std::string utf8 = to_utf8(wide); - UNSCOPED_INFO("bytes"); - REQUIRE(utf8.size() == size_t(1 + 2 + 3 + 4 + 3 + 4)); - UNSCOPED_INFO("agrees with the wide size"); - REQUIRE(utf8str_codepoint_count(utf8) == wide.size()); + CHECK(utf8.size() == size_t(1 + 2 + 3 + 4 + 3 + 4)); + CHECK(utf8str_codepoint_count(utf8) == wide.size()); // Malformed bytes count one each, so the count never disagrees with a // walk over the same text. @@ -2838,9 +2402,7 @@ namespace tut const std::string utf8 = to_utf8(wide); // Bytes 1 + 2 + 3 + 4 + 1; code units 1 + 1 + 1 + 2 + 1. - UNSCOPED_INFO("bytes"); REQUIRE(utf8.size() == size_t(11)); - UNSCOPED_INFO("units"); CHECK(utf8str_utf16_length(utf8, 0, S32_MAX) == S32(6)); // Every prefix, by hand: the byte offset of each character start and @@ -2849,7 +2411,7 @@ namespace tut const S32 unit_at[] = { 0, 1, 2, 3, 5, 6 }; for (size_t cp = 0; cp < 6; ++cp) { - UNSCOPED_INFO("prefix at " + std::to_string(cp)); + CAPTURE(cp); REQUIRE(utf8str_utf16_length(utf8, 0, byte_at[cp]) == unit_at[cp]); } @@ -2859,21 +2421,17 @@ namespace tut for (S32 units = 0; units <= 6; ++units) { bool unaligned = false; - UNSCOPED_INFO("bytes for " + std::to_string(units) + " units"); + CAPTURE(units); REQUIRE(utf8str_length_from_utf16_length(utf8, 0, units, &unaligned) == bytes_for_units[units]); - UNSCOPED_INFO("unaligned only mid-pair"); REQUIRE(unaligned == (units == 4)); } // Offsets are honoured, not just whole-string calls. - UNSCOPED_INFO("offset form"); CHECK(utf8str_utf16_length(utf8, 3, S32_MAX) == S32(4)); // Out of range clamps rather than running off. - UNSCOPED_INFO("past the end"); CHECK(utf8str_utf16_length(utf8, 999, 999) == S32(0)); - UNSCOPED_INFO("no budget"); CHECK(utf8str_length_from_utf16_length(utf8, 0, 0) == S32(0)); } @@ -2883,21 +2441,17 @@ namespace tut TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: utf8str_sanitize returns valid text byte for byte", "[LLStringUTF]") { const std::string ascii = "plain ascii"; - UNSCOPED_INFO("ascii untouched"); CHECK(utf8str_sanitize(ascii) == ascii); const std::u32string wide = { (llwchar)'a', (llwchar)0x00E9, (llwchar)0x65E5, (llwchar)0x1F600, (llwchar)'z' }; const std::string mixed = to_utf8(wide); - UNSCOPED_INFO("mixed untouched"); CHECK(utf8str_sanitize(mixed) == mixed); - UNSCOPED_INFO("empty"); CHECK(utf8str_sanitize(std::string_view()) == std::string()); // Embedded NUL is valid UTF-8 and must survive; string_view carries it. const std::string with_nul("a\0b", 3); - UNSCOPED_INFO("nul survives"); CHECK(utf8str_sanitize(with_nul) == with_nul); } @@ -2930,18 +2484,12 @@ namespace tut REQUIRE_FALSE(is_valid(s)); // The predicate the widgets ask before deciding whether to repair // has to agree with simdutf, or a widget skips a repair it needed. - UNSCOPED_INFO("utf8str_is_valid agrees with simdutf"); REQUIRE(utf8str_is_valid(s) == is_valid(s)); const std::string fixed = utf8str_sanitize(s); - UNSCOPED_INFO("output is valid"); REQUIRE(is_valid(fixed)); - UNSCOPED_INFO("and the predicate says so too"); - REQUIRE(utf8str_is_valid(fixed) == true); - { - INFO("the good bytes are still there"); - REQUIRE(fixed.find("ok") == 0); - REQUIRE(fixed.rfind("ok") == fixed.size() - 2); - } + REQUIRE(utf8str_is_valid(fixed)); + REQUIRE(fixed.find("ok") == 0); + REQUIRE(fixed.rfind("ok") == fixed.size() - 2); } } @@ -2964,22 +2512,16 @@ namespace tut { const std::string s(c.bytes); const LLCodepointAt at = utf8str_decode_at(s, 0); - UNSCOPED_INFO(std::string(c.what) + " decodes to the replacement"); + INFO(c.what); REQUIRE((S32)at.cp == (S32)0xFFFD); - UNSCOPED_INFO(std::string(c.what) + " consumes exactly one byte"); REQUIRE((S32)at.next == 1); } // The shapes that are legal still decode whole. - UNSCOPED_INFO("ASCII"); CHECK((S32)utf8str_decode_at(std::string("A"), 0).cp == (S32)'A'); - UNSCOPED_INFO("two-byte"); CHECK((S32)utf8str_decode_at(std::string("\xC3\xA9"), 0).cp == (S32)0xE9); - UNSCOPED_INFO("three-byte"); CHECK((S32)utf8str_decode_at(std::string("\xE6\x97\xA5"), 0).cp == (S32)0x65E5); - UNSCOPED_INFO("four-byte"); CHECK((S32)utf8str_decode_at(std::string("\xF0\x9F\x90\xB6"), 0).cp == (S32)0x1F436); - UNSCOPED_INFO("four-byte spans four"); CHECK((S32)utf8str_decode_at(std::string("\xF0\x9F\x90\xB6"), 0).next == (S32)4); } @@ -2995,16 +2537,13 @@ namespace tut for (S32 budget = 1; budget <= 5; ++budget) { const std::string cut = utf8str_symbol_truncate(text, budget); - UNSCOPED_INFO("a budget under the first cluster still returns text"); + CAPTURE(budget); REQUIRE_FALSE(cut.empty()); - UNSCOPED_INFO("and returns the whole of that cluster"); REQUIRE(cut == family); } // Once the budget clears the cluster the next characters come too. - UNSCOPED_INFO("budget past the cluster takes the next character"); CHECK(utf8str_symbol_truncate(text, 6) == family + "a"); - UNSCOPED_INFO("input already short enough is untouched"); CHECK(utf8str_symbol_truncate(std::string("abc"), 10) == std::string("abc")); } @@ -3012,23 +2551,17 @@ namespace tut // never allowed cannot pass for the one it did. TEST_CASE_METHOD(llstring_utf_data, "LLStringUTF: LLStringUtil::isEqualInsensitiveASCII folds ASCII and nothing else", "[LLStringUTF]") { - UNSCOPED_INFO("same word, different case"); CHECK(LLStringUtil::isEqualInsensitiveASCII(std::string("llsd/binary"), std::string("LLSD/Binary"))); - UNSCOPED_INFO("a different length never matches"); CHECK_FALSE(LLStringUtil::isEqualInsensitiveASCII(std::string("llsd"), std::string("llsd/binary"))); // Fullwidth 'l' is its own character, whatever a collator makes of it. - UNSCOPED_INFO("fullwidth is not the ASCII letter"); CHECK_FALSE(LLStringUtil::isEqualInsensitiveASCII( std::string("\xEF\xBD\x8C\xEF\xBD\x8Csd/binary"), std::string("llsd/binary"))); // A soft hyphen is a character, not an absence of one. - UNSCOPED_INFO("an ignorable is still a difference"); CHECK_FALSE(LLStringUtil::isEqualInsensitiveASCII( std::string("llsd/bin\xC2\xAD" "ary"), std::string("llsd/binary"))); // Above ASCII nothing is folded -- these are simply different bytes. - UNSCOPED_INFO("no folding above ASCII"); CHECK_FALSE(LLStringUtil::isEqualInsensitiveASCII( std::string("caf\xC3\xA9"), std::string("caf\xC3\x89"))); - UNSCOPED_INFO("empty equals empty"); CHECK(LLStringUtil::isEqualInsensitiveASCII(std::string(), std::string())); } @@ -3040,42 +2573,29 @@ namespace tut { // ASCII first, so the byte answers can be read against the wide test. const std::string two_words = "foo bar"; - UNSCOPED_INFO("fwd to next word"); CHECK(utf8str_step_word_forward(two_words, 0) == size_t(4)); - UNSCOPED_INFO("fwd from next word"); CHECK(utf8str_step_word_forward(two_words, 4) == size_t(7)); - UNSCOPED_INFO("back from end"); CHECK(utf8str_step_word_backward(two_words, 7) == size_t(4)); - UNSCOPED_INFO("back over the space"); CHECK(utf8str_step_word_backward(two_words, 4) == size_t(0)); // And where a character is not a byte. "café bar": the accent makes // the second word start at byte 6 where it is codepoint 5, so these // numbers are wrong under any reading but the byte one. const std::string accented = "caf\xC3\xA9 bar"; - UNSCOPED_INFO("the fixture string is nine bytes"); REQUIRE(accented.size() == size_t(9)); - UNSCOPED_INFO("fwd over an accented word"); CHECK(utf8str_step_word_forward(accented, 0) == size_t(6)); - UNSCOPED_INFO("fwd from the last word runs to the end"); CHECK(utf8str_step_word_forward(accented, 6) == size_t(9)); - UNSCOPED_INFO("back from the end"); CHECK(utf8str_step_word_backward(accented, 9) == size_t(6)); - UNSCOPED_INFO("back over the space"); CHECK(utf8str_step_word_backward(accented, 6) == size_t(0)); // An offset inside the accent is still inside the first word. - UNSCOPED_INFO("stepping from inside a character"); CHECK(utf8str_step_word_forward(accented, 4) == size_t(6)); // The walkers stay on their line -- LLTextEditor::nextWordPos crosses // the break itself, and would stall on every newline if this changed. const std::string lines = "ab\ncd"; - UNSCOPED_INFO("fwd stops at eol"); CHECK(utf8str_step_word_forward(lines, 0) == size_t(2)); - UNSCOPED_INFO("fwd parked on eol"); CHECK(utf8str_step_word_forward(lines, 2) == size_t(2)); - UNSCOPED_INFO("back parked on bol"); CHECK(utf8str_step_word_backward(lines, 3) == size_t(3)); } @@ -3089,16 +2609,13 @@ namespace tut for (size_t at : { size_t(0), size_t(2), size_t(3), size_t(4) }) { const auto range = utf8str_word_range_at(accented, at); - UNSCOPED_INFO("word range begins at 0"); + CAPTURE(at); REQUIRE(range.first == size_t(0)); - UNSCOPED_INFO("word range ends after the accent"); REQUIRE(range.second == size_t(5)); } const auto second = utf8str_word_range_at(accented, 7); - UNSCOPED_INFO("second word begins at 6"); CHECK(second.first == size_t(6)); - UNSCOPED_INFO("second word ends at 9"); CHECK(second.second == size_t(9)); // next_word_range finds the word whose end lies past the position -- @@ -3106,24 +2623,18 @@ namespace tut // the following word. From inside the first word that is still the // first word. const auto from_start = utf8str_next_word_range(accented, 0); - UNSCOPED_INFO("from 0 the word is the one at 0"); CHECK(from_start.first == size_t(0)); - UNSCOPED_INFO("and it ends after the accent"); CHECK(from_start.second == size_t(5)); // Standing on the space, the answer is the word after it. const auto from_space = utf8str_next_word_range(accented, 5); - UNSCOPED_INFO("from the space the next word begins at 6"); CHECK(from_space.first == size_t(6)); - UNSCOPED_INFO("and ends at 9"); CHECK(from_space.second == size_t(9)); // Past the last word there is nothing further to select. const auto none = utf8str_next_word_range(accented, accented.size()); - UNSCOPED_INFO("nothing begins past the end"); - REQUIRE(none.first == accented.size()); - UNSCOPED_INFO("nothing ends past the end"); - REQUIRE(none.second == accented.size()); + CHECK(none.first == accented.size()); + CHECK(none.second == accented.size()); } // The caret forms differ from the raw walkers in exactly one way: they @@ -3135,51 +2646,40 @@ namespace tut const std::string lines = "one two\nthree four"; // Away from a break the two are the same walk. - UNSCOPED_INFO("forward agrees mid-line"); CHECK(utf8str_caret_word_forward(lines, 0) == utf8str_step_word_forward(lines, 0)); - UNSCOPED_INFO("backward agrees mid-line"); CHECK(utf8str_caret_word_backward(lines, 7) == utf8str_step_word_backward(lines, 7)); // A caret already at the edge of a word must not skip the adjacent // word, and a caret inside a word must land at that word's start. const std::string adjacent = "foo bar"; - UNSCOPED_INFO("forward from a word end reaches the adjacent word"); CHECK(utf8str_caret_word_forward(adjacent, 3) == size_t(4)); - UNSCOPED_INFO("backward from inside a word reaches its start"); CHECK(utf8str_caret_word_backward(adjacent, 5) == size_t(4)); // On the break the raw walker has nowhere to go and says so. - UNSCOPED_INFO("the raw walker stalls on the newline"); CHECK(utf8str_step_word_forward(lines, 7) == size_t(7)); - UNSCOPED_INFO("the caret walker crosses it"); CHECK(utf8str_caret_word_forward(lines, 7) > size_t(7)); // And at a line start, going back. - UNSCOPED_INFO("the raw walker stalls at the line start"); CHECK(utf8str_step_word_backward(lines, 8) == size_t(8)); - UNSCOPED_INFO("the caret walker crosses back"); CHECK(utf8str_caret_word_backward(lines, 8) < size_t(8)); // Neither runs off either end. - UNSCOPED_INFO("nothing before the beginning"); CHECK(utf8str_caret_word_backward(lines, 0) == size_t(0)); - UNSCOPED_INFO("nothing after the end"); - REQUIRE(utf8str_caret_word_forward(lines, lines.size()) == lines.size()); + CHECK(utf8str_caret_word_forward(lines, lines.size()) == lines.size()); // Repeated stepping terminates rather than parking. size_t at = 0; for (int i = 0; i < 32 && at < lines.size(); ++i) { const size_t next = utf8str_caret_word_forward(lines, at); - UNSCOPED_INFO("forward never moves backward"); + CAPTURE(at); REQUIRE(next >= at); if (next == at) break; at = next; } - UNSCOPED_INFO("stepping forward reaches the end"); - REQUIRE(at == lines.size()); + CHECK(at == lines.size()); } // --------------------------------------------------------------- @@ -3199,33 +2699,27 @@ namespace tut std::string s = nbsp + "Rye" + nbsp; LLStringUtil::trim(s); - UNSCOPED_INFO("a no-break space is whitespace"); CHECK(s == std::string("Rye")); s = ideo + cjk + ideo; LLStringUtil::trim(s); - UNSCOPED_INFO("an ideographic space is whitespace"); CHECK(s == cjk); s = em + narrow + " \tx\t " + narrow + em; LLStringUtil::trim(s); - UNSCOPED_INFO("ASCII and Unicode spaces mixed"); CHECK(s == std::string("x")); s = nbsp + "a"; LLStringUtil::trimHead(s); - UNSCOPED_INFO("trimHead crosses one"); CHECK(s == std::string("a")); s = "a" + nbsp; LLStringUtil::trimTail(s); - UNSCOPED_INFO("trimTail crosses one"); CHECK(s == std::string("a")); s = nbsp + ideo + em; LLStringUtil::trim(s); - UNSCOPED_INFO("all whitespace trims to nothing"); - REQUIRE(s.empty()); + CHECK(s.empty()); } // What is not White_Space stays, and malformed bytes are content: a trim @@ -3237,35 +2731,29 @@ namespace tut std::string s = zwsp + "a" + zwsp; LLStringUtil::trim(s); - UNSCOPED_INFO("a zero-width space is not whitespace"); CHECK(s == zwsp + "a" + zwsp); s = bom + "a" + bom; LLStringUtil::trim(s); - UNSCOPED_INFO("a byte order mark is not whitespace"); CHECK(s == bom + "a" + bom); s = std::string("\x80") + "a" + std::string("\x80"); LLStringUtil::trim(s); - UNSCOPED_INFO("a stray continuation byte is content"); - REQUIRE(s.size() == size_t(3)); + CHECK(s.size() == size_t(3)); // U+3000 missing its last byte. The lead byte it shares with a real // ideographic space must not be enough to cut it. s = std::string("a") + "\xE3\x80"; LLStringUtil::trim(s); - UNSCOPED_INFO("a truncated character is left whole"); - REQUIRE(s.size() == size_t(3)); + CHECK(s.size() == size_t(3)); s = " hello "; LLStringUtil::trim(s); - UNSCOPED_INFO("ASCII trims as it always did"); CHECK(s == std::string("hello")); s.clear(); LLStringUtil::trim(s); - UNSCOPED_INFO("an empty string survives"); - REQUIRE(s.empty()); + CHECK(s.empty()); } // The view forms cut where the string forms cut, and utf8str_trim shares @@ -3277,15 +2765,12 @@ namespace tut std::string owned = src; LLStringUtil::trim(owned); - UNSCOPED_INFO("the string form"); CHECK(owned == expected); std::string_view view = src; LLStringUtil::trim(view); - UNSCOPED_INFO("the view form cuts the same place"); CHECK(std::string(view) == expected); - UNSCOPED_INFO("utf8str_trim agrees"); CHECK(utf8str_trim(src) == expected); } @@ -3305,15 +2790,10 @@ namespace tut { stepped.push_back(at); } - UNSCOPED_INFO("the same stops"); - REQUIRE(utf8str_grapheme_starts(utf8, utf8.size()) == stepped); - UNSCOPED_INFO("those before the end asked for"); + CHECK(utf8str_grapheme_starts(utf8, utf8.size()) == stepped); CHECK(utf8str_grapheme_starts(utf8, 11) == std::vector({ 0, 1, 2, 3, 6, 9, 10 })); - { - INFO("nothing of nothing"); - REQUIRE(utf8str_grapheme_starts(std::string(), 5).empty()); - REQUIRE(utf8str_grapheme_starts(utf8, 0).empty()); - } + CHECK(utf8str_grapheme_starts(std::string(), 5).empty()); + CHECK(utf8str_grapheme_starts(utf8, 0).empty()); } // Every word at once is every word utf8str_next_word_range finds one at @@ -3350,12 +2830,11 @@ namespace tut } std::vector> all = { { 9, 9 } }; utf8str_word_ranges(text, all); - UNSCOPED_INFO("the same words in \"" + text + "\""); + CAPTURE(text); REQUIRE(all == stepped); } std::vector> words; utf8str_word_ranges("don't stop", words); - UNSCOPED_INFO("a contraction is one word"); CHECK(words == std::vector>({ { 0, 5 }, { 6, 10 } })); } } diff --git a/indra/llcommon/tests/lltimer_test.cpp b/indra/llcommon/tests/lltimer_test.cpp index 7f791c67bf..ac93d5fe40 100644 --- a/indra/llcommon/tests/lltimer_test.cpp +++ b/indra/llcommon/tests/lltimer_test.cpp @@ -46,10 +46,8 @@ namespace TEST_CASE_METHOD(timer_data, "LLTimer: a new timer reads expired until it is given an expiry", "[LLTimer]") { LLTimer timer; - UNSCOPED_INFO("expired"); CHECK(timer.hasExpired()); timer.setTimerExpirySec(10.f); - UNSCOPED_INFO("not expired once given ten seconds"); CHECK_FALSE(timer.hasExpired()); } @@ -58,11 +56,8 @@ namespace LLTimer timer; ms_sleep(50); timer.resetWithExpiry(10.f); - UNSCOPED_INFO("not expired"); CHECK_FALSE(timer.hasExpired()); - UNSCOPED_INFO("the whole expiry to run"); CHECK(timer.getRemainingTimeF32() > 9.9f); - UNSCOPED_INFO("restarted"); CHECK(timer.getElapsedTimeF32() < 0.04f); } @@ -71,7 +66,6 @@ namespace LLTimer timer; timer.setTimerExpirySec(10.f); timer.start(); - UNSCOPED_INFO("start() cleared the expiry"); CHECK(timer.hasExpired()); } @@ -80,7 +74,6 @@ namespace LLTimer timer; ms_sleep(50); timer.reset(); - UNSCOPED_INFO("the timer was just reset"); CHECK(timer.getElapsedTimeF64() < 0.04); UNSCOPED_INFO("the uptime runs from before the reset"); CHECK(LLTimer::getUptimeSeconds() > timer.getElapsedTimeF64()); @@ -91,7 +84,6 @@ namespace const auto calendar = std::chrono::system_clock::now().time_since_epoch(); const F64 calendar_seconds = std::chrono::duration(calendar).count(); const F64 total = LLTimer::getTotalSeconds(); - UNSCOPED_INFO("anchored to the calendar"); CHECK(std::fabs(total - calendar_seconds) < 1.0); const auto steady_start = std::chrono::steady_clock::now(); @@ -99,7 +91,6 @@ namespace ms_sleep(200); const F64 steady = std::chrono::duration(std::chrono::steady_clock::now() - steady_start).count(); const F64 counted = (F64)((U64)totalTime() - total_start) / 1000000.0; - UNSCOPED_INFO("counts as the steady clock does"); CHECK(std::fabs(counted - steady) < 0.005); } @@ -131,9 +122,7 @@ namespace } const F64 steady = std::chrono::duration(std::chrono::steady_clock::now() - steady_start).count(); const F64 counted = (F64)((U64)totalTime() - total_start) / 1000000.0; - UNSCOPED_INFO("no thread saw it step back"); CHECK_FALSE(stepped_back); - UNSCOPED_INFO("counted no faster than the steady clock"); CHECK(counted < steady + 0.005); } } diff --git a/indra/llcommon/tests/lltrace_test.cpp b/indra/llcommon/tests/lltrace_test.cpp index fd1a42c0c6..3e9e12e749 100644 --- a/indra/llcommon/tests/lltrace_test.cpp +++ b/indra/llcommon/tests/lltrace_test.cpp @@ -63,29 +63,29 @@ namespace { using namespace LLTrace; - // A ThreadRecorder makes itself this thread's recorder, with its own - // timer stack and current accumulators, and leaves all of them null - // when it goes. This takes the main thread's as they were before the - // test's recorder, and puts them back after it. - struct ThreadRecorderRestore + // A ThreadRecorder makes itself this thread's recorder, with its own + // timer stack and current accumulators, and leaves all of them null + // when it goes. This takes the main thread's as they were before the + // test's recorder, and puts them back after it. + struct ThreadRecorderRestore + { + ThreadRecorder* mRecorder{ get_thread_recorder() }; + BlockTimerStackRecord* mTimerStack{ LLThreadLocalSingletonPointer::getInstance() }; + CountAccumulator* mCounts{ LLThreadLocalSingletonPointer::getInstance() }; + SampleAccumulator* mSamples{ LLThreadLocalSingletonPointer::getInstance() }; + EventAccumulator* mEvents{ LLThreadLocalSingletonPointer::getInstance() }; + TimeBlockAccumulator* mTimers{ LLThreadLocalSingletonPointer::getInstance() }; + + ~ThreadRecorderRestore() { - ThreadRecorder* mRecorder{ get_thread_recorder() }; - BlockTimerStackRecord* mTimerStack{ LLThreadLocalSingletonPointer::getInstance() }; - CountAccumulator* mCounts{ LLThreadLocalSingletonPointer::getInstance() }; - SampleAccumulator* mSamples{ LLThreadLocalSingletonPointer::getInstance() }; - EventAccumulator* mEvents{ LLThreadLocalSingletonPointer::getInstance() }; - TimeBlockAccumulator* mTimers{ LLThreadLocalSingletonPointer::getInstance() }; - - ~ThreadRecorderRestore() - { - set_thread_recorder(mRecorder); - LLThreadLocalSingletonPointer::setInstance(mTimerStack); - LLThreadLocalSingletonPointer::setInstance(mCounts); - LLThreadLocalSingletonPointer::setInstance(mSamples); - LLThreadLocalSingletonPointer::setInstance(mEvents); - LLThreadLocalSingletonPointer::setInstance(mTimers); - } - }; + set_thread_recorder(mRecorder); + LLThreadLocalSingletonPointer::setInstance(mTimerStack); + LLThreadLocalSingletonPointer::setInstance(mCounts); + LLThreadLocalSingletonPointer::setInstance(mSamples); + LLThreadLocalSingletonPointer::setInstance(mEvents); + LLThreadLocalSingletonPointer::setInstance(mTimers); + } + }; struct trace { diff --git a/indra/llcommon/tests/lltreeiterators_test.cpp b/indra/llcommon/tests/lltreeiterators_test.cpp index 3288ca1db3..b1bc03d3f1 100644 --- a/indra/llcommon/tests/lltreeiterators_test.cpp +++ b/indra/llcommon/tests/lltreeiterators_test.cpp @@ -244,10 +244,8 @@ namespace pni != end; ++pni) { // std::cout << (*pni)->name() << '\n'; - UNSCOPED_INFO("LLLinkedIter(NULL)"); - REQUIRE(false); + FAIL("LLLinkedIter(NULL) should not iterate"); } - UNSCOPED_INFO(desc1); CHECK(verify(desc1, boost::make_iterator_range(LLLinkedIter(first, boost::bind(&PlainNode::mNext, _1)), @@ -257,7 +255,6 @@ namespace // std::cout << desc2 << ":\n"; // for (LLLinkedIter pni(first, boost::bind(&PlainNode::next, _1)); ! (pni == end); ++pni) // std::cout << (**pni).name() << '\n'; - UNSCOPED_INFO(desc2); CHECK(verify(desc2, boost::make_iterator_range(LLLinkedIter(first, boost::bind(&PlainNode::next, _1)), @@ -270,9 +267,7 @@ namespace } { LLLinkedIter pni(first, boost::bind(&PlainNode::next, _1)); - UNSCOPED_INFO("first"); CHECK((*pni++)->name() == "a"); - UNSCOPED_INFO("second"); CHECK((*pni )->name() == "b"); } } @@ -346,15 +341,15 @@ namespace } // std::cout << "Now the LLLinkedIter is the only remaining reference to RCNode chain\n"; CHECK(last_RCNode_destroyed == ""); - CHECK(rcni != end2); + REQUIRE(rcni != end2); CHECK((*rcni)->name() == "x"); ++rcni; CHECK(last_RCNode_destroyed == "x"); - CHECK(rcni != end2); + REQUIRE(rcni != end2); CHECK((*rcni)->name() == "y"); ++rcni; CHECK(last_RCNode_destroyed == "y"); - CHECK(rcni != end2); + REQUIRE(rcni != end2); CHECK((*rcni)->name() == "z"); ++rcni; CHECK(last_RCNode_destroyed == "z"); @@ -1126,7 +1121,6 @@ namespace { // std::cout << node->name() << '\n'; } - UNSCOPED_INFO(desc1); CHECK(verify(desc1, getRootRange(tnB2b), RootExpected())); EnhancedTreeNodePtr etnroot(example_tree()); @@ -1151,7 +1145,6 @@ namespace { // std::cout << node->name() << '\n'; } - UNSCOPED_INFO(desc2); CHECK(verify(desc2, etnB2b->getRootRange(), RootExpected())); } @@ -1165,17 +1158,14 @@ namespace // TreeNode, using helper function TreeNodePtr tnroot(example_tree()); std::string desc_tnpre("getWalkRange(tnroot)"); - UNSCOPED_INFO(desc_tnpre); CHECK(verify(desc_tnpre, getWalkRange(tnroot), WalkExpected())); std::string desc_tnpost("getWalkRange(tnroot)"); - UNSCOPED_INFO(desc_tnpost); CHECK(verify(desc_tnpost, getWalkRange(tnroot), WalkExpected())); std::string desc_tnb("getWalkRange(tnroot)"); - UNSCOPED_INFO(desc_tnb); CHECK(verify(desc_tnb, getWalkRange(tnroot), WalkExpected())); @@ -1183,17 +1173,14 @@ namespace // EnhancedTreeNode, using method EnhancedTreeNodePtr etnroot(example_tree()); std::string desc_etnpre("etnroot->getWalkRange()"); - UNSCOPED_INFO(desc_etnpre); CHECK(verify(desc_etnpre, etnroot->getWalkRange(), WalkExpected())); std::string desc_etnpost("etnroot->getWalkRange()"); - UNSCOPED_INFO(desc_etnpost); CHECK(verify(desc_etnpost, etnroot->getWalkRange(), WalkExpected())); std::string desc_etnb("etnroot->getWalkRange()"); - UNSCOPED_INFO(desc_etnb); CHECK(verify(desc_etnb, etnroot->getWalkRange(), WalkExpected())); @@ -1202,17 +1189,14 @@ namespace PlainTree* ptroot(example_tree()); Cleanup cleanup(ptroot); std::string desc_ptpre("getWalkRange(ptroot)"); - UNSCOPED_INFO(desc_ptpre); CHECK(verify(desc_ptpre, getWalkRange(ptroot), WalkExpected())); std::string desc_ptpost("getWalkRange(ptroot)"); - UNSCOPED_INFO(desc_ptpost); CHECK(verify(desc_ptpost, getWalkRange(ptroot), WalkExpected())); std::string desc_ptb("getWalkRange(ptroot)"); - UNSCOPED_INFO(desc_ptb); CHECK(verify(desc_ptb, getWalkRange(ptroot), WalkExpected())); diff --git a/indra/llcommon/tests/llunits_test.cpp b/indra/llcommon/tests/llunits_test.cpp index 1f3d743e18..1277e22cbf 100644 --- a/indra/llcommon/tests/llunits_test.cpp +++ b/indra/llcommon/tests/llunits_test.cpp @@ -68,35 +68,27 @@ namespace TEST_CASE("LLUnit: units convert between storage types and integers truncate", "[LLUnit]") { LLUnit float_quatloos; - UNSCOPED_INFO("default float unit is zero"); CHECK(float_quatloos == F32Quatloos(0.f)); LLUnit float_initialize_quatloos(1); - UNSCOPED_INFO("non-zero initialized unit"); CHECK(float_initialize_quatloos == F32Quatloos(1.f)); LLUnit int_quatloos; - UNSCOPED_INFO("default int unit is zero"); CHECK(int_quatloos == S32Quatloos(0)); int_quatloos = S32Quatloos(42); - UNSCOPED_INFO("int assignment is preserved"); CHECK(int_quatloos == S32Quatloos(42)); float_quatloos = int_quatloos; - UNSCOPED_INFO("float assignment from int preserves value"); CHECK(float_quatloos == F32Quatloos(42.f)); int_quatloos = float_quatloos; - UNSCOPED_INFO("int assignment from float preserves value"); CHECK(int_quatloos == S32Quatloos(42)); float_quatloos = F32Quatloos(42.1f); int_quatloos = float_quatloos; - UNSCOPED_INFO("int units truncate float units on assignment"); CHECK(int_quatloos == S32Quatloos(42)); LLUnit unsigned_int_quatloos(float_quatloos); - UNSCOPED_INFO("unsigned int can be initialized from signed int"); CHECK(unsigned_int_quatloos == S32Quatloos(42)); S32Solari int_solari(1); @@ -116,12 +108,10 @@ namespace { LLUnit quatloos(1.f); LLUnit latinum_bars(quatloos); - UNSCOPED_INFO("conversion between units is automatic via initialization"); CHECK(latinum_bars == F32Latinum(1.f / 4.f)); latinum_bars = S32Latinum(256); quatloos = latinum_bars; - UNSCOPED_INFO("conversion between units is automatic via assignment, and bidirectional"); CHECK(quatloos == S32Quatloos(1024)); LLUnit single_quatloo(1); @@ -139,7 +129,6 @@ namespace CHECK(solari == S32Solari(4096)); LLUnit latinum_bars = solari; - UNSCOPED_INFO("Non base units can be converted between each other"); CHECK(latinum_bars == S32Latinum(256)); } @@ -195,22 +184,14 @@ namespace TEST_CASE("LLUnit: units compare across storage types and units", "[LLUnit]") { LLUnit quatloos(1); - UNSCOPED_INFO("can perform less than comparison against same type"); CHECK(quatloos < S32Quatloos(2)); - UNSCOPED_INFO("can perform less than comparison against different storage type"); CHECK(quatloos < F32Quatloos(2.f)); - UNSCOPED_INFO("can perform less than comparison against different units"); CHECK(quatloos < S32Latinum(5)); - UNSCOPED_INFO("can perform less than comparison against different storage type and units"); CHECK(quatloos < F32Latinum(5.f)); - UNSCOPED_INFO("can perform greater than comparison against same type"); CHECK(quatloos > S32Quatloos(0)); - UNSCOPED_INFO("can perform greater than comparison against different storage type"); CHECK(quatloos > F32Quatloos(0.f)); - UNSCOPED_INFO("can perform greater than comparison against different units"); CHECK(quatloos > S32Latinum(0)); - UNSCOPED_INFO("can perform greater than comparison against different storage type and units"); CHECK(quatloos > F32Latinum(0.f)); } @@ -224,9 +205,7 @@ namespace TEST_CASE("LLUnit: unit values pass as function arguments", "[LLUnit]") { S32Quatloos quatloos(1); - UNSCOPED_INFO("can pass unit values as argument"); CHECK(accept_explicit_quatloos(S32Quatloos(1))); - UNSCOPED_INFO("can pass unit values as argument"); CHECK(accept_explicit_quatloos(quatloos)); } @@ -235,14 +214,11 @@ namespace { LLUnit quatloos; LLUnitImplicit quatloos_implicit = quatloos + S32Quatloos(1); - UNSCOPED_INFO("can initialize implicit unit from explicit"); CHECK(quatloos_implicit == 1); quatloos = quatloos_implicit; - UNSCOPED_INFO("can assign implicit unit to explicit unit"); CHECK(quatloos == S32Quatloos(1)); quatloos += quatloos_implicit; - UNSCOPED_INFO("can perform math operation using mixture of implicit and explicit units"); CHECK(quatloos == S32Quatloos(2)); // math operations on implicits @@ -275,42 +251,29 @@ namespace // implicit conversion to POD F32 float_val = quatloos_implicit; - UNSCOPED_INFO("implicit units convert implicitly to regular values"); CHECK(float_val == 16); S32 int_val = (S32)quatloos_implicit; - UNSCOPED_INFO("implicit units convert implicitly to regular values"); CHECK(int_val == 16); // conversion of implicits LLUnitImplicit latinum_implicit(2); - UNSCOPED_INFO("implicit units of different types are comparable"); CHECK(latinum_implicit * 2 == quatloos_implicit); quatloos_implicit += F32Quatloos(10); - UNSCOPED_INFO("can add-assign explicit units"); CHECK(quatloos_implicit == 26); quatloos_implicit -= F32Quatloos(10); - UNSCOPED_INFO("can subtract-assign explicit units"); CHECK(quatloos_implicit == 16); // comparisons - UNSCOPED_INFO("can compare greater than implicit unit"); CHECK(quatloos_implicit > F32QuatloosImplicit(0.f)); - UNSCOPED_INFO("can compare greater than non-implicit unit"); CHECK(quatloos_implicit > F32Quatloos(0.f)); - UNSCOPED_INFO("can compare greater than or equal to implicit unit"); CHECK(quatloos_implicit >= F32QuatloosImplicit(0.f)); - UNSCOPED_INFO("can compare greater than or equal to non-implicit unit"); CHECK(quatloos_implicit >= F32Quatloos(0.f)); - UNSCOPED_INFO("can compare less than implicit unit"); CHECK(quatloos_implicit < F32QuatloosImplicit(20.f)); - UNSCOPED_INFO("can compare less than non-implicit unit"); CHECK(quatloos_implicit < F32Quatloos(20.f)); - UNSCOPED_INFO("can compare less than or equal to implicit unit"); CHECK(quatloos_implicit <= F32QuatloosImplicit(20.f)); - UNSCOPED_INFO("can compare less than or equal to non-implicit unit"); CHECK(quatloos_implicit <= F32Quatloos(20.f)); } @@ -323,11 +286,9 @@ namespace CHECK(mega_bytes == (S32Megabytes)4095); mega_bytes = (S32Megabytes)-5 + (U32Megabytes)1; - UNSCOPED_INFO("can mix signed and unsigned in units addition"); CHECK(mega_bytes == (S32Megabytes)-4); mega_bytes = (U32Megabytes)5 + (S32Megabytes)-1; - UNSCOPED_INFO("can mix unsigned and signed in units addition"); CHECK(mega_bytes == (S32Megabytes)4); } @@ -339,19 +300,14 @@ namespace U32Kilobytes KB(GB); U32Bytes B(GB); - UNSCOPED_INFO("GB -> MB conversion"); CHECK(MB.value() == 1024); - UNSCOPED_INFO("GB -> KB conversion"); CHECK(KB.value() == 1024 * 1024); - UNSCOPED_INFO("GB -> B conversion"); CHECK(B.value() == 1024 * 1024 * 1024); KB = U32Kilobytes(1); U32Kilobits Kb(KB); U32Bits b(KB); - UNSCOPED_INFO("KB -> Kb conversion"); CHECK(Kb.value() == 8); - UNSCOPED_INFO("KB -> b conversion"); CHECK(b.value() == 8 * 1024); U32Days days(1); @@ -360,13 +316,9 @@ namespace U32Seconds seconds(days); U32Milliseconds ms(days); - UNSCOPED_INFO("days -> hours conversion"); CHECK(hours.value() == 24); - UNSCOPED_INFO("days -> minutes conversion"); CHECK(minutes.value() == 24 * 60); - UNSCOPED_INFO("days -> seconds conversion"); CHECK(seconds.value() == 24 * 60 * 60); - UNSCOPED_INFO("days -> ms conversion"); CHECK(ms.value() == 24 * 60 * 60 * 1000); U32Kilometers km(1); @@ -374,11 +326,8 @@ namespace U32Centimeters cm(km); U32Millimeters mm(km); - UNSCOPED_INFO("km -> m conversion"); CHECK(m.value() == 1000); - UNSCOPED_INFO("km -> cm conversion"); CHECK(cm.value() == 1000 * 100); - UNSCOPED_INFO("km -> mm conversion"); CHECK(mm.value() == 1000 * 1000); U32Gigahertz GHz(1); @@ -386,26 +335,20 @@ namespace U32Kilohertz KHz(GHz); U32Hertz Hz(GHz); - UNSCOPED_INFO("GHz -> MHz conversion"); CHECK(MHz.value() == 1000); - UNSCOPED_INFO("GHz -> KHz conversion"); CHECK(KHz.value() == 1000 * 1000); - UNSCOPED_INFO("GHz -> Hz conversion"); CHECK(Hz.value() == 1000 * 1000 * 1000); F32Radians rad(6.2831853071795f); S32Degrees deg(rad); - UNSCOPED_INFO("radians -> degrees conversion"); CHECK(deg.value() == 360); F32Percent percent(50); F32Ratio ratio(percent); - UNSCOPED_INFO("percent -> ratio conversion"); CHECK(ratio.value() == 0.5f); U32Kilotriangles ktris(1); U32Triangles tris(ktris); - UNSCOPED_INFO("kilotriangles -> triangles conversion"); CHECK(tris.value() == 1000); } @@ -419,20 +362,16 @@ namespace { F32Celcius float_celcius(100); F32Fahrenheit float_fahrenheit(float_celcius); - UNSCOPED_INFO("floating point celcius -> fahrenheit conversion using linear transform"); CHECK(value_near(float_fahrenheit.value(), 212, 0.1f)); float_celcius = float_fahrenheit; - UNSCOPED_INFO("floating point fahrenheit -> celcius conversion using linear transform (round trip)"); CHECK(value_near(float_celcius.value(), 100.f, 0.1f)); S32Celcius int_celcius(100); S32Fahrenheit int_fahrenheit(int_celcius); - UNSCOPED_INFO("integer celcius -> fahrenheit conversion using linear transform"); CHECK(int_fahrenheit.value() == 212); int_celcius = int_fahrenheit; - UNSCOPED_INFO("integer fahrenheit -> celcius conversion using linear transform (round trip)"); CHECK(int_celcius.value() == 100); } } diff --git a/indra/llcommon/tests/lluri_test.cpp b/indra/llcommon/tests/lluri_test.cpp index a3544df2b6..f364a5a995 100644 --- a/indra/llcommon/tests/lluri_test.cpp +++ b/indra/llcommon/tests/lluri_test.cpp @@ -35,33 +35,27 @@ namespace { struct URITestData { - void checkParts(const LLURI& u, + void require_parts(const LLURI& u, const char* expectedScheme, const char* expectedOpaque, const char* expectedAuthority, const char* expectedPath, const char* expectedQuery = "") { - UNSCOPED_INFO("scheme"); + INFO("uri " << u.asString()); REQUIRE(u.scheme() == expectedScheme); - UNSCOPED_INFO("opaque"); REQUIRE(u.opaque() == expectedOpaque); - UNSCOPED_INFO("authority"); REQUIRE(u.authority() == expectedAuthority); - UNSCOPED_INFO("path"); REQUIRE(u.path() == expectedPath); - UNSCOPED_INFO("query"); REQUIRE(u.query() == expectedQuery); } - void escapeRoundTrip(const std::string& uri_raw_1) + void require_escape_round_trip(const std::string& uri_raw_1) { std::string uri_esc_1(LLURI::escape(uri_raw_1)); std::string uri_raw_2(LLURI::unescape(uri_esc_1)); - UNSCOPED_INFO("escape/unescape raw"); REQUIRE(uri_raw_2 == uri_raw_1); std::string uri_esc_2(LLURI::escape(uri_raw_2)); - UNSCOPED_INFO("escape/unescape escaped"); REQUIRE(uri_esc_2 == uri_esc_1); } }; @@ -70,91 +64,81 @@ namespace { LLURI u("http://abc.com/def/ghi?x=37&y=hello"); - UNSCOPED_INFO("scheme"); CHECK(u.scheme() == "http"); - UNSCOPED_INFO("authority"); CHECK(u.authority() == "abc.com"); - UNSCOPED_INFO("path"); CHECK(u.path() == "/def/ghi"); - UNSCOPED_INFO("query"); CHECK(u.query() == "x=37&y=hello"); - UNSCOPED_INFO("host name"); CHECK(u.hostName() == "abc.com"); - UNSCOPED_INFO("host port"); CHECK(u.hostPort() == 80); LLSD query = u.queryMap(); - UNSCOPED_INFO("query x"); CHECK(query["x"].asInteger() == 37); - UNSCOPED_INFO("query y"); CHECK(query["y"].asString() == "hello"); query = LLURI::queryMap("x=22.23&y=https://lindenlab.com/"); - UNSCOPED_INFO("query x"); CHECK(query["x"].asReal() == 22.23); - UNSCOPED_INFO("query y"); CHECK(query["y"].asURI().asString() == "https://lindenlab.com/"); } TEST_CASE_METHOD(URITestData, "LLURI: an empty string has no parts", "[LLURI]") { - checkParts(LLURI(""), "", "", "", ""); + require_parts(LLURI(""), "", "", "", ""); } TEST_CASE_METHOD(URITestData, "LLURI: without a scheme the whole string is opaque", "[LLURI]") { - checkParts(LLURI("foo"), "", "foo", "", ""); - checkParts(LLURI("foo%3A"), "", "foo:", "", ""); + require_parts(LLURI("foo"), "", "foo", "", ""); + require_parts(LLURI("foo%3A"), "", "foo:", "", ""); } TEST_CASE_METHOD(URITestData, "LLURI: a scheme without paths leaves no authority or path", "[LLURI]") { - checkParts(LLURI("mailto:zero@ll.com"), + require_parts(LLURI("mailto:zero@ll.com"), "mailto", "zero@ll.com", "", ""); - checkParts(LLURI("silly://abc/def?foo"), + require_parts(LLURI("silly://abc/def?foo"), "silly", "//abc/def?foo", "", ""); } TEST_CASE_METHOD(URITestData, "LLURI: the authority ends at the first unescaped slash or question mark", "[LLURI]") { - checkParts(LLURI("http:///"), + require_parts(LLURI("http:///"), "http", "///", "", "/"); - checkParts(LLURI("http://abc"), + require_parts(LLURI("http://abc"), "http", "//abc", "abc", ""); - checkParts(LLURI("http://a%2Fb/cd"), + require_parts(LLURI("http://a%2Fb/cd"), "http", "//a/b/cd", "a/b", "/cd"); - checkParts(LLURI("http://host?"), + require_parts(LLURI("http://host?"), "http", "//host?", "host", ""); } TEST_CASE_METHOD(URITestData, "LLURI: the path is unescaped and keeps colons and trailing slashes", "[LLURI]") { - checkParts(LLURI("http://host/a/b/"), + require_parts(LLURI("http://host/a/b/"), "http", "//host/a/b/", "host", "/a/b/"); - checkParts(LLURI("http://host/a%3Fb/"), + require_parts(LLURI("http://host/a%3Fb/"), "http", "//host/a?b/", "host", "/a?b/"); - checkParts(LLURI("http://host/a:b/"), + require_parts(LLURI("http://host/a:b/"), "http", "//host/a:b/", "host", "/a:b/"); } TEST_CASE_METHOD(URITestData, "LLURI: the query follows the first question mark and is unescaped", "[LLURI]") { - checkParts(LLURI("http://host/?"), + require_parts(LLURI("http://host/?"), "http", "//host/?", "host", "/", ""); - checkParts(LLURI("http://host/?x"), + require_parts(LLURI("http://host/?x"), "http", "//host/?x", "host", "/", "x"); - checkParts(LLURI("http://host/??"), + require_parts(LLURI("http://host/??"), "http", "//host/??", "host", "/", "?"); - checkParts(LLURI("http://host/?%3F"), + require_parts(LLURI("http://host/?%3F"), "http", "//host/??", "host", "/", "?"); } @@ -163,13 +147,13 @@ namespace LLSD path; path.append("x"); path.append("123"); - checkParts(LLURI::buildHTTP("host", path), + require_parts(LLURI::buildHTTP("host", path), "http", "//host/x/123", "host", "/x/123"); LLSD query; query["123"] = "12"; query["abcd"] = "abc"; - checkParts(LLURI::buildHTTP("host", path, query), + require_parts(LLURI::buildHTTP("host", path, query), "http", "//host/x/123?123=12&abcd=abc", "host", "/x/123", "123=12&abcd=abc"); @@ -204,7 +188,7 @@ namespace LLSD path; path.append("x@*//*$&^"); path.append("123"); - checkParts(LLURI::buildHTTP("host", path), + require_parts(LLURI::buildHTTP("host", path), "http", "//host/x@*//*$&^/123", "host", "/x@*//*$&^/123"); } @@ -216,7 +200,7 @@ namespace LLSD query; query["123"] = "?&*#//"; query["**@&?//"] = "abc"; - checkParts(LLURI::buildHTTP("host", path, query), + require_parts(LLURI::buildHTTP("host", path, query), "http", "//host/x/123?**@&?//=abc&123=?&*#//", "host", "/x/123", "**@&?//=abc&123=?&*#//"); } @@ -229,22 +213,22 @@ namespace LLSD query; query["123"] = "12"; query["abcd"] = "abc"; - checkParts(LLURI::buildHTTP("hi123*33--}{:portstuffs", path, query), + require_parts(LLURI::buildHTTP("hi123*33--}{:portstuffs", path, query), "http", "//hi123*33--}{:portstuffs/x/123?123=12&abcd=abc", "hi123*33--}{:portstuffs", "/x/123", "123=12&abcd=abc"); } TEST_CASE_METHOD(URITestData, "LLURI: buildHTTP takes a host_port that is really a URI prefix", "[LLURI]") { - checkParts(LLURI::buildHTTP("http://example.com:8080", LLSD()), + require_parts(LLURI::buildHTTP("http://example.com:8080", LLSD()), "http", "//example.com:8080", "example.com:8080", ""); - checkParts(LLURI::buildHTTP("http://example.com:8080/", LLSD()), + require_parts(LLURI::buildHTTP("http://example.com:8080/", LLSD()), "http", "//example.com:8080/", "example.com:8080", "/"); - checkParts(LLURI::buildHTTP("http://example.com:8080/a/b", LLSD()), + require_parts(LLURI::buildHTTP("http://example.com:8080/a/b", LLSD()), "http", "//example.com:8080/a/b", "example.com:8080", "/a/b"); } @@ -255,9 +239,7 @@ namespace "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" "0123456789" "-._~"; - UNSCOPED_INFO("escaping"); CHECK(LLURI::escape("abcdefg", "abcdef") == "abcdef%67"); - UNSCOPED_INFO("escaping"); CHECK(LLURI::escape("|/&\\+-_!@", "") == "%7C%2F%26%5C%2B%2D%5F%21%40"); UNSCOPED_INFO("escaping as query variable"); CHECK(LLURI::escape("http://10.0.1.4:12032/agent/god/agent-id/map/layer/?resume=http://station3.ll.com:12032/agent/203ad6df-b522-491d-ba48-4e24eb57aeff/send-postcard", unreserved + ":@!$'()*+,=") == @@ -271,7 +253,6 @@ namespace std::string cedilla; cedilla.push_back( (char)0xC3 ); cedilla.push_back( (char)0xA7 ); - UNSCOPED_INFO("escape UTF8"); CHECK(LLURI::escape( cedilla, unreserved) == "%C3%A7"); } @@ -279,38 +260,34 @@ namespace TEST_CASE_METHOD(URITestData, "LLURI: escape and unescape of an empty string are empty", "[LLURI]") { std::string uri_esc(LLURI::escape("")); - UNSCOPED_INFO("escape string empty"); - REQUIRE(uri_esc.empty()); + CHECK(uri_esc.empty()); std::string uri_raw(LLURI::unescape("")); - UNSCOPED_INFO("unescape string empty"); - REQUIRE(uri_raw.empty()); + CHECK(uri_raw.empty()); } TEST_CASE_METHOD(URITestData, "LLURI: escape and unescape round-trip URIs", "[LLURI]") { - escapeRoundTrip("http://secondlife.com"); - escapeRoundTrip("http://secondlife.com/url with spaces"); - escapeRoundTrip("http://bad[domain]name.com/"); - escapeRoundTrip("ftp://bill.gates@ms/micro$oft.com/c:\\autoexec.bat"); - escapeRoundTrip(""); + require_escape_round_trip("http://secondlife.com"); + require_escape_round_trip("http://secondlife.com/url with spaces"); + require_escape_round_trip("http://bad[domain]name.com/"); + require_escape_round_trip("ftp://bill.gates@ms/micro$oft.com/c:\\autoexec.bat"); + require_escape_round_trip(""); } TEST_CASE_METHOD(URITestData, "LLURI: escape by default escapes colons and slashes too", "[LLURI]") { // yes -- this mangles the url. This is expected behavior std::string simple("http://secondlife.com"); - UNSCOPED_INFO("simple http"); CHECK(LLURI::escape(simple) == "http%3A%2F%2Fsecondlife.com"); - UNSCOPED_INFO("needs escape"); CHECK(LLURI::escape("http://get.secondlife.com/windows viewer") == "http%3A%2F%2Fget.secondlife.com%2Fwindows%20viewer"); } TEST_CASE_METHOD(URITestData, "LLURI: escape and unescape round-trip very long strings", "[LLURI]") { - escapeRoundTrip("Welcome to Second Life.We hope you'll have a richly rewarding experience, filled with creativity, self expression and fun.The goals of the Community Standards are simple: treat each other with respect and without harassment, adhere to local standards as indicated by simulator ratings, and refrain from any hate activity which slurs a real-world individual or real-world community. Behavioral Guidelines - The Big Six"); - escapeRoundTrip( + require_escape_round_trip("Welcome to Second Life.We hope you'll have a richly rewarding experience, filled with creativity, self expression and fun.The goals of the Community Standards are simple: treat each other with respect and without harassment, adhere to local standards as indicated by simulator ratings, and refrain from any hate activity which slurs a real-world individual or real-world community. Behavioral Guidelines - The Big Six"); + require_escape_round_trip( "'asset_data':b(12100){'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444921\n\ttotal_crc\t323\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.368634403\t0.00781063363\t-0.569040775\n\toldpos\t150.117996\t25.8658009\t8.19664001\n\trotation\t-0.06293071806430816650390625\t-0.6995697021484375\t-0.7002241611480712890625\t0.1277817934751510620117188\n\tchildpos\t-0.00499999989\t-0.0359999985\t0.307999998\n\tchildrot\t-0.515492737293243408203125\t-0.46601200103759765625\t0.529055416584014892578125\t0.4870323240756988525390625\n\tscale" "\t0.074629\t0.289956\t0.01\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tf" "aces\t6\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204" @@ -333,53 +310,33 @@ namespace { LLURI u("secondlife:///app/login?first_name=Testert4&last_name=Tester&web_login_key=test"); // if secondlife is the scheme, LLURI should parse /app/login as path, with no authority - UNSCOPED_INFO("scheme"); CHECK(u.scheme() == "secondlife"); - UNSCOPED_INFO("authority"); CHECK(u.authority() == ""); - UNSCOPED_INFO("path"); CHECK(u.path() == "/app/login"); - UNSCOPED_INFO("pathmap"); CHECK(u.pathArray()[0].asString() == "app"); - UNSCOPED_INFO("pathmap"); CHECK(u.pathArray()[1].asString() == "login"); - UNSCOPED_INFO("query"); CHECK(u.query() == "first_name=Testert4&last_name=Tester&web_login_key=test"); - UNSCOPED_INFO("query map element"); CHECK(u.queryMap()["last_name"].asString() == "Tester"); u = LLURI("secondlife://Da Boom/128/128/128"); // if secondlife is the scheme, LLURI should parse /128/128/128 as path, with Da Boom as authority - UNSCOPED_INFO("scheme"); CHECK(u.scheme() == "secondlife"); - UNSCOPED_INFO("authority"); CHECK(u.authority() == "Da Boom"); - UNSCOPED_INFO("path"); CHECK(u.path() == "/128/128/128"); - UNSCOPED_INFO("pathmap"); CHECK(u.pathArray()[0].asString() == "128"); - UNSCOPED_INFO("pathmap"); CHECK(u.pathArray()[1].asString() == "128"); - UNSCOPED_INFO("pathmap"); CHECK(u.pathArray()[2].asString() == "128"); - UNSCOPED_INFO("query"); CHECK(u.query() == ""); } TEST_CASE_METHOD(URITestData, "LLURI: an about: URI has a path and a query", "[LLURI]") { LLURI u("about:blank?redirect-http-hack=secondlife%3A%2F%2F%2Fapp%2Flogin%3Ffirst_name%3DCallum%26last_name%3DLinden%26location%3Dspecify%26grid%3Dvaak%26region%3D%2FMorris%2F128%2F128%26web_login_key%3Defaa4795-c2aa-4c58-8966-763c27931e78"); - UNSCOPED_INFO("scheme"); CHECK(u.scheme() == "about"); - UNSCOPED_INFO("authority"); CHECK(u.authority() == ""); - UNSCOPED_INFO("path"); CHECK(u.path() == "blank"); - UNSCOPED_INFO("pathmap"); CHECK(u.pathArray()[0].asString() == "blank"); - UNSCOPED_INFO("query"); CHECK(u.query() == "redirect-http-hack=secondlife:///app/login?first_name=Callum&last_name=Linden&location=specify&grid=vaak®ion=/Morris/128/128&web_login_key=efaa4795-c2aa-4c58-8966-763c27931e78"); - UNSCOPED_INFO("query map element"); CHECK(u.queryMap()["redirect-http-hack"].asString() == "secondlife:///app/login?first_name=Callum&last_name=Linden&location=specify&grid=vaak®ion=/Morris/128/128&web_login_key=efaa4795-c2aa-4c58-8966-763c27931e78"); } diff --git a/indra/llcommon/tests/lluuid_test.cpp b/indra/llcommon/tests/lluuid_test.cpp index 629b7dd944..bcb8ea0012 100644 --- a/indra/llcommon/tests/lluuid_test.cpp +++ b/indra/llcommon/tests/lluuid_test.cpp @@ -40,7 +40,7 @@ namespace static_assert(LLUUID{}.mData[0] == 0, "default LLUUID is zero-initialised at compile time"); // LLUUID::null / LLTransactionID::tnull are exported out-of-line constants // (dllimport in consumers), so they are no longer constexpr. Their all-zero - // value is verified at runtime in test<1> instead. + // value is verified at runtime in the default-construction case instead. constexpr U8 kConstexprBytes[UUID_BYTES] = { 0xde, 0xad, 0xbe, 0xef, 0xfe, 0xed, 0xfa, 0xce, @@ -57,7 +57,7 @@ namespace } // --------------------------------------------------------------------- - // Helpers for the hash group (tests 9, 15-21). + // Helpers for the hash cases. // // An LLUUID is sixteen bytes, and the viewer uses it as its general // 16-byte key, not only as a random v4 UUID: tracking ids, composed asset @@ -91,33 +91,33 @@ namespace return z ^ (z >> 31); } - // Occupancy of a 256-bucket table indexed from one end of the hash or the - // other: containers take the index from the low bits (MSVC's - // std::unordered_map, any hand-rolled `h & (n-1)`) or from the high bits - // (boost's open-addressing tables), so both ends have to be mixed. - struct BucketSpread - { - static constexpr size_t NUM_BUCKETS = 256; - int mLowMax = 0, mHighMax = 0, mLowEmpty = 0, mHighEmpty = 0; + // Occupancy of a 256-bucket table indexed from one end of the hash or the + // other: containers take the index from the low bits (MSVC's + // std::unordered_map, any hand-rolled `h & (n-1)`) or from the high bits + // (boost's open-addressing tables), so both ends have to be mixed. + struct BucketSpread + { + static constexpr size_t NUM_BUCKETS = 256; + int mLowMax = 0, mHighMax = 0, mLowEmpty = 0, mHighEmpty = 0; - explicit BucketSpread(const std::vector& ids) + explicit BucketSpread(const std::vector& ids) + { + std::vector low(NUM_BUCKETS, 0), high(NUM_BUCKETS, 0); + for (const LLUUID& id : ids) { - std::vector low(NUM_BUCKETS, 0), high(NUM_BUCKETS, 0); - for (const LLUUID& id : ids) - { - const size_t h = hash_value(id); - ++low[h & (NUM_BUCKETS - 1)]; - ++high[(h >> (sizeof(size_t) * 8 - 8)) & (NUM_BUCKETS - 1)]; - } - for (size_t i = 0; i < NUM_BUCKETS; ++i) - { - if (low[i] > mLowMax) mLowMax = low[i]; - if (high[i] > mHighMax) mHighMax = high[i]; - if (!low[i]) ++mLowEmpty; - if (!high[i]) ++mHighEmpty; - } + const size_t h = hash_value(id); + ++low[h & (NUM_BUCKETS - 1)]; + ++high[(h >> (sizeof(size_t) * 8 - 8)) & (NUM_BUCKETS - 1)]; } - }; + for (size_t i = 0; i < NUM_BUCKETS; ++i) + { + if (low[i] > mLowMax) mLowMax = low[i]; + if (high[i] > mHighMax) mHighMax = high[i]; + if (!low[i]) ++mLowEmpty; + if (!high[i]) ++mHighEmpty; + } + } + }; // Structured payloads: a counter walking each 8-byte window, every // one-bit value, a pair of small ints in the outermost lanes, and short @@ -169,7 +169,8 @@ namespace // The hash tells boost::unordered's open-addressing tables that it is // already mixed, which makes them skip their own mulx pass. Every property - // measured in tests 15-19 is what backs that claim. + // measured in the cases from both halves to the degenerate inputs is what + // backs that claim. static_assert(boost::hash_is_avalanching>::value, "boost::hash must be declared avalanching"); static_assert(boost::hash_is_avalanching>::value, @@ -179,38 +180,30 @@ namespace TEST_CASE("LLUUID: default construction is null and so are null and tnull", "[LLUUID]") { LLUUID id; - UNSCOPED_INFO("default-constructed isNull"); CHECK(id.isNull()); - UNSCOPED_INFO("default-constructed !notNull"); CHECK_FALSE(id.notNull()); - UNSCOPED_INFO("default-constructed == LLUUID::null"); CHECK(id == LLUUID::null); // null / tnull are the all-zero UUID (verified at runtime since they are // exported out-of-line constants, no longer constexpr). - UNSCOPED_INFO("LLUUID::null isNull"); CHECK(LLUUID::null.isNull()); - UNSCOPED_INFO("LLTransactionID::tnull isNull"); CHECK(LLTransactionID::tnull.isNull()); for (int i = 0; i < UUID_BYTES; ++i) { - UNSCOPED_INFO("default byte zero"); + CAPTURE(i); REQUIRE((int)id.mData[i] == 0); } } - // setNull zeroes every byte. TEST_CASE("LLUUID: setNull zeroes every byte", "[LLUUID]") { LLUUID id; fill_sample(id); - UNSCOPED_INFO("filled UUID is not null"); CHECK(id.notNull()); id.setNull(); - UNSCOPED_INFO("setNull -> isNull"); CHECK(id.isNull()); for (int i = 0; i < UUID_BYTES; ++i) { - UNSCOPED_INFO("setNull byte zero"); + CAPTURE(i); REQUIRE((int)id.mData[i] == 0); } } @@ -219,32 +212,25 @@ namespace TEST_CASE("LLUUID: set() parses a string that asString() gives back", "[LLUUID]") { LLUUID id; - UNSCOPED_INFO("set valid string"); - REQUIRE(id.set(kSampleStr)); - UNSCOPED_INFO("asString round-trips"); + CHECK(id.set(kSampleStr)); CHECK(id.asString() == kSampleStr); for (int i = 0; i < UUID_BYTES; ++i) { - UNSCOPED_INFO("byte parsed correctly"); + CAPTURE(i); REQUIRE((int)id.mData[i] == i + 1); } } - // set() with an empty string clears to null; invalid input also nulls. TEST_CASE("LLUUID: set() with an empty string clears to null; invalid input also nulls", "[LLUUID]") { LLUUID id; fill_sample(id); - UNSCOPED_INFO("empty string set returns true"); CHECK(id.set(std::string())); - UNSCOPED_INFO("empty string -> null"); CHECK(id.isNull()); fill_sample(id); - UNSCOPED_INFO("bogus string set returns false"); CHECK_FALSE(id.set(std::string("not-a-uuid"), false)); - UNSCOPED_INFO("bogus string -> null"); CHECK(id.isNull()); } @@ -257,13 +243,9 @@ namespace fill_sample(c); c.mData[UUID_BYTES - 1] ^= 0x01; - UNSCOPED_INFO("a == b"); CHECK(a == b); - UNSCOPED_INFO("!(a != b)"); CHECK_FALSE(a != b); - UNSCOPED_INFO("a != c"); CHECK(a != c); - UNSCOPED_INFO("!(a == c)"); CHECK_FALSE(a == c); } @@ -278,13 +260,9 @@ namespace low.mData[1] = 0x01; // word value 0x100 on LE high.mData[0] = 0x01; // word value 0x001 on LE — but first byte wins - UNSCOPED_INFO("byte-lex: low < high"); CHECK(low < high); - UNSCOPED_INFO("byte-lex: high > low"); CHECK(high > low); - UNSCOPED_INFO("irreflexive: !(low < low)"); CHECK_FALSE(low < low); - UNSCOPED_INFO("irreflexive: !(low > low)"); CHECK_FALSE(low > low); } @@ -299,15 +277,12 @@ namespace b.set("11223344-5566-7788-99aa-bbccddeeff00"); LLUUID xored = a ^ b; - UNSCOPED_INFO("XOR changes value"); CHECK(xored != a); LLUUID xored_twice = xored ^ b; - UNSCOPED_INFO("double XOR cancels"); CHECK(xored_twice == original); LLUUID xored_self = a ^ a; - UNSCOPED_INFO("self-XOR yields null"); CHECK(xored_self.isNull()); } @@ -317,19 +292,15 @@ namespace // compare these checksums are unaffected. TEST_CASE("LLUUID: getCRC32 and getCRC16 give their pinned values", "[LLUUID]") { - UNSCOPED_INFO("null CRC32 == 0"); CHECK(LLUUID::null.getCRC32() == (U32)0); - UNSCOPED_INFO("null CRC16 == 0"); CHECK(LLUUID::null.getCRC16() == (U16)0); LLUUID id; fill_sample(id); // mData = {0x01, 0x02, ..., 0x10} // LE U32s: 0x04030201, 0x08070605, 0x0c0b0a09, 0x100f0e0d — sum 0x2824201C - UNSCOPED_INFO("sample CRC32"); CHECK(id.getCRC32() == (U32)0x2824201C); // LE U16s sum: high bytes (2+4+...+16)=72=0x48; low bytes (1+3+...+15)=64=0x40 - UNSCOPED_INFO("sample CRC16"); CHECK(id.getCRC16() == (U16)0x4840); } @@ -339,26 +310,20 @@ namespace LLUUID a, b; fill_sample(a); fill_sample(b); - UNSCOPED_INFO("equal IDs equal std::hash"); CHECK(std::hash{}(a) == std::hash{}(b)); - UNSCOPED_INFO("std::hash matches hash_value"); CHECK(std::hash{}(a) == hash_value(b)); - UNSCOPED_INFO("boost::hash matches hash_value"); CHECK(boost::hash{}(a) == hash_value(b)); // A different UUID should (overwhelmingly likely) hash differently. LLUUID c; c.set("ffffffff-ffff-ffff-ffff-ffffffffffff"); - UNSCOPED_INFO("distinct IDs typically hash differently"); CHECK(std::hash{}(a) != std::hash{}(c)); // LLTransactionID and LLAssetID reach the same hash through the base. LLTransactionID tid; fill_sample(tid); - UNSCOPED_INFO("LLTransactionID hashes as its LLUUID bytes"); CHECK(boost::hash{}(tid) == hash_value(a)); const LLAssetID aid = a; - UNSCOPED_INFO("LLAssetID hashes as its LLUUID bytes"); CHECK(std::hash{}(aid) == hash_value(a)); } @@ -367,14 +332,10 @@ namespace { LLUUID a = LLUUID::generateNewID(); LLUUID b = LLUUID::generateNewID(); - UNSCOPED_INFO("generated id is not null"); CHECK(a.notNull()); - UNSCOPED_INFO("generated id is not null (2)"); CHECK(b.notNull()); - UNSCOPED_INFO("two generated ids differ"); CHECK(a != b); // validate() should accept the generated string form - UNSCOPED_INFO("generated id validates"); CHECK(LLUUID::validate(a.asString())); } @@ -385,35 +346,26 @@ namespace id.set(kSampleStr); char buf[UUID_STR_LENGTH] = {}; id.to_chars(buf); - UNSCOPED_INFO("to_chars matches set string"); CHECK(std::string(buf) == kSampleStr); - UNSCOPED_INFO("to_chars length"); CHECK(strlen(buf) == (size_t)(UUID_STR_LENGTH - 1)); } - // ostream insertion delegates to the canonical 36-char form. TEST_CASE("LLUUID: ostream insertion delegates to the canonical 36-char form", "[LLUUID]") { LLUUID id; id.set(kSampleStr); std::ostringstream os; os << id; - UNSCOPED_INFO("operator<< matches asString"); CHECK(os.str() == id.asString()); } - // validate() accepts canonical forms and rejects garbage. TEST_CASE("LLUUID: validate() accepts canonical forms and rejects garbage", "[LLUUID]") { - UNSCOPED_INFO("validate canonical"); CHECK(LLUUID::validate(kSampleStr)); - UNSCOPED_INFO("reject short string"); CHECK_FALSE(LLUUID::validate("abc")); - UNSCOPED_INFO("reject non-hex characters"); CHECK_FALSE(LLUUID::validate("zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz")); } - // std::set via the default operator< yields byte-lex iteration. TEST_CASE("LLUUID: std::set via the default operator< yields byte-lex iteration", "[LLUUID]") { LLUUID one, two; @@ -427,10 +379,8 @@ namespace s.insert(one); auto it = s.begin(); - UNSCOPED_INFO("first sorted entry has lowest first byte"); CHECK((int)it->mData[0] == 1); ++it; - UNSCOPED_INFO("second sorted entry has next first byte"); CHECK((int)it->mData[0] == 2); } @@ -501,14 +451,9 @@ namespace for (int o = 0; o < kOutputBits; ++o) { const double rate = (double)flips[o] / (double)trials; - { - INFO("every output bit must flip near half the time for a " - "single-bit input change (output bit " - + std::to_string(o) + " flipped " - + std::to_string(rate) + " of the time)"); - REQUIRE(rate >= 0.35); - REQUIRE(rate <= 0.65); - } + CAPTURE(o); + REQUIRE(rate >= 0.35); + REQUIRE(rate <= 0.65); } } @@ -532,15 +477,11 @@ namespace const BucketSpread spread(ids); const int mean = (int)(ids.size() / BucketSpread::NUM_BUCKETS); // 16 - const std::string where = " (lane " + std::to_string(lane) + ")"; - UNSCOPED_INFO("counter in one lane must not pile up in the low bits" + where); + CAPTURE(lane); REQUIRE(spread.mLowMax <= 3 * mean); - UNSCOPED_INFO("counter in one lane must not pile up in the high bits" + where); REQUIRE(spread.mHighMax <= 3 * mean); - UNSCOPED_INFO("counter in one lane must reach every low-bit bucket" + where); REQUIRE(spread.mLowEmpty == 0); - UNSCOPED_INFO("counter in one lane must reach every high-bit bucket" + where); REQUIRE(spread.mHighEmpty == 0); } } @@ -560,14 +501,11 @@ namespace { if (!seen.insert(hash_value(id)).second) ++collisions; } - UNSCOPED_INFO("structured 16-byte payloads must not collide outright"); CHECK(collisions == 0); const BucketSpread spread(corpus); const int mean = (int)(corpus.size() / BucketSpread::NUM_BUCKETS); - UNSCOPED_INFO("structured payloads must not pile up in the low bits"); CHECK(spread.mLowMax <= 3 * mean); - UNSCOPED_INFO("structured payloads must not pile up in the high bits"); CHECK(spread.mHighMax <= 3 * mean); } @@ -575,7 +513,6 @@ namespace // the 128 one-bit values must all land somewhere different. TEST_CASE("LLUUID: the null UUID hashes nonzero and the one-bit values hash apart", "[LLUUID]") { - UNSCOPED_INFO("the null UUID must not hash to zero"); CHECK(hash_value(LLUUID::null) != (size_t)0); std::set seen; @@ -583,8 +520,7 @@ namespace { seen.insert(hash_value(make_bit_id(bit))); } - UNSCOPED_INFO("each of the 128 one-bit values hashes differently"); - REQUIRE(seen.size() == (size_t)(UUID_BYTES * 8)); + CHECK(seen.size() == (size_t)(UUID_BYTES * 8)); } // Pinned values for the hash. Nothing persists a hash of an LLUUID, so @@ -592,7 +528,7 @@ namespace // reversed half -- rather than to freeze the algorithm. Changing the hash // on purpose means updating these three lines, deliberately. // - // The values assume a little-endian host, as test<8> does. + // The values assume a little-endian host, as the CRC regression does. TEST_CASE("LLUUID: hash_value gives its pinned values on 64-bit hosts", "[LLUUID]") { if constexpr (sizeof(size_t) == 8) @@ -602,11 +538,9 @@ namespace LLUUID ones; std::memset(ones.mData, 0xff, UUID_BYTES); - UNSCOPED_INFO("pinned hash of the null UUID"); CHECK((U64)hash_value(LLUUID::null) == 0xc64369fbab9cf4d7ULL); - UNSCOPED_INFO("pinned hash of 01020304-0506-0708-090a-0b0c0d0e0f10"); + UNSCOPED_INFO("the sample is 01020304-0506-0708-090a-0b0c0d0e0f10"); CHECK((U64)hash_value(sample) == 0x47c8de24eeda6e35ULL); - UNSCOPED_INFO("pinned hash of the all-ones UUID"); CHECK((U64)hash_value(ones) == 0x24af15be29dd63f0ULL); } } @@ -625,23 +559,17 @@ namespace flat.emplace(corpus[i], i); node.emplace(corpus[i], i); } - UNSCOPED_INFO("flat map holds every distinct key"); - REQUIRE(flat.size() == corpus.size()); - UNSCOPED_INFO("node map holds every distinct key"); - REQUIRE(node.size() == corpus.size()); + CHECK(flat.size() == corpus.size()); + CHECK(node.size() == corpus.size()); for (size_t i = 0; i < corpus.size(); ++i) { auto fit = flat.find(corpus[i]); - UNSCOPED_INFO("flat map finds every key"); REQUIRE(fit != flat.end()); - UNSCOPED_INFO("flat map returns the right value"); REQUIRE(fit->second == i); auto nit = node.find(corpus[i]); - UNSCOPED_INFO("node map finds every key"); REQUIRE(nit != node.end()); - UNSCOPED_INFO("node map returns the right value"); REQUIRE(nit->second == i); } @@ -650,7 +578,6 @@ namespace probe.set(kSampleStr); LLUUID same; fill_sample(same); - UNSCOPED_INFO("equal bytes hash equally regardless of how they arrived"); CHECK(hash_value(probe) == hash_value(same)); } } diff --git a/indra/llcommon/tests/stringize_test.cpp b/indra/llcommon/tests/stringize_test.cpp index 7c2fbdb708..b1e4948f4d 100644 --- a/indra/llcommon/tests/stringize_test.cpp +++ b/indra/llcommon/tests/stringize_test.cpp @@ -48,7 +48,7 @@ #include -namespace tut +namespace { struct stringize_data { @@ -106,15 +106,15 @@ namespace tut { //Tests rely on validity of wstring_to_utf8str() #if LL_WINDOWS // Windows wstring is a 2byte UTF16 type - REQUIRE(ll_convert(wstringize(c)) == ll_convert(std::wstring(L"c"))); - REQUIRE(ll_convert(wstringize(s)) == ll_convert(std::wstring(L"17"))); - REQUIRE(ll_convert(wstringize(i)) == ll_convert(std::wstring(L"34"))); - REQUIRE(ll_convert(wstringize(l)) == ll_convert(std::wstring(L"68"))); - REQUIRE(ll_convert(wstringize(f)) == ll_convert(std::wstring(L"3.14159"))); - REQUIRE(ll_convert(wstringize(d)) == ll_convert(std::wstring(L"3.14159"))); - REQUIRE(ll_convert(wstringize(abc)) == ll_convert(std::wstring(L"abc def"))); - REQUIRE(ll_convert(wstringize(abc)) == ll_convert(wstringize(abc.c_str()))); - REQUIRE(ll_convert(wstringize(def)) == ll_convert(std::wstring(L"def ghi"))); + CHECK(ll_convert(wstringize(c)) == ll_convert(std::wstring(L"c"))); + CHECK(ll_convert(wstringize(s)) == ll_convert(std::wstring(L"17"))); + CHECK(ll_convert(wstringize(i)) == ll_convert(std::wstring(L"34"))); + CHECK(ll_convert(wstringize(l)) == ll_convert(std::wstring(L"68"))); + CHECK(ll_convert(wstringize(f)) == ll_convert(std::wstring(L"3.14159"))); + CHECK(ll_convert(wstringize(d)) == ll_convert(std::wstring(L"3.14159"))); + CHECK(ll_convert(wstringize(abc)) == ll_convert(std::wstring(L"abc def"))); + CHECK(ll_convert(wstringize(abc)) == ll_convert(wstringize(abc.c_str()))); + CHECK(ll_convert(wstringize(def)) == ll_convert(std::wstring(L"def ghi"))); #else // ensure_equals(ll_convert(wstringize(c)), wstring_to_utf8str(U"c")); // ensure_equals(ll_convert(wstringize(s)), wstring_to_utf8str(U"17")); @@ -128,4 +128,4 @@ namespace tut #endif // ensure_equals(wstring_to_utf8str(wstringize(llsd)), wstring_to_utf8str(L"{'abc':'abc def','d':r3.14159,'i':i34}")); } -} // namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/threadsafeschedule_test.cpp b/indra/llcommon/tests/threadsafeschedule_test.cpp index fd4c007caf..6afc637d6d 100644 --- a/indra/llcommon/tests/threadsafeschedule_test.cpp +++ b/indra/llcommon/tests/threadsafeschedule_test.cpp @@ -49,25 +49,19 @@ namespace queue.push(now + 100ms, "def"s); queue.close(); auto entry = queue.pop(); - UNSCOPED_INFO("failed to pop first"); CHECK(std::get<0>(entry) == "abc"s); entry = queue.pop(); - UNSCOPED_INFO("failed to pop second"); CHECK(std::get<0>(entry) == "def"s); - UNSCOPED_INFO("queue not closed"); CHECK(queue.isClosed()); - UNSCOPED_INFO("queue prematurely done"); CHECK_FALSE(queue.done()); std::string s; bool popped = queue.tryPopFor(1s, s); UNSCOPED_INFO("failed to pop third"); CHECK(popped); - UNSCOPED_INFO("third is wrong"); CHECK(s == "ghi"s); popped = queue.tryPop(s); UNSCOPED_INFO("queue not empty"); CHECK_FALSE(popped); - UNSCOPED_INFO("queue not done"); CHECK(queue.done()); } } // anonymous namespace diff --git a/indra/llcommon/tests/tuple_test.cpp b/indra/llcommon/tests/tuple_test.cpp index 11f75620e9..b29251f9ad 100644 --- a/indra/llcommon/tests/tuple_test.cpp +++ b/indra/llcommon/tests/tuple_test.cpp @@ -31,11 +31,8 @@ namespace std::tuple tup2; int i; std::tie(i, tup2) = tuple_split(ptup); - UNSCOPED_INFO("tuple_car() fail"); CHECK(i == 34); - UNSCOPED_INFO("tuple_cdr() (0) fail"); CHECK(std::get<0>(tup2) == "abc"); - UNSCOPED_INFO("tuple_cdr() (1) fail"); CHECK(std::get<1>(tup2) == 17); } } // anonymous namespace diff --git a/indra/llcommon/tests/workqueue_test.cpp b/indra/llcommon/tests/workqueue_test.cpp index 33992f6351..5a78de08b6 100644 --- a/indra/llcommon/tests/workqueue_test.cpp +++ b/indra/llcommon/tests/workqueue_test.cpp @@ -44,12 +44,9 @@ namespace TEST_CASE_METHOD(workqueue_data, "workqueue: a schedule is findable by name and an unnamed one gets a default", "[workqueue]") { - UNSCOPED_INFO("didn't capture name"); CHECK(queue.getKey() == "queue"); - UNSCOPED_INFO("not findable"); CHECK(WorkSchedule::getInstance("queue") == queue.getWeak().lock()); WorkSchedule q2; - UNSCOPED_INFO("has no name"); CHECK(LLStringUtil::startsWith(q2.getKey(), "WorkQueue")); } @@ -63,7 +60,6 @@ namespace UNSCOPED_INFO("ran too soon"); CHECK_FALSE(wasRun); queue.runUntilClose(); - UNSCOPED_INFO("didn't run"); CHECK(wasRun); } @@ -111,7 +107,6 @@ namespace } // Take a copy of the captured deque. Shared result = data.get(); - UNSCOPED_INFO("called wrong number of times"); REQUIRE(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 @@ -123,10 +118,9 @@ namespace start += interval; try { - UNSCOPED_INFO((STRINGIZE("call " << i << " too soon"))); - REQUIRE((diff >= interval)); - UNSCOPED_INFO((STRINGIZE("call " << i << " too late"))); - REQUIRE((diff < interval*1.5)); + CAPTURE(i); + REQUIRE(diff >= interval); + REQUIRE(diff < interval*1.5); } catch (const ll_test::Failure&) { @@ -156,7 +150,6 @@ namespace qptr->runOne(); // this should run the callback main.runOne(); - UNSCOPED_INFO("failed to run int callback"); CHECK(result == 17); std::string alpha; @@ -167,7 +160,6 @@ namespace [&alpha](const std::string& s){ alpha = s; }); qptr->runPending(); main.runPending(); - UNSCOPED_INFO("failed to run string callback"); CHECK(alpha == "abc"); } @@ -186,7 +178,6 @@ namespace [&observe](){ observe.append(";main"); }); qptr->runOne(); main.runOne(); - UNSCOPED_INFO("failed to run both lambdas"); CHECK(observe == "queue;main"); } @@ -199,11 +190,11 @@ namespace [this, &stored](){ stored = queue.waitForResult( [](){ return "should throw"; }); }) }; UNSCOPED_INFO("lambda should not have run"); - REQUIRE(stored.empty()); + CHECK(stored.empty()); UNSCOPED_INFO("waitForResult() should have thrown"); - REQUIRE_FALSE(what.empty()); - UNSCOPED_INFO((STRINGIZE("should mention waitForResult: " << what))); - REQUIRE(what.find("waitForResult") != std::string::npos); + CHECK_FALSE(what.empty()); + UNSCOPED_INFO("exception: " << what); + CHECK(what.find("waitForResult") != std::string::npos); // Call waitForResult() on a coroutine, with a string result. LLCoros::instance().launch( @@ -217,7 +208,6 @@ namespace // 'queue'. Service it. queue.runOne(); llcoro::suspend(); - UNSCOPED_INFO("bad waitForResult return"); CHECK(stored == "string result"); // Call waitForResult() on a coroutine, with a void callable. @@ -233,7 +223,6 @@ namespace llcoro::suspend(); queue.runOne(); llcoro::suspend(); - UNSCOPED_INFO("didn't run coroutine"); CHECK(stored == "ran"); UNSCOPED_INFO("void waitForResult() didn't return"); CHECK(done); From 03a5b6d8342b245d06c1719939bcc72880628e78 Mon Sep 17 00:00:00 2001 From: Rye Date: Mon, 12 Oct 2026 18:58:19 -0400 Subject: [PATCH 08/10] llcommon's assertions in coroutines and event listeners are CHECKs A REQUIRE that fails throws, which inside a coroutine LLCoros catches and keeps to rethrow in the main fiber. The failure is already recorded, but the throw ends the coroutine. In llcond's two-coroutine case it ended it before it set the value the test then waits for, and the case hung until it was killed. In lleventcoro the throw would wait to be rethrown in whichever case came next. In llprocess's postend listener it would go back through LLProcess's own handling of a child's end, mid-post. Each of the four guards nothing after it, so each is a CHECK, which records the failure and lets the rest run. A wrong expectation in each now fails its case at once, and llcond's no longer hangs. No assertion in llcommon's tests runs on a thread of its own: the threads only work, and each case checks their results after joining them. Co-Authored-By: Claude Opus 5.5 --- indra/llcommon/tests/llcond_test.cpp | 6 ++++-- indra/llcommon/tests/lleventcoro_test.cpp | 4 +++- indra/llcommon/tests/llprocess_test.cpp | 4 +++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/indra/llcommon/tests/llcond_test.cpp b/indra/llcommon/tests/llcond_test.cpp index 95f897657e..399f14a6a5 100644 --- a/indra/llcommon/tests/llcond_test.cpp +++ b/indra/llcommon/tests/llcond_test.cpp @@ -44,10 +44,12 @@ namespace [this]() { // Lambda immediately entered -- control comes here first. - REQUIRE(cond.get() == 0); + // CHECKs: a REQUIRE that failed would end the coroutine before + // it set what the test waits for, and the case would hang. + CHECK(cond.get() == 0); cond.set_all(1); cond.wait_equal(2); - REQUIRE(cond.get() == 2); + CHECK(cond.get() == 2); cond.set_all(3); }); // Main coroutine is resumed only when the lambda waits. diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index f1c8a0009e..6de805ba98 100644 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -170,7 +170,9 @@ namespace debug("about to suspend"); stringdata = future.get(); mSync.bump(); - REQUIRE(stringdata == "received"); + // A CHECK: a REQUIRE's throw would leave this coroutine for + // LLCoros to rethrow in whichever case came next. + CHECK(stringdata == "received"); } END } diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp index c8aa57c239..4e7df2af71 100644 --- a/indra/llcommon/tests/llprocess_test.cpp +++ b/indra/llcommon/tests/llprocess_test.cpp @@ -1642,7 +1642,9 @@ namespace tut bool postend(const LLSD&) { mTriggered = true; - REQUIRE(mReadPipe.read(mReadPipe.size()) == mExpect); + // A CHECK: a REQUIRE's throw would go back through LLProcess's + // own handling of the child's end, mid-post. + CHECK(mReadPipe.read(mReadPipe.size()) == mExpect); return false; } From 94177cefc3eb598718e4aae827182ed047ea9321 Mon Sep 17 00:00:00 2001 From: Rye Date: Mon, 12 Oct 2026 18:59:30 -0400 Subject: [PATCH 09/10] llprocess's Windows autokill helper runs through --al-helper The autokill tests launch the test executable again as a helper that starts children, writes their ids and exits, so the test can see Windows end the children with it. The relaunched executable ran its tests until llprocess's fixture, seeing the helper's environment, ran the helper and exited. In llcommon_python_tests that would run llleap's and llsdserialize_python's tests in the child first, or any case at all under a random order. The helper is registered as llprocess_autokill and launched with --al-helper=llprocess_autokill, so the main runs only it; the fixture no longer looks. This code is Windows-only and is not compiled here: it uses only what test_support_tests shows working on every platform, the registration, Helper::argument and the main's dispatch. Co-Authored-By: Claude Opus 5.5 --- indra/llcommon/tests/llprocess_test.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp index 4e7df2af71..d793c94aaf 100644 --- a/indra/llcommon/tests/llprocess_test.cpp +++ b/indra/llcommon/tests/llprocess_test.cpp @@ -29,6 +29,7 @@ #include #include #include "altestfailure.h" +#include "altesthelper.h" #include "altestmatchers.h" #include "altestname.h" #include "../test/namedtempfile.h" @@ -404,6 +405,16 @@ namespace std::exit(0); } + // verify_autokill_on_helper_exit's helper: this binary run again as + // nothing but run_autokill_helper_from_environment(), which exits the + // process itself once the test releases it. + AL_TEST_HELPER(llprocess_autokill) + { + run_autokill_helper_from_environment(); + // It returns only when the environment names no script. + return AUTOKILL_HELPER_INVALID_ENV_EXIT; + } + std::vector wait_for_helper_pids( const std::string& pidfile, int expected_count, @@ -446,6 +457,7 @@ namespace LLProcess::Params params; params.executable = get_current_executable_path(); + params.args.add(ll_test::Helper::argument("llprocess_autokill")); params.desc = desc + " helper"; LLProcessPtr helper = LLProcess::create(params); REQUIRE(bool(helper)); @@ -538,13 +550,6 @@ namespace tut { struct llprocess_data { - llprocess_data() - { -#if LL_WINDOWS - run_autokill_helper_from_environment(); -#endif - } - LLAPRPool pool; }; From 9f14a44ed23c824d66e937971f7d8c0f81f50540 Mon Sep 17 00:00:00 2001 From: Rye Date: Mon, 12 Oct 2026 19:51:03 -0400 Subject: [PATCH 10/10] llcommon's tests have the state guard watch what llcommon keeps for the process The harness already checks what every LL process has: the working directory, the environment, the floating-point state, the C locale, the APR pool, LLTrace's master recorder, the LLApp and LLError's fatal function. llcommon_test_checks, an OBJECT library in llcommon_tests and llcommon_python_tests, adds: - the live thread pools and work queues, by name: a case that leaves a worker running leaves it working, and counting LLSD objects, through every case after it; - LLError's default level. It also ends LLEventPumps after every case, as the plan has it, so a pump a case made, and the listeners still on it, cannot reach the next; lazyeventapi's cases did this for themselves already. One case left a thread running: alrecoverystore's first write makes ALRecoveryWriter, a singleton whose one thread the viewer stops in its cleanup. Its tests now make it once for the process, before the first case that writes would, and stop it in the harness's teardown, telling the guard, as lllogin's accessor does for its app. Each check was shown to name the case responsible: a case changed to leave a thread pool, a work queue or a lowered log level behind was named for each. Each binary passes 10 random orders and 20 repeats with no guard report. Co-Authored-By: Claude Opus 5.5 --- indra/llcommon/CMakeLists.txt | 9 ++ indra/llcommon/tests/alrecoverystore_test.cpp | 20 +++++ indra/llcommon/tests/llcommon_test_checks.cpp | 88 +++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 indra/llcommon/tests/llcommon_test_checks.cpp diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 186b5d6b43..24ddb265ad 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -295,6 +295,13 @@ target_link_libraries( ) if(AL_BUILD_TESTS) + # The state guard's checks on what llcommon keeps for the process, and the + # event pumps' end after every case, in every binary of llcommon's tests + # that shares a process between files. + add_library(llcommon_test_checks OBJECT tests/llcommon_test_checks.cpp) + target_link_libraries(llcommon_test_checks PRIVATE al::flags al_test_support llcommon) + set_target_properties(llcommon_test_checks PROPERTIES FOLDER "Tests/llcommon") + # One Catch2 binary for llcommon's tests, each test file run in a process # of its own. None of them may delete every singleton: the files will # share a process. @@ -355,6 +362,7 @@ if(AL_BUILD_TESTS) tests/threadsafeschedule_test.cpp tests/tuple_test.cpp tests/workqueue_test.cpp + SUPPORT llcommon_test_checks LIBRARIES llcommon ) @@ -366,6 +374,7 @@ if(AL_BUILD_TESTS) llcommon_python_tests PROJECT llcommon SOURCES tests/llleap_test.cpp tests/llprocess_test.cpp tests/llsdserialize_python_test.cpp + SUPPORT llcommon_test_checks LIBRARIES llcommon PYTHON ENVIRONMENT PYTHONDONTWRITEBYTECODE=1 diff --git a/indra/llcommon/tests/alrecoverystore_test.cpp b/indra/llcommon/tests/alrecoverystore_test.cpp index 316bcd456f..b120767e53 100644 --- a/indra/llcommon/tests/alrecoverystore_test.cpp +++ b/indra/llcommon/tests/alrecoverystore_test.cpp @@ -32,6 +32,8 @@ #include "lltimer.h" #include +#include "alteststateguard.h" +#include "altestteardown.h" #include #include @@ -40,6 +42,23 @@ namespace { + // The stores write on one thread for the process, ALRecoveryWriter's, + // which the viewer stops in its cleanup. Here it is made once, before the + // first case that writes would make it, and stopped by the harness's + // teardown: that it then runs is no change a case made. + void makeRecoveryWriter() + { + static bool sMade = false; + if (!sMade) + { + sMade = true; + ALRecoveryWriter::getInstance(); + ll_test::Teardown::add([] { ALRecoveryWriter::deleteSingleton(); }); + ll_test::StateGuard::accept("the live thread pools"); + ll_test::StateGuard::accept("the live work queues"); + } + } + struct alrecoverystore_data { // A folder of its own for each test, gone after it. @@ -47,6 +66,7 @@ namespace alrecoverystore_data() { + makeRecoveryWriter(); folder = fsyspath(std::filesystem::temp_directory_path() / fsyspath("alrecoverystore_" + LLUUID::generateNewID().asString())).string(); std::error_code ec; std::filesystem::create_directories(fsyspath(folder), ec); diff --git a/indra/llcommon/tests/llcommon_test_checks.cpp b/indra/llcommon/tests/llcommon_test_checks.cpp new file mode 100644 index 0000000000..ec4b618ee2 --- /dev/null +++ b/indra/llcommon/tests/llcommon_test_checks.cpp @@ -0,0 +1,88 @@ +/** + * @file llcommon_test_checks.cpp + * @brief The state guard's checks on what llcommon keeps for the process, and + * the event pumps' end after every test case. + * + * $LicenseInfo:firstyear=2026&license=viewerlgpl$ + * Alchemy Viewer Source Code + * Copyright (C) 2026, Rye + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "alteststateguard.h" +#include "llerrorcontrol.h" +#include "llevents.h" +#include "threadpool.h" +#include "workqueue.h" + +#include +#include + +#include +#include +#include + +namespace +{ + // The names a tracked class's live instances go by, sorted. + template + std::string liveNames() + { + std::vector names; + for (const std::string& name : typename TRACKED::key_snapshot()) + { + names.push_back(name); + } + std::sort(names.begin(), names.end()); + std::string text; + for (const std::string& name : names) + { + text += (text.empty() ? "" : ", ") + name; + } + return text; + } + + class LLCommonChecks : public Catch::EventListenerBase + { + public: + using Catch::EventListenerBase::EventListenerBase; + + void testRunStarting(Catch::TestRunInfo const&) override + { + // A case that starts worker threads stops them before it ends: + // a thread still running would be working, and counting LLSD + // objects, through every later case. + ll_test::StateGuard::add("the live thread pools", liveNames); + ll_test::StateGuard::add("the live work queues", liveNames); + // A case that turns logging up or down for itself puts it back. + ll_test::StateGuard::add("LLError's default level", + [] { return std::to_string(LLError::getDefaultLevel()); }); + } + + // Each case starts with no event pumps: a pump a case made, and the + // listeners still on it, would otherwise reach the cases after it, + // as a file's own cases always reached each other under TUT. + void testCaseEnded(Catch::TestCaseStats const&) override + { + LLEventPumps::deleteSingleton(); + } + }; +} + +CATCH_REGISTER_LISTENER(LLCommonChecks)