diff --git a/.github/actions/setup-build/action.yaml b/.github/actions/setup-build/action.yaml index 0f0c4e490b0..8cc0810f1ce 100644 --- a/.github/actions/setup-build/action.yaml +++ b/.github/actions/setup-build/action.yaml @@ -51,7 +51,7 @@ runs: libpipewire-0.3-dev libpulse-dev libsndio-dev libtext-unidecode-perl \ libthai-dev libtool libudev-dev libunwind-dev liburing-dev libwayland-dev \ libx11-dev libxcursor-dev libxext-dev libxfixes-dev libxft-dev libxi-dev libxinerama-dev \ - libxkbcommon-dev libxrandr-dev libxss-dev libxtst-dev linux-libc-dev mold \ + libxkbcommon-dev libxrandr-dev libxss-dev libxtst-dev linux-libc-dev \ nasm ninja-build pkgconf rpm tar tex-common texinfo unzip zip sudo locale-gen en_US.UTF-8 @@ -66,6 +66,28 @@ runs: sudo rm -rf /usr/local/.ghcup df -h + # mold from its release rather than the image's 2.40: before 2.42.1 it + # can leave a function with no unwind entry the unwinder finds, so that + # an exception thrown through it ends the program (rui314/mold#1648). + # Pinned by digest, under /usr/local, which the path puts first. + - name: Linux mold install + if: runner.os == 'Linux' + shell: bash + run: | + version=2.42.1 + case "$(uname -m)" in + x86_64) arch=x86_64 sha256=6ff270c9bf07d2bec5c98aa324eb7c4daf6a1a4d815c05ff1708049616047855 ;; + aarch64) arch=aarch64 sha256=16b025652d3d7456689e6025a77e1903bb2a15e7630877c26cc133f5df95b9c6 ;; + *) echo "::error::No mold release pinned for $(uname -m)"; exit 1 ;; + esac + archive="$RUNNER_TEMP/mold.tar.gz" + curl -fsSL -o "$archive" \ + "https://github.com/rui314/mold/releases/download/v$version/mold-$version-$arch-linux.tar.gz" + echo "$sha256 $archive" | sha256sum -c - + sudo tar -xzf "$archive" -C /usr/local --strip-components=1 --no-same-owner + rm "$archive" + mold --version + - name: macOS Homebrew dependency install if: runner.os == 'macOS' shell: bash diff --git a/indra/alscript/tests/alincludesearch_test.cpp b/indra/alscript/tests/alincludesearch_test.cpp index 367b234976f..cc5875d713b 100644 --- a/indra/alscript/tests/alincludesearch_test.cpp +++ b/indra/alscript/tests/alincludesearch_test.cpp @@ -428,23 +428,26 @@ namespace tut // the code still, which keeps it for a require found nowhere, for each // name it took; an alias's it read through the `.luaurc`, which is gone, // and is kept as it found it. - struct RequireCase + namespace { - // What it shows, and whose case it is. - const char* what; - // The file asking, under the tree; "object" for a script in an - // object, which with world includes off reads nothing of the world. - const char* from; - const char* name; - // What it finds, under the tree, or nothing: what the search found - // before the navigator, and what it finds now, as LAD9 has it. - const char* before; - const char* chosen; - // Whose rule LAD9 picks, where the references or we disagree: and - // what Luau's navigator answers, where it differs from the choice. - const char* rule; - const char* luau; - }; + struct RequireCase + { + // What it shows, and whose case it is. + const char* what; + // The file asking, under the tree; "object" for a script in an + // object, which with world includes off reads nothing of the world. + const char* from; + const char* name; + // What it finds, under the tree, or nothing: what the search found + // before the navigator, and what it finds now, as LAD9 has it. + const char* before; + const char* chosen; + // Whose rule LAD9 picks, where the references or we disagree: and + // what Luau's navigator answers, where it differs from the choice. + const char* rule; + const char* luau; + }; + } // anonymous namespace template<> template<> void alincludesearch_object::test<7>() diff --git a/indra/alscript/tests/allslservice_test.cpp b/indra/alscript/tests/allslservice_test.cpp index 2df9e973bc3..bfff29bfd8b 100644 --- a/indra/alscript/tests/allslservice_test.cpp +++ b/indra/alscript/tests/allslservice_test.cpp @@ -742,7 +742,9 @@ namespace tut all << in.rdbuf(); text = all.str(); } - const std::string path = (std::filesystem::temp_directory_path() / "alscript-test-builtins-newer.txt").string(); + // a name of its own, so two runs at once never write one file + const std::string path = + (std::filesystem::temp_directory_path() / ("alscript-test-builtins-newer-" + LLUUID::generateNewID().asString() + ".txt")).string(); { llofstream out(path, std::ios::out | std::ios::binary | std::ios::trunc); out << text << "integer llBrandNewThing( integer Value )\n"; diff --git a/indra/alscript/tests/alluauconfig_test.cpp b/indra/alscript/tests/alluauconfig_test.cpp index e313a66a032..3ba1ed29359 100644 --- a/indra/alscript/tests/alluauconfig_test.cpp +++ b/indra/alscript/tests/alluauconfig_test.cpp @@ -28,6 +28,8 @@ #include "../luau/alluauconfigscript.h" #include "../lint/alscriptlintpass.h" +#include "lluuid.h" + #include "../test/lltut.h" #include @@ -298,12 +300,16 @@ namespace tut ensure("an __index that would loop is never asked", ALLuauConfigScript::asLuaurc("return setmetatable({}, { __index = function() while true do end end })", json, error) && json == "{}"); - const auto started = std::chrono::steady_clock::now(); - ensure_equals("a loop that never ends, stopped", (ALLuauConfigScript::asLuaurc("while true do end", json, error), error), + // A run stopped by the deadline is kept a while, by its text: a text + // of this run's own is run, not answered from what another test, or + // this one before, asked. + const std::string forever = "while true do end -- " + LLUUID::generateNewID().asString(); + const auto started = std::chrono::steady_clock::now(); + ensure_equals("a loop that never ends, stopped", (ALLuauConfigScript::asLuaurc(forever, json, error), error), std::string("configuration execution timed out")); const F64 took = std::chrono::duration(std::chrono::steady_clock::now() - started).count(); ensure(llformat("stopped at its time, not long after: %.2f s", took), took >= ALLuauConfigScript::MOST_SECONDS && took < ALLuauConfigScript::MOST_SECONDS + 2.0); - ensure("asked again at once, kept for a while: no second wait", (ALLuauConfigScript::asLuaurc("while true do end", json, error), + ensure("asked again at once, kept for a while: no second wait", (ALLuauConfigScript::asLuaurc(forever, json, error), std::chrono::duration(std::chrono::steady_clock::now() - started).count() < took + 0.1)); // table.move's copy is a loop in C the deadline does not reach, and // nils moved into a table that holds none take no memory: held to so diff --git a/indra/alscript/tests/alpreprocessor_test.cpp b/indra/alscript/tests/alpreprocessor_test.cpp index 944b0e07489..b19d3d871ee 100644 --- a/indra/alscript/tests/alpreprocessor_test.cpp +++ b/indra/alscript/tests/alpreprocessor_test.cpp @@ -194,6 +194,16 @@ namespace tut return ALPreprocessor::run(ended(source, ending), o); } + // The grid's builtins, which the optimizer and const folding read. A + // test that needs them asks for them itself: Tailslide keeps them for + // the process once loaded, so whichever asks first loads them. + static void ensureGridBuiltins() + { + ALLSLService service; + std::string error; + ensure("builtins: " + error, service.loadBuiltins(std::string(AL_LSL_DEFINITIONS_DIR) + "/builtins.txt", error)); + } + // The same but the notes of a require nothing reads, which a script // made to try something else may leave about (test 50 tries them). static std::string faults(const ALPreprocessor::Result& r) @@ -660,11 +670,7 @@ namespace tut void alpreprocessor_object::test<13>() { set_test_name("the optimizer runs over the expanded text, and its notes come back to the source"); - { - ALLSLService service; - std::string error; - ensure("builtins: " + error, service.loadBuiltins(std::string(AL_LSL_DEFINITIONS_DIR) + "/builtins.txt", error)); - } + ensureGridBuiltins(); add("consts.lsl", "#define CHANNEL 7\ninteger unused = 1;\n"); ALPreprocessor::Options o = options(); o.optimize = true; @@ -1192,11 +1198,7 @@ namespace tut void alpreprocessor_object::test<28>() { set_test_name("the optimizer's run, weighed, says what it saved in code: the whole, and each note what its lines came to less"); - { - ALLSLService service; - std::string error; - ensure("builtins: " + error, service.loadBuiltins(std::string(AL_LSL_DEFINITIONS_DIR) + "/builtins.txt", error)); - } + ensureGridBuiltins(); const std::string source = "integer unused(integer n)\n" "{\n" " return n * 3;\n" @@ -1249,11 +1251,7 @@ namespace tut void alpreprocessor_object::test<29>() { set_test_name("a script that does not compile is not the preprocessor's error: the optimizer stands aside with a note where the first error is"); - { - ALLSLService service; - std::string error; - ensure("builtins: " + error, service.loadBuiltins(std::string(AL_LSL_DEFINITIONS_DIR) + "/builtins.txt", error)); - } + ensureGridBuiltins(); // Nexii's library, whose ObjectLinksetSittingAvatars has a parameter // without its type, included as a save would include it. llifstream in(std::string(AL_ALSCRIPT_TEST_DIR) + "/preprocessor/include/linkset.lsl", std::ios::binary); @@ -1514,6 +1512,7 @@ namespace tut void alpreprocessor_object::test<39>() { set_test_name("a function marked inline in any of the ways a mark is written, at the top of the script, and the marks taken off with the extensions off"); + ensureGridBuiltins(); ALPreprocessor::Options o = options(); o.extensions = true; ALPreprocessor::Result r = ALPreprocessor::run("integer f(integer a) inline { return a; }\n" @@ -1554,6 +1553,7 @@ namespace tut void alpreprocessor_object::test<40>() { set_test_name("const before a type is taken off, and each name it declares said where it is, a function's as a function"); + ensureGridBuiltins(); const auto at = [](const ALPreprocessor::Result& r, size_t i, const std::string& name, S32 line, S32 column, bool function) { return i < r.consts.size() && r.consts[i].name == name && r.consts[i].line == line && r.consts[i].column == column && r.consts[i].function == function; @@ -1838,7 +1838,7 @@ namespace tut // How often each module ran, as a module says it through `counted`, // a builtin of the VM, as SLua's are: a module's own globals are its // own, and what it sets is not the script's. - std::map gRuns; + static std::map gRuns; template<> template<> void alpreprocessor_object::test<48>() diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index b8b51170f1e..9b932204048 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -300,6 +300,27 @@ if(LINUX) endif() endif() +# No hot/cold splitting where GCC links with a mold older than 2.42.1. GCC +# moves a function's unlikely blocks into a part of their own ("[clone +# .cold]"), and where they all optimise away it still emits the part, empty, +# with an unwind entry covering no bytes at the address of whatever follows +# it. Those mold versions (rui314/mold#1648) put that entry in .eh_frame_hdr's +# search table beside the real function's, in an order their unstable sort +# leaves to chance; where the empty one comes last the unwinder finds no +# entry for the function, and an exception thrown through it ends the program +# in std::terminate whatever would have caught it. Without splitting there +# are no such parts. The link option is for LTO, which generates the code +# there. The vcpkg ports keep their empty parts: only a mold that leaves them +# out of the table mends those. +if( + COMPILER_IS_GCC + AND CMAKE_CXX_COMPILER_LINKER_ID STREQUAL "MOLD" + AND CMAKE_CXX_COMPILER_LINKER_VERSION VERSION_LESS 2.42.1 +) + target_compile_options(al_flags INTERFACE -fno-reorder-blocks-and-partition) + target_link_options(al_flags INTERFACE -fno-reorder-blocks-and-partition) +endif() + # Toolchain conformance and miscellany. if(WINDOWS) target_compile_options( diff --git a/indra/llcharacter/CMakeLists.txt b/indra/llcharacter/CMakeLists.txt index 51fcebbd12f..6f88b72dc5c 100644 --- a/indra/llcharacter/CMakeLists.txt +++ b/indra/llcharacter/CMakeLists.txt @@ -63,10 +63,9 @@ target_link_libraries( target_precompile_headers(llcharacter REUSE_FROM llprecompiled) if(AL_BUILD_TESTS) - al_add_test(lljoint PROJECT llcharacter SOURCES lljoint.cpp LIBRARIES llcommon llmath) - foreach( test + lljoint llpose llcharacter llmotioncontroller diff --git a/indra/llcharacter/tests/llbvhloader_test.cpp b/indra/llcharacter/tests/llbvhloader_test.cpp index c282e549b8e..9aaf370aed1 100644 --- a/indra/llcharacter/tests/llbvhloader_test.cpp +++ b/indra/llcharacter/tests/llbvhloader_test.cpp @@ -318,6 +318,8 @@ namespace } namespace tut +{ +namespace { struct llbvhloader_data { @@ -410,6 +412,7 @@ namespace tut typedef test_group llbvhloader_test; typedef llbvhloader_test::object llbvhloader_object; tut::llbvhloader_test llbvhloader_testcase("LLBVHLoader"); +} template<> template<> void llbvhloader_object::test<1>() diff --git a/indra/llcharacter/tests/llcharacter_test.cpp b/indra/llcharacter/tests/llcharacter_test.cpp index 557904c431d..ab4dd2b519e 100644 --- a/indra/llcharacter/tests/llcharacter_test.cpp +++ b/indra/llcharacter/tests/llcharacter_test.cpp @@ -106,6 +106,8 @@ namespace } namespace tut +{ +namespace { struct llcharacter_data { @@ -162,6 +164,7 @@ namespace tut typedef test_group llcharacter_test; typedef llcharacter_test::object llcharacter_object; tut::llcharacter_test llcharacter_testcase("LLCharacter"); +} template<> template<> void llcharacter_object::test<1>() diff --git a/indra/llcharacter/tests/lljoint_test.cpp b/indra/llcharacter/tests/lljoint_test.cpp index 62f44b7cdae..3c92936e6e2 100644 --- a/indra/llcharacter/tests/lljoint_test.cpp +++ b/indra/llcharacter/tests/lljoint_test.cpp @@ -36,6 +36,8 @@ namespace tut +{ +namespace { struct lljoint_data { @@ -43,6 +45,7 @@ namespace tut typedef test_group lljoint_test; typedef lljoint_test::object lljoint_object; tut::lljoint_test lljoint_testcase("LLJoint"); +} template<> template<> void lljoint_object::test<1>() diff --git a/indra/llcharacter/tests/llkeyframemotion_test.cpp b/indra/llcharacter/tests/llkeyframemotion_test.cpp index 0e25dc82ab2..2406d4595ad 100644 --- a/indra/llcharacter/tests/llkeyframemotion_test.cpp +++ b/indra/llcharacter/tests/llkeyframemotion_test.cpp @@ -487,6 +487,8 @@ namespace } namespace tut +{ +namespace { struct llkeyframemotion_data { @@ -521,6 +523,7 @@ namespace tut typedef test_group llkeyframemotion_test; typedef llkeyframemotion_test::object llkeyframemotion_object; tut::llkeyframemotion_test llkeyframemotion_testcase("LLKeyframeMotion"); +} template<> template<> void llkeyframemotion_object::test<1>() diff --git a/indra/llcharacter/tests/llmotioncontroller_test.cpp b/indra/llcharacter/tests/llmotioncontroller_test.cpp index 2bae0e02268..ceece16b058 100644 --- a/indra/llcharacter/tests/llmotioncontroller_test.cpp +++ b/indra/llcharacter/tests/llmotioncontroller_test.cpp @@ -39,6 +39,8 @@ namespace } namespace tut +{ +namespace { struct llmotioncontroller_data { @@ -46,6 +48,7 @@ namespace tut typedef test_group llmotioncontroller_test; typedef llmotioncontroller_test::object llmotioncontroller_object; tut::llmotioncontroller_test llmotioncontroller_testcase("LLMotionController"); +} typedef LLMotionController::QuantumStep QuantumStep; diff --git a/indra/llcharacter/tests/llmultigesture_test.cpp b/indra/llcharacter/tests/llmultigesture_test.cpp index a6ab7a53b81..20a20db4e7d 100644 --- a/indra/llcharacter/tests/llmultigesture_test.cpp +++ b/indra/llcharacter/tests/llmultigesture_test.cpp @@ -117,6 +117,8 @@ namespace } namespace tut +{ +namespace { struct llmultigesture_data { @@ -124,6 +126,7 @@ namespace tut typedef test_group llmultigesture_test; typedef llmultigesture_test::object llmultigesture_object; tut::llmultigesture_test llmultigesture_testcase("LLMultiGesture"); +} template<> template<> void llmultigesture_object::test<1>() diff --git a/indra/llcharacter/tests/llpose_test.cpp b/indra/llcharacter/tests/llpose_test.cpp index 5239046ba9c..a722691dd15 100644 --- a/indra/llcharacter/tests/llpose_test.cpp +++ b/indra/llcharacter/tests/llpose_test.cpp @@ -76,6 +76,8 @@ namespace } namespace tut +{ +namespace { struct joints { @@ -93,16 +95,20 @@ namespace tut mC.setJointNum(2); } }; +} //------------------------------------------------------------------------- // LLJointState //------------------------------------------------------------------------- +namespace +{ struct lljointstate_data : public joints { }; typedef test_group lljointstate_test; typedef lljointstate_test::object lljointstate_object; tut::lljointstate_test lljointstate_testcase("LLJointState"); +} template<> template<> void lljointstate_object::test<1>() @@ -171,6 +177,8 @@ namespace tut //------------------------------------------------------------------------- // LLPose //------------------------------------------------------------------------- +namespace +{ struct llpose_data : public joints { LLPose mPose; @@ -178,6 +186,7 @@ namespace tut typedef test_group llpose_test; typedef llpose_test::object llpose_object; tut::llpose_test llpose_testcase("LLPose"); +} template<> template<> void llpose_object::test<1>() @@ -316,6 +325,8 @@ namespace tut //------------------------------------------------------------------------- // LLJointStateBlender //------------------------------------------------------------------------- +namespace +{ struct lljointstateblender_data : public joints { LLJointStateBlender mBlender; @@ -323,6 +334,7 @@ namespace tut typedef test_group lljointstateblender_test; typedef lljointstateblender_test::object lljointstateblender_object; tut::lljointstateblender_test lljointstateblender_testcase("LLJointStateBlender"); +} template<> template<> void lljointstateblender_object::test<1>() @@ -590,6 +602,8 @@ namespace tut //------------------------------------------------------------------------- // LLPoseBlender //------------------------------------------------------------------------- +namespace +{ struct llposeblender_data : public joints { LLPoseBlender mBlender; @@ -605,6 +619,7 @@ namespace tut typedef test_group llposeblender_test; typedef llposeblender_test::object llposeblender_object; tut::llposeblender_test llposeblender_testcase("LLPoseBlender"); +} template<> template<> void llposeblender_object::test<1>() diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 3927400b6a4..d9724dbb607 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -358,9 +358,16 @@ if(AL_BUILD_TESTS) # 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. + # 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) - al_add_test(${test} PROJECT llcommon LIBRARIES llcommon PYTHON) + al_add_test( + ${test} + PROJECT llcommon + LIBRARIES llcommon + PYTHON + ENVIRONMENT PYTHONDONTWRITEBYTECODE=1 + ) endforeach() # The SIMD ops layer, at the tree's tier and, where the tiers differ, at diff --git a/indra/llcommon/tests/StringVec.h b/indra/llcommon/tests/StringVec.h index 4311cba992d..1c5341d1a49 100644 --- a/indra/llcommon/tests/StringVec.h +++ b/indra/llcommon/tests/StringVec.h @@ -18,7 +18,7 @@ typedef std::vector StringVec; -std::ostream& operator<<(std::ostream& out, const StringVec& strings) +inline std::ostream& operator<<(std::ostream& out, const StringVec& strings) { out << '('; StringVec::const_iterator begin(strings.begin()), end(strings.end()); diff --git a/indra/llcommon/tests/alwatchedfile_test.cpp b/indra/llcommon/tests/alwatchedfile_test.cpp index 7b62c024ded..852488ccc9e 100644 --- a/indra/llcommon/tests/alwatchedfile_test.cpp +++ b/indra/llcommon/tests/alwatchedfile_test.cpp @@ -108,23 +108,26 @@ namespace tut } // The main loop's queue, which the watcher hands its looks back - // through, as the viewer's is. - static LL::WorkQueue::ptr_t mainLoop() - { - static LL::WorkQueue queue("mainloop", 1024); - return LL::WorkQueue::getInstance("mainloop"); - } + // through, as the viewer's is. Made when a test first pumps, and + // gone with its fixture: one left alive would stand in for the + // viewer's main loop for every test after it in the process. The + // watched files go first, and with the last of them the watcher's + // worker is joined, so nothing posts here once it is gone. + std::unique_ptr main_loop; // The timers ticked and the main loop run, as the viewer's frames // do, until `until` holds or a few seconds go by. - static bool pumped(const std::function& until) + bool pumped(const std::function& until) { - const LL::WorkQueue::ptr_t main = mainLoop(); - LLTimer timer; + if (!main_loop) + { + main_loop = std::make_unique("mainloop", 1024); + } + LLTimer timer; while (timer.getElapsedTimeF32() < 5.f) { LLEventTimer::updateClass(); - main->runPending(); + main_loop->runPending(); if (until()) { return true; diff --git a/indra/llcommon/tests/apply_test.cpp b/indra/llcommon/tests/apply_test.cpp index 6d213c2db9b..6bb4e23b009 100644 --- a/indra/llcommon/tests/apply_test.cpp +++ b/indra/llcommon/tests/apply_test.cpp @@ -15,7 +15,6 @@ #include "apply.h" // STL headers // std headers -#include // external library headers // other Linden headers #include "llsd.h" @@ -24,19 +23,8 @@ #include #include -// for ensure_equals -std::ostream& operator<<(std::ostream& out, const std::vector& stringvec) -{ - const char* delim = "["; - for (const auto& str : stringvec) - { - out << delim << std::quoted(str); - delim = ", "; - } - return out << ']'; -} - -// the above must be declared BEFORE ensure_equals(std::vector) +// for ensure_equals(std::vector): must come BEFORE lltut.h +#include "StringVec.h" #include "../test/lltut.h" /***************************************************************************** @@ -71,12 +59,12 @@ namespace tut // ensure that apply() actually reaches the target method -- // lack of ensure_equals() failure could be due to no-op apply() - bool called{ false }; + static bool called{ false }; // capture calls from collect() - std::vector collected; + static std::vector collected; /*------------------------- test functions -------------------------*/ - void various(LLSD::Boolean b, LLSD::Integer i, LLSD::Real f, const LLSD::String& s, + static void various(LLSD::Boolean b, LLSD::Integer i, LLSD::Real f, const LLSD::String& s, const LLSD::UUID& uu, const LLSD::Date& dt, const LLSD::URI& uri, const LLSD::Binary& bin) { @@ -91,7 +79,7 @@ namespace tut ensure_equals("bin mismatch", bin, statics::bin); } - void strings(std::string s0, std::string s1, std::string s2, std::string s3, std::string s4) + 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]); @@ -101,7 +89,7 @@ namespace tut ensure_equals("s4 mismatch", s4, statics::quick[4]); } - void ints(int i0, int i1, int i2, int i3, int i4) + static void ints(int i0, int i1, int i2, int i3, int i4) { called = true; ensure_equals("i0 mismatch", i0, statics::fibs[0]); @@ -111,32 +99,32 @@ namespace tut ensure_equals("i4 mismatch", i4, statics::fibs[4]); } - void sdfunc(const LLSD& sd) + static void sdfunc(const LLSD& sd) { called = true; ensure_equals("sd mismatch", sd.asInteger(), statics::i); } - void intfunc(int i) + static void intfunc(int i) { called = true; ensure_equals("i mismatch", i, statics::i); } - void voidfunc() + static void voidfunc() { called = true; } // recursion tail - void collect() + static void collect() { called = true; } // collect(arbitrary) template - void collect(const std::string& first, ARGS&&... rest) + static void collect(const std::string& first, ARGS&&... rest) { statics::collected.push_back(first); collect(std::forward(rest)...); diff --git a/indra/llcommon/tests/classic_callback_test.cpp b/indra/llcommon/tests/classic_callback_test.cpp index fe79179ce65..ec255ed1c7d 100644 --- a/indra/llcommon/tests/classic_callback_test.cpp +++ b/indra/llcommon/tests/classic_callback_test.cpp @@ -21,6 +21,9 @@ // other Linden headers #include "../test/lltut.h" +namespace +{ + /***************************************************************************** * example callback *****************************************************************************/ @@ -83,6 +86,8 @@ struct OtherCallback } }; +} // anonymous namespace + /***************************************************************************** * TUT *****************************************************************************/ diff --git a/indra/llcommon/tests/lazyeventapi_test.cpp b/indra/llcommon/tests/lazyeventapi_test.cpp index f3fcf84e495..f2c2bf168eb 100644 --- a/indra/llcommon/tests/lazyeventapi_test.cpp +++ b/indra/llcommon/tests/lazyeventapi_test.cpp @@ -24,6 +24,9 @@ // observable side effect, solely for testing static LLSD data; +namespace +{ + // LLEventAPI listener subclass class MyListener: public LLEventAPI { @@ -56,6 +59,9 @@ class MyRegistrar: public LL::LazyEventAPI add("set", "This is a set operation", &listener::set_data); } }; + +} // anonymous namespace + // Normally we'd declare a static instance of MyRegistrar -- but because we // want to test both with and without, defer declaration to individual test // methods. diff --git a/indra/llcommon/tests/listener.h b/indra/llcommon/tests/listener.h index 887a2c90825..9baa4c56274 100644 --- a/indra/llcommon/tests/listener.h +++ b/indra/llcommon/tests/listener.h @@ -38,7 +38,7 @@ * test listener class *****************************************************************************/ class Listener; -std::ostream& operator<<(std::ostream&, const Listener&); +inline std::ostream& operator<<(std::ostream&, const Listener&); /// Bear in mind that this is strictly for testing class Listener @@ -116,7 +116,7 @@ class Listener LLSD mLastEvent; }; -std::ostream& operator<<(std::ostream& out, const Listener& listener) +inline std::ostream& operator<<(std::ostream& out, const Listener& listener) { out << "Listener(" << listener.getName() /* << "@" << &listener */ << ')'; return out; diff --git a/indra/llcommon/tests/lldependencies_test.cpp b/indra/llcommon/tests/lldependencies_test.cpp index fd554005a35..b13576a9853 100644 --- a/indra/llcommon/tests/lldependencies_test.cpp +++ b/indra/llcommon/tests/lldependencies_test.cpp @@ -44,6 +44,9 @@ /***************************************************************************** * Display helpers: must be defined BEFORE lltut.h! *****************************************************************************/ +namespace +{ + // Display an arbitary value as itself... template std::ostream& display(std::ostream& out, const T& value) @@ -97,6 +100,8 @@ std::ostream& operator<<(std::ostream& out, const std::set& set) return out; } +} // anonymous namespace + /***************************************************************************** * Now we can #include lltut.h *****************************************************************************/ @@ -105,6 +110,9 @@ std::ostream& operator<<(std::ostream& out, const std::set& set) /***************************************************************************** * Other helpers *****************************************************************************/ +namespace +{ + typedef LLDependencies<> StringDeps; typedef StringDeps::KeyList StringList; @@ -142,6 +150,8 @@ bool is_empty(const RANGE& range) return boost::begin(range) == boost::end(range); } +} // anonymous namespace + /***************************************************************************** * tut test group *****************************************************************************/ diff --git a/indra/llcommon/tests/lldoubledispatch_test.cpp b/indra/llcommon/tests/lldoubledispatch_test.cpp index 79c4edc0585..bafa7a74041 100644 --- a/indra/llcommon/tests/lldoubledispatch_test.cpp +++ b/indra/llcommon/tests/lldoubledispatch_test.cpp @@ -43,6 +43,8 @@ // other Linden headers #include "lltut.h" +namespace +{ /*---------------------------- Class hierarchy -----------------------------*/ // All objects are GameObjects. @@ -118,6 +120,8 @@ std::string asteroidStation(Asteroid& rock, SpaceStation& dock) return "asteroidStation"; } +} // anonymous namespace + /*------------------------------- Test code --------------------------------*/ namespace tut { diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp index e44cbf94e93..ce48c34a5a6 100644 --- a/indra/llcommon/tests/llerror_test.cpp +++ b/indra/llcommon/tests/llerror_test.cpp @@ -37,6 +37,9 @@ #include "../test/lltut.h" +namespace +{ + enum LogFieldIndex { TIME_FIELD, @@ -57,6 +60,8 @@ static const char* FieldName[] = "MSG" }; +} // anonymous namespace + namespace { #if defined(LL_CLANG) @@ -98,38 +103,41 @@ namespace namespace tut { - class TestRecorder : public LLError::Recorder + namespace { - public: - TestRecorder() + class TestRecorder : public LLError::Recorder + { + public: + TestRecorder() + { + showTime(false); + } + virtual ~TestRecorder() + {} + + virtual void recordMessage(LLError::ELevel level, + const std::string& message) { - showTime(false); + mMessages.push_back(message); } - virtual ~TestRecorder() - {} - virtual void recordMessage(LLError::ELevel level, - const std::string& message) - { - mMessages.push_back(message); - } - - int countMessages() { return (int) mMessages.size(); } - void clearMessages() { mMessages.clear(); } + int countMessages() { return (int) mMessages.size(); } + void clearMessages() { mMessages.clear(); } - std::string message(int n) - { - std::ostringstream test_name; - test_name << "testing message " << n << ", not enough messages"; + std::string message(int n) + { + std::ostringstream test_name; + test_name << "testing message " << n << ", not enough messages"; - tut::ensure(test_name.str(), n < countMessages()); - return mMessages[n]; - } + tut::ensure(test_name.str(), n < countMessages()); + return mMessages[n]; + } - private: - typedef std::vector MessageVector; - MessageVector mMessages; - }; + private: + typedef std::vector MessageVector; + MessageVector mMessages; + }; + } // anonymous namespace struct ErrorTestData { diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index 05c25e6627a..a16acb33077 100644 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -53,6 +53,9 @@ using namespace llcoro; /***************************************************************************** * Test helpers *****************************************************************************/ +namespace +{ + /// Simulate an event API whose response is immediate: sent on receipt of the /// initial request, rather than after some delay. This is the case that /// distinguishes postAndSuspend() from calling post(), then calling @@ -85,19 +88,48 @@ class ImmediateAPI Sync& mSync; }; +/// The coroutines need LLApp::isRunning(), and on Windows LLCoros hands an +/// exception escaping one to LLApp::instance(). An app per test, as this had, +/// 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. +class CoroTestApp: public LLTestApp +{ +public: + struct Running + { + Running() + { + app(); + setStatus(APP_STATUS_RUNNING); + } + ~Running() { setStopped(); } + }; + +private: + static CoroTestApp& app() + { + static CoroTestApp* sApp = new CoroTestApp; + return *sApp; + } +}; + +} // anonymous namespace + /***************************************************************************** * TUT *****************************************************************************/ namespace tut { - struct test_data + struct lleventcoro_data { Sync mSync; ImmediateAPI immediateAPI{mSync}; std::string replyName, errorName, threw, stringdata; LLSD result, errordata; int which; - LLTestApp testApp; + CoroTestApp::Running running; void explicit_wait(std::shared_ptr>& cbp); void waitForEventOn1(); @@ -105,11 +137,11 @@ namespace tut void postAndWait1(); void coroPumpPost(); }; - typedef test_group coroutine_group; + typedef test_group coroutine_group; typedef coroutine_group::object object; coroutine_group coroutinegrp("coroutine"); - void test_data::explicit_wait(std::shared_ptr>& cbp) + void lleventcoro_data::explicit_wait(std::shared_ptr>& cbp) { BEGIN { @@ -159,7 +191,7 @@ namespace tut ensure_equals(stringdata, "received"); } - void test_data::waitForEventOn1() + void lleventcoro_data::waitForEventOn1() { BEGIN { @@ -185,7 +217,7 @@ namespace tut ensure_equals(result.asString(), "received"); } - void test_data::coroPump() + void lleventcoro_data::coroPump() { BEGIN { @@ -213,7 +245,7 @@ namespace tut ensure_equals(result.asString(), "received"); } - void test_data::postAndWait1() + void lleventcoro_data::postAndWait1() { BEGIN { @@ -236,7 +268,7 @@ namespace tut ensure_equals(result.asInteger(), 18); } - void test_data::coroPumpPost() + void lleventcoro_data::coroPumpPost() { BEGIN { @@ -258,8 +290,9 @@ namespace tut ensure_equals(result.asInteger(), 18); } + // A coroutine consumes what a PUMP holds for it, one value a wait. template - void test() + static void check_coro_consumes() { PUMP pump(typeid(PUMP).name()); bool running{false}; @@ -324,13 +357,13 @@ namespace tut void object::test<6>() { set_test_name("LLEventMailDrop"); - tut::test(); + check_coro_consumes(); } template<> template<> void object::test<7>() { set_test_name("LLEventLogProxyFor"); - tut::test< LLEventLogProxyFor >(); + check_coro_consumes< LLEventLogProxyFor >(); } } diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp index 8b206f7b14c..ada009b99b2 100644 --- a/indra/llcommon/tests/lleventdispatcher_test.cpp +++ b/indra/llcommon/tests/lleventdispatcher_test.cpp @@ -65,6 +65,9 @@ using namespace llsd; #define NARGSa b, i, f, d, cp #define NARGSb s, uuid, date, uri, bin +namespace +{ + // For some registration methods we need methods on a subclass of // LLEventDispatcher. To simplify things, we'll use this Dispatcher subclass // for all our testing, including testing its own methods. @@ -172,7 +175,9 @@ struct Vars const_cast(this)->method1(obj); } - static void smethod1(const LLSD& obj); + // Nothing registers it, but it keeps the set of three whole; internal + // now, and so unused as far as GCC's -Wunused-function is concerned. + [[maybe_unused]] static void smethod1(const LLSD& obj); /*-------- Arbitrary-params (non-const, const, static) methods ---------*/ void methodna(NPARAMSa) @@ -312,19 +317,21 @@ void freenb(NPARAMSb) g.methodnb(NARGSb); } +} // anonymous namespace + /***************************************************************************** * TUT *****************************************************************************/ namespace tut { - void ensure_has(const std::string& outer, const std::string& inner) + 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); } template - std::string call_exc(CALLABLE&& func, const std::string& exc_frag) + static std::string call_exc(CALLABLE&& func, const std::string& exc_frag) { std::string what = catch_what(std::forward(func)); @@ -333,7 +340,7 @@ namespace tut } template - void call_logerr(CALLABLE&& func, const std::string& frag) + static void call_logerr(CALLABLE&& func, const std::string& frag) { CaptureLog capture; // the error should be logged; we just need to stop the exception @@ -1080,11 +1087,14 @@ namespace tut } // Cannot be defined inside function body... remind me again why we use C++... :-P - struct CallablesTriple + namespace { - std::string name, name_req; - LLSD& llsd; - }; + struct CallablesTriple + { + std::string name, name_req; + LLSD& llsd; + }; + } // anonymous namespace template<> template<> void object::test<16>() @@ -1170,10 +1180,10 @@ namespace tut } // Break out this data because we use it in a couple different tests. - LLSD array_funcs(llsd::array - (LLSDMap("a", "freena_array") ("b", "freenb_array"), - LLSDMap("a", "smethodna_array")("b", "smethodnb_array"), - LLSDMap("a", "methodna_array") ("b", "methodnb_array"))); + static LLSD array_funcs(llsd::array + (LLSDMap("a", "freena_array") ("b", "freenb_array"), + LLSDMap("a", "smethodna_array")("b", "smethodnb_array"), + LLSDMap("a", "methodna_array") ("b", "methodnb_array"))); template<> template<> void object::test<19>() @@ -1331,40 +1341,43 @@ namespace tut } } - struct DispatchResult: public LLDispatchListener + namespace { - using DR = DispatchResult; - - DispatchResult(): LLDispatchListener("results", "op") + struct DispatchResult: public LLDispatchListener { - add("strfunc", "return string", &DR::strfunc); - add("voidfunc", "void function", &DR::voidfunc); - add("emptyfunc", "return empty LLSD", &DR::emptyfunc); - add("intfunc", "return Integer LLSD", &DR::intfunc); - add("llsdfunc", "return passed LLSD", &DR::llsdfunc); - add("mapfunc", "return map LLSD", &DR::mapfunc); - add("arrayfunc", "return array LLSD", &DR::arrayfunc); - } + using DR = DispatchResult; - std::string strfunc(const std::string& str) const { return "got " + str; } - void voidfunc() const {} - LLSD emptyfunc() const { return {}; } - int intfunc(int i) const { return -i; } - LLSD llsdfunc(const LLSD& event) const - { - LLSD result{ event }; - result["with"] = "string"; - return result; - } - LLSD mapfunc(int i, const std::string& str) const - { - return llsd::map("i", intfunc(i), "str", strfunc(str)); - } - LLSD arrayfunc(int i, const std::string& str) const - { - return llsd::array(intfunc(i), strfunc(str)); - } - }; + DispatchResult(): LLDispatchListener("results", "op") + { + add("strfunc", "return string", &DR::strfunc); + add("voidfunc", "void function", &DR::voidfunc); + add("emptyfunc", "return empty LLSD", &DR::emptyfunc); + add("intfunc", "return Integer LLSD", &DR::intfunc); + add("llsdfunc", "return passed LLSD", &DR::llsdfunc); + add("mapfunc", "return map LLSD", &DR::mapfunc); + add("arrayfunc", "return array LLSD", &DR::arrayfunc); + } + + std::string strfunc(const std::string& str) const { return "got " + str; } + void voidfunc() const {} + LLSD emptyfunc() const { return {}; } + int intfunc(int i) const { return -i; } + LLSD llsdfunc(const LLSD& event) const + { + LLSD result{ event }; + result["with"] = "string"; + return result; + } + LLSD mapfunc(int i, const std::string& str) const + { + return llsd::map("i", intfunc(i), "str", strfunc(str)); + } + LLSD arrayfunc(int i, const std::string& str) const + { + return llsd::array(intfunc(i), strfunc(str)); + } + }; + } // anonymous namespace template<> template<> void object::test<23>() diff --git a/indra/llcommon/tests/lleventfilter_test.cpp b/indra/llcommon/tests/lleventfilter_test.cpp index 2a93d1946c5..e8dd37ee60e 100644 --- a/indra/llcommon/tests/lleventfilter_test.cpp +++ b/indra/llcommon/tests/lleventfilter_test.cpp @@ -45,6 +45,9 @@ /***************************************************************************** * Test classes *****************************************************************************/ +namespace +{ + // Strictly speaking, we're testing LLEventTimeoutBase rather than the // production LLEventTimeout (using LLTimer) because we don't want every test // run to pause for some number of seconds until we reach a real timeout. But @@ -152,6 +155,8 @@ class TestEventThrottle: public LLEventThrottleBase LLEventTimeoutBase::Action mAlarmAction; }; +} // anonymous namespace + /***************************************************************************** * TUT *****************************************************************************/ @@ -405,8 +410,9 @@ namespace tut ensure_equals("17", cat.result, "136;12;17"); // "17" delivered } + // A PUMP keeps what no listener consumed, and replays it on the next listen(). template - void test() + static void check_mail_drop_replay() { PUMP pump(typeid(PUMP).name()); LLSD data{LLSD::emptyArray()}; @@ -467,14 +473,14 @@ namespace tut void filter_object::test<6>() { set_test_name("LLEventMailDrop"); - tut::test(); + check_mail_drop_replay(); } template<> template<> void filter_object::test<7>() { set_test_name("LLEventLogProxyFor"); - tut::test< LLEventLogProxyFor >(); + check_mail_drop_replay< LLEventLogProxyFor >(); } } // namespace tut diff --git a/indra/llcommon/tests/llevents_test.cpp b/indra/llcommon/tests/llevents_test.cpp index c5d5dcb9d40..4232efd9ca7 100644 --- a/indra/llcommon/tests/llevents_test.cpp +++ b/indra/llcommon/tests/llevents_test.cpp @@ -51,7 +51,7 @@ #include "stringize.h" template -T make(const T& value) +static T make(const T& value) { return value; } @@ -69,6 +69,23 @@ struct events_data listener1("second") { } + // The named pumps these tests obtain go with each test, and every + // listener on them, bound to this fixture or to a test's locals, with + // them: each test finds them new whatever ran before it, and none is + // left for a later post to call into a dead object. + ~events_data() + { + for (const char* name : { "per-frame", "login", "upstream", "filter0", "filter1", "button", + "stream", "heaptest" }) + { + const auto found = pumps.mPumpMap.find(name); + if (found != pumps.mPumpMap.end() && pumps.mOurPumps.count(found->second)) + { + // unregisters itself + delete found->second; + } + } + } LLEventPumps& pumps; Listener listener0; Listener listener1; @@ -187,20 +204,6 @@ void events_object::test<2>() check_listener("got", listener1, 0); } -bool chainEvents(Listener& someListener, const LLSD& event) -{ - // Make this call so we can watch for side effects for test purposes. - someListener.call(event); - // This function represents a recursive event chain -- or some other - // scenario in which an event handler raises additional events. - int value = event.asInteger(); - if (value) - { - LLEventPumps::instance().obtain("login").post(value - 1); - } - return false; -} - template<> template<> void events_object::test<3>() { @@ -371,7 +374,7 @@ void events_object::test<7>() } // Define a function that accepts an LLListenerOrPumpName -void eventSource(const LLListenerOrPumpName& listener) +static void eventSource(const LLListenerOrPumpName& listener) { // Pretend that some time has elapsed. Call listener immediately. listener(17); @@ -400,6 +403,9 @@ void events_object::test<8>() ensure("threw Empty", !threw.empty()); } +namespace +{ + class TempListener: public Listener { public: @@ -418,6 +424,8 @@ class TempListener: public Listener bool& mLiveFlag; }; +} // anonymous namespace + template<> template<> void events_object::test<9>() { @@ -447,6 +455,9 @@ void events_object::test<9>() heaptest.stopListening("temp"); } +namespace +{ + class TempTrackableListener: public TempListener, public LLEventTrackable { public: @@ -455,6 +466,8 @@ class TempTrackableListener: public TempListener, public LLEventTrackable {} }; +} // anonymous namespace + template<> template<> void events_object::test<10>() { diff --git a/indra/llcommon/tests/llfile_test.cpp b/indra/llcommon/tests/llfile_test.cpp index cf39294882f..46ea565b45b 100644 --- a/indra/llcommon/tests/llfile_test.cpp +++ b/indra/llcommon/tests/llfile_test.cpp @@ -30,6 +30,7 @@ #include "lltut.h" #include "linden_common.h" #include "llfile.h" +#include "lluuid.h" namespace tut { @@ -45,21 +46,32 @@ namespace tut return path.append(element); } - static std::filesystem::path get_testdir(const std::filesystem::path& tempdir) + // Each named for its test alone, by `unique`: two runs at once, or a + // test that failed before it cleaned up, cannot meet another's files. + static std::filesystem::path get_testdir(const std::filesystem::path& tempdir, const std::string& unique) { - return append_filename(tempdir, std::u8string(u8"test_dir")); + return append_filename(tempdir, std::u8string(u8"test_dir_") + std::u8string(unique.begin(), unique.end())); } - static std::filesystem::path get_testdir_unicode(const std::filesystem::path& tempdir) + static std::filesystem::path get_testdir_unicode(const std::filesystem::path& tempdir, const std::string& unique) { // Example Unicode directory name: "test_ユニコード_dir" - return append_filename(tempdir, std::u8string(u8"test_\xE3\x83\xA6\xE3\x83\x8B\xE3\x82\xB3\xE3\x83\xBC\xE3\x83\x89_dir")); + return append_filename(tempdir, std::u8string(u8"test_\xE3\x83\xA6\xE3\x83\x8B\xE3\x82\xB3\xE3\x83\xBC\xE3\x83\x89_dir_") + + std::u8string(unique.begin(), unique.end())); } struct llfile_test { + std::string unique = LLUUID::generateNewID().asString(); std::filesystem::path tempdir = LLFile::tmpdir(); - std::filesystem::path testdir = get_testdir(tempdir); + std::filesystem::path testdir = get_testdir(tempdir, unique); + + // whatever a test leaves in its folders goes with it + ~llfile_test() + { + clear_entire_dir(testdir); + clear_entire_dir(get_testdir_unicode(tempdir, unique)); + } }; typedef test_group llfile_test_t; typedef llfile_test_t::object llfile_test_object_t; @@ -159,6 +171,7 @@ namespace tut const size_t numints = 1024; // Testing the LLFile class implementation + LLFile::mkdir(testdir); std::filesystem::path testfile = testdir; testfile.append("llfile_test.bin"); @@ -228,6 +241,11 @@ namespace tut std::filesystem::path testfile = testdir; testfile.append("llfile_test.bin"); + // a file already there, for noreplace to refuse + LLFile::mkdir(testdir); + const char* testdata = "testdata"; + LLFile::write(testfile, testdata, 0, strlen(testdata)); + std::error_code ec; LLFile file(testfile, LLFile::out | LLFile::noreplace, ec); ensure("LLFile constructor should not have opened the already existing file", !file); @@ -255,7 +273,7 @@ namespace tut void llfile_test_object_t::test<5>() { // Test file and directory operations with Unicode paths and filenames - std::filesystem::path testdir_unicode = get_testdir_unicode(tempdir); + std::filesystem::path testdir_unicode = get_testdir_unicode(tempdir, unique); // Unicode filename: "ファイル_テスト.bin" (means "file_test.bin" in Japanese) std::string unicode_filename = "\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB_\xE3\x83\x86\xE3\x82\xB9\xE3\x83\x88.bin"; diff --git a/indra/llcommon/tests/llheteromap_test.cpp b/indra/llcommon/tests/llheteromap_test.cpp index cabfb915939..7d7a3f96f00 100644 --- a/indra/llcommon/tests/llheteromap_test.cpp +++ b/indra/llcommon/tests/llheteromap_test.cpp @@ -19,7 +19,7 @@ // external library headers // (pacify clang) -std::ostream& operator<<(std::ostream& out, const std::set& strset); +static std::ostream& operator<<(std::ostream& out, const std::set& strset); // other Linden headers #include "../test/lltut.h" @@ -27,7 +27,7 @@ static std::string clog; static std::set dlog; // want to be able to use ensure_equals() on a set -std::ostream& operator<<(std::ostream& out, const std::set& strset) +static std::ostream& operator<<(std::ostream& out, const std::set& strset) { out << '{'; const char* delim = ""; @@ -41,6 +41,9 @@ std::ostream& operator<<(std::ostream& out, const std::set& strset) return out; } +namespace +{ + // unrelated test classes struct Chalk { @@ -100,6 +103,8 @@ struct Chowdah Chowdah(const Chowdah&); // no implementation }; +} // anonymous namespace + /***************************************************************************** * TUT *****************************************************************************/ diff --git a/indra/llcommon/tests/llhttpdate_test.cpp b/indra/llcommon/tests/llhttpdate_test.cpp index b580b09a9f2..023987f1710 100644 --- a/indra/llcommon/tests/llhttpdate_test.cpp +++ b/indra/llcommon/tests/llhttpdate_test.cpp @@ -99,8 +99,29 @@ namespace tut ensure("Current time in RFC 1123", (strcmp(expected, actual.c_str()) == 0)); } - void test_date_string(const std::string &locale, struct tm *t, - const std::string &fmt, const std::string &expected) + 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 + // 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() + { + LLStringUtil::setLocale(mLocale); + struct tm any{}; + LLDate::toHTTPDateString(&any, "%Y"); + 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) { std::string result = LLDate::toHTTPDateString(t, fmt); LLStringUtil::toLower(result); @@ -115,15 +136,13 @@ namespace tut const char *en_locale = "en_US.UTF-8"; const char *fr_locale = "fr_FR.UTF-8"; - std::string prev_locale = LLStringUtil::getLocale(); - std::string prev_clocale = std::string(setlocale(LC_TIME, NULL)); + TimeLocaleRestore restore; time_t test_time = 1252374030; // 8 Sep 2009 01:40:01 struct tm *t = gmtime(&test_time); setlocale(LC_TIME, en_locale); if (strcmp(setlocale(LC_TIME, NULL), en_locale) != 0) { - setlocale(LC_TIME, prev_clocale.c_str()); skip("Cannot set English locale"); } @@ -141,8 +160,6 @@ namespace tut setlocale(LC_TIME, fr_locale); if (strcmp(setlocale(LC_TIME, NULL), fr_locale) != 0) { - LLStringUtil::setLocale(prev_locale); - setlocale(LC_TIME, prev_clocale.c_str()); skip("Cannot set French locale"); } @@ -155,8 +172,5 @@ namespace tut test_date_string(fr_locale, t, "%Y", "2009"); test_date_string(fr_locale, t, "%A", "mardi"); test_date_string(fr_locale, t, "%B", "septembre"); - - LLStringUtil::setLocale(prev_locale); - setlocale(LC_TIME, prev_clocale.c_str()); } } diff --git a/indra/llcommon/tests/llinitparam_test.cpp b/indra/llcommon/tests/llinitparam_test.cpp index 7d9bee378e4..0096b98fe3d 100644 --- a/indra/llcommon/tests/llinitparam_test.cpp +++ b/indra/llcommon/tests/llinitparam_test.cpp @@ -42,6 +42,9 @@ // ParamValue<> is specialised for TestPoint below. // +namespace +{ + // A named-value lookup, the mechanism behind "left"/"center" in XUI. struct TestNames : public LLInitParam::TypeValuesHelper { @@ -126,6 +129,21 @@ 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. +struct FirstChoiceOfThings : public LLInitParam::ChoiceBlock +{ + Alternative number; + Alternative text; + + FirstChoiceOfThings() + : number("number", 0), + text("text", "") + {} +}; + struct IgnoredBlock : public LLInitParam::Block { Optional kept; @@ -151,6 +169,8 @@ struct TestPoint S32 mY; }; +} // anonymous namespace + namespace LLInitParam { template<> @@ -188,6 +208,9 @@ namespace LLInitParam }; } +namespace +{ + struct PointBlock : public LLInitParam::Block { Optional point; @@ -210,6 +233,8 @@ struct PointChoice : public LLInitParam::ChoiceBlock {} }; +} // anonymous namespace + namespace tut { struct llinitparam_data @@ -356,7 +381,7 @@ namespace tut { set_test_name("choosing one alternative unprovides the other"); - ChoiceOfThings choice; + FirstChoiceOfThings choice; ensure("first declared alternative is chosen initially", choice.number.isChosen()); choice.text = std::string("hello"); diff --git a/indra/llcommon/tests/llinstancetracker_test.cpp b/indra/llcommon/tests/llinstancetracker_test.cpp index bf661dc0514..ae315e45c48 100644 --- a/indra/llcommon/tests/llinstancetracker_test.cpp +++ b/indra/llcommon/tests/llinstancetracker_test.cpp @@ -40,6 +40,9 @@ // other Linden headers #include "../test/lltut.h" +namespace +{ + struct Badness: public std::runtime_error { Badness(const std::string& what): std::runtime_error(what) {} @@ -69,6 +72,8 @@ struct Unkeyed: public LLInstanceTracker } }; +} // anonymous namespace + /***************************************************************************** * TUT *****************************************************************************/ diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp index ae20e7ed279..d833e6185fe 100644 --- a/indra/llcommon/tests/llleap_test.cpp +++ b/indra/llcommon/tests/llleap_test.cpp @@ -45,7 +45,7 @@ const size_t BUFFERED_LENGTH = 1023*1024; // try wrangling just under a megabyte // capture std::weak_ptrs to LLLeap instances so we can tell when they expire typedef std::vector> LLLeapVector; -void waitfor(const LLLeapVector& instances, int timeout=60) +static void waitfor(const LLLeapVector& instances, int timeout=60) { int i; for (i = 0; i < timeout; ++i) @@ -81,7 +81,7 @@ void waitfor(const LLLeapVector& instances, int timeout=60) i < timeout); } -void waitfor(LLLeap* instance, int timeout=60) +static void waitfor(LLLeap* instance, int timeout=60) { LLLeapVector instances; instances.push_back(instance->getWeak()); @@ -307,67 +307,70 @@ namespace tut ensure("bad launch returned non-NULL", ! LLLeap::create("bad exe", BADPYTHON, false)); } - // Generic self-contained listener: derive from this and override its - // call() method, then tell somebody to post on the pump named getName(). - // Control will reach your call() override. - struct ListenerBase + namespace { - // Pass the pump name you want; will tweak for uniqueness. - ListenerBase(const std::string& name): - mPump(name, true) + // Generic self-contained listener: derive from this and override its + // call() method, then tell somebody to post on the pump named getName(). + // Control will reach your call() override. + struct ListenerBase { - mPump.listen(name, boost::bind(&ListenerBase::call, this, _1)); - } - - virtual ~ListenerBase() {} // pacify MSVC + // Pass the pump name you want; will tweak for uniqueness. + ListenerBase(const std::string& name): + mPump(name, true) + { + mPump.listen(name, boost::bind(&ListenerBase::call, this, _1)); + } - virtual bool call(const LLSD& request) - { - return false; - } + virtual ~ListenerBase() {} // pacify MSVC - LLEventPump& getPump() { return mPump; } - const LLEventPump& getPump() const { return mPump; } + virtual bool call(const LLSD& request) + { + return false; + } - std::string getName() const { return mPump.getName(); } - void post(const LLSD& data) { mPump.post(data); } + LLEventPump& getPump() { return mPump; } + const LLEventPump& getPump() const { return mPump; } - LLEventStream mPump; - }; + std::string getName() const { return mPump.getName(); } + void post(const LLSD& data) { mPump.post(data); } - // Mimic a dummy little LLEventAPI that merely sends a reply back to its - // requester on the "reply" pump. - struct AckAPI: public ListenerBase - { - AckAPI(): ListenerBase("AckAPI") {} + LLEventStream mPump; + }; - virtual bool call(const LLSD& request) + // Mimic a dummy little LLEventAPI that merely sends a reply back to its + // requester on the "reply" pump. + struct AckAPI: public ListenerBase { - LLEventPumps::instance().obtain(request["reply"]).post("ack"); - return false; - } - }; + AckAPI(): ListenerBase("AckAPI") {} - // Give LLLeap script a way to post success/failure. - struct Result: public ListenerBase - { - Result(): ListenerBase("Result") {} + virtual bool call(const LLSD& request) + { + LLEventPumps::instance().obtain(request["reply"]).post("ack"); + return false; + } + }; - virtual bool call(const LLSD& request) + // Give LLLeap script a way to post success/failure. + struct Result: public ListenerBase { - mData = request; - return false; - } + Result(): ListenerBase("Result") {} - void ensure() const - { - tut::ensure(std::string("never posted to ") + getName(), mData.isDefined()); - // Post an empty string for success, non-empty string is failure message. - tut::ensure(mData, mData.asString().empty()); - } + virtual bool call(const LLSD& request) + { + mData = request; + return false; + } - LLSD mData; - }; + void ensure() const + { + tut::ensure(std::string("never posted to ") + getName(), mData.isDefined()); + // Post an empty string for success, non-empty string is failure message. + tut::ensure(mData, mData.asString().empty()); + } + + LLSD mData; + }; + } // anonymous namespace template<> template<> void object::test<8>() @@ -390,17 +393,20 @@ namespace tut result.ensure(); } - struct ReqIDAPI: public ListenerBase + namespace { - ReqIDAPI(): ListenerBase("ReqIDAPI") {} - - virtual bool call(const LLSD& request) + struct ReqIDAPI: public ListenerBase { - // free function from llevents.h - sendReply(LLSD(), request); - return false; - } - }; + ReqIDAPI(): ListenerBase("ReqIDAPI") {} + + virtual bool call(const LLSD& request) + { + // free function from llevents.h + sendReply(LLSD(), request); + return false; + } + }; + } // anonymous namespace template<> template<> void object::test<9>() @@ -452,8 +458,8 @@ namespace tut // This is the body of test<10>, extracted so we can run it over a number // of large-message sizes. - void test_large_message(const std::string& PYTHON, const std::string& reader_module, - const std::string& test_name, size_t size) + static void test_large_message(const std::string& PYTHON, const std::string& reader_module, + const std::string& test_name, size_t size) { ReqIDAPI api; Result result; @@ -517,73 +523,76 @@ namespace tut result.ensure(); } - struct TestLargeMessage + namespace { - TestLargeMessage(const std::string& PYTHON_, const std::string& reader_module_, - const std::string& test_name_): - PYTHON(PYTHON_), - reader_module(reader_module_), - test_name(test_name_) - {} - - bool operator()(size_t left, size_t right) const + struct TestLargeMessage { - // We don't know whether upper_bound is going to pass the "sought - // value" as the left or the right operand. We pass 0 as the - // "sought value" so we can distinguish it. Of course that means - // the sequence we're searching must not itself contain 0! - size_t size; - bool success; - if (left) + TestLargeMessage(const std::string& PYTHON_, const std::string& reader_module_, + const std::string& test_name_): + PYTHON(PYTHON_), + reader_module(reader_module_), + test_name(test_name_) + {} + + bool operator()(size_t left, size_t right) const { - size = left; - // Consider our return value carefully. Normal binary_search - // (or, in our case, upper_bound) expects a container sorted - // in ascending order, and defaults to the std::less - // comparator. Our container is in fact in ascending order, so - // return consistently with std::less. Here we were called as - // compare(item, sought). If std::less were called that way, - // 'true' would mean to move right (to higher numbers) within - // the sequence: the item being considered is less than the - // sought value. For us, that means that test_large_message() - // success should return 'true'. - success = true; - } - else - { - size = right; - // Here we were called as compare(sought, item). If std::less - // were called that way, 'true' would mean to move left (to - // lower numbers) within the sequence: the sought value is - // less than the item being considered. For us, that means - // test_large_message() FAILURE should return 'true', hence - // test_large_message() success should return 'false'. - success = false; - } + // We don't know whether upper_bound is going to pass the "sought + // value" as the left or the right operand. We pass 0 as the + // "sought value" so we can distinguish it. Of course that means + // the sequence we're searching must not itself contain 0! + size_t size; + bool success; + if (left) + { + size = left; + // Consider our return value carefully. Normal binary_search + // (or, in our case, upper_bound) expects a container sorted + // in ascending order, and defaults to the std::less + // comparator. Our container is in fact in ascending order, so + // return consistently with std::less. Here we were called as + // compare(item, sought). If std::less were called that way, + // 'true' would mean to move right (to higher numbers) within + // the sequence: the item being considered is less than the + // sought value. For us, that means that test_large_message() + // success should return 'true'. + success = true; + } + else + { + size = right; + // Here we were called as compare(sought, item). If std::less + // were called that way, 'true' would mean to move left (to + // lower numbers) within the sequence: the sought value is + // less than the item being considered. For us, that means + // test_large_message() FAILURE should return 'true', hence + // test_large_message() success should return 'false'. + success = false; + } - try - { - test_large_message(PYTHON, reader_module, test_name, size); - std::cout << "test_large_message(" << size << ") succeeded" << std::endl; - return success; - } - catch (const failure& e) - { - std::cout << "test_large_message(" << size << ") failed: " << e.what() << std::endl; - return ! success; + try + { + test_large_message(PYTHON, reader_module, test_name, size); + std::cout << "test_large_message(" << size << ") succeeded" << std::endl; + return success; + } + catch (const failure& e) + { + std::cout << "test_large_message(" << size << ") failed: " << e.what() << std::endl; + return ! success; + } } - } - const std::string PYTHON, reader_module, test_name; - }; + const std::string PYTHON, reader_module, test_name; + }; + } // anonymous namespace // The point of this function is to try to find a size at which // test_large_message() can succeed. We still want the overall test to // fail; otherwise we won't get the coder's attention -- but if // test_large_message() fails, try to find a plausible size at which it // DOES work. - void test_or_split(const std::string& PYTHON, const std::string& reader_module, - const std::string& test_name, size_t size) + static void test_or_split(const std::string& PYTHON, const std::string& reader_module, + const std::string& test_name, size_t size) { try { diff --git a/indra/llcommon/tests/llmainthreadtask_test.cpp b/indra/llcommon/tests/llmainthreadtask_test.cpp index 9ccf391327e..a4fab4ba2fa 100644 --- a/indra/llcommon/tests/llmainthreadtask_test.cpp +++ b/indra/llcommon/tests/llmainthreadtask_test.cpp @@ -59,12 +59,17 @@ namespace tut ensure("didn't return result", result); } - struct StaticData + namespace { - 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; + } // anonymous namespace template<> template<> void object::test<2>() diff --git a/indra/llcommon/tests/llpounceable_test.cpp b/indra/llcommon/tests/llpounceable_test.cpp index b3024966c5f..20954be5d02 100644 --- a/indra/llcommon/tests/llpounceable_test.cpp +++ b/indra/llcommon/tests/llpounceable_test.cpp @@ -20,6 +20,9 @@ // other Linden headers #include "../test/lltut.h" +namespace +{ + /*----------------------------- string testing -----------------------------*/ void append(std::string* dest, const std::string& src) { @@ -42,8 +45,8 @@ void setter(Data** dest, Data* ptr) static Data* static_check = 0; -// Set up an extern pointer to an LLPounceableStatic so the linker will fill -// in the forward reference from below, before runtime. +// Declare an LLPounceableStatic ahead of its definition below, so the +// forward reference is resolved before runtime. extern LLPounceable gForward; struct EnqueueCall @@ -66,6 +69,8 @@ struct EnqueueCall // Now declare gForward. Its constructor should not run until after nqcall's. LLPounceable gForward; +} // anonymous namespace + /***************************************************************************** * TUT *****************************************************************************/ @@ -88,6 +93,16 @@ namespace tut // LLPounceableStatic. ensure("static_check should still be null", ! 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. + struct LetGo + { + ~LetGo() + { + gForward = nullptr; + static_check = nullptr; + } + } let_go; gForward = &myData; // should run setter ensure_equals("static_check should be &myData", static_check, &myData); } diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp index 43d1b243f2d..a17247d2325 100644 --- a/indra/llcommon/tests/llprocess_test.cpp +++ b/indra/llcommon/tests/llprocess_test.cpp @@ -43,7 +43,7 @@ #include #endif -std::string apr_strerror_helper(apr_status_t rv) +static std::string apr_strerror_helper(apr_status_t rv) { char errbuf[256]; apr_strerror(rv, errbuf, sizeof(errbuf)); @@ -126,7 +126,7 @@ static std::string readfile_if_present(const std::string& pathname) /// Looping on LLProcess::isRunning() must now be accompanied by pumping /// "mainloop" -- otherwise the status won't update and you get an infinite /// loop. -void yield(int seconds=1) +static void yield(int seconds=1) { // This function simulates waiting for another viewer frame sleep(seconds); @@ -136,7 +136,7 @@ void yield(int seconds=1) constexpr int EOF_EVENT_RETRY_COUNT = 20; constexpr auto EOF_EVENT_RETRY_DELAY = std::chrono::milliseconds(50); -void waitfor(LLProcess& proc, int timeout=60) +static void waitfor(LLProcess& proc, int timeout=60) { int i = 0; for ( ; i < timeout && proc.isRunning(); ++i) @@ -149,7 +149,7 @@ void waitfor(LLProcess& proc, int timeout=60) tut::ensure(msg, i < timeout); } -void waitfor(LLProcess::handle h, const std::string& desc, int timeout=60) +static void waitfor(LLProcess::handle h, const std::string& desc, int timeout=60) { int i = 0; for ( ; i < timeout && LLProcess::isRunning(h, desc); ++i) @@ -162,6 +162,9 @@ void waitfor(LLProcess::handle h, const std::string& desc, int timeout=60) tut::ensure(msg, i < timeout); } +namespace +{ + /** * Construct an LLProcess to run a Python script. */ @@ -264,6 +267,8 @@ struct PythonProcessLauncher NamedExtTempFile mScript; }; +} // anonymous namespace + #if LL_WINDOWS namespace { @@ -489,6 +494,9 @@ static std::string python_out(const std::string& desc, const CONTENT& script) return py.run_read(); } +namespace +{ + /// Create a temporary directory and clean it up later. class NamedTempDir { @@ -518,6 +526,8 @@ class NamedTempDir bool mCreated; }; +} // anonymous namespace + /***************************************************************************** * TUT *****************************************************************************/ @@ -538,13 +548,16 @@ namespace tut typedef llprocess_group::object object; llprocess_group llprocessgrp("llprocess"); - struct Item + namespace { - Item(): tries(0) {} - unsigned tries; - std::string which; - std::string what; - }; + struct Item + { + Item(): tries(0) {} + unsigned tries; + std::string which; + std::string what; + }; + } // anonymous namespace /*==========================================================================*| #define tabent(symbol) { symbol, #symbol } @@ -564,21 +577,24 @@ namespace tut #undef tabent |*==========================================================================*/ - struct WaitInfo - { - WaitInfo(apr_proc_t* child_): - child(child_), - rv(-1), // we haven't yet called apr_proc_wait() - rc(0), // child's exit code - why(apr_exit_why_e(0)) - {} - apr_proc_t* child; // which subprocess - apr_status_t rv; // return from apr_proc_wait() - int rc; // child's exit code - apr_exit_why_e why; // APR_PROC_EXIT, APR_PROC_SIGNAL, APR_PROC_SIGNAL_CORE - }; + namespace + { + struct WaitInfo + { + WaitInfo(apr_proc_t* child_): + child(child_), + rv(-1), // we haven't yet called apr_proc_wait() + rc(0), // child's exit code + why(apr_exit_why_e(0)) + {} + apr_proc_t* child; // which subprocess + apr_status_t rv; // return from apr_proc_wait() + int rc; // child's exit code + apr_exit_why_e why; // APR_PROC_EXIT, APR_PROC_SIGNAL, APR_PROC_SIGNAL_CORE + }; + } // anonymous namespace - void child_status_callback(int reason, void* data, int status) + static void child_status_callback(int reason, void* data, int status) { /*==========================================================================*| std::string reason_str; @@ -1329,46 +1345,49 @@ namespace tut ensure_equals("bad child exit code", py.mPy->getStatus().mData, 0); } - struct EventListener + namespace { - EventListener(const EventListener&) = delete; - EventListener& operator=(const EventListener&) = delete; - - EventListener(LLEventPump& pump) + struct EventListener { - mConnection = - pump.listen("EventListener", boost::bind(&EventListener::tick, this, _1)); - } + EventListener(const EventListener&) = delete; + EventListener& operator=(const EventListener&) = delete; - bool tick(const LLSD& data) - { - mHistory.push_back(data); - return false; - } + EventListener(LLEventPump& pump) + { + mConnection = + pump.listen("EventListener", boost::bind(&EventListener::tick, this, _1)); + } - template - void checkHistory(CALLABLE&& code) - { - try + bool tick(const LLSD& data) { - // we expect this lambda to contain tut::ensure() calls - std::forward(code)(mHistory); + mHistory.push_back(data); + return false; } - catch (const failure&) + + template + void checkHistory(CALLABLE&& code) { - LL_INFOS() << "event history:" << LL_ENDL; - for (const LLSD& item : mHistory) + try + { + // we expect this lambda to contain tut::ensure() calls + std::forward(code)(mHistory); + } + catch (const failure&) { - LL_INFOS() << item << LL_ENDL; + LL_INFOS() << "event history:" << LL_ENDL; + for (const LLSD& item : mHistory) + { + LL_INFOS() << item << LL_ENDL; + } + throw; } - throw; } - } - using Listory = std::list; - Listory mHistory; - LLTempBoundListener mConnection; - }; + using Listory = std::list; + Listory mHistory; + LLTempBoundListener mConnection; + }; + } // anonymous namespace static bool ack(std::ostream& out, const LLSD& data) { @@ -1627,30 +1646,36 @@ namespace tut }); } - struct PostendListener + namespace { - PostendListener(LLProcess::ReadPipe& rpipe, - const std::string& pumpname, - const std::string& expect): - mReadPipe(rpipe), - mExpect(expect), - mTriggered(false) + struct PostendListener { - LLEventPumps::instance().obtain(pumpname) - .listen("PostendListener", boost::bind(&PostendListener::postend, this, _1)); - } + PostendListener(LLProcess::ReadPipe& rpipe, + const std::string& pumpname, + const std::string& expect): + mReadPipe(rpipe), + mExpect(expect), + mTriggered(false) + { + // Held, so the listener goes with this: left on the pump, it + // would be called on a dead object by the next post there. + mConnection = LLEventPumps::instance().obtain(pumpname) + .listen("PostendListener", boost::bind(&PostendListener::postend, this, _1)); + } - bool postend(const LLSD&) - { - mTriggered = true; - ensure_equals("postend listener", mReadPipe.read(mReadPipe.size()), mExpect); - return false; - } + bool postend(const LLSD&) + { + mTriggered = true; + ensure_equals("postend listener", mReadPipe.read(mReadPipe.size()), mExpect); + return false; + } - LLProcess::ReadPipe& mReadPipe; - std::string mExpect; - bool mTriggered; - }; + LLProcess::ReadPipe& mReadPipe; + std::string mExpect; + bool mTriggered; + LLTempBoundListener mConnection; + }; + } // anonymous namespace template<> template<> void object::test<24>() diff --git a/indra/llcommon/tests/llrand_test.cpp b/indra/llcommon/tests/llrand_test.cpp index fd64a6b3a2f..fca8275c6c2 100644 --- a/indra/llcommon/tests/llrand_test.cpp +++ b/indra/llcommon/tests/llrand_test.cpp @@ -27,6 +27,7 @@ #include "linden_common.h" #include "../test/lltut.h" +#include "../test/alrandomreseed.h" #include "../llrand.h" #include "../alrandmap.h" @@ -45,8 +46,8 @@ // testing extent < 0, negate the return value and the extent before passing // into ensure_in_range(). template -void ensure_in_range(const std::string_view& name, - NUMBER value, NUMBER low, NUMBER high) +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)); @@ -133,6 +134,8 @@ namespace tut { struct random { + // Tests here seed the main thread's generator. + ~random() { ll_test::reseedRandomFromEntropy(); } }; typedef test_group random_t; diff --git a/indra/llcommon/tests/llsd_test.cpp b/indra/llcommon/tests/llsd_test.cpp index cafdb051fbc..900ba8c9d0d 100644 --- a/indra/llcommon/tests/llsd_test.cpp +++ b/indra/llcommon/tests/llsd_test.cpp @@ -37,63 +37,67 @@ using std::fpclassify; namespace tut { - class SDCleanupCheck + namespace { - private: - U32 mOutstandingAtStart; - public: - SDCleanupCheck() : mOutstandingAtStart(llsd::outstandingCount()) { } - ~SDCleanupCheck() + // 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 + // failing the test. + template + void ensure_cleaned_up(BODY&& body) { + const U32 outstanding = llsd::outstandingCount(); + body(); ensure_equals("SDCleanupCheck", - llsd::outstandingCount(), mOutstandingAtStart); + llsd::outstandingCount(), outstanding); } - }; - class SDAllocationCheck : public SDCleanupCheck - { - private: - std::string mMessage; - U32 mExpectedAllocations; - U32 mAllocationAtStart; - public: - SDAllocationCheck(const std::string& message, int expectedAllocations) - : mMessage(message), - mExpectedAllocations(expectedAllocations), - mAllocationAtStart(llsd::allocationCount()) - { } - ~SDAllocationCheck() + // ...and the body allocated exactly as many LLSD values as expected. + template + void ensure_allocations(const std::string& message, U32 expectedAllocations, BODY&& body) { - ensure_equals(mMessage + " SDAllocationCheck", - llsd::allocationCount() - mAllocationAtStart, - mExpectedAllocations); + const U32 outstanding = llsd::outstandingCount(); + const U32 allocations = llsd::allocationCount(); + body(); + ensure_equals(message + " SDAllocationCheck", + llsd::allocationCount() - allocations, + expectedAllocations); + ensure_equals("SDCleanupCheck", + llsd::outstandingCount(), outstanding); } - }; + } // anonymous namespace - struct SDTestData { - template - static void ensureTypeAndValue(const char* msg, const LLSD& actual, - T expectedValue) - { - LLSDTraits traits; + // Free functions rather than members of SDTestData: the tests call + // them inside the bodies they hand the checks above, and MSVC does + // not find a member of the data struct by its bare name in a lambda. + // 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, + T expectedValue) + { + LLSDTraits traits; - std::string s(msg); + std::string s(msg); - ensure( s + " type", traits.checkType(actual)); - ensure_equals( s + " value", traits.get(actual), expectedValue); - } + ensure( s + " type", traits.checkType(actual)); + ensure_equals( s + " value", traits.get(actual), expectedValue); + } - template - static void ensureTypeAndRefValue(const char* msg, const LLSD& actual, - const T& expectedValue) - { - LLSDTraits traits; + template + static void ensureTypeAndRefValue(const char* msg, const LLSD& actual, + const T& expectedValue) + { + LLSDTraits traits; - std::string s(msg); + std::string s(msg); - ensure( s + " type", traits.checkType(actual)); - ensure_equals( s + " value", traits.get(actual), expectedValue); - } + ensure( s + " type", traits.checkType(actual)); + ensure_equals( s + " value", traits.get(actual), expectedValue); + } + + struct SDTestData + { }; typedef test_group SDTestGroup; @@ -115,58 +119,59 @@ namespace tut void SDTestObject::test<2>() // setting and fetching scalar types { - SDCleanupCheck check; - - LLSD v; + ensure_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; ensureTypeAndValue("set true", v, true); + v = false; ensureTypeAndValue("set false", v, false); + v = true; ensureTypeAndValue("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; 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 = 3.14159265359; - ensureTypeAndValue("set to pi", v, 3.14159265359); - ensure_not_equals("isn't float", v.asReal(), - (float)3.14159265359); - v = 6.7e256; ensureTypeAndValue("set to big", v, 6.7e256); + v = 3.14159265359; + ensureTypeAndValue("set to pi", v, 3.14159265359); + ensure_not_equals("isn't float", v.asReal(), + (float)3.14159265359); + v = 6.7e256; ensureTypeAndValue("set to big", v, 6.7e256); - LLUUID nullUUID; - LLUUID newUUID; - newUUID.generate(); + 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; 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); - // strings must be tested with two types of string objects - std::string s = "now is the time"; - const char* cs = "for all good zorks"; + // 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; ensureTypeAndValue("set to std::string", v, s); + v = cs; ensureTypeAndValue("set to const char*", v, cs); - LLDate epoch; - LLDate aDay("2001-10-22T10:11:12.00Z"); + 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; ensureTypeAndValue("set to epoch", v, epoch); + v = aDay; ensureTypeAndValue("set to a day", v, aDay); - LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/"); + LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/"); - v = path; ensureTypeAndValue("set to a uri", v, path); + v = path; ensureTypeAndValue("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)); + 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; ensureTypeAndRefValue("set to data", v, data); - v.clear(); - ensure("reset to undefined", v.type() == LLSD::TypeUndefined); + v.clear(); + ensure("reset to undefined", v.type() == LLSD::TypeUndefined); + }); } template<> template<> @@ -174,51 +179,52 @@ namespace tut // construction via scalar values // tests both constructor and initialize forms { - SDCleanupCheck check; - - 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); - - LLUUID newUUID; - newUUID.generate(); - LLSD u1(newUUID); - ensureTypeAndValue("construct UUID", u1, newUUID); - LLSD u2 = newUUID; - ensureTypeAndValue("initialize UUID", u2, newUUID); - - LLSD ss1(std::string("abc")); - ensureTypeAndValue("construct std::string", ss1, "abc"); - LLSD ss2 = std::string("abc"); - ensureTypeAndValue("initialize std::string",ss2, "abc"); - LLSD sl1(std::string("def")); - ensureTypeAndValue("construct std::string", sl1, "def"); - LLSD sl2 = std::string("def"); - ensureTypeAndValue("initialize std::string", sl2, "def"); - LLSD sc1("ghi"); - ensureTypeAndValue("construct const char*", sc1, "ghi"); - LLSD sc2 = "ghi"; - ensureTypeAndValue("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); - - 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); - - 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); + ensure_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); + + LLUUID newUUID; + newUUID.generate(); + LLSD u1(newUUID); + ensureTypeAndValue("construct UUID", u1, newUUID); + LLSD u2 = newUUID; + ensureTypeAndValue("initialize UUID", u2, newUUID); + + LLSD ss1(std::string("abc")); + ensureTypeAndValue("construct std::string", ss1, "abc"); + LLSD ss2 = std::string("abc"); + ensureTypeAndValue("initialize std::string",ss2, "abc"); + LLSD sl1(std::string("def")); + ensureTypeAndValue("construct std::string", sl1, "def"); + LLSD sl2 = std::string("def"); + ensureTypeAndValue("initialize std::string", sl2, "def"); + LLSD sc1("ghi"); + ensureTypeAndValue("construct const char*", sc1, "ghi"); + LLSD sc2 = "ghi"; + ensureTypeAndValue("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); + + 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); + + 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); + }); } - void checkConversions(const char* msg, const LLSD& v, + static void checkConversions(const char* msg, const LLSD& v, LLSD::Boolean eBoolean, LLSD::Integer eInteger, LLSD::Real eReal, const LLSD::String& eString) { @@ -251,46 +257,47 @@ namespace tut // conversion between undefined and basic scalar types: // boolean, integer, real and string { - SDCleanupCheck check; - - LLSD v; checkConversions("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 = 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.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"); - // 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 - // on different platforms in the asString() test. But LLSD's - // ImplReal::asString() does not itself recognize NaN! It merely - // passes the value through to llformat(), which passes it through to - // the library vsnprintf(). That is, even when we do produce NaN, - // 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 = ""; 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, - 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"); + ensure_cleaned_up([&] + { + LLSD v; checkConversions("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 = 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.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"); + // 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 + // on different platforms in the asString() test. But LLSD's + // ImplReal::asString() does not itself recognize NaN! It merely + // passes the value through to llformat(), which passes it through to + // the library vsnprintf(). That is, even when we do produce NaN, + // 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 = ""; 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, + 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"); + }); } template - void checkRoundTrip(const std::string& msg, const LLSD& actual, + static void checkRoundTrip(const std::string& msg, const LLSD& actual, const char* sExpected, T vExpected) { std::string str = actual.asString(); @@ -310,38 +317,39 @@ namespace tut void SDTestObject::test<5>() // conversion of String to and from UUID, Date and URI. { - SDCleanupCheck check; - - LLSD v; + ensure_cleaned_up([&] + { + LLSD v; - LLUUID nullUUID; - LLUUID someUUID; - someUUID.generate(); - - v = nullUUID; checkRoundTrip("null uuid", v, - "00000000-0000-0000-0000-000000000000", nullUUID); - v = someUUID; checkRoundTrip("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, - "2003-04-30T04:00:00Z", beta); - v = oneOh; checkRoundTrip("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, - "http://slurl.com/secondlife/Ambleside/57/104/26/", - path); - v = mail; checkRoundTrip("mail URI", v, - "mailto:zero.linden@secondlife.com", mail); + LLUUID nullUUID; + LLUUID someUUID; + someUUID.generate(); + + v = nullUUID; checkRoundTrip("null uuid", v, + "00000000-0000-0000-0000-000000000000", nullUUID); + v = someUUID; checkRoundTrip("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, + "2003-04-30T04:00:00Z", beta); + v = oneOh; checkRoundTrip("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, + "http://slurl.com/secondlife/Ambleside/57/104/26/", + path); + v = mail; checkRoundTrip("mail URI", v, + "mailto:zero.linden@secondlife.com", mail); + }); } template<> template<> @@ -350,74 +358,75 @@ namespace tut // checking for shared values after constr. or assignment // checking in both the same type and change of type case { - SDCleanupCheck check; - + ensure_cleaned_up([&] { - LLSD v = 42; + { + LLSD v = 42; - LLSD w0(v); - ensureTypeAndValue("int constr.", w0, 42); + LLSD w0(v); + ensureTypeAndValue("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); + LLSD w1(v); + w1 = 13; + ensureTypeAndValue("int constr. change case 1", w1, 13); + ensureTypeAndValue("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); - } + LLSD w2(v); + v = 7; + ensureTypeAndValue("int constr. change case 3", w2, 42); + ensureTypeAndValue("int constr. change case 4", v, 7); + } - { - LLSD v = 42; + { + LLSD v = 42; - LLSD w1(v); - w1 = "bob"; - ensureTypeAndValue("string constr. change case 1", w1, "bob"); - ensureTypeAndValue("string constr. change case 2", v, 42); + LLSD w1(v); + w1 = "bob"; + ensureTypeAndValue("string constr. change case 1", w1, "bob"); + ensureTypeAndValue("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"); - } + LLSD w2(v); + v = "amy"; + ensureTypeAndValue("string constr. change case 3", w2, 42); + ensureTypeAndValue("string constr. change case 4", v, "amy"); + } - { - LLSD v = 42; - - LLSD w0; - w0 = v; - ensureTypeAndValue("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); - - LLSD w2; - w2 = v; - v = 7; - ensureTypeAndValue("int assign change case 3", w2, 42); - ensureTypeAndValue("int assign change case 4", v, 7); - } + { + LLSD v = 42; - { - LLSD v = 42; - - LLSD w1; - w1 = v; - w1 = "bob"; - ensureTypeAndValue("string assign change case 1", w1, "bob"); - ensureTypeAndValue("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"); - } + LLSD w0; + w0 = v; + ensureTypeAndValue("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); + + LLSD w2; + w2 = v; + v = 7; + ensureTypeAndValue("int assign change case 3", w2, 42); + ensureTypeAndValue("int assign change case 4", v, 7); + } + + { + LLSD v = 42; + + LLSD w1; + w1 = v; + w1 = "bob"; + ensureTypeAndValue("string assign change case 1", w1, "bob"); + ensureTypeAndValue("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"); + } + }); } @@ -427,49 +436,50 @@ namespace tut // assignments should invoke the right conversion without it being // mentioned explicitly. The few exceptions are marked SAD. { - SDCleanupCheck check; - - 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); - - int i = 99; - i = v; ensure_equals("assign to int", i, 42); - i = (int)v; ensure_equals("cast to int", 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); - - 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"); - - std::string uuidStr = "b1e50c2b-b627-4d23-8a86-a65d97b6319b"; - v = uuidStr; - LLUUID u; - u = v; - ensure_equals("assign to LLUUID", u, LLUUID(uuidStr)); -// SAD u = (LLUUID)v; -// ensure_equals("cast to LLUUID", u, LLUUID(uuidStr)); - - std::string dateStr = "2005-10-24T15:00:00Z"; - v = dateStr; - LLDate date; - date = v; - ensure_equals("assign to LLDate", date.asString(), dateStr); -// SAD date = (LLDate)v; -// ensure_equals("cast to LLDate", date.asString(), dateStr); - - std::string uriStr = "http://secondlife.com"; - v = uriStr; - LLURI uri; - uri = v; - ensure_equals("assign to LLURI", uri.asString(), uriStr); -// SAD uri = (LLURI)v; -// ensure_equals("cast to LLURI", uri.asString(), uriStr); + ensure_cleaned_up([&] + { + 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); + + int i = 99; + i = v; ensure_equals("assign to int", i, 42); + i = (int)v; ensure_equals("cast to int", 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); + + 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"); + + std::string uuidStr = "b1e50c2b-b627-4d23-8a86-a65d97b6319b"; + v = uuidStr; + LLUUID u; + u = v; + ensure_equals("assign to LLUUID", u, LLUUID(uuidStr)); + // SAD u = (LLUUID)v; + // ensure_equals("cast to LLUUID", u, LLUUID(uuidStr)); + + std::string dateStr = "2005-10-24T15:00:00Z"; + v = dateStr; + LLDate date; + date = v; + ensure_equals("assign to LLDate", date.asString(), dateStr); + // SAD date = (LLDate)v; + // ensure_equals("cast to LLDate", date.asString(), dateStr); + + std::string uriStr = "http://secondlife.com"; + v = uriStr; + LLURI uri; + uri = v; + ensure_equals("assign to LLURI", uri.asString(), uriStr); + // SAD uri = (LLURI)v; + // ensure_equals("cast to LLURI", uri.asString(), uriStr); + }); } template<> template<> @@ -479,49 +489,50 @@ namespace tut // These should invoke the right conversion without it being // mentioned explicitly. The few exceptions are marked SAD. { - SDCleanupCheck check; - - LLSD v(" 42.375"); - - bool b1(v); ensure_equals("contruct bool", b1, true); - bool b2 = v; ensure_equals("initialize bool", b2, true); - - int i1(v); ensure_equals("contruct int", i1, 42); - int i2 = v; ensure_equals("initialize int", i2, 42); - - double d1(v); ensure_equals("contruct double", d1, 42.375); - double d2 = v; ensure_equals("initialize double", 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"); - - 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"); - - 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)); - - 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); - - 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); + ensure_cleaned_up([&] + { + LLSD v(" 42.375"); + + bool b1(v); ensure_equals("contruct bool", b1, true); + bool b2 = v; ensure_equals("initialize bool", b2, true); + + int i1(v); ensure_equals("contruct int", i1, 42); + int i2 = v; ensure_equals("initialize int", i2, 42); + + double d1(v); ensure_equals("contruct double", d1, 42.375); + double d2 = v; ensure_equals("initialize double", 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"); + + 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"); + + 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)); + + 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); + + 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); + }); } @@ -530,86 +541,88 @@ namespace tut // test to make sure v is interpreted as a bool in a various // scenarios. { - SDCleanupCheck check; - - LLSD v = "0"; - // magic value that is interpreted as boolean true, but integer false! + ensure_cleaned_up([&] + { + 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); + ensure_equals("trinary operator bool", (v ? true : false), true); + ensure_equals("convert to int, then bool", + ((int)v ? true : false), false); - if(v) - { - ensure("if converted to bool", true); - } - else - { - fail("bool did not convert to a bool in if statement."); - } + if(v) + { + ensure("if converted to bool", true); + } + else + { + 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."); - } + if(!v) + { + fail("bool did not convert to a bool in negated if statement."); + } + }); } template<> template<> void SDTestObject::test<10>() // map operations { - SDCleanupCheck check; - - LLSD v; - ensure("undefined has no members", !v.has("amy")); - ensure("undefined get() is undefined", 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()); - - v.clear(); - v.insert("eli", 43); - ensure("insert converts to map", v.isMap()); - ensure("inserted key is present", 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")); - 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()); - 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()); - - v["gil"] = (std::string)"good morning"; - ensure("third key present", 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")); - - 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()); - - LLSD x; - x = v; - ensure("copy map type", x.isMap()); - ensureTypeAndValue("copy map value gil", x.get("gil"), "good morning"); + ensure_cleaned_up([&] + { + LLSD v; + ensure("undefined has no members", !v.has("amy")); + ensure("undefined get() is undefined", 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()); + + v.clear(); + v.insert("eli", 43); + ensure("insert converts to map", v.isMap()); + ensure("inserted key is present", 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")); + 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()); + 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()); + + v["gil"] = (std::string)"good morning"; + ensure("third key present", 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")); + + 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()); + + LLSD x; + x = v; + ensure("copy map type", x.isMap()); + ensureTypeAndValue("copy map value gil", x.get("gil"), "good morning"); + }); } @@ -617,71 +630,72 @@ namespace tut void SDTestObject::test<11>() // array operations { - SDCleanupCheck check; + ensure_cleaned_up([&] + { + LLSD v; + ensure_equals("undefined has no size", v.size(), 0); + ensure("undefined get() is undefined", 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()); + + 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()); + ensureTypeAndValue("append 0", v[0], 88); + ensureTypeAndValue("append 1", v[1], "noodle"); + ensureTypeAndValue("append 2", v[2], true); + + v.insert(0, 77); + v.insert(2, "soba"); + v.insert(4, false); + ensure_equals("inserted array size", 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); + + 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()); + ensureTypeAndValue("beyond-end insert value", beyond[2], "far"); + + ensureTypeAndValue("get 1", v.get(1), 88); + v.set(1, "hot"); + ensureTypeAndValue("set 1", v.get(1), "hot"); + + v.erase(3); + ensure_equals("post erase array size", 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); + + v.append(34); + ensure_equals("size after append", v.size(), 6); + ensureTypeAndValue("post append 5", v[5], 34); - LLSD v; - ensure_equals("undefined has no size", v.size(), 0); - ensure("undefined get() is undefined", 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()); - - 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()); - ensureTypeAndValue("append 0", v[0], 88); - ensureTypeAndValue("append 1", v[1], "noodle"); - ensureTypeAndValue("append 2", v[2], true); - - v.insert(0, 77); - v.insert(2, "soba"); - v.insert(4, false); - ensure_equals("inserted array size", 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); - - 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()); - ensureTypeAndValue("beyond-end insert value", beyond[2], "far"); - - ensureTypeAndValue("get 1", v.get(1), 88); - v.set(1, "hot"); - ensureTypeAndValue("set 1", v.get(1), "hot"); - - v.erase(3); - ensure_equals("post erase array size", 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); - - v.append(34); - ensure_equals("size after append", 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); - 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); + LLSD w; + w = v; + ensure("copy array type", w.isArray()); + ensure_equals("copy array size", 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); + }); } @@ -689,126 +703,127 @@ namespace tut void SDTestObject::test<12>() // no sharing { - SDCleanupCheck check; - - LLSD a = 99; - LLSD b = a; - a = 34; - ensureTypeAndValue("top level original changed", a, 34); - ensureTypeAndValue("top level copy unaltered", b, 99); - b = a; - b = 66; - ensureTypeAndValue("top level original unaltered", a, 34); - ensureTypeAndValue("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); - b = a; - b[1] = 66; - ensureTypeAndValue("array member original unaltered", a[1], 34); - ensureTypeAndValue("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); - b = a; - b["beta"] = 66; - ensureTypeAndValue("map member original unaltered", a["beta"], 34); - ensureTypeAndValue("map member copy changed", b["beta"], 66); + ensure_cleaned_up([&] + { + LLSD a = 99; + LLSD b = a; + a = 34; + ensureTypeAndValue("top level original changed", a, 34); + ensureTypeAndValue("top level copy unaltered", b, 99); + b = a; + b = 66; + ensureTypeAndValue("top level original unaltered", a, 34); + ensureTypeAndValue("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); + b = a; + b[1] = 66; + ensureTypeAndValue("array member original unaltered", a[1], 34); + ensureTypeAndValue("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); + b = a; + b["beta"] = 66; + ensureTypeAndValue("map member original unaltered", a["beta"], 34); + ensureTypeAndValue("map member copy changed", b["beta"], 66); + }); } template<> template<> void SDTestObject::test<13>() // sharing implementation { - SDCleanupCheck check; - - { - SDAllocationCheck check("copy construct undefinded", 0); - LLSD v; - LLSD w = v; - } - + ensure_cleaned_up([&] { - SDAllocationCheck check("assign undefined", 0); - LLSD v; - LLSD w; - w = v; - } + ensure_allocations("copy construct undefinded", 0, [] + { + LLSD v; + LLSD w = v; + }); + + ensure_allocations("assign undefined", 0, [] + { + LLSD v; + LLSD w; + w = v; + }); - { // scalar values are stored inline and never allocate - SDAllocationCheck check("assign integer value", 0); - LLSD v = 45; - v = 33; - v = 0; - } - - { - SDAllocationCheck check("copy construct integer", 0); - LLSD v = 45; - LLSD w = v; - } - - { - SDAllocationCheck check("assign integer", 0); - LLSD v = 45; - LLSD w; - w = v; - } - - { - SDAllocationCheck check("avoids extra clone", 1); - LLSD v = 45; - LLSD w = v; - w = "nice day"; - } + ensure_allocations("assign integer value", 0, [] + { + LLSD v = 45; + v = 33; + v = 0; + }); + + ensure_allocations("copy construct integer", 0, [] + { + LLSD v = 45; + LLSD w = v; + }); + + ensure_allocations("assign integer", 0, [] + { + LLSD v = 45; + LLSD w; + w = v; + }); + + ensure_allocations("avoids extra clone", 1, [] + { + LLSD v = 45; + LLSD w = v; + w = "nice day"; + }); - { // map + array + two strings; integers, reals and undefs are inline - SDAllocationCheck check("shared values test for threaded work", 4); - - //U32 start_llsd_count = LLSD::outstandingCount(); + ensure_allocations("shared values test for threaded work", 4, [] + { + //U32 start_llsd_count = LLSD::outstandingCount(); - LLSD m = LLSD::emptyMap(); + LLSD m = LLSD::emptyMap(); - m["one"] = 1; - m["two"] = 2; - m["one_copy"] = m["one"]; // 1 (m; the integers are inline) + m["one"] = 1; + m["two"] = 2; + m["one_copy"] = m["one"]; // 1 (m; the integers are inline) - m["undef_one"] = LLSD(); - m["undef_two"] = LLSD(); - m["undef_one_copy"] = m["undef_one"]; + m["undef_one"] = LLSD(); + m["undef_two"] = LLSD(); + m["undef_one_copy"] = m["undef_one"]; - { // Ensure first_array gets freed to avoid counting it - LLSD first_array = LLSD::emptyArray(); - first_array.append(1.0f); - first_array.append(2.0f); - first_array.append(3.0f); // 2 (the reals are inline) + { // Ensure first_array gets freed to avoid counting it + LLSD first_array = LLSD::emptyArray(); + first_array.append(1.0f); + first_array.append(2.0f); + first_array.append(3.0f); // 2 (the reals are inline) - m["array"] = first_array; - m["array_clone"] = first_array; - m["array_copy"] = m["array"]; // 2 (shared, no clones) - } + m["array"] = first_array; + m["array_clone"] = first_array; + m["array_copy"] = m["array"]; // 2 (shared, no clones) + } - m["string_one"] = "string one value"; - m["string_two"] = "string two value"; - m["string_one_copy"] = m["string_one"]; // 4 + m["string_one"] = "string one value"; + m["string_two"] = "string two value"; + m["string_one_copy"] = m["string_one"]; // 4 - //U32 llsd_object_count = LLSD::outstandingCount(); - //std::cout << "Using " << (llsd_object_count - start_llsd_count) << " LLSD objects" << std::endl; + //U32 llsd_object_count = LLSD::outstandingCount(); + //std::cout << "Using " << (llsd_object_count - start_llsd_count) << " LLSD objects" << std::endl; - //m.dumpStats(); - } + //m.dumpStats(); + }); + }); } template<> template<> @@ -824,34 +839,35 @@ namespace tut void SDTestObject::test<15>() // XMLRPC value serialization { - SDCleanupCheck check; - - ensure_equals("xmlrpc undef", LLSD().asXMLRPCValue(), - ""); - ensure_equals("xmlrpc bool", LLSD(true).asXMLRPCValue(), - "1"); - ensure_equals("xmlrpc int", LLSD(3).asXMLRPCValue(), - "3"); - ensure_equals("xmlrpc real", 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(), - "" - "1" - "two" - ""); - - LLSD map = LLSD::emptyMap(); - map["a"] = 1; - ensure_equals("xmlrpc map", map.asXMLRPCValue(), - "" - "a1" - ""); + ensure_cleaned_up([&] + { + ensure_equals("xmlrpc undef", LLSD().asXMLRPCValue(), + ""); + ensure_equals("xmlrpc bool", LLSD(true).asXMLRPCValue(), + "1"); + ensure_equals("xmlrpc int", LLSD(3).asXMLRPCValue(), + "3"); + ensure_equals("xmlrpc real", 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(), + "" + "1" + "two" + ""); + + LLSD map = LLSD::emptyMap(); + map["a"] = 1; + ensure_equals("xmlrpc map", map.asXMLRPCValue(), + "" + "a1" + ""); + }); } /* TO DO: diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp index f8222692e9f..189c18420c2 100644 --- a/indra/llcommon/tests/llsdserialize_test.cpp +++ b/indra/llcommon/tests/llsdserialize_test.cpp @@ -65,7 +65,7 @@ typedef U32 uint32_t; typedef std::function FormatterFunction; typedef std::function ParserFunction; -std::vector string_to_vector(const std::string& str) +static std::vector string_to_vector(const std::string& str) { return std::vector(str.begin(), str.end()); } @@ -2259,8 +2259,8 @@ namespace tut // helper for TestPythonCompatible template - void python_expect(const std::string& desc, const CONTENT& script, int expect=0, - ARGS&&... args) + 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()); @@ -2328,7 +2328,7 @@ namespace tut // helper for TestPythonCompatible template - void python(const std::string& desc, const CONTENT& script, ARGS&&... args) + 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)...); @@ -2528,7 +2528,7 @@ namespace tut |*==========================================================================*/ // helper for test<8> - test<12> - bool itemFromStream(std::istream& istr, LLSD& item, const ParserFunction& parse) + static bool itemFromStream(std::istream& istr, LLSD& item, const ParserFunction& parse) { // reset the output value for debugging clarity item.clear(); @@ -2548,10 +2548,10 @@ namespace tut } // helper for test<8> - test<12> - void fromPythonUsing(const std::string& pyformatter, - const ParserFunction& parse= - [](std::istream& istr, LLSD& data, llssize max_bytes) - { return LLSDSerialize::deserialize(data, istr, max_bytes); }) + 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 diff --git a/indra/llcommon/tests/llsdutil_test.cpp b/indra/llcommon/tests/llsdutil_test.cpp index de6e0bede53..1209bb3807a 100644 --- a/indra/llcommon/tests/llsdutil_test.cpp +++ b/indra/llcommon/tests/llsdutil_test.cpp @@ -77,7 +77,7 @@ namespace tut }; typedef test_group llsdutil_test;; typedef llsdutil_test::object llsdutil_object; - tut::llsdutil_test tutil("llsdutil"); + tut::llsdutil_test tut_llsdutil("llsdutil"); template<> template<> void llsdutil_object::test<1>() diff --git a/indra/llcommon/tests/llsingleton_test.cpp b/indra/llcommon/tests/llsingleton_test.cpp index adf58042722..51f2b92f12a 100644 --- a/indra/llcommon/tests/llsingleton_test.cpp +++ b/indra/llcommon/tests/llsingleton_test.cpp @@ -32,6 +32,9 @@ #include "wrapllerrs.h" #include "llsd.h" +namespace +{ + // Capture execution sequence by appending to log string. std::string sLog; @@ -88,6 +91,8 @@ CLS::~CLS() \ DEFINE_MEMBERS(A, B) DEFINE_MEMBERS(B, A) +} // anonymous namespace + namespace tut { struct singleton @@ -101,7 +106,7 @@ namespace tut typedef test_group singleton_t; typedef singleton_t::object singleton_object_t; - tut::singleton_t tut_singleton("LLSingleton"); + tut::singleton_t tut_llsingleton("LLSingleton"); template<> template<> void singleton_object_t::test<1>() @@ -209,8 +214,11 @@ 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. - PARAMSINGLETON(PSing1); - PARAMSINGLETON(PSing2); + namespace + { + PARAMSINGLETON(PSing1); + PARAMSINGLETON(PSing2); + } // anonymous namespace template<> template<> void singleton_object_t::test<12>() @@ -276,14 +284,17 @@ namespace tut ensure_contains("other ctor(string) didn't throw", threw, "twice"); } - class CircularPCtor: public LLParamSingleton + namespace { - LLSINGLETON(CircularPCtor) + class CircularPCtor: public LLParamSingleton { - // never mind indirection, just go straight for the circularity - (void)instance(); - } - }; + LLSINGLETON(CircularPCtor) + { + // never mind indirection, just go straight for the circularity + (void)instance(); + } + }; + } // anonymous namespace template<> template<> void singleton_object_t::test<14>() @@ -296,20 +307,23 @@ namespace tut ensure_contains("constructor circularity didn't throw", threw, "constructor"); } - class CircularPInit: public LLParamSingleton + namespace { - LLSINGLETON_EMPTY_CTOR(CircularPInit); - public: - virtual void initSingleton() override + class CircularPInit: public LLParamSingleton { - // never mind indirection, just go straight for the circularity - CircularPInit *pt = getInstance(); - if (!pt) + LLSINGLETON_EMPTY_CTOR(CircularPInit); + public: + virtual void initSingleton() override { - throw; + // never mind indirection, just go straight for the circularity + CircularPInit *pt = getInstance(); + if (!pt) + { + throw; + } } - } - }; + }; + } // anonymous namespace template<> template<> void singleton_object_t::test<15>() diff --git a/indra/llcommon/tests/llstring_test.cpp b/indra/llcommon/tests/llstring_test.cpp index ef3fa86eec1..44a1d3f0875 100644 --- a/indra/llcommon/tests/llstring_test.cpp +++ b/indra/llcommon/tests/llstring_test.cpp @@ -919,12 +919,12 @@ namespace tut // 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. - 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 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) { ensure_equals(desc + " - no esc", LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes), @@ -937,11 +937,11 @@ namespace tut expect); } - 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 ensure_getTokens(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); } diff --git a/indra/llcommon/tests/lltrace_test.cpp b/indra/llcommon/tests/lltrace_test.cpp index 923a67ac8ef..af8fe639113 100644 --- a/indra/llcommon/tests/lltrace_test.cpp +++ b/indra/llcommon/tests/lltrace_test.cpp @@ -38,14 +38,17 @@ namespace LLUnits { - // using powers of 2 to allow strict floating point equality - LL_DECLARE_BASE_UNIT(Ounces, "oz"); - LL_DECLARE_DERIVED_UNIT(TallCup, "", Ounces, / 12); - LL_DECLARE_DERIVED_UNIT(GrandeCup, "", Ounces, / 16); - LL_DECLARE_DERIVED_UNIT(VentiCup, "", Ounces, / 20); - - LL_DECLARE_BASE_UNIT(Grams, "g"); - LL_DECLARE_DERIVED_UNIT(Milligrams, "mg", Grams, * 1000); + namespace + { + // using powers of 2 to allow strict floating point equality + LL_DECLARE_BASE_UNIT(Ounces, "oz"); + LL_DECLARE_DERIVED_UNIT(TallCup, "", Ounces, / 12); + LL_DECLARE_DERIVED_UNIT(GrandeCup, "", Ounces, / 16); + LL_DECLARE_DERIVED_UNIT(VentiCup, "", Ounces, / 20); + + LL_DECLARE_BASE_UNIT(Grams, "g"); + LL_DECLARE_DERIVED_UNIT(Milligrams, "mg", Grams, * 1000); + } // anonymous namespace } LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Ounces); @@ -59,14 +62,44 @@ LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Milligrams); namespace tut { 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 + // 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() + { + set_thread_recorder(mRecorder); + LLThreadLocalSingletonPointer::setInstance(mTimerStack); + LLThreadLocalSingletonPointer::setInstance(mCounts); + LLThreadLocalSingletonPointer::setInstance(mSamples); + LLThreadLocalSingletonPointer::setInstance(mEvents); + LLThreadLocalSingletonPointer::setInstance(mTimers); + } + }; + } // anonymous namespace + struct trace { - ThreadRecorder mRecorder; + // before mRecorder, so it is put back after mRecorder is gone + ThreadRecorderRestore mRestore; + ThreadRecorder mRecorder; }; typedef test_group trace_t; typedef trace_t::object trace_object_t; - tut::trace_t tut_singleton("LLTrace"); + tut::trace_t tut_lltrace("LLTrace"); static CountStatHandle sCupsOfCoffeeConsumed("coffeeconsumed", "Delicious cup of dark roast."); static SampleStatHandle sCaffeineLevelStat("caffeinelevel", "Coffee buzz quotient"); @@ -75,7 +108,7 @@ namespace tut static F32 sCaffeineLevel(0.f); const F32Milligrams sCaffeinePerOz(18.f); - void drink_coffee(S32 num_cups, S32Ounces cup_size) + static void drink_coffee(S32 num_cups, S32Ounces cup_size) { add(sCupsOfCoffeeConsumed, num_cups); for (S32 i = 0; i < num_cups; i++) diff --git a/indra/llcommon/tests/lltreeiterators_test.cpp b/indra/llcommon/tests/lltreeiterators_test.cpp index 6734596d257..44932ce7f69 100644 --- a/indra/llcommon/tests/lltreeiterators_test.cpp +++ b/indra/llcommon/tests/lltreeiterators_test.cpp @@ -59,6 +59,9 @@ namespace tut tut::iter_group ig("LLTreeIterators"); } // namespace tut +namespace +{ + /***************************************************************************** * boost::get_pointer() specialization for LLPointer<> *****************************************************************************/ @@ -149,7 +152,7 @@ struct Expected // We have a couple of generic Expected template subclasses. This list of // strings is used for the "else" case when all specializations fail. -const char* bad_strings[] = { "FAIL" }; +[[maybe_unused]] const char* bad_strings[] = { "FAIL" }; /***************************************************************************** * verify() @@ -226,6 +229,8 @@ class PlainNode std::string mName; }; +} // anonymous namespace + namespace tut { template<> template<> @@ -277,6 +282,9 @@ namespace tut } } // tut +namespace +{ + /***************************************************************************** * RCNode: LLLinkIter, refcounted *****************************************************************************/ @@ -307,7 +315,7 @@ class RCNode: public LLRefCount std::string mName; }; -std::ostream& operator<<(std::ostream& out, const RCNode& node) +[[maybe_unused]] std::ostream& operator<<(std::ostream& out, const RCNode& node) { out << "RCNode(" << node.name() << ')'; return out; @@ -325,6 +333,8 @@ RCNode::~RCNode() last_RCNode_destroyed = mName; } +} // anonymous namespace + namespace tut { template<> template<> @@ -357,6 +367,9 @@ namespace tut } } +namespace +{ + /***************************************************************************** * TreeNode *****************************************************************************/ @@ -1078,6 +1091,8 @@ bool LLTreeIter_tests(const std::string& nodename, return success; } +} // anonymous namespace + namespace tut { template<> template<> diff --git a/indra/llcommon/tests/llunits_test.cpp b/indra/llcommon/tests/llunits_test.cpp index 98a58eb47ea..cb7924f6303 100644 --- a/indra/llcommon/tests/llunits_test.cpp +++ b/indra/llcommon/tests/llunits_test.cpp @@ -32,10 +32,13 @@ namespace LLUnits { - // using powers of 2 to allow strict floating point equality - LL_DECLARE_BASE_UNIT(Quatloos, "Quat"); - LL_DECLARE_DERIVED_UNIT(Latinum, "Lat", Quatloos, / 4); - LL_DECLARE_DERIVED_UNIT(Solari, "Sol", Latinum, * 16); + namespace + { + // using powers of 2 to allow strict floating point equality + LL_DECLARE_BASE_UNIT(Quatloos, "Quat"); + LL_DECLARE_DERIVED_UNIT(Latinum, "Lat", Quatloos, / 4); + LL_DECLARE_DERIVED_UNIT(Solari, "Sol", Latinum, * 16); + } // anonymous namespace } LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Quatloos); @@ -44,9 +47,12 @@ LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Solari); namespace LLUnits { - LL_DECLARE_BASE_UNIT(Celcius, "c"); - LL_DECLARE_DERIVED_UNIT(Fahrenheit, "f", Celcius, * 9 / 5 + 32); - LL_DECLARE_DERIVED_UNIT(Kelvin, "k", Celcius, + 273.15f); + namespace + { + LL_DECLARE_BASE_UNIT(Celcius, "c"); + LL_DECLARE_DERIVED_UNIT(Fahrenheit, "f", Celcius, * 9 / 5 + 32); + LL_DECLARE_DERIVED_UNIT(Kelvin, "k", Celcius, + 273.15f); + } // anonymous namespace } LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Celcius); @@ -63,7 +69,7 @@ namespace tut typedef test_group units_t; typedef units_t::object units_object_t; - tut::units_t tut_singleton("LLUnit"); + tut::units_t tut_llunits("LLUnit"); // storage type conversions template<> template<> @@ -198,12 +204,7 @@ namespace tut } - bool accept_explicit_quatloos(S32Quatloos q) - { - return true; - } - - bool accept_implicit_quatloos(S32Quatloos q) + static bool accept_explicit_quatloos(S32Quatloos q) { return true; } @@ -362,7 +363,7 @@ namespace tut ensure("kilotriangles -> triangles conversion", tris.value() == 1000); } - bool value_near(F32 value, F32 target, F32 threshold) + static bool value_near(F32 value, F32 target, F32 threshold) { return fabsf(value - target) < threshold; } diff --git a/indra/llcommon/tests/lluuid_test.cpp b/indra/llcommon/tests/lluuid_test.cpp index be1304c8513..45da7e37051 100644 --- a/indra/llcommon/tests/lluuid_test.cpp +++ b/indra/llcommon/tests/lluuid_test.cpp @@ -96,33 +96,36 @@ namespace tut 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 + namespace { - static constexpr size_t NUM_BUCKETS = 256; - int mLowMax = 0, mHighMax = 0, mLowEmpty = 0, mHighEmpty = 0; - - explicit BucketSpread(const std::vector& ids) + // 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 { - 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) + static constexpr size_t NUM_BUCKETS = 256; + int mLowMax = 0, mHighMax = 0, mLowEmpty = 0, mHighEmpty = 0; + + explicit BucketSpread(const std::vector& ids) { - if (low[i] > mLowMax) mLowMax = low[i]; - if (high[i] > mHighMax) mHighMax = high[i]; - if (!low[i]) ++mLowEmpty; - if (!high[i]) ++mHighEmpty; + 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; + } } - } - }; + }; + } // 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 diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index cc3c241a966..a5035556022 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -75,6 +75,9 @@ int main() #endif // 0 +namespace +{ + void init_curl() { curl_global_init(CURL_GLOBAL_ALL); @@ -122,3 +125,5 @@ void stop_thread(LLCore::HttpRequest * req) } } } + +} diff --git a/indra/llcorehttp/tests/llcorehttp_test.h b/indra/llcorehttp/tests/llcorehttp_test.h index 133cb3121a3..c0b575d1a5e 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.h +++ b/indra/llcorehttp/tests/llcorehttp_test.h @@ -36,15 +36,20 @@ #include "httprequest.h" +// The suites and these helpers are one translation unit, +// llcorehttp_test.cpp, so the helpers have internal linkage. +namespace +{ + // Initialization and cleanup for libcurl. Mainly provides // a mutex callback for SSL and a thread ID hash for libcurl. // If you don't use these (or equivalent) and do use libcurl, // you'll see stalls and other anomalies when performing curl // operations. -extern void init_curl(); -extern void term_curl(); -extern std::string get_base_url(); -extern void stop_thread(LLCore::HttpRequest * req); +void init_curl(); +void term_curl(); +std::string get_base_url(); +void stop_thread(LLCore::HttpRequest * req); class ScopedCurlInit { @@ -60,5 +65,7 @@ class ScopedCurlInit } }; +} + #endif // _LLCOREHTTP_TEST_H_ diff --git a/indra/llcorehttp/tests/test_bufferarray.hpp b/indra/llcorehttp/tests/test_bufferarray.hpp index 2a272e73d77..da2b742350b 100644 --- a/indra/llcorehttp/tests/test_bufferarray.hpp +++ b/indra/llcorehttp/tests/test_bufferarray.hpp @@ -37,6 +37,8 @@ using namespace LLCore; namespace tut { +namespace +{ struct BufferArrayTestData { @@ -47,6 +49,7 @@ struct BufferArrayTestData typedef test_group BufferArrayTestGroupType; typedef BufferArrayTestGroupType::object BufferArrayTestObjectType; BufferArrayTestGroupType BufferArrayTestGroup("BufferArray Tests"); +} template <> template <> void BufferArrayTestObjectType::test<1>() diff --git a/indra/llcorehttp/tests/test_bufferstream.hpp b/indra/llcorehttp/tests/test_bufferstream.hpp index 556abf45a25..4bd4cba38a6 100644 --- a/indra/llcorehttp/tests/test_bufferstream.hpp +++ b/indra/llcorehttp/tests/test_bufferstream.hpp @@ -39,6 +39,8 @@ using namespace LLCore; namespace tut { +namespace +{ struct BufferStreamTestData { @@ -50,6 +52,7 @@ typedef test_group BufferStreamTestGroupType; typedef BufferStreamTestGroupType::object BufferStreamTestObjectType; BufferStreamTestGroupType BufferStreamTestGroup("BufferStream Tests"); typedef BufferArrayStreamBuf::traits_type tst_traits_t; +} template <> template <> diff --git a/indra/llcorehttp/tests/test_httpheaders.hpp b/indra/llcorehttp/tests/test_httpheaders.hpp index d8eff78ca5e..d2d76aa2813 100644 --- a/indra/llcorehttp/tests/test_httpheaders.hpp +++ b/indra/llcorehttp/tests/test_httpheaders.hpp @@ -36,6 +36,8 @@ using namespace LLCoreInt; namespace tut { +namespace +{ struct HttpHeadersTestData { @@ -46,6 +48,7 @@ struct HttpHeadersTestData typedef test_group HttpHeadersTestGroupType; typedef HttpHeadersTestGroupType::object HttpHeadersTestObjectType; HttpHeadersTestGroupType HttpHeadersTestGroup("HttpHeaders Tests"); +} template <> template <> void HttpHeadersTestObjectType::test<1>() diff --git a/indra/llcorehttp/tests/test_httpoperation.hpp b/indra/llcorehttp/tests/test_httpoperation.hpp index 361091c7b21..2d17d00eabe 100644 --- a/indra/llcorehttp/tests/test_httpoperation.hpp +++ b/indra/llcorehttp/tests/test_httpoperation.hpp @@ -53,6 +53,8 @@ class TestHandler : public LLCore::HttpHandler namespace tut +{ +namespace { struct HttpOperationTestData { @@ -63,6 +65,7 @@ namespace tut typedef test_group HttpOperationTestGroupType; typedef HttpOperationTestGroupType::object HttpOperationTestObjectType; HttpOperationTestGroupType HttpOperationTestGroup("HttpOperation Tests"); +} template <> template <> void HttpOperationTestObjectType::test<1>() diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 5dacfc9e947..6d5b8ab0a39 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -67,6 +67,8 @@ void usleep(unsigned long usec); namespace tut { +namespace +{ typedef std::vector > regex_container_t; @@ -184,6 +186,7 @@ class TestHandler2 : public LLCore::HttpHandler typedef test_group HttpRequestTestGroupType; typedef HttpRequestTestGroupType::object HttpRequestTestObjectType; HttpRequestTestGroupType HttpRequestTestGroup("HttpRequest Tests"); +} template <> template <> void HttpRequestTestObjectType::test<1>() diff --git a/indra/llcorehttp/tests/test_httprequestqueue.hpp b/indra/llcorehttp/tests/test_httprequestqueue.hpp index c6fa1b7534a..11a00169120 100644 --- a/indra/llcorehttp/tests/test_httprequestqueue.hpp +++ b/indra/llcorehttp/tests/test_httprequestqueue.hpp @@ -39,6 +39,8 @@ using namespace LLCoreInt; namespace tut { +namespace +{ struct HttpRequestqueueTestData { @@ -49,6 +51,7 @@ struct HttpRequestqueueTestData typedef test_group HttpRequestqueueTestGroupType; typedef HttpRequestqueueTestGroupType::object HttpRequestqueueTestObjectType; HttpRequestqueueTestGroupType HttpRequestqueueTestGroup("HttpRequestqueue Tests"); +} template <> template <> void HttpRequestqueueTestObjectType::test<1>() diff --git a/indra/llcorehttp/tests/test_httpstatus.hpp b/indra/llcorehttp/tests/test_httpstatus.hpp index eac7ba8557b..3a0ff04370f 100644 --- a/indra/llcorehttp/tests/test_httpstatus.hpp +++ b/indra/llcorehttp/tests/test_httpstatus.hpp @@ -36,6 +36,8 @@ using namespace LLCore; namespace tut { +namespace +{ struct HttpStatusTestData { @@ -47,6 +49,7 @@ typedef test_group HttpStatusTestGroupType; typedef HttpStatusTestGroupType::object HttpStatusTestObjectType; HttpStatusTestGroupType HttpStatusTestGroup("HttpStatus Tests"); +} template <> template <> void HttpStatusTestObjectType::test<1>() diff --git a/indra/llcorehttp/tests/test_jsonrpcws.hpp b/indra/llcorehttp/tests/test_jsonrpcws.hpp index e87b2a19d4d..5ae24a3181b 100644 --- a/indra/llcorehttp/tests/test_jsonrpcws.hpp +++ b/indra/llcorehttp/tests/test_jsonrpcws.hpp @@ -29,6 +29,8 @@ class TestJSONRPCConnection : public LLJSONRPCConnection } namespace tut +{ +namespace { struct JSONRPCWSTestData { @@ -37,6 +39,7 @@ namespace tut typedef test_group JSONRPCWSTestGroupType; typedef JSONRPCWSTestGroupType::object JSONRPCWSTestObjectType; JSONRPCWSTestGroupType JSONRPCWSTestGroup("LLJSONRPCConnection Tests"); +} template<> template<> void JSONRPCWSTestObjectType::test<1>() diff --git a/indra/llcorehttp/tests/test_refcounted.hpp b/indra/llcorehttp/tests/test_refcounted.hpp index eb23a25545a..9b1a48b8c73 100644 --- a/indra/llcorehttp/tests/test_refcounted.hpp +++ b/indra/llcorehttp/tests/test_refcounted.hpp @@ -31,6 +31,8 @@ using namespace LLCoreInt; namespace tut +{ +namespace { struct RefCountedTestData { @@ -41,6 +43,7 @@ namespace tut typedef test_group RefCountedTestGroupType; typedef RefCountedTestGroupType::object RefCountedTestObjectType; RefCountedTestGroupType RefCountedTestGroup("RefCounted Tests"); +} template <> template <> void RefCountedTestObjectType::test<1>() diff --git a/indra/llcorehttp/tests/test_websocketmgr.hpp b/indra/llcorehttp/tests/test_websocketmgr.hpp index 8f7737427cc..0f46d0fe996 100644 --- a/indra/llcorehttp/tests/test_websocketmgr.hpp +++ b/indra/llcorehttp/tests/test_websocketmgr.hpp @@ -424,14 +424,25 @@ namespace } namespace tut +{ +namespace { struct WebsocketMgrTestData { + // Each test adds its servers to the manager and removes them, and one + // that fails first would leave them listening into the next. The + // manager goes with the test, as it was not there before it, and + // stops whatever servers it still holds as it goes. + ~WebsocketMgrTestData() + { + LLWebsocketMgr::deleteSingleton(); + } }; typedef test_group WebsocketMgrTestGroupType; typedef WebsocketMgrTestGroupType::object WebsocketMgrTestObjectType; WebsocketMgrTestGroupType WebsocketMgrTestGroup("LLWebsocketMgr Tests"); +} template<> template<> void WebsocketMgrTestObjectType::test<1>() diff --git a/indra/llfilesystem/CMakeLists.txt b/indra/llfilesystem/CMakeLists.txt index ecebdd1030f..dce31cb115a 100644 --- a/indra/llfilesystem/CMakeLists.txt +++ b/indra/llfilesystem/CMakeLists.txt @@ -32,6 +32,6 @@ target_precompile_headers(llfilesystem REUSE_FROM llprecompiled) # Add tests if(AL_BUILD_TESTS) - al_add_test(lldiriterator UNIT PROJECT llfilesystem) + al_add_test(lldiriterator PROJECT llfilesystem LIBRARIES llfilesystem) al_add_test(lldir PROJECT llfilesystem LIBRARIES llmath llcommon llfilesystem) endif() diff --git a/indra/llfilesystem/tests/lldir_test.cpp b/indra/llfilesystem/tests/lldir_test.cpp index 4c928e5e0ea..3f243d1e3b6 100644 --- a/indra/llfilesystem/tests/lldir_test.cpp +++ b/indra/llfilesystem/tests/lldir_test.cpp @@ -46,6 +46,8 @@ #include #endif +namespace +{ // For some tests, use a dummy LLDir that uses memory data instead of touching // the filesystem struct LLDir_Dummy: public LLDir @@ -192,6 +194,7 @@ struct LLDir_Relocated: public LLDir_Dummy mDefaultCacheDir = "cachedir"; } }; +} #if LL_LINUX namespace @@ -270,6 +273,8 @@ namespace #endif // LL_LINUX namespace tut +{ +namespace { struct LLDirTest { @@ -277,6 +282,7 @@ namespace tut typedef test_group LLDirTest_t; typedef LLDirTest_t::object LLDirTest_object_t; tut::LLDirTest_t tut_LLDirTest("LLDir"); +} template<> template<> void LLDirTest_object_t::test<1>() @@ -494,6 +500,8 @@ namespace tut "ext"); } +namespace +{ std::string makeTestFile( const std::string& dir, const std::string& file ) { std::string path = dir + file; @@ -564,6 +572,7 @@ namespace tut } } } +} template<> template<> void LLDirTest_object_t::test<5>() diff --git a/indra/llfilesystem/tests/lldiriterator_test.cpp b/indra/llfilesystem/tests/lldiriterator_test.cpp index dc573b55559..15470bd9666 100644 --- a/indra/llfilesystem/tests/lldiriterator_test.cpp +++ b/indra/llfilesystem/tests/lldiriterator_test.cpp @@ -33,6 +33,8 @@ namespace tut { +namespace +{ struct LLDirIteratorFixture { LLDirIteratorFixture() @@ -55,6 +57,7 @@ namespace tut LLDirIterator iter1(".","))--@---bad-group-name2((?\?-??.*\\.txt"); LLDirIterator iter2(".","__^v--x)Cuide d sua vida(x--v^__?\?-??.*"); } +} template<> template<> void LLDirIteratorTest_t::test<1>() diff --git a/indra/llimage/CMakeLists.txt b/indra/llimage/CMakeLists.txt index 5976f054e3a..5d9d9642ab1 100644 --- a/indra/llimage/CMakeLists.txt +++ b/indra/llimage/CMakeLists.txt @@ -57,5 +57,7 @@ target_precompile_headers(llimage REUSE_FROM llprecompiled) # Add tests if(AL_BUILD_TESTS) + # It stubs the image classes the library defines, so it compiles the one + # file it tests instead of linking the library. al_add_test(llimageworker UNIT PROJECT llimage) endif() diff --git a/indra/llimage/tests/llimageworker_test.cpp b/indra/llimage/tests/llimageworker_test.cpp index 28c3feb2ab9..663142d79d0 100644 --- a/indra/llimage/tests/llimageworker_test.cpp +++ b/indra/llimage/tests/llimageworker_test.cpp @@ -99,6 +99,8 @@ const std::string& LLImage::getLastThreadError() { static std::string msg; retur // ------------------------------------------------------------------------------------------- namespace tut +{ +namespace { // Test wrapper declarations @@ -151,6 +153,7 @@ namespace tut typedef test_group imagedecodethread_t; typedef imagedecodethread_t::object imagedecodethread_object_t; tut::imagedecodethread_t tut_imagedecodethread("LLImageDecodeThread"); +} // --------------------------------------------------------------------------------------- // Test functions diff --git a/indra/llinventory/tests/inventorymisc_test.cpp b/indra/llinventory/tests/inventorymisc_test.cpp index f11a4c3bf71..1a7eeeb26b7 100644 --- a/indra/llinventory/tests/inventorymisc_test.cpp +++ b/indra/llinventory/tests/inventorymisc_test.cpp @@ -32,6 +32,7 @@ #include "../llinventory.h" #include "../test/lltut.h" +#include "../test/namedtempfile.h" #if LL_WINDOWS @@ -39,6 +40,8 @@ #pragma warning(disable: 4702) #endif +namespace +{ void set_random_inventory_metadata(LLInventoryObject* obj) { S32 extra = rand() % 4; @@ -122,8 +125,11 @@ LLPointer create_random_inventory_cat() set_random_inventory_metadata(cat); return cat; } +} namespace tut +{ +namespace { struct inventory_data { @@ -131,6 +137,7 @@ namespace tut typedef test_group inventory_test; typedef inventory_test::object inventory_object; tut::inventory_test inv("LLInventory"); +} //***class LLInventoryType***// @@ -351,7 +358,8 @@ namespace tut template<> template<> void inventory_object::test<7>() { - std::string filename("linden_file.dat"); + NamedTempFile temp_file("linden_file", "", ".dat"); + std::string filename(temp_file.getPath().string()); llofstream fileXML(filename.c_str()); if (!fileXML.is_open()) { @@ -484,7 +492,8 @@ namespace tut template<> template<> void inventory_object::test<13>() { - std::string filename("linden_file.dat"); + NamedTempFile temp_file("linden_file", "", ".dat"); + std::string filename(temp_file.getPath().string()); llofstream fileXML(filename.c_str()); if (!fileXML.is_open()) { diff --git a/indra/llinventory/tests/llparcel_test.cpp b/indra/llinventory/tests/llparcel_test.cpp index c6b433d4689..c32d17b449a 100644 --- a/indra/llinventory/tests/llparcel_test.cpp +++ b/indra/llinventory/tests/llparcel_test.cpp @@ -34,6 +34,8 @@ #include "../test/lltut.h" namespace tut +{ +namespace { struct llinventoryparcel_data { @@ -41,6 +43,7 @@ namespace tut typedef test_group llinventoryparcel_test; typedef llinventoryparcel_test::object llinventoryparcel_object; tut::llinventoryparcel_test llinventoryparcel("LLInventoryParcel"); +} template<> template<> void llinventoryparcel_object::test<1>() diff --git a/indra/llinventory/tests/llpermissions_test.cpp b/indra/llinventory/tests/llpermissions_test.cpp index 13289390049..c600e3a45d1 100644 --- a/indra/llinventory/tests/llpermissions_test.cpp +++ b/indra/llinventory/tests/llpermissions_test.cpp @@ -36,6 +36,8 @@ namespace tut +{ +namespace { struct permission { @@ -43,6 +45,7 @@ namespace tut typedef test_group permission_t; typedef permission_t::object permission_object_t; tut::permission_t tut_permission("permission"); +} template<> template<> void permission_object_t::test<1>() diff --git a/indra/llinventory/tests/llsaleinfo_test.cpp b/indra/llinventory/tests/llsaleinfo_test.cpp index 1da7101aa39..44c47c26625 100644 --- a/indra/llinventory/tests/llsaleinfo_test.cpp +++ b/indra/llinventory/tests/llsaleinfo_test.cpp @@ -32,6 +32,8 @@ #include "llsaleinfo.h" namespace tut +{ +namespace { struct llsaleinfo_tut { @@ -39,6 +41,7 @@ namespace tut typedef test_group llsaleinfo_tut_t; typedef llsaleinfo_tut_t::object llsaleinfo_test_t; tut::llsaleinfo_tut_t tut_llsaleinfo_test("llsaleinfo"); +} template<> template<> void llsaleinfo_test_t::test<1>() diff --git a/indra/llinventory/tests/llsettingssky_test.cpp b/indra/llinventory/tests/llsettingssky_test.cpp index 0e56ce3c986..9e64572853e 100644 --- a/indra/llinventory/tests/llsettingssky_test.cpp +++ b/indra/llinventory/tests/llsettingssky_test.cpp @@ -67,6 +67,8 @@ namespace } namespace tut +{ +namespace { struct llsettingssky_data { @@ -74,6 +76,7 @@ namespace tut typedef test_group llsettingssky_group; typedef llsettingssky_group::object llsettingssky_object; tut::llsettingssky_group tut_llsettingssky("llsettingssky"); +} // setMieAnisotropy changes the sky's g and the LLSD it saves. template<> template<> diff --git a/indra/llinventory/tests/lluserrelations_test.cpp b/indra/llinventory/tests/lluserrelations_test.cpp index 80a659da65c..0b0706c9b50 100644 --- a/indra/llinventory/tests/lluserrelations_test.cpp +++ b/indra/llinventory/tests/lluserrelations_test.cpp @@ -32,6 +32,8 @@ #include "lluserrelations.h" namespace tut +{ +namespace { struct user_relationship { @@ -40,6 +42,7 @@ namespace tut typedef test_group user_relationship_t; typedef user_relationship_t::object user_relationship_object_t; tut::user_relationship_t tut_user_relationship("relationships"); +} template<> template<> void user_relationship_object_t::test<1>() diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt index 87ff6123fd4..a5dbe13fd98 100644 --- a/indra/llmath/CMakeLists.txt +++ b/indra/llmath/CMakeLists.txt @@ -103,6 +103,8 @@ target_precompile_headers(llmath REUSE_FROM llprecompiled) # Add tests if(AL_BUILD_TESTS) + # Each links the library rather than compiling a copy of the file it + # tests, so the code under test is the code the viewer runs. foreach( test llbboxlocal @@ -112,12 +114,6 @@ if(AL_BUILD_TESTS) v3color v4color v4coloru - ) - al_add_test(${test} UNIT PROJECT llmath) - endforeach() - - foreach( - test alignment alplaneset llsdutil_math @@ -126,16 +122,16 @@ if(AL_BUILD_TESTS) llmatrix3a llmatrix4a llvector4a + llbbox + llquaternion + llquaternion2 + v3dmath + v3math + v4math + xform ) al_add_test(${test} PROJECT llmath LIBRARIES llmath llcommon) endforeach() - al_add_test(llbbox PROJECT llmath SOURCES llbbox.cpp LIBRARIES llmath llcommon) - al_add_test(llquaternion PROJECT llmath SOURCES llquaternion.cpp LIBRARIES llmath llcommon) - al_add_test(llquaternion2 PROJECT llmath SOURCES llquaternion.cpp LIBRARIES llmath llcommon) - al_add_test(v3dmath PROJECT llmath SOURCES v3dmath.cpp LIBRARIES llmath llcommon) - al_add_test(v3math PROJECT llmath SOURCES v3math.cpp LIBRARIES llmath llcommon) - al_add_test(v4math PROJECT llmath SOURCES v4math.cpp LIBRARIES llmath llcommon) - al_add_test(xform PROJECT llmath SOURCES xform.cpp LIBRARIES llmath llcommon) # The batch kernels at every width, at the tree's tier and, where the # tiers differ, at each of them; a variant the host cannot run skips. diff --git a/indra/llmath/tests/alignment_test.cpp b/indra/llmath/tests/alignment_test.cpp index 0d77896958d..3e4a25c66f8 100644 --- a/indra/llmath/tests/alignment_test.cpp +++ b/indra/llmath/tests/alignment_test.cpp @@ -44,6 +44,8 @@ namespace tut #define is_aligned(ptr,alignment) ((reinterpret_cast(ptr))%(alignment)==0) #define is_aligned_relative(ptr,base_ptr,alignment) ((reinterpret_cast(ptr)-reinterpret_cast(base_ptr))%(alignment)==0) +namespace +{ struct alignment_test {}; typedef test_group alignment_test_t; @@ -63,6 +65,7 @@ struct alignas(64) MyCacheLine { LLQuad mQ[4]; }; +} // Verify that aligned allocators perform as advertised. template<> template<> diff --git a/indra/llmath/tests/alplaneset_test.cpp b/indra/llmath/tests/alplaneset_test.cpp index 7baa4b3fd81..f39e19482d1 100644 --- a/indra/llmath/tests/alplaneset_test.cpp +++ b/indra/llmath/tests/alplaneset_test.cpp @@ -99,6 +99,8 @@ namespace } // namespace tut namespace tut +{ +namespace { struct alplaneset_data { @@ -123,6 +125,7 @@ namespace tut typedef test_group alplaneset_test; typedef alplaneset_test::object alplaneset_object; tut::alplaneset_test alplaneset_testcase("ALPlaneSet"); +} // An empty set contains everything; a set with one plane splits space. template<> template<> diff --git a/indra/llmath/tests/alsimdkernels_test.cpp b/indra/llmath/tests/alsimdkernels_test.cpp index eeff0e57221..ccf24a3dea0 100644 --- a/indra/llmath/tests/alsimdkernels_test.cpp +++ b/indra/llmath/tests/alsimdkernels_test.cpp @@ -135,10 +135,47 @@ namespace } const LLVector4a SENTINEL(1234.f, 1234.f, 1234.f, 1234.f); + + // The floating-point control state set_thread_fp_mode() writes: MXCSR on + // x86-64, FPCR on aarch64. It is per thread, so a test that changes it + // on the main thread changes it for every test after it; this puts back + // what it found. + class FPModeRestorer + { + public: + FPModeRestorer() : mSaved(read()) {} + ~FPModeRestorer() { write(mSaved); } + FPModeRestorer(const FPModeRestorer&) = delete; + FPModeRestorer& operator=(const FPModeRestorer&) = delete; + + private: +#if AL_SIMD_X86 + using State = U32; + static State read() { return _mm_getcsr(); } + static void write(State state) { _mm_setcsr(state); } +#elif defined(_MSC_VER) && !defined(__clang__) + using State = U64; + static State read() { return static_cast(_ReadStatusReg(ARM64_FPCR)); } + static void write(State state) { _WriteStatusReg(ARM64_FPCR, static_cast<__int64>(state)); } +#else + using State = U64; + static State read() + { + State fpcr; + __asm__ volatile("mrs %0, fpcr" : "=r"(fpcr)); + return fpcr; + } + static void write(State state) { __asm__ volatile("msr fpcr, %0" : : "r"(state)); } +#endif + + State mSaved; + }; } } // namespace tut namespace tut +{ +namespace { struct alsimdkernels_data { @@ -146,6 +183,7 @@ namespace tut typedef test_group alsimdkernels_test; typedef alsimdkernels_test::object alsimdkernels_object; tut::alsimdkernels_test alsimdkernels_testcase("alsimdkernels"); +} // Points, with w from the rows and with w set, against // affineTransform, bit for bit, at every count and width; nothing @@ -683,6 +721,7 @@ namespace tut template<> template<> void alsimdkernels_object::test<10>() { + FPModeRestorer restore_fp_mode; set_thread_fp_mode(); const LLMatrix4a m = some_matrix(); for (S32 index : {0, 1, 7, 15}) diff --git a/indra/llmath/tests/llbbox_test.cpp b/indra/llmath/tests/llbbox_test.cpp index bd6bd9e4b0f..8b2c2bfaa2e 100644 --- a/indra/llmath/tests/llbbox_test.cpp +++ b/indra/llmath/tests/llbbox_test.cpp @@ -37,6 +37,8 @@ #define APPROX_EQUAL(a, b) (dist_vec_squared((a),(b)) < 1e-10) namespace tut +{ +namespace { struct LLBBoxData { @@ -45,6 +47,7 @@ namespace tut typedef test_group factory; typedef factory::object object; } +} namespace { diff --git a/indra/llmath/tests/llbboxlocal_test.cpp b/indra/llmath/tests/llbboxlocal_test.cpp index 120e18cadf7..41d6326bc5a 100644 --- a/indra/llmath/tests/llbboxlocal_test.cpp +++ b/indra/llmath/tests/llbboxlocal_test.cpp @@ -31,6 +31,8 @@ #include "../llbboxlocal.h" namespace tut +{ +namespace { struct LLBBoxLocalData { @@ -39,6 +41,7 @@ namespace tut typedef test_group factory; typedef factory::object object; } +} namespace { diff --git a/indra/llmath/tests/llmatrix3a_test.cpp b/indra/llmath/tests/llmatrix3a_test.cpp index d1c99e70d32..b0da1591ddd 100644 --- a/indra/llmath/tests/llmatrix3a_test.cpp +++ b/indra/llmath/tests/llmatrix3a_test.cpp @@ -147,6 +147,8 @@ namespace } // namespace tut namespace tut +{ +namespace { struct llmatrix3a_data { @@ -154,6 +156,7 @@ namespace tut typedef test_group llmatrix3a_test; typedef llmatrix3a_test::object llmatrix3a_object; tut::llmatrix3a_test llmatrix3a_testcase("LLMatrix3a"); +} // Plain data of three registers; the identity; set and get by row and // by column. diff --git a/indra/llmath/tests/llmatrix4a_test.cpp b/indra/llmath/tests/llmatrix4a_test.cpp index 74455efe8de..32192faead7 100644 --- a/indra/llmath/tests/llmatrix4a_test.cpp +++ b/indra/llmath/tests/llmatrix4a_test.cpp @@ -70,6 +70,8 @@ namespace } namespace tut +{ +namespace { struct llmatrix4a_data { @@ -307,6 +309,7 @@ namespace tut { return LLVector4a(v.mV[0], v.mV[1], v.mV[2], 0.f); } +} template<> template<> void llmatrix4a_object::test<1>() diff --git a/indra/llmath/tests/llmodularmath_test.cpp b/indra/llmath/tests/llmodularmath_test.cpp index 399b3af05d8..c9d23c07e0d 100644 --- a/indra/llmath/tests/llmodularmath_test.cpp +++ b/indra/llmath/tests/llmodularmath_test.cpp @@ -33,6 +33,8 @@ #include "../test/lltut.h" namespace tut +{ +namespace { struct modularmath_data { @@ -40,6 +42,7 @@ namespace tut typedef test_group modularmath_test; typedef modularmath_test::object modularmath_object; tut::modularmath_test modularmath_testcase("LLModularMath"); +} template<> template<> void modularmath_object::test<1>() diff --git a/indra/llmath/tests/llquaternion2_test.cpp b/indra/llmath/tests/llquaternion2_test.cpp index 7f5bf09ab41..db80e82076c 100644 --- a/indra/llmath/tests/llquaternion2_test.cpp +++ b/indra/llmath/tests/llquaternion2_test.cpp @@ -91,6 +91,8 @@ namespace } namespace tut +{ +namespace { struct llquaternion2_data { @@ -99,6 +101,7 @@ namespace tut typedef test_group llquaternion2_test; typedef llquaternion2_test::object llquaternion2_object; tut::llquaternion2_test llquaternion2_testcase("LLQuaternion2"); +} template<> template<> void llquaternion2_object::test<1>() diff --git a/indra/llmath/tests/llquaternion_test.cpp b/indra/llmath/tests/llquaternion_test.cpp index cc09d59f258..a826eafa13f 100644 --- a/indra/llmath/tests/llquaternion_test.cpp +++ b/indra/llmath/tests/llquaternion_test.cpp @@ -37,6 +37,8 @@ #include "../llquaternion.h" namespace tut +{ +namespace { struct llquat_test { @@ -44,6 +46,7 @@ namespace tut typedef test_group llquat_test_t; typedef llquat_test_t::object llquat_test_object_t; tut::llquat_test_t tut_llquat_test("LLQuaternion"); +} //test case for LLQuaternion::LLQuaternion(void) fn. template<> template<> diff --git a/indra/llmath/tests/llrect_test.cpp b/indra/llmath/tests/llrect_test.cpp index 0fd5602d4a4..a98907140cb 100644 --- a/indra/llmath/tests/llrect_test.cpp +++ b/indra/llmath/tests/llrect_test.cpp @@ -31,6 +31,8 @@ #include "../llrect.h" namespace tut +{ +namespace { struct LLRectData { @@ -39,6 +41,7 @@ namespace tut typedef test_group factory; typedef factory::object object; } +} namespace { diff --git a/indra/llmath/tests/llsdutil_math_test.cpp b/indra/llmath/tests/llsdutil_math_test.cpp index f7f096e59a2..8c79d002dac 100644 --- a/indra/llmath/tests/llsdutil_math_test.cpp +++ b/indra/llmath/tests/llsdutil_math_test.cpp @@ -44,6 +44,8 @@ #include namespace tut +{ +namespace { struct llsdutil_math_data { @@ -77,7 +79,8 @@ namespace tut }; typedef test_group llsdutil_math_test; typedef llsdutil_math_test::object llsdutil_math_object; - tut::llsdutil_math_test tutil("llsdutil_math"); + tut::llsdutil_math_test llsdutil_math_testcase("llsdutil_math"); +} template<> template<> void llsdutil_math_object::test<1>() diff --git a/indra/llmath/tests/llvector4a_test.cpp b/indra/llmath/tests/llvector4a_test.cpp index dcd73b96d81..18b34bb9745 100644 --- a/indra/llmath/tests/llvector4a_test.cpp +++ b/indra/llmath/tests/llvector4a_test.cpp @@ -159,6 +159,8 @@ namespace } // namespace tut namespace tut +{ +namespace { struct llvector4a_data { @@ -181,6 +183,7 @@ namespace tut typedef test_group llvector4a_test; typedef llvector4a_test::object llvector4a_object; tut::llvector4a_test llvector4a_testcase("LLVector4a"); +} // Plain data of one register, and the two constants. template<> template<> diff --git a/indra/llmath/tests/m3math_test.cpp b/indra/llmath/tests/m3math_test.cpp index ab71ec6039b..6a9a841436a 100644 --- a/indra/llmath/tests/m3math_test.cpp +++ b/indra/llmath/tests/m3math_test.cpp @@ -48,6 +48,8 @@ #endif namespace tut +{ +namespace { struct m3math_test { @@ -55,6 +57,7 @@ namespace tut typedef test_group m3math_test_t; typedef m3math_test_t::object m3math_test_object_t; tut::m3math_test_t tut_m3math_test("m3math_h"); +} //test case for setIdentity() fn. template<> template<> diff --git a/indra/llmath/tests/mathmisc_test.cpp b/indra/llmath/tests/mathmisc_test.cpp index 917d7cded30..c28d353ace8 100644 --- a/indra/llmath/tests/mathmisc_test.cpp +++ b/indra/llmath/tests/mathmisc_test.cpp @@ -28,6 +28,7 @@ #include "linden_common.h" #include "../test/lltut.h" +#include "../test/alrandomreseed.h" #include "llcrc.h" #include "llrand.h" @@ -40,6 +41,8 @@ #include namespace tut +{ +namespace { struct math_data { @@ -47,6 +50,7 @@ namespace tut typedef test_group math_test; typedef math_test::object math_object; tut::math_test tm("BasicLindenMath"); +} template<> template<> void math_object::test<1>() @@ -172,6 +176,8 @@ namespace tut } namespace tut +{ +namespace { struct uuid_data { @@ -179,7 +185,8 @@ namespace tut }; typedef test_group uuid_test; typedef uuid_test::object uuid_object; - tut::uuid_test tu("LLUUID"); + tut::uuid_test tu("mathmisc LLUUID"); +} template<> template<> void uuid_object::test<1>() @@ -228,6 +235,8 @@ namespace tut } namespace tut +{ +namespace { struct crc_data { @@ -235,6 +244,7 @@ namespace tut typedef test_group crc_test; typedef crc_test::object crc_object; tut::crc_test tc("LLCrc"); +} template<> template<> void crc_object::test<1>() @@ -280,6 +290,8 @@ namespace tut } namespace tut +{ +namespace { F32 SMALL_RADIUS = 1.0f; F32 MEDIUM_RADIUS = 5.0f; @@ -287,10 +299,13 @@ namespace tut struct line_data { + // Tests 1 and 3 seed the main thread's generator. + ~line_data() { ll_test::reseedRandomFromEntropy(); } }; typedef test_group line_test; typedef line_test::object line_object; tut::line_test tline("LLLine"); +} template<> template<> void line_object::test<1>() @@ -466,7 +481,11 @@ namespace tut */ } +namespace +{ F32 ALMOST_PARALLEL = 0.99f; +} + template<> template<> void line_object::test<3>() { diff --git a/indra/llmath/tests/v2math_test.cpp b/indra/llmath/tests/v2math_test.cpp index 860e3ef6725..7bce7d5fe74 100644 --- a/indra/llmath/tests/v2math_test.cpp +++ b/indra/llmath/tests/v2math_test.cpp @@ -33,6 +33,8 @@ namespace tut +{ +namespace { struct v2math_data { @@ -40,6 +42,7 @@ namespace tut typedef test_group v2math_test; typedef v2math_test::object v2math_object; tut::v2math_test v2math_testcase("v2math_h"); +} template<> template<> void v2math_object::test<1>() diff --git a/indra/llmath/tests/v3color_test.cpp b/indra/llmath/tests/v3color_test.cpp index 8897d483650..0003c3cd605 100644 --- a/indra/llmath/tests/v3color_test.cpp +++ b/indra/llmath/tests/v3color_test.cpp @@ -33,6 +33,8 @@ namespace tut +{ +namespace { struct v3color_data { @@ -40,6 +42,7 @@ namespace tut typedef test_group v3color_test; typedef v3color_test::object v3color_object; tut::v3color_test v3color_testcase("v3color_h"); +} template<> template<> void v3color_object::test<1>() diff --git a/indra/llmath/tests/v3dmath_test.cpp b/indra/llmath/tests/v3dmath_test.cpp index f66962eaf74..10ee4c3523a 100644 --- a/indra/llmath/tests/v3dmath_test.cpp +++ b/indra/llmath/tests/v3dmath_test.cpp @@ -37,6 +37,8 @@ #include "../llquaternion.h" namespace tut +{ +namespace { struct v3dmath_data { @@ -44,6 +46,7 @@ namespace tut typedef test_group v3dmath_test; typedef v3dmath_test::object v3dmath_object; tut::v3dmath_test v3dmath_testcase("v3dmath_h"); +} template<> template<> void v3dmath_object::test<1>() diff --git a/indra/llmath/tests/v3math_test.cpp b/indra/llmath/tests/v3math_test.cpp index 6d95a9e5b75..805d692114c 100644 --- a/indra/llmath/tests/v3math_test.cpp +++ b/indra/llmath/tests/v3math_test.cpp @@ -39,6 +39,8 @@ namespace tut +{ +namespace { struct v3math_data { @@ -46,6 +48,7 @@ namespace tut typedef test_group v3math_test; typedef v3math_test::object v3math_object; tut::v3math_test v3math_testcase("v3math_h"); +} template<> template<> void v3math_object::test<1>() diff --git a/indra/llmath/tests/v4color_test.cpp b/indra/llmath/tests/v4color_test.cpp index e5d914a065d..b70a43f62f5 100644 --- a/indra/llmath/tests/v4color_test.cpp +++ b/indra/llmath/tests/v4color_test.cpp @@ -37,6 +37,8 @@ namespace tut +{ +namespace { struct v4color_data { @@ -44,6 +46,7 @@ namespace tut typedef test_group v4color_test; typedef v4color_test::object v4color_object; tut::v4color_test v4color_testcase("v4color_h"); +} template<> template<> void v4color_object::test<1>() diff --git a/indra/llmath/tests/v4coloru_test.cpp b/indra/llmath/tests/v4coloru_test.cpp index ad181e76b2c..1dd48d801a3 100644 --- a/indra/llmath/tests/v4coloru_test.cpp +++ b/indra/llmath/tests/v4coloru_test.cpp @@ -36,6 +36,8 @@ namespace tut +{ +namespace { struct v4coloru_data { @@ -43,6 +45,7 @@ namespace tut typedef test_group v4coloru_test; typedef v4coloru_test::object v4coloru_object; tut::v4coloru_test v4coloru_testcase("v4coloru_h"); +} template<> template<> void v4coloru_object::test<1>() diff --git a/indra/llmath/tests/v4math_test.cpp b/indra/llmath/tests/v4math_test.cpp index 236585f13b2..aba953563f1 100644 --- a/indra/llmath/tests/v4math_test.cpp +++ b/indra/llmath/tests/v4math_test.cpp @@ -35,6 +35,8 @@ #include "../llquaternion.h" namespace tut +{ +namespace { struct v4math_data { @@ -42,6 +44,7 @@ namespace tut typedef test_group v4math_test; typedef v4math_test::object v4math_object; tut::v4math_test v4math_testcase("v4math_h"); +} template<> template<> void v4math_object::test<1>() diff --git a/indra/llmath/tests/xform_test.cpp b/indra/llmath/tests/xform_test.cpp index 6d6a64ec4c8..39a72a1cc55 100644 --- a/indra/llmath/tests/xform_test.cpp +++ b/indra/llmath/tests/xform_test.cpp @@ -32,6 +32,8 @@ #include "../xform.h" namespace tut +{ +namespace { struct xform_test { @@ -39,6 +41,7 @@ namespace tut typedef test_group xform_test_t; typedef xform_test_t::object xform_test_object_t; tut::xform_test_t tut_xform_test("LLXForm"); +} //test case for init(), getParent(), getRotation(), getPositionW(), getWorldRotation() fns. template<> template<> diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 476b07b9df9..018693a9213 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -170,7 +170,9 @@ target_precompile_headers(llmessage REUSE_FROM llprecompiled) # tests if(AL_BUILD_TESTS) - foreach(test llcoproceduremanager llnamevalue lltrustedmessageservice lltemplatemessagedispatcher) + # These three stand in for parts of llmessage and llcorehttp with stubs of + # their own, so they build their one file and do not link llmessage. + foreach(test llcoproceduremanager lltrustedmessageservice lltemplatemessagedispatcher) al_add_test(${test} UNIT PROJECT llmessage LIBRARIES llmath llcorehttp) endforeach() @@ -195,6 +197,7 @@ if(AL_BUILD_TESTS) llsdmessagereader llmessageconfig llmessagetemplateparser + llnamevalue llpartdata lltemplatemessagebuilder llxfer_file diff --git a/indra/llmessage/tests/alnetimpairment_test.cpp b/indra/llmessage/tests/alnetimpairment_test.cpp index 60ce0ae8c7b..9dc0c393561 100644 --- a/indra/llmessage/tests/alnetimpairment_test.cpp +++ b/indra/llmessage/tests/alnetimpairment_test.cpp @@ -25,6 +25,8 @@ #include namespace tut +{ +namespace { struct impairment_data { @@ -74,6 +76,7 @@ namespace tut typedef test_group impairment_group_t; typedef impairment_group_t::object impairment_object_t; tut::impairment_group_t impairment_group("ALNetImpairment"); +} template<> template<> void impairment_object_t::test<1>() @@ -233,10 +236,13 @@ namespace tut // Nothing to test in a build the impairment is compiled out of, but the test binary still has to // link and report success. namespace tut +{ +namespace { struct impairment_data {}; typedef test_group impairment_group_t; tut::impairment_group_t impairment_group("ALNetImpairment"); +} template<> template<> void impairment_group_t::object::test<1>() diff --git a/indra/llmessage/tests/io_test.cpp b/indra/llmessage/tests/io_test.cpp index 68a5c100602..683142d18c3 100644 --- a/indra/llmessage/tests/io_test.cpp +++ b/indra/llmessage/tests/io_test.cpp @@ -48,6 +48,8 @@ #include "llinstantmessage.h" namespace tut +{ +namespace { struct heap_buffer_data { @@ -58,6 +60,7 @@ namespace tut typedef test_group heap_buffer_test; typedef heap_buffer_test::object heap_buffer_object; tut::heap_buffer_test thb("heap_buffer"); +} template<> template<> void heap_buffer_object::test<1>() @@ -143,6 +146,8 @@ namespace tut } namespace tut +{ +namespace { struct buffer_data { @@ -151,6 +156,7 @@ namespace tut typedef test_group buffer_test; typedef buffer_test::object buffer_object; tut::buffer_test tba("buffer_array"); +} template<> template<> void buffer_object::test<1>() @@ -399,6 +405,8 @@ namespace tut } namespace tut +{ +namespace { struct buffer_and_stream_data { @@ -407,6 +415,7 @@ namespace tut typedef test_group bas_test; typedef bas_test::object bas_object; tut::bas_test tbs("buffer_stream"); +} template<> template<> void bas_object::test<1>() @@ -819,6 +828,8 @@ namespace tut namespace tut +{ +namespace { class PumpAndChainTestData { @@ -844,6 +855,7 @@ namespace tut typedef test_group PumpAndChainTestGroup; typedef PumpAndChainTestGroup::object PumpAndChainTestObject; PumpAndChainTestGroup pumpAndChainTestGroup("pump_and_chain"); +} template<> template<> void PumpAndChainTestObject::test<1>() @@ -895,6 +907,8 @@ namespace tut */ namespace tut +{ +namespace { /** * @brief we want to test the pipes & pumps under bad conditions. @@ -902,21 +916,39 @@ namespace tut struct pipe_and_pump_fitness { public: - enum - { - SERVER_LISTEN_PORT = 13050 - }; - pipe_and_pump_fitness() { LLFrameTimer::updateFrameTime(); apr_pool_create(&mPool, NULL); mPump = new LLPumpIO(mPool); - mSocket = LLSocket::create( - mPool, - LLSocket::STREAM_TCP, - SERVER_LISTEN_PORT, - "127.0.0.1"); + mSocket = listenOnAnyPort(mPool, mServerPort); + } + + // LLSocket::create binds and listens only on a port it is given, so + // the fixture does as it does on port 0 and reads back the port the + // OS chose: two runs at once then never contend for one. + static LLSocket::ptr_t listenOnAnyPort(apr_pool_t* parent, U16& port) + { + port = 0; + apr_pool_t* pool = NULL; + if (apr_pool_create(&pool, parent) != APR_SUCCESS) + { + return LLSocket::ptr_t(); + } + apr_socket_t* socket = NULL; + apr_sockaddr_t* address = NULL; + if (apr_socket_create(&socket, APR_INET, SOCK_STREAM, APR_PROTO_TCP, pool) != APR_SUCCESS + || apr_sockaddr_info_get(&address, "127.0.0.1", APR_INET, 0, 0, pool) != APR_SUCCESS + || apr_socket_bind(socket, address) != APR_SUCCESS + || apr_socket_listen(socket, 10) != APR_SUCCESS + || apr_socket_addr_get(&address, APR_LOCAL, socket) != APR_SUCCESS) + { + apr_pool_destroy(pool); + return LLSocket::ptr_t(); + } + port = address->port; + // The socket takes the pool, as it does one LLSocket::create made. + return LLSocket::create(socket, pool); } ~pipe_and_pump_fitness() @@ -930,10 +962,12 @@ namespace tut apr_pool_t* mPool; LLPumpIO* mPump; LLSocket::ptr_t mSocket; + U16 mServerPort = 0; }; typedef test_group fitness_test_group; typedef fitness_test_group::object fitness_test_object; fitness_test_group fitness("pipe and pump fitness"); +} template<> template<> void fitness_test_object::test<1>() @@ -965,7 +999,7 @@ namespace tut //LL_DEBUGS() << "fitness_test_object::test<1> - connecting client." // << LL_ENDL; LLSocket::ptr_t client = LLSocket::create(mPool, LLSocket::STREAM_TCP); - LLHost server_host("127.0.0.1", SERVER_LISTEN_PORT); + LLHost server_host("127.0.0.1", mServerPort); bool connected = client->blockingConnect(server_host); ensure("Connected to server", connected); LL_DEBUGS() << "connected" << LL_ENDL; @@ -1008,7 +1042,7 @@ namespace tut // Set up the client LLSocket::ptr_t client = LLSocket::create(mPool, LLSocket::STREAM_TCP); - LLHost server_host("127.0.0.1", SERVER_LISTEN_PORT); + LLHost server_host("127.0.0.1", mServerPort); bool connected = client->blockingConnect(server_host); ensure("Connected to server", connected); LL_DEBUGS() << "connected" << LL_ENDL; @@ -1051,7 +1085,7 @@ namespace tut // Set up the client LLSocket::ptr_t client = LLSocket::create(mPool, LLSocket::STREAM_TCP); - LLHost server_host("127.0.0.1", SERVER_LISTEN_PORT); + LLHost server_host("127.0.0.1", mServerPort); bool connected = client->blockingConnect(server_host); ensure("Connected to server", connected); LL_DEBUGS() << "connected" << LL_ENDL; @@ -1094,7 +1128,7 @@ namespace tut // Set up the client LLSocket::ptr_t client = LLSocket::create(mPool, LLSocket::STREAM_TCP); - LLHost server_host("127.0.0.1", SERVER_LISTEN_PORT); + LLHost server_host("127.0.0.1", mServerPort); bool connected = client->blockingConnect(server_host); ensure("Connected to server", connected); LL_DEBUGS() << "connected" << LL_ENDL; @@ -1137,7 +1171,7 @@ namespace tut // Set up the client LLSocket::ptr_t client = LLSocket::create(mPool, LLSocket::STREAM_TCP); - LLHost server_host("127.0.0.1", SERVER_LISTEN_PORT); + LLHost server_host("127.0.0.1", mServerPort); bool connected = client->blockingConnect(server_host); ensure("Connected to server", connected); LL_DEBUGS() << "connected" << LL_ENDL; diff --git a/indra/llmessage/tests/llavatarnamecache_test.cpp b/indra/llmessage/tests/llavatarnamecache_test.cpp index 3735f42c472..c45c9067c4d 100644 --- a/indra/llmessage/tests/llavatarnamecache_test.cpp +++ b/indra/llmessage/tests/llavatarnamecache_test.cpp @@ -32,6 +32,8 @@ #include "../test/lltut.h" namespace tut +{ +namespace { struct avatarnamecache_data { @@ -39,6 +41,7 @@ namespace tut typedef test_group avatarnamecache_test; typedef avatarnamecache_test::object avatarnamecache_object; tut::avatarnamecache_test avatarnamecache_testcase("LLAvatarNameCache"); +} template<> template<> void avatarnamecache_object::test<1>() diff --git a/indra/llmessage/tests/llbuffer_test.cpp b/indra/llmessage/tests/llbuffer_test.cpp index 330a4f288f9..26a00fb4919 100644 --- a/indra/llmessage/tests/llbuffer_test.cpp +++ b/indra/llmessage/tests/llbuffer_test.cpp @@ -35,6 +35,8 @@ namespace tut +{ +namespace { struct buffer { @@ -43,6 +45,7 @@ namespace tut typedef test_group buffer_t; typedef buffer_t::object buffer_object_t; tut::buffer_t tut_buffer("buffer"); +} template<> template<> void buffer_object_t::test<1>() diff --git a/indra/llmessage/tests/llcoproceduremanager_test.cpp b/indra/llmessage/tests/llcoproceduremanager_test.cpp index 0d014b1ca2b..d17f7730c4f 100644 --- a/indra/llmessage/tests/llcoproceduremanager_test.cpp +++ b/indra/llmessage/tests/llcoproceduremanager_test.cpp @@ -70,6 +70,8 @@ LLCore::HttpRequest::~HttpRequest() } namespace tut +{ +namespace { struct coproceduremanager_test { @@ -85,6 +87,7 @@ namespace tut typedef test_group coproceduremanager_t; typedef coproceduremanager_t::object coproceduremanager_object_t; tut::coproceduremanager_t tut_coproceduremanager("LLCoprocedureManager"); +} template<> template<> diff --git a/indra/llmessage/tests/lldatapacker_test.cpp b/indra/llmessage/tests/lldatapacker_test.cpp index cce5a238262..2caeeb3fc88 100644 --- a/indra/llmessage/tests/lldatapacker_test.cpp +++ b/indra/llmessage/tests/lldatapacker_test.cpp @@ -37,10 +37,12 @@ #include "v3math.h" #include "v4math.h" #include "llsdserialize.h" +#include "../test/namedtempfile.h" -#define TEST_FILE_NAME "datapacker_test.txt" namespace tut +{ +namespace { struct datapacker_test { @@ -48,6 +50,7 @@ namespace tut typedef test_group datapacker_test_t; typedef datapacker_test_t::object datapacker_test_object_t; tut::datapacker_test_t tut_datapacker_test("datapacker_test"); +} //*********LLDataPackerBinaryBuffer template<> template<> @@ -348,7 +351,10 @@ namespace tut { F32 f_val = 44.44f, f_unpkval; - LLFILE* fp = LLFile::fopen(TEST_FILE_NAME, LLFILE_MODE("w+")); + // A file of the test's own, where no other run writes, removed as + // the test ends. + NamedTempFile file("datapacker_test", "", ".txt"); + LLFILE* fp = LLFile::fopen(file.getPath().string(), LLFILE_MODE("w+")); if(!fp) { LL_ERRS() << "File couldnt be open" << LL_ENDL; @@ -403,7 +409,10 @@ namespace tut LLVector4 unpkllvec4; LLUUID unpkuuid; - LLFILE* fp = LLFile::fopen(TEST_FILE_NAME, LLFILE_MODE("w+")); + // A file of the test's own, where no other run writes, removed as + // the test ends. + NamedTempFile file("datapacker_test", "", ".txt"); + LLFILE* fp = LLFile::fopen(file.getPath().string(), LLFILE_MODE("w+")); if(!fp) { LL_ERRS() << "File couldnt be open" << LL_ENDL; diff --git a/indra/llmessage/tests/llhost_test.cpp b/indra/llmessage/tests/llhost_test.cpp index a66655ad6c6..8f4214e8bb9 100644 --- a/indra/llmessage/tests/llhost_test.cpp +++ b/indra/llmessage/tests/llhost_test.cpp @@ -33,6 +33,8 @@ #include "../test/lltut.h" namespace tut +{ +namespace { struct host_data { @@ -40,6 +42,7 @@ namespace tut typedef test_group host_test; typedef host_test::object host_object; tut::host_test host_testcase("LLHost"); +} template<> template<> diff --git a/indra/llmessage/tests/llhttpnode_test.cpp b/indra/llmessage/tests/llhttpnode_test.cpp index c38dee120b2..a6c0f3da63b 100644 --- a/indra/llmessage/tests/llhttpnode_test.cpp +++ b/indra/llmessage/tests/llhttpnode_test.cpp @@ -31,6 +31,8 @@ #include "llsdhttpserver.h" namespace tut +{ +namespace { struct HTTPNodeTestData { @@ -144,6 +146,7 @@ namespace tut typedef test_group HTTPNodeTestGroup; typedef HTTPNodeTestGroup::object HTTPNodeTestObject; HTTPNodeTestGroup httpNodeTestGroup("http node"); +} template<> template<> void HTTPNodeTestObject::test<1>() @@ -243,6 +246,8 @@ namespace tut ensure_equals("root of aleph", alephNode->rootNode(), &mRoot); } +namespace +{ class IntegerNode : public LLHTTPNode { public: @@ -284,6 +289,7 @@ namespace tut response->result(n*n); } }; +} template<> template<> void HTTPNodeTestObject::test<5>() @@ -312,6 +318,8 @@ namespace tut ensure_equals("square result", response->mResult.asInteger(), 9); } +namespace +{ class AlphaNode : public LLHTTPNode { public: @@ -332,6 +340,7 @@ namespace tut return true; } }; +} template<> template<> void HTTPNodeTestObject::test<6>() diff --git a/indra/llmessage/tests/llmessageconfig_test.cpp b/indra/llmessage/tests/llmessageconfig_test.cpp index b8942e99b3b..d886d248a4d 100644 --- a/indra/llmessage/tests/llmessageconfig_test.cpp +++ b/indra/llmessage/tests/llmessageconfig_test.cpp @@ -35,6 +35,8 @@ #include "llsdutil.h" namespace tut +{ +namespace { struct LLMessageConfigTestData { std::string mTestConfigDir; @@ -56,14 +58,20 @@ namespace tut LLMessageConfig::initClass("simulator", mTestConfigDir); } + // A destructor cannot fail a test, so the clean-up only says what + // it could not remove. ~LLMessageConfigTestData() { // rm contents of temp dir - int rmfile = LLFile::remove((mTestConfigDir + "/message.xml")); - ensure_equals("rmfile value", rmfile, 0); + if (LLFile::remove((mTestConfigDir + "/message.xml")) != 0) + { + LL_WARNS() << "Could not remove " << mTestConfigDir << "/message.xml" << LL_ENDL; + } // rm temp dir - int rmdir = LLFile::remove(mTestConfigDir); - ensure_equals("rmdir value", rmdir, 0); + if (LLFile::remove(mTestConfigDir) != 0) + { + LL_WARNS() << "Could not remove " << mTestConfigDir << LL_ENDL; + } } void writeConfigFile(const LLSD& config) @@ -80,6 +88,7 @@ namespace tut typedef test_group LLMessageConfigTestGroup; typedef LLMessageConfigTestGroup::object LLMessageConfigTestObject; LLMessageConfigTestGroup llMessageConfigTestGroup("LLMessageConfig"); +} template<> template<> void LLMessageConfigTestObject::test<1>() diff --git a/indra/llmessage/tests/llmessagesystem_fixture.h b/indra/llmessage/tests/llmessagesystem_fixture.h new file mode 100644 index 00000000000..6e2b5892908 --- /dev/null +++ b/indra/llmessage/tests/llmessagesystem_fixture.h @@ -0,0 +1,81 @@ +/** + * @file llmessagesystem_fixture.h + * @brief A message system a test starts and ends itself. + * + * $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 LL_LLMESSAGESYSTEM_FIXTURE_H +#define LL_LLMESSAGESYSTEM_FIXTURE_H + +#include "message.h" +#include "net.h" + +namespace ll_test +{ + // gMessageSystem is a message system of the test's own while this lives, + // and what it was before once it ends. The system reads no template file, + // so it is the disconnected one the tests want, and its socket is on a + // port the OS picks, so two of them never contend for one. + class MessageSystemScope + { + public: + MessageSystemScope() + : mPrior(gMessageSystem) + { + const F32 circuit_heartbeat_interval = 5; + const F32 circuit_timeout = 100; + start_messaging_system("notafile", NET_USE_OS_ASSIGNED_PORT, + 1, + 0, + 0, + false, + "notasharedsecret", + NULL, + false, + circuit_heartbeat_interval, + circuit_timeout); + } + + ~MessageSystemScope() + { + LLMessageSystem* system = gMessageSystem; + // Without its template the system is not OK, and a system that is + // not OK leaves its socket open when it is deleted, so the scope + // closes it. A socket that never opened is still 0. + if (system && !system->isOK() && system->mSocket > 0) + { + end_net(system->mSocket); + } + // Not end_messaging_system(): a system that is not OK never + // started the transfer manager that would end. + delete system; + gMessageSystem = mPrior; + } + + MessageSystemScope(const MessageSystemScope&) = delete; + MessageSystemScope& operator=(const MessageSystemScope&) = delete; + + private: + LLMessageSystem* mPrior; + }; +} + +#endif // LL_LLMESSAGESYSTEM_FIXTURE_H diff --git a/indra/llmessage/tests/llmessagetemplateparser_test.cpp b/indra/llmessage/tests/llmessagetemplateparser_test.cpp index f665fdde4dd..3c963955d4e 100644 --- a/indra/llmessage/tests/llmessagetemplateparser_test.cpp +++ b/indra/llmessage/tests/llmessagetemplateparser_test.cpp @@ -30,6 +30,8 @@ #include "lltut.h" namespace tut +{ +namespace { struct LLMessageTemplateParserTestData { LLMessageTemplateParserTestData() : mMessage("unset message") @@ -93,6 +95,7 @@ namespace tut typedef test_group LLMessageTemplateParserTestGroup; typedef LLMessageTemplateParserTestGroup::object LLMessageTemplateParserTestObject; LLMessageTemplateParserTestGroup llMessageTemplateParserTestGroup("LLMessageTemplateParser"); +} template<> template<> void LLMessageTemplateParserTestObject::test<1>() diff --git a/indra/llmessage/tests/llnamevalue_test.cpp b/indra/llmessage/tests/llnamevalue_test.cpp index f77cd102f48..c06abd41c9d 100644 --- a/indra/llmessage/tests/llnamevalue_test.cpp +++ b/indra/llmessage/tests/llnamevalue_test.cpp @@ -40,6 +40,8 @@ #endif namespace tut +{ +namespace { struct namevalue_test { @@ -50,6 +52,7 @@ namespace tut typedef test_group namevalue_t; typedef namevalue_t::object namevalue_object_t; tut::namevalue_t tut_namevalue("LLNameValue"); +} template<> template<> diff --git a/indra/llmessage/tests/llpartdata_test.cpp b/indra/llmessage/tests/llpartdata_test.cpp index c517232bccd..017fbbd0c45 100644 --- a/indra/llmessage/tests/llpartdata_test.cpp +++ b/indra/llmessage/tests/llpartdata_test.cpp @@ -37,6 +37,8 @@ #include "../test/lltut.h" namespace tut +{ +namespace { //bunch of sniffed data that *should* be a valid particle system @@ -69,6 +71,7 @@ namespace tut typedef test_group partdata_test_t; typedef partdata_test_t::object partdata_test_object_t; tut::partdata_test_t tut_partdata_test("LLPartData"); +} template<> template<> void partdata_test_object_t::test<1>() diff --git a/indra/llmessage/tests/llpipeutil.cpp b/indra/llmessage/tests/llpipeutil.cpp index 1dd4bdfa3c0..c95c459f44e 100644 --- a/indra/llmessage/tests/llpipeutil.cpp +++ b/indra/llmessage/tests/llpipeutil.cpp @@ -114,6 +114,8 @@ LLIOPipe::EStatus LLIOFuzz::process_impl( return STATUS_OK; } +namespace +{ struct random_ascii_generator { random_ascii_generator() {} @@ -125,6 +127,7 @@ struct random_ascii_generator return rv; } }; +} // virtual LLIOPipe::EStatus LLIOASCIIFuzz::process_impl( diff --git a/indra/llmessage/tests/llsdmessagebuilder_test.cpp b/indra/llmessage/tests/llsdmessagebuilder_test.cpp index 7dcc03edc14..9e7bee37581 100644 --- a/indra/llmessage/tests/llsdmessagebuilder_test.cpp +++ b/indra/llmessage/tests/llsdmessagebuilder_test.cpp @@ -40,11 +40,12 @@ #include "v3math.h" #include "v4math.h" #include "llsdutil.h" -//#include "llsdutil.cpp" -#include "llsdutil_math.cpp" +#include "llsdutil_math.h" #include "lltemplatemessagebuilder.h" namespace tut +{ +namespace { static LLTemplateMessageBuilder::message_template_name_map_t templateNameMap; @@ -115,6 +116,7 @@ namespace tut typedef test_group LLSDMessageBuilderTestGroup; typedef LLSDMessageBuilderTestGroup::object LLSDMessageBuilderTestObject; LLSDMessageBuilderTestGroup llsdMessageBuilderTestGroup("LLSDMessageBuilder"); +} template<> template<> void LLSDMessageBuilderTestObject::test<1>() diff --git a/indra/llmessage/tests/llsdmessagereader_test.cpp b/indra/llmessage/tests/llsdmessagereader_test.cpp index dd6520ea334..62ac6ed0293 100644 --- a/indra/llmessage/tests/llsdmessagereader_test.cpp +++ b/indra/llmessage/tests/llsdmessagereader_test.cpp @@ -39,6 +39,8 @@ #include "llsdutil_math.h" namespace tut +{ +namespace { struct LLSDMessageReaderTestData { static void ensureMessageName(const std::string& msg_name, @@ -86,6 +88,7 @@ namespace tut typedef test_group LLSDMessageReaderTestGroup; typedef LLSDMessageReaderTestGroup::object LLSDMessageReaderTestObject; LLSDMessageReaderTestGroup llsdMessageReaderTestGroup("LLSDMessageReader"); +} template<> template<> void LLSDMessageReaderTestObject::test<1>() @@ -134,6 +137,8 @@ namespace tut ensureMessageSize(message, 0); } +namespace +{ template LLSDMessageReader testType(const T& value) { @@ -143,6 +148,7 @@ namespace tut msg.setMessage("fakename", message); return msg; } +} template<> template<> void LLSDMessageReaderTestObject::test<4>() diff --git a/indra/llmessage/tests/lltemplatemessagebuilder_test.cpp b/indra/llmessage/tests/lltemplatemessagebuilder_test.cpp index e2bf97803e8..5449f5a9598 100644 --- a/indra/llmessage/tests/lltemplatemessagebuilder_test.cpp +++ b/indra/llmessage/tests/lltemplatemessagebuilder_test.cpp @@ -29,7 +29,7 @@ #include "linden_common.h" #include "lltut.h" -#include "llapr.h" +#include "llmessagesystem_fixture.h" #include "llmessagetemplate.h" #include "llmath.h" #include "llquaternion.h" @@ -42,34 +42,20 @@ #include "v4math.h" namespace tut +{ +namespace { static LLTemplateMessageBuilder::message_template_name_map_t nameMap; static LLTemplateMessageReader::message_template_number_map_t numberMap; struct LLTemplateMessageBuilderTestData { + // The reader hands what it reads to gMessageSystem, so each test has + // one, and leaves gMessageSystem as it found it. + ll_test::MessageSystemScope mMessageSystem; + static LLMessageTemplate defaultTemplate() { - static bool init = false; - if(! init) - { - ll_init_apr(); - const F32 circuit_heartbeat_interval=5; - const F32 circuit_timeout=100; - - start_messaging_system("notafile", 13035, - 1, - 0, - 0, - false, - "notasharedsecret", - NULL, - false, - circuit_heartbeat_interval, - circuit_timeout); - //init_prehash_data(); - init = true; - } return LLMessageTemplate(_PREHASH_TestMessage, 1, MFT_HIGH); } @@ -121,6 +107,7 @@ namespace tut typedef test_group LLTemplateMessageBuilderTestGroup; typedef LLTemplateMessageBuilderTestGroup::object LLTemplateMessageBuilderTestObject; LLTemplateMessageBuilderTestGroup templateMessageBuilderTestGroup("LLTemplateMessageBuilder"); +} template<> template<> void LLTemplateMessageBuilderTestObject::test<1>() diff --git a/indra/llmessage/tests/lltemplatemessagedispatcher_test.cpp b/indra/llmessage/tests/lltemplatemessagedispatcher_test.cpp index bb213368925..8169afd2c0b 100644 --- a/indra/llmessage/tests/lltemplatemessagedispatcher_test.cpp +++ b/indra/llmessage/tests/lltemplatemessagedispatcher_test.cpp @@ -38,15 +38,29 @@ LLPounceable gMessageSystem; -// sensor test doubles +// What the test doubles below record, and the test's own helpers. +namespace +{ bool gClearRecvWasCalled = false; +char gUdpDispatchedData[MAX_BUFFER_SIZE]; +bool gUdpDispatchWasCalled = false; +bool gValidateMessage = false; +LLHost host; + +const char* gBinaryTemplateData = "BINARYTEMPLATEDATA"; +void fillVector(std::vector& vector_data, const char* data) +{ + vector_data.resize(strlen(data) + 1); + strcpy(reinterpret_cast(&vector_data[0]), data); +} +} + +// sensor test doubles void LLMessageSystem::clearReceiveState(void) { gClearRecvWasCalled = true; } -char gUdpDispatchedData[MAX_BUFFER_SIZE]; -bool gUdpDispatchWasCalled = false; bool LLTemplateMessageReader::readMessage(const U8* data,class LLHost const &) { gUdpDispatchWasCalled = true; @@ -54,26 +68,19 @@ bool LLTemplateMessageReader::readMessage(const U8* data,class LLHost const &) return true; } -bool gValidateMessage = false; bool LLTemplateMessageReader::validateMessage(const U8*, S32 buffer_size, LLHost const &sender, bool trusted, bool faked) { return gValidateMessage; } -LLHost host; const LLHost& LLMessageSystem::getSender() const { return host; } -const char* gBinaryTemplateData = "BINARYTEMPLATEDATA"; -void fillVector(std::vector& vector_data, const char* data) -{ - vector_data.resize(strlen(data) + 1); - strcpy(reinterpret_cast(&vector_data[0]), data); -} - namespace tut +{ +namespace { static LLTemplateMessageReader::message_template_number_map_t numberMap; @@ -96,6 +103,7 @@ namespace tut typedef test_group factory; typedef factory::object object; } +} namespace { diff --git a/indra/llmessage/tests/lltrustedmessageservice_test.cpp b/indra/llmessage/tests/lltrustedmessageservice_test.cpp index cc199141b83..aff273c755e 100644 --- a/indra/llmessage/tests/lltrustedmessageservice_test.cpp +++ b/indra/llmessage/tests/lltrustedmessageservice_test.cpp @@ -37,6 +37,15 @@ LLPounceable gMessageSystem; +// What the test doubles below record. +namespace +{ +bool messageDispatched = false; +bool messageDispatchedAsBinary = false; +LLSD lastLLSD; +std::string lastMessageName; +} + LLMessageConfig::SenderTrust LLMessageConfig::getSenderTrustedness(const std::string& msg_name) { @@ -57,11 +66,6 @@ bool LLMessageSystem::isTrustedMessage(const std::string& name) const return false; } -bool messageDispatched = false; -bool messageDispatchedAsBinary = false; -LLSD lastLLSD; -std::string lastMessageName; - void LLMessageSystem::dispatch(const std::string& msg_name, const LLSD& message, LLHTTPNode::ResponsePtr responsep) @@ -81,6 +85,8 @@ void LLMessageSystem::dispatchTemplate(const std::string& msg_name, } namespace tut +{ +namespace { struct LLTrustedMessageServiceData { @@ -97,6 +103,7 @@ namespace tut typedef test_group factory; typedef factory::object object; } +} namespace { diff --git a/indra/llmessage/tests/llxfer_file_test.cpp b/indra/llmessage/tests/llxfer_file_test.cpp index 358659a5b90..e7479f7402a 100644 --- a/indra/llmessage/tests/llxfer_file_test.cpp +++ b/indra/llmessage/tests/llxfer_file_test.cpp @@ -32,6 +32,8 @@ #include "../test/lltut.h" namespace tut +{ +namespace { struct llxfer_data { @@ -39,6 +41,7 @@ namespace tut typedef test_group llxfer_test; typedef llxfer_test::object llxfer_object; tut::llxfer_test llxfer("LLXferFile"); +} template<> template<> void llxfer_object::test<1>() diff --git a/indra/llmessage/tests/llxorcipher_test.cpp b/indra/llmessage/tests/llxorcipher_test.cpp index 72ae98afb24..992fe27f946 100644 --- a/indra/llmessage/tests/llxorcipher_test.cpp +++ b/indra/llmessage/tests/llxorcipher_test.cpp @@ -31,6 +31,8 @@ #include "llxorcipher.h" namespace tut +{ +namespace { struct cipher { @@ -38,6 +40,7 @@ namespace tut typedef test_group cipher_t; typedef cipher_t::object cipher_object_t; tut::cipher_t tut_cipher("cipher"); +} //encrypt->decrypt template<> template<> diff --git a/indra/llmessage/tests/llzerocode_test.cpp b/indra/llmessage/tests/llzerocode_test.cpp index 7a52c90b46b..84426be1470 100644 --- a/indra/llmessage/tests/llzerocode_test.cpp +++ b/indra/llmessage/tests/llzerocode_test.cpp @@ -33,6 +33,8 @@ #include namespace tut +{ +namespace { struct zerocode_data { @@ -90,6 +92,7 @@ namespace tut ensure(std::string(msg) + ": decoded bytes should match original", memcmp(dec.data(), src.data(), src.size()) == 0); } +} // Basic mixed body; header carries an unrelated flag bit (0x40) that must survive untouched. template<> template<> diff --git a/indra/llmessage/tests/message_test.cpp b/indra/llmessage/tests/message_test.cpp index 6fb2b928039..2e3be7f8990 100644 --- a/indra/llmessage/tests/message_test.cpp +++ b/indra/llmessage/tests/message_test.cpp @@ -29,8 +29,8 @@ #include "linden_common.h" #include "lltut.h" #include "llhttpconstants.h" -#include "llapr.h" #include "llmessageconfig.h" +#include "llmessagesystem_fixture.h" #include "llsdserialize.h" #include "message.h" #include "message_prehash.h" @@ -51,37 +51,18 @@ namespace } namespace tut +{ +namespace { struct LLMessageSystemTestData { + // currently test disconnected message system + ll_test::MessageSystemScope mMessageSystem; std::string mTestConfigDir; std::string mSep; LLMessageSystemTestData() { - static bool init = false; - if(!init) - { - ll_init_apr(); - //init_prehash_data(); - init = true; - } - const F32 circuit_heartbeat_interval=5; - const F32 circuit_timeout=100; - - - // currently test disconnected message system - start_messaging_system("notafile", 13035, - 1, - 0, - 0, - false, - "notasharedsecret", - NULL, - false, - circuit_heartbeat_interval, - circuit_timeout - ); // generate temp dir std::ostringstream ostr; #if LL_WINDOWS @@ -100,21 +81,23 @@ namespace tut LLMessageConfig::initClass("simulator", ostr.str()); } + // A destructor cannot fail a test, so the clean-up only says what + // it could not remove. ~LLMessageSystemTestData() { - // not end_messaging_system() - delete static_cast(gMessageSystem); - gMessageSystem = NULL; - // rm contents of temp dir std::ostringstream ostr; ostr << mTestConfigDir << mSep << "message.xml"; - int rmfile = LLFile::remove(ostr.str()); - ensure_equals("rmfile value", rmfile, 0); + if (LLFile::remove(ostr.str()) != 0) + { + LL_WARNS() << "Could not remove " << ostr.str() << LL_ENDL; + } // rm temp dir - int rmdir = LLFile::remove(mTestConfigDir); - ensure_equals("rmdir value", rmdir, 0); + if (LLFile::remove(mTestConfigDir) != 0) + { + LL_WARNS() << "Could not remove " << mTestConfigDir << LL_ENDL; + } } void writeConfigFile(const LLSD& config) @@ -132,6 +115,7 @@ namespace tut typedef test_group LLMessageSystemTestGroup; typedef LLMessageSystemTestGroup::object LLMessageSystemTestObject; LLMessageSystemTestGroup messageTestGroup("LLMessageSystem"); +} template<> template<> void LLMessageSystemTestObject::test<1>() diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt index 10c25f77981..109b537ef20 100644 --- a/indra/llprimitive/CMakeLists.txt +++ b/indra/llprimitive/CMakeLists.txt @@ -58,7 +58,9 @@ target_precompile_headers(llprimitive REUSE_FROM llprecompiled) # add unit tests if(AL_BUILD_TESTS) foreach(test llmediaentry llgltfmaterial llmaterialid llmaterial) - al_add_test(${test} UNIT PROJECT llprimitive) + al_add_test(${test} PROJECT llprimitive LIBRARIES llprimitive) endforeach() + # This one stubs LLMaterialID and LLPrimTextureList, which the library + # defines, so it compiles the one file it tests instead of linking it. al_add_test(llprimitive UNIT PROJECT llprimitive LIBRARIES llmessage) endif() diff --git a/indra/llprimitive/tests/llgltfmaterial_test.cpp b/indra/llprimitive/tests/llgltfmaterial_test.cpp index dbeea1a1f5e..479f3fc0cd3 100644 --- a/indra/llprimitive/tests/llgltfmaterial_test.cpp +++ b/indra/llprimitive/tests/llgltfmaterial_test.cpp @@ -52,6 +52,8 @@ #include namespace tut +{ +namespace { struct llgltfmaterial { @@ -135,6 +137,7 @@ namespace tut { ensure("LLGLTFMaterial serialization trims property '" + must_not_contain + "'", material_json.find(must_not_contain) == std::string::npos); } +} // Test that GLTF material fields have not changed since these tests were // written. The previous sizeof(LLGLTFMaterial) tripwire was removed when @@ -366,6 +369,8 @@ namespace tut } } +namespace +{ template void ensure_material_hash_pre(LLGLTFMaterial& material, T& material_field, const T new_value, const std::string& field_name) { @@ -397,6 +402,7 @@ namespace tut // If this test fails, consult LLGLTFMaterial::getHash, and optionally consult http://www.catb.org/esr/structure-packing/ for guidance on optimal memory packing (effectiveness is platform-dependent) ensure_not_equals(("LLGLTFMaterial: Hash: Perturbing " + field_name + " to new value changes the hash").c_str(), material.getHash(), old_material.getHash()); } +} #define ENSURE_HASH_NOT_CHANGED(HASH_MAT, SOURCE_MAT, FIELD) ensure_material_hash_not_changed(HASH_MAT, HASH_MAT.FIELD, SOURCE_MAT.FIELD, #FIELD) #define ENSURE_HASH_CHANGED(HASH_MAT, SOURCE_MAT, FIELD) ensure_material_hash_changed(HASH_MAT, HASH_MAT.FIELD, SOURCE_MAT.FIELD, #FIELD) diff --git a/indra/llprimitive/tests/llmaterial_test.cpp b/indra/llprimitive/tests/llmaterial_test.cpp index dfc44840ff9..ce9168dea10 100644 --- a/indra/llprimitive/tests/llmaterial_test.cpp +++ b/indra/llprimitive/tests/llmaterial_test.cpp @@ -24,11 +24,14 @@ #include "../llmaterial.h" namespace tut +{ +namespace { struct llmaterial_data { }; typedef test_group llmaterial_t; typedef llmaterial_t::object llmaterial_object_t; tut::llmaterial_t tut_llmaterial("llmaterial"); +} // Two distinct UUIDs used as before/after values for ID setters. static const LLUUID kIdA("11111111-2222-3333-4444-555555555555"); diff --git a/indra/llprimitive/tests/llmaterialid_test.cpp b/indra/llprimitive/tests/llmaterialid_test.cpp index 89c5883287d..1060aaff414 100644 --- a/indra/llprimitive/tests/llmaterialid_test.cpp +++ b/indra/llprimitive/tests/llmaterialid_test.cpp @@ -26,11 +26,14 @@ #include "../llmaterialid.h" namespace tut +{ +namespace { struct llmaterialid_data { }; typedef test_group llmaterialid_t; typedef llmaterialid_t::object llmaterialid_object_t; tut::llmaterialid_t tut_llmaterialid("llmaterialid"); +} static const U8 kSampleBytes[MATERIAL_ID_SIZE] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, diff --git a/indra/llprimitive/tests/llmediaentry_test.cpp b/indra/llprimitive/tests/llmediaentry_test.cpp index 414df666805..cb4c17e061f 100644 --- a/indra/llprimitive/tests/llmediaentry_test.cpp +++ b/indra/llprimitive/tests/llmediaentry_test.cpp @@ -136,6 +136,8 @@ " namespace tut +{ +namespace { // this is fixture data that gets created before each test and destroyed // after each test. this is where we put all of the setup/takedown code @@ -159,6 +161,7 @@ namespace tut typedef test_group factory; typedef factory::object object; } +} namespace @@ -168,6 +171,8 @@ namespace } namespace tut +{ +namespace { void ensure_llsd_equals(const std::string& msg, const LLSD& expected, const LLSD& actual) { @@ -183,20 +188,6 @@ namespace tut } } - void ensure_string_equals(const std::string& msg, const std::string& expected, const std::string& actual) - { - if ( expected != actual ) - { - std::string message = msg; - message += ": actual: "; - message += actual; - message += "\n expected: "; - message += expected; - message += "\n"; - ensure(message, false); - } - } - void set_whitelist(LLMediaEntry &entry, const char *str) { std::vector tokens; @@ -227,10 +218,7 @@ namespace tut { whitelist_test(num, true, whitelist, candidate_url, expected_pass); } - void whitelist_test(int num, const char *whitelist, const char *candidate_url) - { - whitelist_test(num, true, whitelist, candidate_url, true); - } +} template<> template<> void object::test<1>() @@ -297,9 +285,12 @@ namespace tut ensure_llsd_equals(get_test_name() + " failed", defaultMediaEntryLLSD, entry2.asLLSD()); } +namespace +{ // limit tests const char *URL_OK = "http://www.example.com"; const char *URL_TOO_BIG = "http://www.example.com.qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"; +} template<> template<> void object::test<6>() diff --git a/indra/llprimitive/tests/llprimitive_test.cpp b/indra/llprimitive/tests/llprimitive_test.cpp index 75c3613eceb..d2008f43b05 100644 --- a/indra/llprimitive/tests/llprimitive_test.cpp +++ b/indra/llprimitive/tests/llprimitive_test.cpp @@ -32,6 +32,8 @@ #include "../../llmath/llvolumemgr.h" +namespace +{ class DummyVolumeMgr : public LLVolumeMgr { public: @@ -70,6 +72,7 @@ class DummyVolumeMgr : public LLVolumeMgr LLVolumeParams mCurrParamsTest; S32 mCurrDetailTest; }; +} LLMaterialID::LLMaterialID() = default; void LLMaterialID::set(void const*) { } @@ -109,6 +112,8 @@ void LLPrimTextureList::setAllIDs(const LLUUID &id) { } LLTextureEntry * LLPrimTextureList::getTexture(const U8 index) const { return nullptr; } S32 LLPrimTextureList::size() const { return static_cast(mEntryList.size()); } +namespace +{ class PRIMITIVE_TEST_SETUP { public: @@ -124,8 +129,11 @@ class PRIMITIVE_TEST_SETUP } DummyVolumeMgr * volume_manager_test; }; +} namespace tut +{ +namespace { struct llprimitive { @@ -135,6 +143,7 @@ namespace tut typedef test_group llprimitive_t; typedef llprimitive_t::object llprimitive_object_t; tut::llprimitive_t tut_llprimitive("LLPrimitive"); +} template<> template<> void llprimitive_object_t::test<1>() diff --git a/indra/llrender/tests/alfontcolrv1_gl_test.cpp b/indra/llrender/tests/alfontcolrv1_gl_test.cpp index b6f8d3c6ff1..9312abb5308 100644 --- a/indra/llrender/tests/alfontcolrv1_gl_test.cpp +++ b/indra/llrender/tests/alfontcolrv1_gl_test.cpp @@ -44,12 +44,14 @@ #include namespace tut +{ +namespace { using namespace ll_test; // ------------------------------------------------------------- // GL-backed group: COLRv1 painter integrated with the LLFontFreetype - // / LLFontBitmapCache / LLImageGL pipeline. The shared HeadlessGL + // / LLFontBitmapCache / LLImageGL pipeline. Each test's own HeadlessGL // context is what the addGlyph path's gGL.bind plus the // setSubImageBGRA → LLImageGL upload land on. // ------------------------------------------------------------- @@ -63,6 +65,7 @@ namespace tut typedef test_group alfontcolrv1_render_test; typedef alfontcolrv1_render_test::object alfontcolrv1_render_object; tut::alfontcolrv1_render_test alfontcolrv1_render_testcase("ALFontColrV1Render"); +} // End-to-end: requesting a Color glyph for the fire emoji on // Noto-COLRv1 must route through renderColrV1Glyph → painter → diff --git a/indra/llrender/tests/alfontcolrv1_test.cpp b/indra/llrender/tests/alfontcolrv1_test.cpp index bf6436ddfad..ba95c9af03a 100644 --- a/indra/llrender/tests/alfontcolrv1_test.cpp +++ b/indra/llrender/tests/alfontcolrv1_test.cpp @@ -62,6 +62,8 @@ namespace } namespace tut +{ +namespace { using namespace ll_test; @@ -84,6 +86,7 @@ namespace tut typedef test_group alfontcolrv1_test; typedef alfontcolrv1_test::object alfontcolrv1_object; tut::alfontcolrv1_test alfontcolrv1_testcase("ALFontColrV1"); +} // Painter input validation: null hb_font fails cleanly without a crash. // Doesn't need a real font, so this test runs even when the test data diff --git a/indra/llrender/tests/alfontshaping_gl_test.cpp b/indra/llrender/tests/alfontshaping_gl_test.cpp index 410fc168190..5d9bd95e1eb 100644 --- a/indra/llrender/tests/alfontshaping_gl_test.cpp +++ b/indra/llrender/tests/alfontshaping_gl_test.cpp @@ -42,6 +42,8 @@ #include namespace tut +{ +namespace { using namespace ll_test; @@ -59,6 +61,7 @@ namespace tut typedef test_group alfontshaping_gl_test; typedef alfontshaping_gl_test::object alfontshaping_gl_object; tut::alfontshaping_gl_test alfontshaping_gl_testcase("ALFontShapingGL"); +} // Strict-monospace (ligatures off): shape "AB" through DejaVuSansMono. // Routes through HB with the kFixedWidthStrict feature plan diff --git a/indra/llrender/tests/alfontshaping_test.cpp b/indra/llrender/tests/alfontshaping_test.cpp index 77d5948fdc4..58526638955 100644 --- a/indra/llrender/tests/alfontshaping_test.cpp +++ b/indra/llrender/tests/alfontshaping_test.cpp @@ -61,6 +61,8 @@ extern FT_Library gFTLibrary; namespace tut +{ +namespace { using namespace ll_test; @@ -86,6 +88,7 @@ namespace tut typedef test_group alfontshaping_test; typedef alfontshaping_test::object alfontshaping_object; tut::alfontshaping_test alfontshaping_testcase("ALFontShaping"); +} // Null root face and empty/inverted ranges all produce empty // output. The header documents these as the safe-fallback diff --git a/indra/llrender/tests/alinfiniteprojection_gl_test.cpp b/indra/llrender/tests/alinfiniteprojection_gl_test.cpp index d6a0b1394e2..68d6ae21feb 100644 --- a/indra/llrender/tests/alinfiniteprojection_gl_test.cpp +++ b/indra/llrender/tests/alinfiniteprojection_gl_test.cpp @@ -93,34 +93,18 @@ namespace tut { return llmax(4.0 * depthStep(d), 4.0 * (F64)(std::nextafter(dist, 2.f * dist) - dist)); } - } struct alinfiniteprojection_data { - static ll_test::HeadlessGL& gl() - { - static ll_test::HeadlessGL instance(true, true, true, false); - return instance; - } - alinfiniteprojection_data() { - gl(); LLRender::sReverseZ = true; } + // The convention, clip control, depth state, viewport, target and + // program the tests change go back with mGL. ~alinfiniteprojection_data() { - LLRender::sReverseZ = false; - if (mReady) - { - glClipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE); - glClearDepth(1.0); - glDepthFunc(GL_LESS); - glDisable(GL_DEPTH_TEST); - glViewport(0, 0, ll_test::HeadlessGL::WIDTH, ll_test::HeadlessGL::HEIGHT); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - } if (mFBO) { glDeleteFramebuffers(1, &mFBO); @@ -135,7 +119,6 @@ namespace tut } if (mProgram) { - glUseProgram(0); glDeleteProgram(mProgram); } } @@ -175,7 +158,6 @@ namespace tut glReadBuffer(GL_NONE); ensure_equals("complete", (U32)glCheckFramebufferStatus(GL_FRAMEBUFFER), (U32)GL_FRAMEBUFFER_COMPLETE); - mReady = true; glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); glViewport(0, 0, SIZE, SIZE); glEnable(GL_DEPTH_TEST); @@ -206,16 +188,17 @@ namespace tut return d; } + ll_test::SharedGLScope mGL; GLuint mFBO = 0; GLuint mDepth = 0; GLuint mVAO = 0; GLuint mProgram = 0; - bool mReady = false; }; typedef test_group alinfiniteprojection_t; typedef alinfiniteprojection_t::object alinfiniteprojection_object_t; tut::alinfiniteprojection_t tut_alinfiniteprojection("alinfiniteprojection_gl"); + } // The matrix: recognised as infinite, stores near / distance, and its inverse gives w = 0 at the cleared // depth, which the reconstruction floor turns into MAX_RECONSTRUCT_DISTANCE. Finite and forward matrices are not. diff --git a/indra/llrender/tests/alrenderstate_gl_test.cpp b/indra/llrender/tests/alrenderstate_gl_test.cpp index 91513340ded..a11edb45847 100644 --- a/indra/llrender/tests/alrenderstate_gl_test.cpp +++ b/indra/llrender/tests/alrenderstate_gl_test.cpp @@ -67,21 +67,10 @@ namespace tut box[i] = value[i]; } } - } + // The clear colour and scissor box the tests set go back with mGL. struct alrenderstate_data { - static ll_test::HeadlessGL& gl() - { - static ll_test::HeadlessGL instance(true, true, true, false); - return instance; - } - - alrenderstate_data() - { - gl(); - } - ~alrenderstate_data() { // A failed check throws out of a test with the target still bound, and a bound target cannot be released. @@ -90,7 +79,6 @@ namespace tut mTarget.flush(); } mTarget.release(); - gGL.setClearColor(LLColor4::transparent); } void setUp() @@ -98,12 +86,14 @@ namespace tut ensure("target allocates", mTarget.allocate(SIZE, SIZE, GL_RGBA8)); } + ll_test::SharedGLScope mGL; LLRenderTarget mTarget; }; typedef test_group alrenderstate_t; typedef alrenderstate_t::object alrenderstate_object_t; tut::alrenderstate_t tut_alrenderstate("alrenderstate_gl"); + } // The clear colour a target's clear fills with is the one gGL was given, and GL holds what gGL says it does. template<> template<> diff --git a/indra/llrender/tests/altessellation_gl_test.cpp b/indra/llrender/tests/altessellation_gl_test.cpp index b9a0c512e80..7e8073e8940 100644 --- a/indra/llrender/tests/altessellation_gl_test.cpp +++ b/indra/llrender/tests/altessellation_gl_test.cpp @@ -95,22 +95,24 @@ namespace tut const std::array kCW = { -1.f, -1.f, -1.f, 1.f, 1.f, 1.f, 1.f, -1.f }; struct Vertex { F32 x, y, z, w; }; - } struct altessellation_data { static ll_test::HeadlessGL& gl() { - static ll_test::HeadlessGL instance(/*needs_vbos=*/true, - /*needs_imagegl=*/false, - /*needs_llrender=*/true, - /*needs_render=*/false); - return instance; + return ll_test::sharedHeadlessGL(); } altessellation_data() { gl(); + // The shared context has gUIProgram bound, with LLVertexBuffer's + // arrays enabled for it, and a draw with an array enabled that no + // buffer stands behind fails. The fixture turns them off for the + // tests, and puts them, the program and the buffer binding back. + glGetIntegerv(GL_CURRENT_PROGRAM, &mPriorProgram); + mPriorArrays = LLVertexBuffer::sLastMask; + LLVertexBuffer::setupClientArrays(0); mProgram = glCreateProgram(); const std::array, 4> stages = {{ { GL_VERTEX_SHADER, kVertex }, @@ -156,9 +158,12 @@ namespace tut ~altessellation_data() { - glUseProgram(0); + glUseProgram(mPriorProgram); glDisableVertexAttribArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); + LLVertexBuffer::setupClientArrays(mPriorArrays); + // Through LLVertexBuffer, so that its next draw binds its buffer + // and points the arrays at it again rather than at ours. + LLVertexBuffer::unbind(); glDeleteQueries(1, &mQuery); glDeleteBuffers(1, &mCapture); glDeleteBuffers(1, &mCorners); @@ -264,11 +269,14 @@ namespace tut GLint mInner = -1; bool mCompiled = true; bool mLinked = false; + GLint mPriorProgram = 0; + U32 mPriorArrays = 0; }; typedef test_group altessellation_test; typedef altessellation_test::object altessellation_object; tut::altessellation_test altessellation_testcase("ALTessellation"); + } // The context compiles both tessellation stages and links them with a // vertex and fragment shader. Everything below assumes this. diff --git a/indra/llrender/tests/alwindowsdlheadless_test.cpp b/indra/llrender/tests/alwindowsdlheadless_test.cpp index 612e924c5ee..145ec90e067 100644 --- a/indra/llrender/tests/alwindowsdlheadless_test.cpp +++ b/indra/llrender/tests/alwindowsdlheadless_test.cpp @@ -36,16 +36,18 @@ #include namespace tut +{ +namespace { struct alwindowsdlheadless_data { + // The window is the shared context's, which has the image, render + // and shader classes up on it: the tests look only at the window + // and its shared contexts, and leave the context's bindings as + // they found them. static ll_test::HeadlessGL& gl() { - static ll_test::HeadlessGL instance(/*needs_vbos=*/false, - /*needs_imagegl=*/false, - /*needs_llrender=*/false, - /*needs_render=*/false); - return instance; + return ll_test::sharedHeadlessGL(); } alwindowsdlheadless_data() { gl(); } @@ -54,6 +56,7 @@ namespace tut typedef test_group alwindowsdlheadless_test; typedef alwindowsdlheadless_test::object alwindowsdlheadless_object; tut::alwindowsdlheadless_test alwindowsdlheadless_testcase("ALWindowSDLHeadless"); +} // The window the manager handed the fixture is the hidden backend, valid, // with an SDL window behind it and the size that was asked for. @@ -110,10 +113,14 @@ namespace tut ensure("the worker made a texture", name != 0); ensure("the main context sees the worker's texture", glIsTexture(name) == GL_TRUE); + // The shared context's texture slots cache what they bound, so the + // read puts back the texture it found. + GLint prior = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &prior); glBindTexture(GL_TEXTURE_2D, name); GLint width = 0; glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); - glBindTexture(GL_TEXTURE_2D, 0); + glBindTexture(GL_TEXTURE_2D, prior); ensure_equals("with the storage the worker gave it", width, 1); ensure_equals("and no error on this side", glGetError(), (GLenum)GL_NO_ERROR); diff --git a/indra/llrender/tests/llfontbitmapcache_gl_test.cpp b/indra/llrender/tests/llfontbitmapcache_gl_test.cpp index e38f93569aa..de3ca03b412 100644 --- a/indra/llrender/tests/llfontbitmapcache_gl_test.cpp +++ b/indra/llrender/tests/llfontbitmapcache_gl_test.cpp @@ -34,11 +34,13 @@ #include "../test/lltut.h" namespace tut +{ +namespace { using namespace ll_test; - // GL-backed fixture: shares the binary's HeadlessGL singleton so - // every test below has a live GL context available for the + // GL-backed fixture: a HeadlessGL of each test's own, so every + // test below has a live GL context available for the // gGL.bind call inside nextOpenPos and the destroyGLTexture call // inside releaseSheet. struct llfontbitmapcache_gl_data @@ -49,6 +51,7 @@ namespace tut typedef test_group llfontbitmapcache_gl_test; typedef llfontbitmapcache_gl_test::object llfontbitmapcache_gl_object; tut::llfontbitmapcache_gl_test llfontbitmapcache_gl_testcase("LLFontBitmapCacheGL"); +} // After a Grayscale allocation, the underlying LLImageRaw must // carry 2 components (R+G with R=255 stem, G=alpha) — pins diff --git a/indra/llrender/tests/llfontbitmapcache_test.cpp b/indra/llrender/tests/llfontbitmapcache_test.cpp index a760cce0ce5..745a211ca9a 100644 --- a/indra/llrender/tests/llfontbitmapcache_test.cpp +++ b/indra/llrender/tests/llfontbitmapcache_test.cpp @@ -35,6 +35,8 @@ #include "../test/lltut.h" namespace tut +{ +namespace { // Pure-CPU fixture: no GL context required. The tests in this group // touch only the bookkeeping state that LLFontBitmapCache maintains @@ -46,6 +48,7 @@ namespace tut typedef test_group llfontbitmapcache_test; typedef llfontbitmapcache_test::object llfontbitmapcache_object; tut::llfontbitmapcache_test llfontbitmapcache_testcase("LLFontBitmapCache"); +} // Construction takes a fresh value from sNextGeneration. Two // back-to-back instances must therefore land on distinct diff --git a/indra/llrender/tests/llfontfreetype_gl_test.cpp b/indra/llrender/tests/llfontfreetype_gl_test.cpp index 4704e797ccb..3339f7b5107 100644 --- a/indra/llrender/tests/llfontfreetype_gl_test.cpp +++ b/indra/llrender/tests/llfontfreetype_gl_test.cpp @@ -39,12 +39,14 @@ #include namespace tut +{ +namespace { using namespace ll_test; // ------------------------------------------------------------- - // GL-backed group: rasterizer-touching paths. The HeadlessGl - // singleton supplies the GL context so addGlyph's + // GL-backed group: rasterizer-touching paths. Each test's own + // HeadlessGL supplies the GL context so addGlyph's // gGL.bind(image_gl) call lands on a live GL state. // ------------------------------------------------------------- @@ -57,6 +59,7 @@ namespace tut typedef test_group llfontfreetype_render_test; typedef llfontfreetype_render_test::object llfontfreetype_render_object; tut::llfontfreetype_render_test llfontfreetype_render_testcase("LLFontFreetypeRender"); +} // After getGlyphInfo for an ASCII glyph, the bitmap cache has at // least one Grayscale page, and the returned info has positive diff --git a/indra/llrender/tests/llfontfreetype_test.cpp b/indra/llrender/tests/llfontfreetype_test.cpp index 822f0344831..6903243a8f4 100644 --- a/indra/llrender/tests/llfontfreetype_test.cpp +++ b/indra/llrender/tests/llfontfreetype_test.cpp @@ -43,6 +43,8 @@ #include namespace tut +{ +namespace { using namespace ll_test; @@ -58,6 +60,7 @@ namespace tut typedef test_group llfontfreetype_test; typedef llfontfreetype_test::object llfontfreetype_object; tut::llfontfreetype_test llfontfreetype_testcase("LLFontFreetype"); +} // initClass is idempotent — repeat calls don't replace gFontManagerp. template<> template<> @@ -356,6 +359,8 @@ namespace tut // cmap cache, refcount sharing semantics. // ------------------------------------------------------------- +namespace +{ struct alfontface_data { alfontface_data() { LLFontManager::initClass(); } @@ -365,6 +370,7 @@ namespace tut typedef test_group alfontface_test; typedef alfontface_test::object alfontface_object; tut::alfontface_test alfontface_testcase("ALFontFace"); +} // ALFontFaceKey equality + hash: equal keys equal+hash-equal; // any single-field difference produces unequal keys. diff --git a/indra/llrender/tests/llfontgl_test.cpp b/indra/llrender/tests/llfontgl_test.cpp index cf4af026062..b95af706c93 100644 --- a/indra/llrender/tests/llfontgl_test.cpp +++ b/indra/llrender/tests/llfontgl_test.cpp @@ -73,29 +73,22 @@ namespace namespace tut { - // Shared GL fixture. TUT spins fixtures per-test method, but +namespace +{ + // The shared GL context. TUT spins fixtures per-test method, but // per-fixture HeadlessGL recreation poisons LLFontGL's per-getter // static fontp caches after ~10 cycles (the per-test new GL // context invalidates LLImageGL textures the fontps reference). // Sharing the GL context for the binary's lifetime keeps font // state coherent across all tests. // - // needs_render=true so the render group can drive gGL.beginList / - // flush against a real bound gUIProgram. Layout-only tests (which + // It binds gUIProgram, so the render group can drive gGL.beginList / + // flush against a real shader. Layout-only tests (which // never issue a draw) still work fine — the bound shader is harmless // when no draw call is in flight. - inline ll_test::HeadlessGL& getSharedFontGL() - { - static ll_test::HeadlessGL gl(/*needs_vbos=*/true, - /*needs_imagegl=*/true, - /*needs_llrender=*/true, - /*needs_render=*/true); - return gl; - } - struct llfontgl_data { - ll_test::HeadlessGL& gl = getSharedFontGL(); + ll_test::HeadlessGL& gl = ll_test::sharedHeadlessGL(); llfontgl_data() = default; ~llfontgl_data() = default; }; @@ -103,6 +96,7 @@ namespace tut typedef test_group llfontgl_test; typedef llfontgl_test::object llfontgl_object; tut::llfontgl_test llfontgl_testcase("LLFontGL"); +} // initClass through the explicit-path overload should bring up a // working font registry under a real GL context. loadDefaultFonts @@ -991,6 +985,8 @@ namespace tut // static fontp cache after a few cycles (see vertexbuffer fixture). // =================================================================== +namespace +{ inline void ensureRenderLLFontGL() { static bool initialized = false; @@ -1004,7 +1000,7 @@ namespace tut struct llfontgl_render_data { - ll_test::HeadlessGL& gl = getSharedFontGL(); + ll_test::HeadlessGL& gl = ll_test::sharedHeadlessGL(); llfontgl_render_data() { ensureRenderLLFontGL(); } ~llfontgl_render_data() = default; }; @@ -1012,6 +1008,7 @@ namespace tut typedef test_group llfontgl_render_test; typedef llfontgl_render_test::object llfontgl_render_object; tut::llfontgl_render_test llfontgl_render_testcase("LLFontGLRender"); +} // First end-to-end render() call with LEFT/BASELINE places glyph // pixels into the framebuffer at the requested x. Pins the full diff --git a/indra/llrender/tests/llfontregistry_gl_test.cpp b/indra/llrender/tests/llfontregistry_gl_test.cpp index 1160ada76fa..700ace4bbb0 100644 --- a/indra/llrender/tests/llfontregistry_gl_test.cpp +++ b/indra/llrender/tests/llfontregistry_gl_test.cpp @@ -52,7 +52,9 @@ namespace tut // A fixture that owns a local LLFontRegistry with // create_gl_textures=true over the GL context llheadlessgl_fixture.h // stands up. The pure-CPU suite in llfontregistry_test.cpp keeps its - // own create_gl_textures=false fixture and pays for no GL. + // own create_gl_textures=false fixture and pays for no GL. Both keep + // their names and namespace, outside the anonymous one: + // llfontregistry.h friends them by name. namespace { @@ -151,7 +153,10 @@ namespace tut typedef test_group llfontregistry_gl_test; typedef llfontregistry_gl_test::object llfontregistry_gl_object; +namespace +{ tut::llfontregistry_gl_test llfontregistry_gl_testcase("LLFontRegistry-GL"); +} // createFont with mCreateGLTextures=true must produce a head whose // bitmap cache, after rasterizing the ASCII range, has at least one diff --git a/indra/llrender/tests/llfontregistry_test.cpp b/indra/llrender/tests/llfontregistry_test.cpp index d9d1770f83b..76e81740790 100644 --- a/indra/llrender/tests/llfontregistry_test.cpp +++ b/indra/llrender/tests/llfontregistry_test.cpp @@ -56,6 +56,9 @@ namespace tut // construction-time side-effect is caching LLWindow's dynamic fallback // list, which is a static query that's safe to run anywhere llwindow is // linked. We skip LLFontManager::initClass() entirely. + // + // The fixture keeps its name and namespace, outside the anonymous one: + // llfontregistry.h friends it by name. struct llfontregistry_data { // create_gl_textures=false matches the llui_libtest path: createFont @@ -164,7 +167,10 @@ namespace tut // headroom before the silent-drop threshold bites again. typedef test_group llfontregistry_test; typedef llfontregistry_test::object llfontregistry_object; +namespace +{ tut::llfontregistry_test llfontregistry_testcase("LLFontRegistry"); +} // =================================================================== // Group 1: LLFontDescriptor (pure) diff --git a/indra/llrender/tests/llfonttextcache_test.cpp b/indra/llrender/tests/llfonttextcache_test.cpp index b4b92361de2..a5f7540dd1a 100644 --- a/indra/llrender/tests/llfonttextcache_test.cpp +++ b/indra/llrender/tests/llfonttextcache_test.cpp @@ -74,6 +74,8 @@ namespace } namespace tut +{ +namespace { // Shared GL context + LLFontGL bring-up. One per binary. TUT // creates a fresh fixture per test method; per-fixture HeadlessGL @@ -82,20 +84,8 @@ namespace tut // fontps reference (LLFontManager::cleanupClass + LLImageGL:: // cleanupClass in HeadlessGL dtor). Sharing the GL context across // tests keeps all that state coherent for the binary's lifetime. - inline ll_test::HeadlessGL& getSharedHeadlessGL(bool needs_render) - { - // The first test in the binary picks the needs_render mode. - // Both groups in this binary use needs_render=true (the smoke - // tests don't drive rendering, but having gUIProgram bound is - // harmless for them). All subsequent fixtures share this one. - static ll_test::HeadlessGL gl(/*needs_vbos=*/true, - /*needs_imagegl=*/true, - /*needs_llrender=*/true, - /*needs_render=*/true); - (void)needs_render; - return gl; - } - + // Both groups take it with gUIProgram bound (the smoke tests don't + // drive rendering, but having it bound is harmless for them). inline void ensureLLFontGLLoaded() { static bool initialized = false; @@ -111,11 +101,11 @@ namespace tut // Fixture: minimal — just gates the static toggles and ensures the // shared GL fixture is up. The actual HeadlessGL instance lives in - // the static getSharedHeadlessGL() and stays alive for the whole + // ll_test::sharedHeadlessGL() and stays alive for the whole // binary, so font/atlas state stays coherent across tests. struct llfonttextcache_data { - ll_test::HeadlessGL& gl = getSharedHeadlessGL(/*needs_render=*/false); + ll_test::HeadlessGL& gl = ll_test::sharedHeadlessGL(); llfonttextcache_data() { @@ -133,6 +123,7 @@ namespace tut typedef test_group llfonttextcache_test; typedef llfonttextcache_test::object llfonttextcache_object; tut::llfonttextcache_test llfonttextcache_testcase("LLFontTextCache"); +} // Default construction must not crash and must leave the buffer // in a destructible state. Catches uninitialized-member regressions @@ -431,9 +422,11 @@ namespace tut // framebuffer. // =================================================================== +namespace +{ struct llfonttextcache_render_data { - ll_test::HeadlessGL& gl = getSharedHeadlessGL(/*needs_render=*/true); + ll_test::HeadlessGL& gl = ll_test::sharedHeadlessGL(); llfonttextcache_render_data() { @@ -451,6 +444,7 @@ namespace tut typedef test_group llfonttextcache_render_test; typedef llfonttextcache_render_test::object llfonttextcache_render_object; tut::llfonttextcache_render_test llfonttextcache_render_testcase("LLFontTextCacheRender"); +} // First render() populates the buffer cache and returns the // character count rendered. Pins genBuffers + drawBuffer end-to-end diff --git a/indra/llrender/tests/llheadlessgl_fixture.h b/indra/llrender/tests/llheadlessgl_fixture.h index 1576dbb20c6..55e88bd4602 100644 --- a/indra/llrender/tests/llheadlessgl_fixture.h +++ b/indra/llrender/tests/llheadlessgl_fixture.h @@ -8,10 +8,10 @@ * Linux -- and where Linux has no display, SDL's offscreen driver. * * Standing the context up is the expensive part — TUT spins a fresh - * fixture per test method, so we hide a static-local instance - * behind getHeadlessGl() and let every fixture struct embed a - * reference. That keeps each test isolated against its own state - * (font registry, atlas pages) while reusing the GL context. + * fixture per test method, so a test that can share one takes the + * static-local instance behind sharedHeadlessGL() and keeps its own + * state (font registry, atlas pages) in the fixture. A test that + * needs a context of its own makes a HeadlessGL per test instead. * * $LicenseInfo:firstyear=2026&license=viewerlgpl$ * Alchemy Viewer Source Code @@ -52,7 +52,9 @@ #include "llwindow.h" #include "llwindowcallbacks.h" +#include #include +#include #include #include @@ -81,7 +83,7 @@ namespace ll_test }; // Drop transient font caches that would otherwise leak between tests - // sharing this binary's HeadlessGl singleton. Safe to call repeatedly; + // sharing sharedHeadlessGL(). Safe to call repeatedly; // does NOT touch LLFontManager / LLImageGL / GL state — those stay // alive for the whole binary because LLFontGL's static fontp caches // (getFontSansSerif et al.) dangle across a re-init cycle (see @@ -115,19 +117,23 @@ namespace ll_test class TestShaderMgr : public LLShaderMgr { public: + // One made over another, as a test's own over the shared context's, + // stands in for it while it lives and hands the instance back. explicit TestShaderMgr(std::string dir_prefix = std::string()) - : mDirPrefix(std::move(dir_prefix)) + : mDirPrefix(std::move(dir_prefix)), + mPrevious(sInstance) { sInstance = this; initAttribsAndUniforms(); } - ~TestShaderMgr() override { if (sInstance == this) sInstance = nullptr; } + ~TestShaderMgr() override { if (sInstance == this) sInstance = mPrevious; } std::string getShaderDirPrefix() override { return mDirPrefix; } void updateShaderUniforms(LLGLSLShader* /*shader*/) override {} private: std::string mDirPrefix; + LLShaderMgr* mPrevious; }; // GL 3.3 core pass-through UI vertex shader. Same uniform / attribute @@ -369,6 +375,13 @@ namespace ll_test installDebugMessageCallback(); + // The window's viewport, as the viewer's window records it: what a + // render target's flush puts back when no outer target is bound. + gGLViewport[0] = 0; + gGLViewport[1] = 0; + gGLViewport[2] = WIDTH; + gGLViewport[3] = HEIGHT; + // Both of these take an LLWindow only to hand to worker threads // they are not being asked to start: ENABLE_GL_WORK_QUEUE is 0, // and the texture and media threads are off below. @@ -438,6 +451,7 @@ namespace ll_test ALUniformBuffer::cleanupClass(); LLWindowManager::destroyWindow(mWindow); + std::fill(std::begin(gGLViewport), std::end(gGLViewport), 0); } void swapBuffer() @@ -463,6 +477,155 @@ namespace ll_test LLWindow* mWindow = nullptr; std::unique_ptr mShaderMgr; }; + + // The one context of the tests that share theirs: made by the first test + // to ask and kept for the rest of the process, so a binary of them has + // one window. Every part of the fixture is up, the union of what those + // tests need, so each must leave the image, render and shader state as + // it found it. Tests that make a HeadlessGL of their own per test end + // the GL classes under it, and are never linked beside a caller of this. + inline HeadlessGL& sharedHeadlessGL() + { + static HeadlessGL gl(/*needs_vbos=*/true, + /*needs_imagegl=*/true, + /*needs_llrender=*/true, + /*needs_render=*/true); + return gl; + } + + // A test's hold on the shared context. It takes sharedHeadlessGL() as it + // is made and reads what a test may change of the context, then puts that + // back as it goes, whether the test changed it through gGL's and the + // shader's caches or behind them, so the next test finds the context as + // the fixture left it. The first member of a test's fixture, so that it + // goes after the fixture's own GL objects. A test that leaves a matrix + // pushed (only one that failed between a push and its pop) leaves the + // stack deeper; the matrices themselves are put back regardless. + class SharedGLScope + { + public: + SharedGLScope() + : mGL(sharedHeadlessGL()) + { + mShader = LLGLSLShader::sCurBoundShaderPtr; + glGetIntegerv(GL_CURRENT_PROGRAM, &mProgram); + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &mVertexArray); + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &mDrawFramebuffer); + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &mReadFramebuffer); + glGetIntegerv(GL_VIEWPORT, mViewport); + glGetIntegerv(GL_SCISSOR_BOX, mScissor); + mScissorTest = glIsEnabled(GL_SCISSOR_TEST); + mDepthTest = glIsEnabled(GL_DEPTH_TEST); + glGetIntegerv(GL_DEPTH_FUNC, &mDepthFunc); + glGetBooleanv(GL_DEPTH_WRITEMASK, &mDepthMask); + glGetDoublev(GL_DEPTH_CLEAR_VALUE, &mClearDepth); + if (gGLManager.mHasClipControl) + { + glGetIntegerv(GL_CLIP_ORIGIN, &mClipOrigin); + glGetIntegerv(GL_CLIP_DEPTH_MODE, &mClipDepthMode); + } + mClearColor = gGL.getClearColor(); + mReverseZ = LLRender::sReverseZ; + mMatrixMode = gGL.getMatrixMode(); + mModelview = gGL.getModelviewMatrix(); + mProjection = gGL.getProjectionMatrix(); + } + + ~SharedGLScope() + { + // The shader through its own cache, then the program behind it. + if (LLGLSLShader::sCurBoundShaderPtr != mShader) + { + if (mShader) + { + mShader->bind(); + } + else + { + LLGLSLShader::unbind(); + } + } + glUseProgram(mProgram); + // LLRender's own vertex array, and LLVertexBuffer's buffers + // through it, so that its next draw binds and points them again. + glBindVertexArray(mVertexArray); + LLVertexBuffer::unbind(); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDrawFramebuffer); + glBindFramebuffer(GL_READ_FRAMEBUFFER, mReadFramebuffer); + glViewport(mViewport[0], mViewport[1], mViewport[2], mViewport[3]); + + S32 scissor[4]; + gGL.getScissor(scissor); + if (scissor[2] >= 0) + { + gGL.setScissor(mScissor[0], mScissor[1], mScissor[2], mScissor[3]); + } + glScissor(mScissor[0], mScissor[1], mScissor[2], mScissor[3]); + setEnabled(GL_SCISSOR_TEST, mScissorTest); + + setEnabled(GL_DEPTH_TEST, mDepthTest); + glDepthFunc(mDepthFunc); + glDepthMask(mDepthMask); + glClearDepth(mClearDepth); + if (gGLManager.mHasClipControl) + { + glClipControl(mClipOrigin, mClipDepthMode); + } + LLRender::sReverseZ = mReverseZ; + + gGL.setClearColor(mClearColor); + gGL.matrixMode(LLRender::MM_TEXTURE0); + gGL.loadIdentity(); + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.loadMatrix(mProjection); + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(mModelview); + gGL.matrixMode(mMatrixMode); + // The texture slots, colour mask and the rest gGL keeps, issued + // again from its cache over whatever was bound behind it. + gGL.refreshState(); + } + + HeadlessGL& gl() const { return mGL; } + + SharedGLScope(const SharedGLScope&) = delete; + SharedGLScope& operator=(const SharedGLScope&) = delete; + + private: + static void setEnabled(GLenum cap, GLboolean enabled) + { + if (enabled) + { + glEnable(cap); + } + else + { + glDisable(cap); + } + } + + HeadlessGL& mGL; + LLGLSLShader* mShader = nullptr; + GLint mProgram = 0; + GLint mVertexArray = 0; + GLint mDrawFramebuffer = 0; + GLint mReadFramebuffer = 0; + GLint mViewport[4] = { 0, 0, 0, 0 }; + GLint mScissor[4] = { 0, 0, 0, 0 }; + GLboolean mScissorTest = GL_FALSE; + GLboolean mDepthTest = GL_FALSE; + GLint mDepthFunc = GL_LESS; + GLboolean mDepthMask = GL_TRUE; + GLdouble mClearDepth = 1.0; + GLint mClipOrigin = GL_LOWER_LEFT; + GLint mClipDepthMode = GL_NEGATIVE_ONE_TO_ONE; + LLColor4 mClearColor; + bool mReverseZ = false; + LLRender::eMatrixMode mMatrixMode = LLRender::MM_MODELVIEW; + LLMatrix4a mModelview; + LLMatrix4a mProjection; + }; } #endif // LL_LLHEADLESSGL_FIXTURE_H diff --git a/indra/llrender/tests/llimagegl_test.cpp b/indra/llrender/tests/llimagegl_test.cpp index 1287bc51c04..4e6ba83e9e7 100644 --- a/indra/llrender/tests/llimagegl_test.cpp +++ b/indra/llrender/tests/llimagegl_test.cpp @@ -49,6 +49,8 @@ #include namespace tut +{ +namespace { struct llimagegl_data { @@ -97,6 +99,7 @@ namespace tut typedef test_group llimagegl_test; typedef llimagegl_test::object llimagegl_object; tut::llimagegl_test llimagegl_testcase("LLImageGL"); +} // createGLTexture(LLImageRaw) allocates immutable storage under a // fresh GL name. After it succeeds the instance reports @@ -813,7 +816,16 @@ namespace tut // The queue the upload thread hands its results back on, as the viewer's. // It has to exist before the image, which looks it up when built. LL::WorkQueue mainloop("mainloop"); + std::atomic worker_done{ false }; + std::atomic worker_ok{ false }; LLImageGLThread::createInstance(gl->window()); + // The thread ends with the test however the test ends: left running + // after a failure, it would outlive the window its context shares, + // and the flags its work writes to. + struct ThreadEnd + { + ~ThreadEnd() { LLImageGLThread::deleteSingleton(); } + } thread_end; LLPointer img = new LLImageGL(/*usemipmaps=*/false); ensure("createGLTexture succeeded", @@ -821,8 +833,6 @@ namespace tut const U32 old_name = img->getTexName(); LLPointer raw = makeRaw(32, 32, 4, 0x55); - std::atomic worker_done{ false }; - std::atomic worker_ok{ false }; img->beginUpload(); LLImageGLThread::instance().post( diff --git a/indra/llrender/tests/llrender2dutils_gl_test.cpp b/indra/llrender/tests/llrender2dutils_gl_test.cpp index d6f0b47ddc3..2132f31368c 100644 --- a/indra/llrender/tests/llrender2dutils_gl_test.cpp +++ b/indra/llrender/tests/llrender2dutils_gl_test.cpp @@ -41,6 +41,8 @@ #include namespace tut +{ +namespace { struct llrender2dutils_data { @@ -48,11 +50,7 @@ namespace tut // and LLImageGL's class state does not survive a re-init. static ll_test::HeadlessGL& gl() { - static ll_test::HeadlessGL instance(/*needs_vbos=*/true, - /*needs_imagegl=*/true, - /*needs_llrender=*/true, - /*needs_render=*/true); - return instance; + return ll_test::sharedHeadlessGL(); } llrender2dutils_data() @@ -133,6 +131,7 @@ namespace tut typedef test_group llrender2dutils_test; typedef llrender2dutils_test::object llrender2dutils_object; tut::llrender2dutils_test llrender2dutils_testcase("llrender2dutils_gl"); +} // A rotated image turns about its own centre and keeps its size. An // odd-sized image at a quarter turn covers the same pixels it does diff --git a/indra/llrender/tests/llrendermatrix_test.cpp b/indra/llrender/tests/llrendermatrix_test.cpp index 40c497bbb3c..28c170bcaf2 100644 --- a/indra/llrender/tests/llrendermatrix_test.cpp +++ b/indra/llrender/tests/llrendermatrix_test.cpp @@ -38,6 +38,8 @@ #include namespace tut +{ +namespace { struct llrendermatrix_data { @@ -115,6 +117,7 @@ namespace tut -0.8f, 0.6f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 5.f, -7.f, 9.f, 1.f }; +} // The stack ops apply the new transform ahead of what the stack held, // which on the scalar matrix is the product written the other way diff --git a/indra/llrender/tests/llrendertarget_gl_test.cpp b/indra/llrender/tests/llrendertarget_gl_test.cpp index 3546e7e8b01..7dcc963c093 100644 --- a/indra/llrender/tests/llrendertarget_gl_test.cpp +++ b/indra/llrender/tests/llrendertarget_gl_test.cpp @@ -61,21 +61,11 @@ namespace tut const LLColor4U OUTPUT_COLOURS[] = { LLColor4U(255, 0, 0, 255), LLColor4U(0, 255, 0, 255), LLColor4U(0, 0, 255, 255), LLColor4U(255, 255, 0, 255) }; - } + // The program, vertex array and clear colour the test sets go back with + // mGL. struct llrendertarget_data { - static ll_test::HeadlessGL& gl() - { - static ll_test::HeadlessGL instance(true, true, true, false); - return instance; - } - - llrendertarget_data() - { - gl(); - } - ~llrendertarget_data() { mTarget.release(); @@ -85,7 +75,6 @@ namespace tut } if (mProgram) { - glUseProgram(0); glDeleteProgram(mProgram); } } @@ -131,6 +120,7 @@ namespace tut return LLColor4U(pixel[0], pixel[1], pixel[2], pixel[3]); } + ll_test::SharedGLScope mGL; LLRenderTarget mTarget; GLuint mVAO = 0; GLuint mProgram = 0; @@ -139,6 +129,7 @@ namespace tut typedef test_group llrendertarget_t; typedef llrendertarget_t::object llrendertarget_object_t; tut::llrendertarget_t tut_llrendertarget("llrendertarget_gl"); + } // setDrawOutput: a single-attachment target takes the output it names and none of the others, and the next // bindTarget gives it output 0 again. diff --git a/indra/llrender/tests/lluiimage_test.cpp b/indra/llrender/tests/lluiimage_test.cpp index 6ab05b88fa1..d9d0ef772ef 100644 --- a/indra/llrender/tests/lluiimage_test.cpp +++ b/indra/llrender/tests/lluiimage_test.cpp @@ -89,6 +89,8 @@ namespace ll_test } namespace tut +{ +namespace { struct lluiimage_data { @@ -96,11 +98,7 @@ namespace tut // and LLImageGL's class state does not survive a re-init. static ll_test::HeadlessGL& gl() { - static ll_test::HeadlessGL instance(/*needs_vbos=*/true, - /*needs_imagegl=*/true, - /*needs_llrender=*/true, - /*needs_render=*/true); - return instance; + return ll_test::sharedHeadlessGL(); } lluiimage_data() @@ -149,6 +147,7 @@ namespace tut typedef test_group lluiimage_test; typedef lluiimage_test::object lluiimage_object; tut::lluiimage_test lluiimage_testcase("LLUIImage"); +} // One recording serves every position the image is drawn at. A floater // being dragged is this test run at frame rate. diff --git a/indra/llrender/tests/lluitransform_test.cpp b/indra/llrender/tests/lluitransform_test.cpp index 49f6e1b9bd1..7e0af297f18 100644 --- a/indra/llrender/tests/lluitransform_test.cpp +++ b/indra/llrender/tests/lluitransform_test.cpp @@ -30,6 +30,8 @@ #include "../test/lltut.h" namespace tut +{ +namespace { struct lluitransform_data { @@ -62,6 +64,7 @@ namespace tut typedef test_group lluitransform_test; typedef lluitransform_test::object lluitransform_object; tut::lluitransform_test lluitransform_testgroup("lluitransform"); +} // With nothing pushed, a local rect is where it says it is. template<> template<> diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 94d69fba300..a5348deb668 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -562,56 +562,68 @@ if(AL_BUILD_TESTS) llcommon ${spellcheck_lib} ) - al_add_test(llchatautocompletemodel UNIT PROJECT llui LIBRARIES ${test_libs}) - al_add_test(llurlmatch UNIT PROJECT llui LIBRARIES ${test_libs}) - al_add_test(alkeymap UNIT PROJECT llui DIR code LIBRARIES ${test_libs}) - # What comparing two texts is made of, compiled in, below the view. - set( - diff_sources - diff/aldiffedit.cpp - diff/aldiffmoves.cpp - diff/aldiffrangesame.cpp - diff/aldiffsame.cpp - diff/aldiffsplice.cpp - diff/aldifftokens.cpp - diff/allinediff.cpp - diff/allinepairs.cpp - diff/alstructuraldiff.cpp - diff/altextmerge.cpp - diff/alworddiff.cpp + # Four that test templates whole in their headers, and need nothing + # compiled of llui's. + al_add_test(alfenwicktree PROJECT llui LIBRARIES ${test_libs}) + al_add_test(allinetable PROJECT llui LIBRARIES ${test_libs}) + al_add_test(almaxima PROJECT llui LIBRARIES ${test_libs}) + al_add_test(alprefixsums PROJECT llui LIBRARIES ${test_libs}) + + # What llui asks the viewer for, defined once: compiled here and linked + # into every test and benchmark below, which link the library. A test + # file holds no copy of its own, since two in one binary would not link. + add_library(llui_test_fakes OBJECT tests/llui_test_fakes.cpp) + target_link_libraries(llui_test_fakes PRIVATE al::flags llcommon) + set_target_properties(llui_test_fakes PROPERTIES FOLDER "Tests/llui") + + set(test_libs llui llui_test_fakes ${test_libs}) + foreach( + test + llstatbar + lltextparser + llview + lluistring + llxuiparser + llfocusmgr + alxuioverlay + alxuidocuments + alxuifindings + alxuitranslate ) - al_add_test(altextdiff UNIT PROJECT llui DIR diff SOURCES ${diff_sources} LIBRARIES ${test_libs}) - # Two texts' differences written as a unified diff. - al_add_test( - alunifieddiff - UNIT - PROJECT llui - DIR diff - SOURCES diff/altextdiff.cpp ${diff_sources} - LIBRARIES ${test_libs} + al_add_test(${test} PROJECT llui LIBRARIES ${test_libs}) + endforeach() + # Tests of one of llui's files each, linked against the library that + # holds it rather than compiling a copy of their own. + foreach( + test + llchatautocompletemodel + alkeymap + alanchoredranges + altextdocument + altextcarets + altextundo + alnotecarditems + alsyntaxhighlighter + llurlentry + llemojidictionary ) + al_add_test(${test} PROJECT llui LIBRARIES ${test_libs}) + endforeach() + # What comparing two texts is made of, below the view. + al_add_test(altextdiff PROJECT llui LIBRARIES ${test_libs}) + # Two texts' differences written as a unified diff. + al_add_test(alunifieddiff PROJECT llui LIBRARIES ${test_libs}) # A merge settled in a comparison, and lines of a text put in place of # others as one edit of it. al_add_test( aldiffmerge - UNIT PROJECT llui - DIR diff - SOURCES diff/altextdiff.cpp diff/aldifflexer.cpp code/alsyntaxgrammar.cpp ${diff_sources} LIBRARIES ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) al_add_test( aldiffmodel - UNIT PROJECT llui - DIR diff - SOURCES - diff/altextdiff.cpp - diff/aldiffmerge.cpp - diff/aldifflexer.cpp - code/alsyntaxgrammar.cpp - ${diff_sources} LIBRARIES ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -619,68 +631,16 @@ if(AL_BUILD_TESTS) # changed: over the grammars the viewer ships. al_add_test( aldifflexer - UNIT PROJECT llui - DIR diff - SOURCES code/alsyntaxgrammar.cpp diff/altextdiff.cpp ${diff_sources} LIBRARIES ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) - al_add_test(alanchoredranges PROJECT llui SOURCES text/altextdocument.cpp LIBRARIES ${test_libs}) - al_add_test(alfenwicktree PROJECT llui LIBRARIES ${test_libs}) - al_add_test(allinetable PROJECT llui LIBRARIES ${test_libs}) - al_add_test(almaxima PROJECT llui LIBRARIES ${test_libs}) - al_add_test(alprefixsums PROJECT llui LIBRARIES ${test_libs}) - al_add_test(altextdocument UNIT PROJECT llui DIR text LIBRARIES ${test_libs}) - al_add_test( - altextcarets - UNIT - PROJECT llui - DIR text - SOURCES text/altextdocument.cpp - LIBRARIES ${test_libs} - ) - al_add_test( - altextundo - UNIT - PROJECT llui - DIR text - SOURCES text/altextdocument.cpp - LIBRARIES ${test_libs} - ) - al_add_test(alnotecarditems UNIT PROJECT llui DIR text LIBRARIES ${test_libs}) - al_add_test( - alsyntaxhighlighter - UNIT - PROJECT llui - DIR code - SOURCES code/alsyntaxgrammar.cpp text/altextdocument.cpp - LIBRARIES ${test_libs} - ) - - set(test_libs llui ${test_libs}) - foreach( - test - llstatbar - lltextparser - llview - lluistring - llxuiparser - llfocusmgr - alxuioverlay - alxuidocuments - alxuifindings - alxuitranslate - ) - al_add_test(${test} PROJECT llui LIBRARIES ${test_libs}) - endforeach() - al_add_test(llurlentry PROJECT llui SOURCES llurlentry.cpp LIBRARIES ${test_libs}) - al_add_test(llemojidictionary PROJECT llui SOURCES llemojidictionary.cpp LIBRARIES ${test_libs}) # Tests that build widgets above LLView stand up a UI over the source # tree's skins and fonts; this is where it is. foreach( test + llurlmatch lllayoutstack llresizebar llaccordionctrltab diff --git a/indra/llui/base/alsurface.cpp b/indra/llui/base/alsurface.cpp index 54074a01c0f..ac77f8cfc6a 100644 --- a/indra/llui/base/alsurface.cpp +++ b/indra/llui/base/alsurface.cpp @@ -99,33 +99,62 @@ namespace ALSurface gl_rect_2d(rect, frame(ink, alpha), false); } + namespace + { + // A colour of the skin's by name, asked of the table again whenever + // the table has changed rather than only the first time. Asked once, + // a name the table did not have yet -- a control built before the + // skin's colours were in, as a test builds them -- would be the + // fallback for good, and one reset since would still point into the + // table. Every caller is handed the same object, so a reference one + // keeps follows as well. + const LLUIColor& skinColor(LLUIColor& color, U32& seen, std::string_view name, const LLColor4& fallback) + { + const LLUIColorTable& table = LLUIColorTable::instance(); + if (seen != table.generation()) + { + color = table.getColor(name, fallback); + seen = table.generation(); + } + return color; + } + + // Before the first look: no generation the table counts to soon. + constexpr U32 NEVER = U32_MAX; + } + const LLUIColor& well() { - static const LLUIColor color = LLUIColorTable::instance().getColor("DefaultShadowLight", LLColor4::black); - return color; + static LLUIColor color; + static U32 seen = NEVER; + return skinColor(color, seen, "DefaultShadowLight", LLColor4::black); } const LLUIColor& rim() { - static const LLUIColor color = LLUIColorTable::instance().getColor("LabelDisabledColor", LLColor4::grey); - return color; + static LLUIColor color; + static U32 seen = NEVER; + return skinColor(color, seen, "LabelDisabledColor", LLColor4::grey); } const LLUIColor& handle() { - static const LLUIColor color = LLUIColorTable::instance().getColor("EmphasisColor", LLColor4::yellow); - return color; + static LLUIColor color; + static U32 seen = NEVER; + return skinColor(color, seen, "EmphasisColor", LLColor4::yellow); } const LLUIColor& text() { - static const LLUIColor color = LLUIColorTable::instance().getColor("LabelTextColor", LLColor4::white); - return color; + static LLUIColor color; + static U32 seen = NEVER; + return skinColor(color, seen, "LabelTextColor", LLColor4::white); } const LLUIColor& quiet() { - static const LLUIColor color = LLUIColorTable::instance().getColor("LabelDisabledColor", LLColor4::grey); - return color; + static LLUIColor color; + static U32 seen = NEVER; + return skinColor(color, seen, "LabelDisabledColor", LLColor4::grey); } } diff --git a/indra/llui/tests/alangledial_test.cpp b/indra/llui/tests/alangledial_test.cpp index bd92e67f788..fef9b389f3a 100644 --- a/indra/llui/tests/alangledial_test.cpp +++ b/indra/llui/tests/alangledial_test.cpp @@ -31,25 +31,20 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gAngleDialTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gAngleDialTestAnonName; -} - namespace tut { struct alangledial_data { ll_test::HeadlessUI& ui = ll_test::HeadlessUI::get(); + // Focus and capture as the test found them, whatever it leaves + // them on. + ll_test::FocusScope focus; // 70 tall: the dial is the 66-pixel square at the left, its centre // at (35, 35). diff --git a/indra/llui/tests/alcanvasview_test.cpp b/indra/llui/tests/alcanvasview_test.cpp index 202eb5afe3a..0271c4e73b1 100644 --- a/indra/llui/tests/alcanvasview_test.cpp +++ b/indra/llui/tests/alcanvasview_test.cpp @@ -33,21 +33,13 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" #include #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gCanvasTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gCanvasTestAnonName; -} - namespace { // Something on a surface that says where the pointer and a drop reached @@ -90,6 +82,9 @@ namespace tut struct alcanvasview_data { ll_test::HeadlessUI& ui = ll_test::HeadlessUI::get(); + // Focus and capture as the test found them, whatever it leaves + // them on. + ll_test::FocusScope focus; // The room a surface is shown in, in these tests. static constexpr S32 ROOM_W = 700; diff --git a/indra/llui/tests/alchangepeek_test.cpp b/indra/llui/tests/alchangepeek_test.cpp index 5d986630d5e..9e70599d3e0 100644 --- a/indra/llui/tests/alchangepeek_test.cpp +++ b/indra/llui/tests/alchangepeek_test.cpp @@ -40,13 +40,6 @@ #include #include -class LLAvatarName; -const std::string gPeekTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gPeekTestAnonName; -} - namespace tut { struct alchangepeek_data diff --git a/indra/llui/tests/alchoicelist_test.cpp b/indra/llui/tests/alchoicelist_test.cpp index e03ec3c14cc..ef0d6d06be8 100644 --- a/indra/llui/tests/alchoicelist_test.cpp +++ b/indra/llui/tests/alchoicelist_test.cpp @@ -35,13 +35,6 @@ #include -class LLAvatarName; -const std::string gChoiceTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gChoiceTestAnonName; -} - namespace tut { struct alchoicelist_data diff --git a/indra/llui/tests/alcodeeditor_test.cpp b/indra/llui/tests/alcodeeditor_test.cpp index d9687412e3a..3f80cb842b2 100644 --- a/indra/llui/tests/alcodeeditor_test.cpp +++ b/indra/llui/tests/alcodeeditor_test.cpp @@ -39,6 +39,8 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" +#include "altextviewprobe.h" #include "../test/lltut.h" @@ -46,41 +48,22 @@ #include #include -class LLAvatarName; -const std::string gCodeTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gCodeTestAnonName; -} - -namespace ll_test -{ - // The colours the view works out for a row's glyphs, as it draws them. - struct TextViewProbe - { - static std::vector colours(ALTextView& view, S32 line, const ALTextLayout::Row& row) - { - const ALTextLayout::Line& laid = view.layout().line(line); - view.colorRow(line, laid, row, 1.f); - view.tintRow(line, laid, row, 1.f, view.mColorScratch); - return view.mColorScratch; - } - }; -} - namespace tut { struct alcodeeditor_data { ll_test::HeadlessUI& ui = ll_test::HeadlessUI::get(); ALCodeEditor* editor = nullptr; + // Every editor a test made, the last of them being editor: a test + // that makes more than one leaves none of them behind alive. + std::vector made; ~alcodeeditor_data() { gFocusMgr.setKeyboardFocus(nullptr); - if (editor) + for (ALCodeEditor* each : made) { - editor->die(); + each->die(); } } @@ -96,6 +79,7 @@ namespace tut p.default_text = text; p.syntax = syntax; editor = LLUICtrlFactory::create(p); + made.push_back(editor); editor->setFont(LLFontGL::getFontMonospace()); editor->setFocus(true); return *editor; @@ -3625,7 +3609,9 @@ namespace tut { set_test_name("a fix previewed inside a block comment colours what it makes as the comment it is in, not as code"); // The skin's colours for the kinds, told before the editor reads - // them: nothing loads a skin's colours in a test. + // them: nothing loads a skin's colours in a test. Taken back after, + // since every editor made later would read them. + ll_test::ColorTableScope colours; LLUIColorTable& table = LLUIColorTable::instance(); table.setColor("SyntaxComment", LLColor4(0.f, 0.5f, 0.f, 1.f)); table.setColor("SyntaxNumber", LLColor4(0.f, 0.f, 0.8f, 1.f)); diff --git a/indra/llui/tests/alcolorfield_test.cpp b/indra/llui/tests/alcolorfield_test.cpp index faf0fc35055..5b1bdba4d86 100644 --- a/indra/llui/tests/alcolorfield_test.cpp +++ b/indra/llui/tests/alcolorfield_test.cpp @@ -36,15 +36,11 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" -class LLAvatarName; -const std::string gColorFieldTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gColorFieldTestAnonName; -} +#include namespace tut { @@ -61,8 +57,18 @@ namespace tut { loaded = LLUIColorTable::instance().loadFromSettings(); } + colours.emplace(); } + // What a test sets in the table, set back when it ends; begun once + // the skin's colours are in, which every test here reads. + std::optional colours; + ll_test::FocusScope focus; + + // A popover left open holds the keys, and the next test looking for + // the popover showing would find it before its own. + ~alcolorfield_data() { ll_test::escapePopovers(); } + static ALColorField* make() { ALColorField::Params p(LLUICtrlFactory::getDefaultParams()); diff --git a/indra/llui/tests/alcolorpicker_test.cpp b/indra/llui/tests/alcolorpicker_test.cpp index 5b1a0f5d679..9e3675c8069 100644 --- a/indra/llui/tests/alcolorpicker_test.cpp +++ b/indra/llui/tests/alcolorpicker_test.cpp @@ -30,21 +30,18 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" -class LLAvatarName; -const std::string gColorPickerTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gColorPickerTestAnonName; -} - namespace tut { struct alcolorpicker_data { ll_test::HeadlessUI& ui = ll_test::HeadlessUI::get(); + // Focus and capture as the test found them, whatever it leaves + // them on. + ll_test::FocusScope focus; // The layout the picker works out for itself: the ring takes a square // at the left as tall as the widget, the channels what is right of it diff --git a/indra/llui/tests/alcolorsheet_test.cpp b/indra/llui/tests/alcolorsheet_test.cpp index cab4e06971c..b6029ec05a3 100644 --- a/indra/llui/tests/alcolorsheet_test.cpp +++ b/indra/llui/tests/alcolorsheet_test.cpp @@ -28,13 +28,6 @@ #include "../test/lltut.h" -class LLAvatarName; -const std::string gColorSheetTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gColorSheetTestAnonName; -} - namespace tut { using Kind = ALColorSheet::Diagnostic::Kind; diff --git a/indra/llui/tests/alcolortablepanel_test.cpp b/indra/llui/tests/alcolortablepanel_test.cpp index 1a3a1413f40..ff1d177bbe7 100644 --- a/indra/llui/tests/alcolortablepanel_test.cpp +++ b/indra/llui/tests/alcolortablepanel_test.cpp @@ -33,15 +33,11 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" -class LLAvatarName; -const std::string gColorTablePanelTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gColorTablePanelTestAnonName; -} +#include namespace tut { @@ -58,8 +54,13 @@ namespace tut { loaded = LLUIColorTable::instance().loadFromSettings(); } + colours.emplace(); } + // What a test sets in the table, set back when it ends; begun once + // the skin's colours are in, which every test here reads. + std::optional colours; + static ALColorTablePanel* make() { ALColorTablePanel::Params p(LLUICtrlFactory::getDefaultParams()); diff --git a/indra/llui/tests/alcornerfield_test.cpp b/indra/llui/tests/alcornerfield_test.cpp index 82e2a9a972d..08967fb19d8 100644 --- a/indra/llui/tests/alcornerfield_test.cpp +++ b/indra/llui/tests/alcornerfield_test.cpp @@ -36,15 +36,6 @@ #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gCornerFieldTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gCornerFieldTestAnonName; -} - namespace tut { struct alcornerfield_data diff --git a/indra/llui/tests/aldiffview_gl_test.cpp b/indra/llui/tests/aldiffview_gl_test.cpp index ffe514d75e4..bc69164109b 100644 --- a/indra/llui/tests/aldiffview_gl_test.cpp +++ b/indra/llui/tests/aldiffview_gl_test.cpp @@ -26,7 +26,7 @@ #include "llglheaders.h" -#include "../../llrender/tests/llheadlessgl_fixture.h" +#include "aluigl_fixture.h" #include "alcodeeditor.h" #include "aldiffview.h" @@ -41,28 +41,15 @@ #include #include -class LLAvatarName; -const std::string gDiffGLTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gDiffGLTestAnonName; -} - namespace { constexpr S32 W = ll_test::HeadlessGL::WIDTH; constexpr S32 H = ll_test::HeadlessGL::HEIGHT; - ll_test::HeadlessGL& gl() - { - static ll_test::HeadlessGL instance(true, true, true, /*needs_render=*/true); - return instance; - } - // The view drawn over black, read back: RGBA, bottom row first. std::vector drawn(LLView& view) { - gl().clearFramebuffer(); + ll_test::uiGL().clearFramebuffer(); glEnable(GL_BLEND); gGL.setSceneBlendType(LLRender::BT_ALPHA); view.draw(); @@ -103,7 +90,7 @@ namespace tut aldiffview_gl_data() { - gl(); + ll_test::uiGL(); // What a rect without a texture of its own is drawn with. ll_test::installWhiteTexture(); } diff --git a/indra/llui/tests/aldiffview_test.cpp b/indra/llui/tests/aldiffview_test.cpp index 7fae7d9edff..7fa6d6edf4d 100644 --- a/indra/llui/tests/aldiffview_test.cpp +++ b/indra/llui/tests/aldiffview_test.cpp @@ -37,22 +37,17 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" #include #include #include +#include #include #include -class LLAvatarName; -const std::string gDiffTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gDiffTestAnonName; -} - namespace tut { struct aldiffview_data @@ -70,6 +65,9 @@ namespace tut each->die(); } } + // Reaped now, as the next frame would: a test that empties the + // graveyard itself then finds only its own views there. + LLMortician::updateClass(); } ALDiffView& make(const char* left, const char* right, bool inline_view = false) @@ -876,7 +874,8 @@ namespace tut hp.rect = LLRect(0, 1080, 1920, 0); hp.mouse_opaque = false; LLMenuHolderGL* holder = LLUICtrlFactory::create(hp); - LLMenuGL::sMenuContainer = holder; + std::optional menus; + menus.emplace(holder); press(d, "ignore"); LLContextMenu* menu = holder->findChild("menu_diff_ignore"); ensure("shown", menu != nullptr); @@ -893,7 +892,7 @@ namespace tut d.setIgnore("blank_lines", true); ensure_equals("a comment reworded, a blank line gone and blanks at an end: no change", d.changeCount(), 0); ensure("lit", ALViewType::as(d.bar()->getChild("ignore"))->getToggleState()); - LLMenuGL::sMenuContainer = nullptr; + menus.reset(); LLMortician::updateClass(); delete holder; } @@ -1466,7 +1465,7 @@ namespace tut ++typed; return nullptr; }); - LLKeyboard::setStringTranslatorFunc([](std::string_view name) { return std::string(name); }); + ll_test::KeyNamesScope key_names([](std::string_view name) { return std::string(name); }); const auto tip = [&d](const char* name) { return d.bar()->getChild(name)->getToolTip(); }; const auto said = [](KEY key) { return LLKeyboard::stringFromAccelerator(MASK_ALT, key); }; ensure("each key said on its button's tip", diff --git a/indra/llui/tests/aldockpanel_test.cpp b/indra/llui/tests/aldockpanel_test.cpp index bcdfb1121f7..62a3006139b 100644 --- a/indra/llui/tests/aldockpanel_test.cpp +++ b/indra/llui/tests/aldockpanel_test.cpp @@ -35,6 +35,7 @@ #include "llmortician.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -42,20 +43,38 @@ #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gDockTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gDockTestAnonName; -} - namespace tut { struct aldockpanel_data { ll_test::HeadlessUI& ui = ll_test::HeadlessUI::get(); + // Focus and capture as the test found them, whatever it leaves + // them on. + ll_test::FocusScope focus; + + // A pane still out is put back, its window closed as test 4 shows + // that does, and each test's dead views reaped as it ends, as the + // next frame would: test 12 empties the graveyard itself, and then + // finds only its own views there. + ~aldockpanel_data() + { + std::vector> out; + for (LLView* child : *gFloaterView->getChildList()) + { + if (ALPanelFloater* window = child->as(); window && !window->isDead()) + { + out.push_back(window->getHandle()); + } + } + for (const LLHandle& handle : out) + { + if (LLFloater* window = handle.get()) + { + window->closeFloater(); + } + } + LLMortician::updateClass(); + } // A window with a stack of two panes across it, the way a tool is // laid out: a side pane of a fixed width and a middle that takes diff --git a/indra/llui/tests/alemptystate_test.cpp b/indra/llui/tests/alemptystate_test.cpp index 1d0fd7ce93e..55e75c426b3 100644 --- a/indra/llui/tests/alemptystate_test.cpp +++ b/indra/llui/tests/alemptystate_test.cpp @@ -36,15 +36,6 @@ #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gEmptyTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gEmptyTestAnonName; -} - namespace tut { struct alemptystate_data diff --git a/indra/llui/tests/alfindbar_test.cpp b/indra/llui/tests/alfindbar_test.cpp index 3b110817542..5814ab691ea 100644 --- a/indra/llui/tests/alfindbar_test.cpp +++ b/indra/llui/tests/alfindbar_test.cpp @@ -31,16 +31,10 @@ #include "llkeyboard.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" -class LLAvatarName; -const std::string gFindBarTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gFindBarTestAnonName; -} - namespace tut { struct alfindbar_data @@ -129,7 +123,7 @@ namespace tut ensure("another letter is not the bar's", !bar->handleKeyHere('Q', toggle)); ensure("nor the letter with another mask", !bar->handleKeyHere('C', MASK_SHIFT)); // The viewer gives the keys their names; here, as they are. - LLKeyboard::setStringTranslatorFunc([](std::string_view name) { return std::string(name); }); + ll_test::KeyNamesScope key_names([](std::string_view name) { return std::string(name); }); const std::string tip = bar->getChild("match_case")->getToolTip(); ensure("the tip says the key: " + tip, tip.find(LLKeyboard::stringFromAccelerator(toggle, 'C')) != std::string::npos); bar->die(); @@ -163,7 +157,7 @@ namespace tut bar->setCount(-1, 0, std::string()); field->handleKey(KEY_RETURN, replace_all, false); ensure_equals("with nothing found, nothing", every, 1); - LLKeyboard::setStringTranslatorFunc([](std::string_view name) { return std::string(name); }); + ll_test::KeyNamesScope key_names([](std::string_view name) { return std::string(name); }); const std::string tip = bar->getChild("replace_all")->getToolTip(); ensure("the tip says the key: " + tip, tip.find(LLKeyboard::stringFromAccelerator(replace_all, KEY_RETURN)) != std::string::npos); bar->setCount(0, 3, std::string(), false, 1); diff --git a/indra/llui/tests/alflagsfield_test.cpp b/indra/llui/tests/alflagsfield_test.cpp index b49210a20ca..2211c284cb4 100644 --- a/indra/llui/tests/alflagsfield_test.cpp +++ b/indra/llui/tests/alflagsfield_test.cpp @@ -33,13 +33,6 @@ #include "../test/lltut.h" -class LLAvatarName; -const std::string gFlagsFieldTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gFlagsFieldTestAnonName; -} - namespace tut { struct alflagsfield_data diff --git a/indra/llui/tests/alfollowscontrol_test.cpp b/indra/llui/tests/alfollowscontrol_test.cpp index 6368f8a80a4..b906adaed7a 100644 --- a/indra/llui/tests/alfollowscontrol_test.cpp +++ b/indra/llui/tests/alfollowscontrol_test.cpp @@ -35,15 +35,6 @@ #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gFollowsTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gFollowsTestAnonName; -} - namespace tut { struct alfollowscontrol_data diff --git a/indra/llui/tests/alfontfield_test.cpp b/indra/llui/tests/alfontfield_test.cpp index 6f1d6e693ad..78a1b9f8cb7 100644 --- a/indra/llui/tests/alfontfield_test.cpp +++ b/indra/llui/tests/alfontfield_test.cpp @@ -39,21 +39,28 @@ #include "llmortician.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" -class LLAvatarName; -const std::string gFontFieldTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gFontFieldTestAnonName; -} - namespace tut { struct alfontfield_data { ll_test::HeadlessUI& ui = ll_test::HeadlessUI::get(); + // Focus and capture as the test found them, whatever it leaves + // them on. + ll_test::FocusScope focus; + + // Each test's popovers escaped and its dead views reaped as it ends, + // as the next frame would: a test that empties the graveyard itself + // then finds only its own views there, and a popover left open + // holds the keys and is what the next test would find showing. + ~alfontfield_data() + { + ll_test::escapePopovers(); + LLMortician::updateClass(); + } static ALFontField* make() { diff --git a/indra/llui/tests/alheadlessui_fixture.h b/indra/llui/tests/alheadlessui_fixture.h index 4f339d04ab4..a21687f6014 100644 --- a/indra/llui/tests/alheadlessui_fixture.h +++ b/indra/llui/tests/alheadlessui_fixture.h @@ -82,9 +82,19 @@ namespace ll_test public: // With `gl_textures`, the fonts put their glyphs in GL textures, for // a caller that has a context up and draws; the first call decides. + // A later call that asks for the other is an error rather than + // handed what it did not ask for: fonts without GL textures draw + // nothing, and fonts with them need the context to stay current, + // so tests of the two kinds cannot share a process. static HeadlessUI& get(bool gl_textures = false) { static HeadlessUI ui(gl_textures); + if (gl_textures != ui.mGLTextures) + { + LL_ERRS("HeadlessUI") << "HeadlessUI::get(gl_textures=" << gl_textures + << ") after the process's UI was made with gl_textures=" + << ui.mGLTextures << LL_ENDL; + } return ui; } @@ -98,7 +108,8 @@ namespace ll_test private: explicit HeadlessUI(bool gl_textures) : mDir(LLUI_TEST_APP_DIR), - mConfig("config") + mConfig("config"), + mGLTextures(gl_textures) { const std::string fonts_xml = mDir.add(std::string(LLUI_TEST_APP_DIR), "skins/default/xui/en/fonts.xml"); if (std::string(LLUI_TEST_APP_DIR).empty() || !LLFile::isfile(fonts_xml)) @@ -153,6 +164,7 @@ namespace ll_test UIDir mDir; LLControlGroup mConfig; + const bool mGLTextures; bool mOk { false }; }; diff --git a/indra/llui/tests/alhistorylist_test.cpp b/indra/llui/tests/alhistorylist_test.cpp index b5b8595c9c2..f083a33ef48 100644 --- a/indra/llui/tests/alhistorylist_test.cpp +++ b/indra/llui/tests/alhistorylist_test.cpp @@ -35,13 +35,6 @@ #include "../test/lltut.h" -class LLAvatarName; -const std::string gHistoryTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gHistoryTestAnonName; -} - namespace tut { struct alhistorylist_data diff --git a/indra/llui/tests/alimagefield_test.cpp b/indra/llui/tests/alimagefield_test.cpp index 4be4f14483a..2cee7790b31 100644 --- a/indra/llui/tests/alimagefield_test.cpp +++ b/indra/llui/tests/alimagefield_test.cpp @@ -36,21 +36,28 @@ #include "llmortician.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" -class LLAvatarName; -const std::string gImageFieldTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gImageFieldTestAnonName; -} - namespace tut { struct alimagefield_data { ll_test::HeadlessUI& ui = ll_test::HeadlessUI::get(); + // Focus and capture as the test found them, whatever it leaves + // them on. + ll_test::FocusScope focus; + + // Each test's popovers escaped and its dead views reaped as it ends, + // as the next frame would: a test that empties the graveyard itself + // then finds only its own views there, and a popover left open + // holds the keys and is what the next test would find showing. + ~alimagefield_data() + { + ll_test::escapePopovers(); + LLMortician::updateClass(); + } static ALImageField* make() { diff --git a/indra/llui/tests/aljumpbar_test.cpp b/indra/llui/tests/aljumpbar_test.cpp index a5c740957d7..5ed95542a5a 100644 --- a/indra/llui/tests/aljumpbar_test.cpp +++ b/indra/llui/tests/aljumpbar_test.cpp @@ -38,13 +38,6 @@ #include "../test/lltut.h" -class LLAvatarName; -const std::string gJumpTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gJumpTestAnonName; -} - namespace tut { struct aljumpbar_data diff --git a/indra/llui/tests/alkeycapture_test.cpp b/indra/llui/tests/alkeycapture_test.cpp index 4137b75a4e3..51ce735c2c9 100644 --- a/indra/llui/tests/alkeycapture_test.cpp +++ b/indra/llui/tests/alkeycapture_test.cpp @@ -36,19 +36,13 @@ #include "llkeyboard.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" #include #include -class LLAvatarName; -const std::string gKeyCaptureTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gKeyCaptureTestAnonName; -} - namespace tut { struct alkeycapture_data @@ -62,12 +56,24 @@ namespace tut std::vector chosen; std::vector asked; + // The keys named as they are, for these tests and no others. + ll_test::KeyNamesScope key_names{ [](std::string_view name) { return std::string(name); } }; + std::vector> anchors; - alkeycapture_data() { LLKeyboard::setStringTranslatorFunc([](std::string_view name) { return std::string(name); }); } ~alkeycapture_data() { ALKeyChords::stop(); gFocusMgr.setKeyboardFocus(nullptr); + // What each capture was shown over, out of the floater view + // every later test builds its floaters in. + for (const LLHandle& handle : anchors) + { + if (LLView* view = handle.get()) + { + gFloaterView->removeChild(view); + view->die(); + } + } } LLPanel* anchor() @@ -77,6 +83,7 @@ namespace tut p.rect = LLRect(200, 320, 320, 300); LLPanel* view = LLUICtrlFactory::create(p); gFloaterView->addChild(view); + anchors.push_back(view->getHandle()); return view; } diff --git a/indra/llui/tests/almenuslot_test.cpp b/indra/llui/tests/almenuslot_test.cpp index fc8df030f76..de80e60ac56 100644 --- a/indra/llui/tests/almenuslot_test.cpp +++ b/indra/llui/tests/almenuslot_test.cpp @@ -37,19 +37,14 @@ #include "../test/lltut.h" -class LLAvatarName; -const std::string gMenuSlotTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gMenuSlotTestAnonName; -} - namespace tut { struct almenuslot_data { ll_test::HeadlessUI& ui = ll_test::HeadlessUI::get(); LLMenuHolderGL* holder = nullptr; + // Put back when the test ends, rather than left null for the next. + LLMenuHolderGL* container_before = LLMenuGL::sMenuContainer; almenuslot_data() { @@ -68,7 +63,7 @@ namespace tut ~almenuslot_data() { - LLMenuGL::sMenuContainer = nullptr; + LLMenuGL::sMenuContainer = container_before; LLMortician::updateClass(); delete holder; } diff --git a/indra/llui/tests/aloffsetpad_test.cpp b/indra/llui/tests/aloffsetpad_test.cpp index 0b9778f1163..38bbd3fdf93 100644 --- a/indra/llui/tests/aloffsetpad_test.cpp +++ b/indra/llui/tests/aloffsetpad_test.cpp @@ -31,25 +31,20 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gOffsetPadTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gOffsetPadTestAnonName; -} - namespace tut { struct aloffsetpad_data { ll_test::HeadlessUI& ui = ll_test::HeadlessUI::get(); + // Focus and capture as the test found them, whatever it leaves + // them on. + ll_test::FocusScope focus; // 70 tall: the pad is the 66-pixel square at the left, its centre // at (35, 35), reaching 32 either way. diff --git a/indra/llui/tests/aloutputview_test.cpp b/indra/llui/tests/aloutputview_test.cpp index 3cbeb15105f..a4320825c4a 100644 --- a/indra/llui/tests/aloutputview_test.cpp +++ b/indra/llui/tests/aloutputview_test.cpp @@ -31,23 +31,20 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" #include -class LLAvatarName; -const std::string gOutputTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gOutputTestAnonName; -} - namespace tut { struct aloutputview_data { ll_test::HeadlessUI& ui = ll_test::HeadlessUI::get(); + // Focus and capture as the test found them, whatever it leaves + // them on. + ll_test::FocusScope focus; ALOutputView* view = nullptr; ~aloutputview_data() diff --git a/indra/llui/tests/alpanefolds_test.cpp b/indra/llui/tests/alpanefolds_test.cpp index 2ac55e564f9..9da610e49cc 100644 --- a/indra/llui/tests/alpanefolds_test.cpp +++ b/indra/llui/tests/alpanefolds_test.cpp @@ -38,15 +38,6 @@ #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gPaneFoldsTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gPaneFoldsTestAnonName; -} - namespace tut { struct alpanefolds_data diff --git a/indra/llui/tests/alpanelist_test.cpp b/indra/llui/tests/alpanelist_test.cpp index 84db8ce7d13..f8b2c401e9f 100644 --- a/indra/llui/tests/alpanelist_test.cpp +++ b/indra/llui/tests/alpanelist_test.cpp @@ -40,13 +40,6 @@ #include #include -class LLAvatarName; -const std::string gPaneListTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gPaneListTestAnonName; -} - namespace tut { struct alpanelist_data diff --git a/indra/llui/tests/alpopover_test.cpp b/indra/llui/tests/alpopover_test.cpp index 3713badd40c..53ba3dcead4 100644 --- a/indra/llui/tests/alpopover_test.cpp +++ b/indra/llui/tests/alpopover_test.cpp @@ -37,13 +37,6 @@ #include -class LLAvatarName; -const std::string gPopoverTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gPopoverTestAnonName; -} - namespace tut { struct alpopover_data @@ -283,20 +276,25 @@ namespace tut { Remembering(const LLFloater::Params& p) : ALPopover(p) {} }; + // Kinds of its own each time it runs: the sizes are kept for the + // process, and a kind seen before would open at what it was left at. + static S32 runs = 0; + const std::string kind = "popover_test_kind_" + std::to_string(++runs); + const std::string other_kind = "popover_test_other_" + std::to_string(runs); LLPanel* over = anchor(); - ALPopover* first = new Remembering(ALPopover::paramsRemembered("popover_test_kind", 200, 120)); + ALPopover* first = new Remembering(ALPopover::paramsRemembered(kind, 200, 120)); first->openBeside(over); ensure_equals("opens at the size given", first->getRect().getWidth(), 200); first->reshape(260, 150); first->closeFloater(); - ALPopover* second = new Remembering(ALPopover::paramsRemembered("popover_test_kind", 200, 120)); + ALPopover* second = new Remembering(ALPopover::paramsRemembered(kind, 200, 120)); second->openBeside(over); ensure_equals("the next of its kind opens as wide as the last was left", second->getRect().getWidth(), 260); ensure_equals("and as tall", second->getRect().getHeight(), 150); second->closeFloater(); - ALPopover* other = new Remembering(ALPopover::paramsRemembered("popover_test_other", 200, 120)); + ALPopover* other = new Remembering(ALPopover::paramsRemembered(other_kind, 200, 120)); other->openBeside(over); ensure_equals("another kind is not told", other->getRect().getWidth(), 200); other->closeFloater(); diff --git a/indra/llui/tests/alpropertygrid_test.cpp b/indra/llui/tests/alpropertygrid_test.cpp index 9af623951ce..1a2fa17a376 100644 --- a/indra/llui/tests/alpropertygrid_test.cpp +++ b/indra/llui/tests/alpropertygrid_test.cpp @@ -46,15 +46,6 @@ #include #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gPropertyGridTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gPropertyGridTestAnonName; -} - namespace tut { struct alpropertygrid_data diff --git a/indra/llui/tests/alquickopen_test.cpp b/indra/llui/tests/alquickopen_test.cpp index 967c387a0cb..f8b6d6608de 100644 --- a/indra/llui/tests/alquickopen_test.cpp +++ b/indra/llui/tests/alquickopen_test.cpp @@ -30,32 +30,25 @@ #include "llfontgl.h" #include "alpopover.h" -#include "../lllineeditor.h" -#include "../llscrolllistctrl.h" -#include "../lluictrlfactory.h" - -#include "alheadlessui_fixture.h" - #include "../llfloater.h" #include "../llfocusmgr.h" #include "../lllineeditor.h" +#include "../llscrolllistctrl.h" #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" -class LLAvatarName; -const std::string gQuickTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gQuickTestAnonName; -} - namespace tut { struct alquickopen_data { + // Focus and capture as the test found them, whatever it leaves them + // on. + ll_test::FocusScope focus; + static std::vector files() { std::vector made; @@ -79,14 +72,6 @@ namespace tut } }; - // Something in the widgets' own library asks the world for this. - class LLAvatarName; - const std::string gQuickTestAnonName("Anon"); - const std::string& rlvGetAnonym(const LLAvatarName& av_name) - { - return gQuickTestAnonName; - } - typedef test_group alquickopen_test; typedef alquickopen_test::object alquickopen_object; tut::alquickopen_test alquickopen_testgroup("alquickopen"); diff --git a/indra/llui/tests/alscopebar_test.cpp b/indra/llui/tests/alscopebar_test.cpp index 3b26fd126b5..02107abbacf 100644 --- a/indra/llui/tests/alscopebar_test.cpp +++ b/indra/llui/tests/alscopebar_test.cpp @@ -37,13 +37,6 @@ #include "../test/lltut.h" -class LLAvatarName; -const std::string gScopeTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gScopeTestAnonName; -} - namespace tut { struct alscopebar_data diff --git a/indra/llui/tests/alsettingrow_test.cpp b/indra/llui/tests/alsettingrow_test.cpp index af6914ecd6c..1e32b439cb8 100644 --- a/indra/llui/tests/alsettingrow_test.cpp +++ b/indra/llui/tests/alsettingrow_test.cpp @@ -43,26 +43,38 @@ #include "v3math.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" #include #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gSettingRowTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gSettingRowTestAnonName; -} - namespace tut { struct alsettingrow_data { ll_test::HeadlessUI& ui = ll_test::HeadlessUI::get(); + // The settings a test asked for, put back to their defaults when it + // ends as well as when the next asks. + ll_test::SettingsScope settings; + bool gave_words = false; + + // What the reset button's words were given, taken back: nothing else + // in these tests gives LLTrans strings, and it has no way to hand + // back what it held, so it is left as empty as it was found. + ~alsettingrow_data() + { + if (gave_words) + { + const std::string none = ""; + LLXMLNodePtr node; + if (LLXMLNode::parseBuffer(none.data(), none.size(), node)) + { + LLTrans::parseStrings(node, {}); + } + } + } static LLControlGroup& config() { @@ -71,7 +83,7 @@ namespace tut // One setting per test, put back to its default, so no test sees what // another left behind. - static LLControlVariable* setting(const std::string& name, F32 value) + LLControlVariable* setting(const std::string& name, F32 value) { LLControlVariable* control = config().getControl(name).get(); if (!control) @@ -79,7 +91,7 @@ namespace tut control = config().declareF32(name, value, std::string("A setting row test value")); } control->resetToDefault(false); - return control; + return settings.keep(control); } static ALSettingRow::Params params(const std::string& control_name, F32 min_value = 0.f, @@ -108,20 +120,17 @@ namespace tut } // What the reset button says, as the viewer's strings have it: - // nothing here loads them. - static void resetWords() + // nothing here loads them. Given for each test that asks, since + // parsing strings anywhere replaces every string there was. + void resetWords() { - static bool given = false; - if (!given) - { - const std::string source = "Reset to [VALUE]"; - LLXMLNodePtr node; - given = LLXMLNode::parseBuffer(source.data(), source.size(), node) && LLTrans::parseStrings(node, {}); - } + const std::string source = "Reset to [VALUE]"; + LLXMLNodePtr node; + gave_words = LLXMLNode::parseBuffer(source.data(), source.size(), node) && LLTrans::parseStrings(node, {}); } // A vector setting, put back to its default. - static LLControlVariable* vector(const std::string& name, const LLVector3& value) + LLControlVariable* vector(const std::string& name, const LLVector3& value) { LLControlVariable* control = config().getControl(name).get(); if (!control) @@ -129,7 +138,7 @@ namespace tut control = config().declareVec3(name, value, std::string("A setting row test vector")); } control->resetToDefault(false); - return control; + return settings.keep(control); } static bool sameParts(const LLSD& value, F64 x, F64 y, F64 z) diff --git a/indra/llui/tests/alspecimenlist_test.cpp b/indra/llui/tests/alspecimenlist_test.cpp index 04ed6429cf2..d6ce8dd2e49 100644 --- a/indra/llui/tests/alspecimenlist_test.cpp +++ b/indra/llui/tests/alspecimenlist_test.cpp @@ -34,13 +34,6 @@ #include "../test/lltut.h" -class LLAvatarName; -const std::string gSpecimenTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gSpecimenTestAnonName; -} - namespace tut { struct alspecimenlist_data diff --git a/indra/llui/tests/alstudiofloater_test.cpp b/indra/llui/tests/alstudiofloater_test.cpp index 7369d902980..09eb42cca46 100644 --- a/indra/llui/tests/alstudiofloater_test.cpp +++ b/indra/llui/tests/alstudiofloater_test.cpp @@ -40,19 +40,13 @@ #include "llcontrol.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" #include #include -class LLAvatarName; -const std::string gStudioFloaterTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gStudioFloaterTestAnonName; -} - namespace { // A studio with nothing in it but what every studio has. @@ -251,6 +245,10 @@ namespace tut { config->declareLLSD(setting, LLSD(), "a test studio's state", LLControlVariable::PERSIST_NO); } + // Emptied when the test ends, so a studio made later under the same + // setting does not open where this one was saved. + ll_test::SettingsScope settings; + settings.keep(config->getControl(setting).get()); TestStudio* window = studio(setting); window->setCanMinimize(true); const LLRect whole = window->getRect(); @@ -464,7 +462,7 @@ namespace tut void alstudiofloater_object::test<10>() { set_test_name("two keys written apart; a menu item shows keys it does not answer to, and a key it answers to in their place"); - LLKeyboard::setStringTranslatorFunc([](std::string_view name) { return std::string(name); }); + ll_test::KeyNamesScope key_names([](std::string_view name) { return std::string(name); }); const ALKeyChord two{ 'S', MASK_NONE, 'K', MASK_CONTROL }; ensure_equals("the two", two.describe(), LLKeyboard::stringFromAccelerator(MASK_CONTROL, 'K') + " " + LLKeyboard::stringFromAccelerator(MASK_NONE, 'S')); ensure_equals("one", ALKeyChord{ 'S', MASK_CONTROL }.describe(), LLKeyboard::stringFromAccelerator(MASK_CONTROL, 'S')); diff --git a/indra/llui/tests/alsurface_gl_test.cpp b/indra/llui/tests/alsurface_gl_test.cpp index d9c1eab33cd..01448b020ca 100644 --- a/indra/llui/tests/alsurface_gl_test.cpp +++ b/indra/llui/tests/alsurface_gl_test.cpp @@ -26,7 +26,7 @@ #include "llglheaders.h" -#include "../../llrender/tests/llheadlessgl_fixture.h" +#include "aluigl_fixture.h" #include "alsurface.h" #include "altabstrip.h" @@ -41,29 +41,16 @@ #include #include -class LLAvatarName; -const std::string gSurfaceGLTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gSurfaceGLTestAnonName; -} - namespace { constexpr S32 W = ll_test::HeadlessGL::WIDTH; constexpr S32 H = ll_test::HeadlessGL::HEIGHT; - ll_test::HeadlessGL& gl() - { - static ll_test::HeadlessGL instance(true, true, true, /*needs_render=*/true); - return instance; - } - // What is drawn over black, read back: each pixel's red, bottom row // first. std::vector drawn(const std::function& draw) { - gl().clearFramebuffer(); + ll_test::uiGL().clearFramebuffer(); glEnable(GL_BLEND); gGL.setSceneBlendType(LLRender::BT_ALPHA); draw(); @@ -88,7 +75,7 @@ namespace tut { alsurface_gl_data() { - gl(); + ll_test::uiGL(); ll_test::installWhiteTexture(); } }; diff --git a/indra/llui/tests/alsurface_test.cpp b/indra/llui/tests/alsurface_test.cpp index 64f85df2ea5..2df26c7d588 100644 --- a/indra/llui/tests/alsurface_test.cpp +++ b/indra/llui/tests/alsurface_test.cpp @@ -29,19 +29,13 @@ #include "../lluicolortable.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" #include #include -class LLAvatarName; -const std::string gSurfaceTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gSurfaceTestAnonName; -} - namespace tut { struct alsurface_data @@ -96,8 +90,9 @@ namespace tut { skip("no UI: LLUI_TEST_APP_DIR does not point at the source tree"); } - // The skin's, told here before any part is first asked for: nothing - // loads a skin's colours in a test. + // The skin's, told here: nothing loads a skin's colours in a test. + // Taken back after, since every surface drawn later would read them. + ll_test::ColorTableScope colours; LLUIColorTable& table = LLUIColorTable::instance(); table.setColor("DefaultShadowLight", LLColor4(0.1f, 0.1f, 0.1f, 1.f)); table.setColor("LabelDisabledColor", LLColor4(0.2f, 0.3f, 0.4f, 1.f)); diff --git a/indra/llui/tests/alsyntaxgrammars_test.cpp b/indra/llui/tests/alsyntaxgrammars_test.cpp index 16373145c89..22bbd4e51e1 100644 --- a/indra/llui/tests/alsyntaxgrammars_test.cpp +++ b/indra/llui/tests/alsyntaxgrammars_test.cpp @@ -35,16 +35,6 @@ #include #include -// llui reaches the viewer for this one, and linking ALTextView, whose colour -// names are tested here, pulls the object that calls it. Nothing under test -// goes near it. -class LLAvatarName; -const std::string gSyntaxGrammarsTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gSyntaxGrammarsTestAnonName; -} - namespace tut { struct alsyntaxgrammars_data diff --git a/indra/llui/tests/altabstrip_test.cpp b/indra/llui/tests/altabstrip_test.cpp index 771ae9f198c..382ea3a80d8 100644 --- a/indra/llui/tests/altabstrip_test.cpp +++ b/indra/llui/tests/altabstrip_test.cpp @@ -33,21 +33,18 @@ #include "llfontgl.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" -class LLAvatarName; -const std::string gTabStripTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gTabStripTestAnonName; -} - namespace tut { struct altabstrip_data { ll_test::HeadlessUI& ui = ll_test::HeadlessUI::get(); + // Focus and capture as the test found them, whatever it leaves + // them on. + ll_test::FocusScope focus; static constexpr S32 WIDTH = 300; static constexpr S32 HEIGHT = 24; @@ -639,6 +636,7 @@ namespace tut strip->handleMouseUp(strip->rectOf(2).getCenterX() + 10, second.getCenterY(), MASK_NONE); ensure_equals("not torn", torn.size(), size_t(1)); ensure("moved along instead", !orders.empty() && orders.back() == std::vector({ "a", "c", "b" })); + strip->die(); } // With no tab at all, an arrow walks nowhere and is still the strip's: @@ -659,6 +657,7 @@ namespace tut ensure("another key not", !strip->handleKeyHere('X', MASK_NONE)); strip->setTabs(three(), ""); ensure("with tabs, the walk starts", strip->handleKeyHere(KEY_RIGHT, MASK_NONE) && strip->chosen() == "a"); + strip->die(); } // One tab made again by its value: its dot and its tip change where it diff --git a/indra/llui/tests/altesttempdir.h b/indra/llui/tests/altesttempdir.h new file mode 100644 index 00000000000..294af9b4484 --- /dev/null +++ b/indra/llui/tests/altesttempdir.h @@ -0,0 +1,90 @@ +/** + * @file altesttempdir.h + * @brief A directory of a test's own, removed when it is done with + * + * $LicenseInfo:firstyear=2026&license=viewerlgpl$ + * Second Life 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_ALTESTTEMPDIR_H +#define AL_ALTESTTEMPDIR_H + +#include "linden_common.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ll_test +{ + // A new, empty directory under the temporary one, named by the caller + // and a random suffix, and removed with everything in it when this goes. + // A fixed name -- or one under the working directory, which is what + // LLDir's temp directory is under the headless UI -- is shared by two + // runs of the same test at once, and by a run and what a crashed one + // left. + class TempDir + { + public: + explicit TempDir(std::string_view prefix) + { + // Where CI's runner says its jobs' temporary files go, as the + // shared test harness has it, or the system's. + const char* runner_temp = std::getenv("RUNNER_TEMP"); + const std::filesystem::path base = (runner_temp && *runner_temp) ? std::filesystem::path(runner_temp) + : std::filesystem::temp_directory_path(); + std::random_device random; + for (S32 tries = 0; tries < 100; ++tries) + { + char suffix[24]; + std::snprintf(suffix, sizeof(suffix), "_%08x%08x", static_cast(random()), static_cast(random())); + const std::filesystem::path path = base / (std::string(prefix) + suffix); + std::error_code ec; + if (std::filesystem::create_directory(path, ec)) + { + mPath = path; + return; + } + } + throw std::runtime_error("no temporary directory could be made under " + base.string()); + } + + ~TempDir() + { + std::error_code ec; + std::filesystem::remove_all(mPath, ec); + } + + const std::filesystem::path& path() const { return mPath; } + std::string string() const { return mPath.string(); } + + TempDir(const TempDir&) = delete; + TempDir& operator=(const TempDir&) = delete; + + private: + std::filesystem::path mPath; + }; +} + +#endif // AL_ALTESTTEMPDIR_H diff --git a/indra/llui/tests/altextdraw_bench.cpp b/indra/llui/tests/altextdraw_bench.cpp index 9d5f72b5482..45bcf7c8bb2 100644 --- a/indra/llui/tests/altextdraw_bench.cpp +++ b/indra/llui/tests/altextdraw_bench.cpp @@ -59,13 +59,6 @@ #include #include -class LLAvatarName; -const std::string gBenchAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gBenchAnonName; -} - #if defined(LL_RELEASE) namespace { diff --git a/indra/llui/tests/altextengine_bench.cpp b/indra/llui/tests/altextengine_bench.cpp index 7e9307eea1d..2045b3e2fed 100644 --- a/indra/llui/tests/altextengine_bench.cpp +++ b/indra/llui/tests/altextengine_bench.cpp @@ -60,13 +60,6 @@ #include #include -class LLAvatarName; -const std::string gBenchAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gBenchAnonName; -} - // Only an optimised build measures, and only it has a use for these: an // unoptimised one would say they are unused. #if defined(LL_RELEASE) diff --git a/indra/llui/tests/altextfind_test.cpp b/indra/llui/tests/altextfind_test.cpp index d89764d0e9f..9d0c7939141 100644 --- a/indra/llui/tests/altextfind_test.cpp +++ b/indra/llui/tests/altextfind_test.cpp @@ -35,6 +35,9 @@ namespace tut { struct altextfind_data { + // The worker a long text is looked through on, closed and waited + // for as each test ends: nothing of a test's own left running. + ~altextfind_data() { ALTextFind::closeWorker(); } }; typedef test_group altextfind_group; typedef altextfind_group::object altextfind_object; diff --git a/indra/llui/tests/altextgotoline_test.cpp b/indra/llui/tests/altextgotoline_test.cpp index 7922888bfd3..280659eaf63 100644 --- a/indra/llui/tests/altextgotoline_test.cpp +++ b/indra/llui/tests/altextgotoline_test.cpp @@ -34,6 +34,7 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -41,15 +42,10 @@ #include #include -class LLAvatarName; -const std::string gGoToLineTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gGoToLineTestAnonName; -} - -// A place, as a failed check says it. -std::ostream& operator<<(std::ostream& out, const ALTextPos& pos) +// A place, as a failed check says it. Static rather than in the anonymous +// namespace: tut's ensure_equals finds it by argument-dependent lookup, +// which looks in ALTextPos's namespace and not in a nested one. +static std::ostream& operator<<(std::ostream& out, const ALTextPos& pos) { return out << pos.line << ":" << pos.column; } @@ -69,6 +65,9 @@ namespace tut struct altextgotoline_data { ll_test::HeadlessUI& ui = ll_test::HeadlessUI::get(); + // Focus and capture as the test found them, whatever it leaves + // them on. + ll_test::FocusScope focus; Window* window = nullptr; ALTextView* view = nullptr; ALQuickAsk asker; diff --git a/indra/llui/tests/altextlayout_test.cpp b/indra/llui/tests/altextlayout_test.cpp index cfef07a5612..5eaa2d7ef80 100644 --- a/indra/llui/tests/altextlayout_test.cpp +++ b/indra/llui/tests/altextlayout_test.cpp @@ -37,15 +37,6 @@ #include #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gLayoutTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gLayoutTestAnonName; -} - namespace tut { struct altextlayout_data @@ -573,6 +564,9 @@ namespace tut text += "value" + std::to_string(i % 97) + " = f(x, [y, z]) + "; } ready(text.c_str()); + // Counted from empty: the cache is the process's, and one an earlier + // test had filled would trade a piece for a piece and not grow. + ALFontShaping::clearCache(); const size_t before = ALFontShaping::cacheSize(); const ALTextLayout::Line& line = layout.line(0); ensure("shaped in pieces", ALFontShaping::cacheSize() > before + 10); diff --git a/indra/llui/tests/altextview_gl_test.cpp b/indra/llui/tests/altextview_gl_test.cpp index d211eeb1ba9..ce363f2ac43 100644 --- a/indra/llui/tests/altextview_gl_test.cpp +++ b/indra/llui/tests/altextview_gl_test.cpp @@ -26,7 +26,7 @@ #include "llglheaders.h" -#include "../../llrender/tests/llheadlessgl_fixture.h" +#include "aluigl_fixture.h" #include "alcodeeditor.h" #include "altextruler.h" @@ -46,13 +46,6 @@ #include #include -class LLAvatarName; -const std::string gTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gTestAnonName; -} - namespace { // The drawing the text view does, from outside it. @@ -65,17 +58,11 @@ namespace constexpr S32 W = ll_test::HeadlessGL::WIDTH; constexpr S32 H = ll_test::HeadlessGL::HEIGHT; - ll_test::HeadlessGL& gl() - { - static ll_test::HeadlessGL instance(true, true, true, /*needs_render=*/true); - return instance; - } - // White drawn over black, blended by its alpha, and read back: how // much of each pixel it covered, 0 to 255, bottom row first. std::vector coverage(const std::function& draw) { - gl().clearFramebuffer(); + ll_test::uiGL().clearFramebuffer(); glEnable(GL_BLEND); gGL.setSceneBlendType(LLRender::BT_ALPHA); draw(); @@ -122,7 +109,7 @@ namespace // what is drawn past its edges lands where it can be seen. std::vector drawnAt(LLView& view) { - gl().clearFramebuffer(); + ll_test::uiGL().clearFramebuffer(); glEnable(GL_BLEND); gGL.setSceneBlendType(LLRender::BT_ALPHA); LLUI::pushMatrix(); @@ -158,7 +145,7 @@ namespace tut { altextview_gl_data() { - gl(); + ll_test::uiGL(); ll_test::installWhiteTexture(); } @@ -335,7 +322,7 @@ namespace tut const S32 map_left = W - view->scrollMapWidth(); // Every channel of the map's pixels, as the view draws them. const auto map_of = [&]() { - gl().clearFramebuffer(); + ll_test::uiGL().clearFramebuffer(); glEnable(GL_BLEND); gGL.setSceneBlendType(LLRender::BT_ALPHA); view->draw(); @@ -393,7 +380,7 @@ namespace tut editor->setSelectionColor(LLUIColor(LLColor4(0.2f, 0.4f, 0.9f, 0.6f))); const auto frame = [&]() { - gl().clearFramebuffer(); + ll_test::uiGL().clearFramebuffer(); glEnable(GL_BLEND); gGL.setSceneBlendType(LLRender::BT_ALPHA); editor->draw(); @@ -493,7 +480,7 @@ namespace tut ALTextRuler* map = view->findChild("ruler"); // Every pixel of the view, as it draws. const auto drawn = [&]() { - gl().clearFramebuffer(); + ll_test::uiGL().clearFramebuffer(); glEnable(GL_BLEND); gGL.setSceneBlendType(LLRender::BT_ALPHA); view->draw(); @@ -594,7 +581,7 @@ namespace tut view->setFocus(true); const auto frame = [&]() { - gl().clearFramebuffer(); + ll_test::uiGL().clearFramebuffer(); glEnable(GL_BLEND); gGL.setSceneBlendType(LLRender::BT_ALPHA); view->draw(); @@ -689,7 +676,7 @@ namespace tut ALTextView* view = LLUICtrlFactory::create(p); view->setFont(LLFontGL::getFontMonospace()); const auto frame = [&]() { - gl().clearFramebuffer(); + ll_test::uiGL().clearFramebuffer(); glEnable(GL_BLEND); gGL.setSceneBlendType(LLRender::BT_ALPHA); view->draw(); @@ -951,7 +938,7 @@ namespace tut editor->setText(text); const S32 last = editor->document().lineCount() - 1; const auto frame = [&]() { - gl().clearFramebuffer(); + ll_test::uiGL().clearFramebuffer(); editor->draw(); gGL.flush(); glFinish(); @@ -999,7 +986,7 @@ namespace tut view->setFont(LLFontGL::getFontMonospace()); view->setWordWrap(true); const auto frame = [&]() { - gl().clearFramebuffer(); + ll_test::uiGL().clearFramebuffer(); glEnable(GL_BLEND); gGL.setSceneBlendType(LLRender::BT_ALPHA); view->draw(); @@ -1105,7 +1092,7 @@ namespace tut ALTextRuler* map = editor->findChild("ruler"); ensure("a map", map != nullptr); const auto drawn = [&]() { - gl().clearFramebuffer(); + ll_test::uiGL().clearFramebuffer(); glEnable(GL_BLEND); gGL.setSceneBlendType(LLRender::BT_ALPHA); editor->draw(); @@ -1180,7 +1167,7 @@ namespace tut std::vector map; for (S32 pass = 0; pass < 2; ++pass) { - gl().clearFramebuffer(); + ll_test::uiGL().clearFramebuffer(); glEnable(GL_BLEND); gGL.setSceneBlendType(LLRender::BT_ALPHA); view->draw(); @@ -1310,7 +1297,7 @@ namespace tut return indent; }); view->setSelection(ALTextRange(ALTextPos(1, 2), ALTextPos(2, 0))); - gl().clearFramebuffer(); + ll_test::uiGL().clearFramebuffer(); glEnable(GL_BLEND); gGL.setSceneBlendType(LLRender::BT_ALPHA); view->draw(); @@ -1356,7 +1343,7 @@ namespace tut keymap->asked = &asked; view->setModalKeymap(std::move(keymap)); const auto frame = [&]() { - gl().clearFramebuffer(); + ll_test::uiGL().clearFramebuffer(); view->draw(); gGL.flush(); glFinish(); diff --git a/indra/llui/tests/altextview_test.cpp b/indra/llui/tests/altextview_test.cpp index 3bad854ef3d..2eff75ad9f4 100644 --- a/indra/llui/tests/altextview_test.cpp +++ b/indra/llui/tests/altextview_test.cpp @@ -39,6 +39,7 @@ #include "llpreeditor.h" #include "alheadlessui_fixture.h" +#include "altextviewprobe.h" #include "../test/lltut.h" @@ -49,43 +50,6 @@ #include #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gViewTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gViewTestAnonName; -} - -namespace ll_test -{ - // What a test reaches inside the view for: how long Next Misspelling - // may check lines for, and its going on as the next frame would. - struct TextViewProbe - { - static void misspellingBudget(ALTextView& view, F32 seconds) { view.mMisspellingBudget = seconds; } - static bool seeking(const ALTextView& view) { return view.mMisspellingSought.has_value(); } - static void trimLayout(ALTextView& view) { view.trimLayout(); } - // Every selection, the main one among them, in the order they begin. - static std::vector selections(const ALTextView& view) { return view.selectionsInOrder(); } - static S32 heldMost() { return ALTextView::LAYOUT_HELD_MOST; } - // The rows and the gaps in sight, as the view walks them to draw. - static void visibleRows(ALTextView& view, const LLRect& text, const std::function& visit) { view.forEachVisibleRow(text, visit); } - static void visibleGaps(ALTextView& view, const LLRect& text, const std::function& visit) { view.forEachVisibleGap(text, visit); } - // What a frame does before it draws, of what a test reaches: - // Next Misspelling gone on with, and the primary selection offered. - static void nextFrame(ALTextView& view) - { - if (view.mMisspellingSought) - { - view.seekMisspelling(); - } - view.publishPrimary(); - } - }; -} - namespace tut { struct altextview_data @@ -752,7 +716,17 @@ namespace tut } const std::string agent = "secondlife:///app/agent/11111111-1111-1111-1111-111111111111/about"; const std::string object = "secondlife:///app/objectim/22222222-2222-2222-2222-222222222222?name=Thing"; - // Nothing installed: nothing changes. + // Nothing installed: nothing changes. And nothing left installed when + // the test ends, a failed check included, for the next to find. + struct Uninstalled + { + ~Uninstalled() + { + LLUrlAction::setIsFriendCallback(nullptr); + LLUrlAction::setIsObjectBlockedCallback(nullptr); + LLUrlAction::setIsObjectReachableCallback(nullptr); + } + } uninstalled; LLUrlAction::setIsFriendCallback(nullptr); LLUrlAction::setIsObjectBlockedCallback(nullptr); LLUrlAction::setIsObjectReachableCallback(nullptr); @@ -773,9 +747,6 @@ namespace tut menu->getChild("zoom_in")->setEnabled(true); LLUrlAction::adjustMenu(menu, object, true, true, false); ensure("the zoom item left alone when not asked", menu->getChild("zoom_in")->getEnabled()); - LLUrlAction::setIsFriendCallback(nullptr); - LLUrlAction::setIsObjectBlockedCallback(nullptr); - LLUrlAction::setIsObjectReachableCallback(nullptr); menu->die(); } template<> template<> @@ -1544,6 +1515,12 @@ namespace tut void altextview_object::test<50>() { set_test_name("find: no more matches than a list is any use as, said with a plus; a long text looked through once the query settles, not at each key"); + // The worker the long text is looked through on, closed and waited + // for when the test ends: nothing of its own left running. + struct WorkerClosed + { + ~WorkerClosed() { ALTextFind::closeWorker(); } + } worker_closed; std::string many; for (size_t i = 0; i < ALTextFind::LIMIT + 5; ++i) { @@ -3076,6 +3053,12 @@ namespace tut void altextview_object::test<98>() { set_test_name("Next over matches that are empty -- ^ at each line's start -- goes on from the current one at the caret, round the end; one the caret was put on is found first"); + // A pattern across lines is looked for on the worker; closed and + // waited for when the test ends. + struct WorkerClosed + { + ~WorkerClosed() { ALTextFind::closeWorker(); } + } worker_closed; #if LL_DARWIN constexpr MASK toggle = MASK_CONTROL | MASK_ALT; #else diff --git a/indra/llui/tests/altextviewprobe.h b/indra/llui/tests/altextviewprobe.h new file mode 100644 index 00000000000..626ac74cee5 --- /dev/null +++ b/indra/llui/tests/altextviewprobe.h @@ -0,0 +1,74 @@ +/** + * @file altextviewprobe.h + * @brief What the text view's tests reach inside it for + * + * $LicenseInfo:firstyear=2026&license=viewerlgpl$ + * Second Life 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_ALTEXTVIEWPROBE_H +#define AL_ALTEXTVIEWPROBE_H + +#include "altextview.h" + +#include "llrect.h" + +#include +#include + +namespace ll_test +{ + // ALTextView befriends this one name, so every test that reaches + // inside the view shares this one definition: two of them, different, + // in one binary would leave one test running the other's. + struct TextViewProbe + { + // How long Next Misspelling may check lines for, and whether it is + // still going. + static void misspellingBudget(ALTextView& view, F32 seconds) { view.mMisspellingBudget = seconds; } + static bool seeking(const ALTextView& view) { return view.mMisspellingSought.has_value(); } + static void trimLayout(ALTextView& view) { view.trimLayout(); } + // Every selection, the main one among them, in the order they begin. + static std::vector selections(const ALTextView& view) { return view.selectionsInOrder(); } + static S32 heldMost() { return ALTextView::LAYOUT_HELD_MOST; } + // The rows and the gaps in sight, as the view walks them to draw. + static void visibleRows(ALTextView& view, const LLRect& text, const std::function& visit) { view.forEachVisibleRow(text, visit); } + static void visibleGaps(ALTextView& view, const LLRect& text, const std::function& visit) { view.forEachVisibleGap(text, visit); } + // What a frame does before it draws, of what a test reaches: + // Next Misspelling gone on with, and the primary selection offered. + static void nextFrame(ALTextView& view) + { + if (view.mMisspellingSought) + { + view.seekMisspelling(); + } + view.publishPrimary(); + } + // The colours the view works out for a row's glyphs, as it draws them. + static std::vector colours(ALTextView& view, S32 line, const ALTextLayout::Row& row) + { + const ALTextLayout::Line& laid = view.layout().line(line); + view.colorRow(line, laid, row, 1.f); + view.tintRow(line, laid, row, 1.f, view.mColorScratch); + return view.mColorScratch; + } + }; +} + +#endif // AL_ALTEXTVIEWPROBE_H diff --git a/indra/llui/tests/aluigl_fixture.h b/indra/llui/tests/aluigl_fixture.h new file mode 100644 index 00000000000..2303441f989 --- /dev/null +++ b/indra/llui/tests/aluigl_fixture.h @@ -0,0 +1,45 @@ +/** + * @file aluigl_fixture.h + * @brief The one hidden window and GL context llui's GL tests draw into + * + * $LicenseInfo:firstyear=2026&license=viewerlgpl$ + * Second Life 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_ALUIGL_FIXTURE_H +#define AL_ALUIGL_FIXTURE_H + +#include "../../llrender/tests/llheadlessgl_fixture.h" + +namespace ll_test +{ + // A hidden window, its GL context current, and the 2D renderer and UI + // shader set up in it: made by the first llui GL test that asks, and + // kept for the process. Inline, so every test file in a binary shares + // the one: a second would make another window, take the context, put + // the renderer's singletons through their setup again and, at exit, + // quit SDL while the first was still alive. + inline HeadlessGL& uiGL() + { + static HeadlessGL instance(true, true, true, /*needs_render=*/true); + return instance; + } +} + +#endif // AL_ALUIGL_FIXTURE_H diff --git a/indra/llui/tests/aluistatescope.h b/indra/llui/tests/aluistatescope.h new file mode 100644 index 00000000000..f54b2b4e473 --- /dev/null +++ b/indra/llui/tests/aluistatescope.h @@ -0,0 +1,349 @@ +/** + * @file aluistatescope.h + * @brief What a UI test changes of the process, put back when it ends + * + * $LicenseInfo:firstyear=2026&license=viewerlgpl$ + * Second Life 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_ALUISTATESCOPE_H +#define AL_ALUISTATESCOPE_H + +#include "linden_common.h" + +#include "alcolorsheet.h" +#include "alpopover.h" +#include "../llfloater.h" +#include "../llfocusmgr.h" +#include "../llmenugl.h" +#include "../lluicolortable.h" + +#include "llcontrol.h" +#include "llkeyboard.h" +#include "llxmlnode.h" + +#include +#include +#include +#include + +// Each of these is a global or a singleton the next test reads as this one +// left it. Held in a test or its fixture, they put it back however the test +// ends, a failed check included: a check throws, and only a destructor runs +// after it. +namespace ll_test +{ + // gFloaterView, which every floater adds itself to as it is built, set to + // a test's own floater view and back to the one before. Left null or + // pointing at a view that has gone, the next floater built anywhere in + // the process would dereference it. + class FloaterViewScope + { + public: + explicit FloaterViewScope(LLFloaterView* view) + : mPrevious(gFloaterView) + { + gFloaterView = view; + } + ~FloaterViewScope() { gFloaterView = mPrevious; } + + FloaterViewScope(const FloaterViewScope&) = delete; + FloaterViewScope& operator=(const FloaterViewScope&) = delete; + + private: + LLFloaterView* mPrevious; + }; + + // Keyboard focus, mouse capture and the top control, as a test found + // them. A test that leaves one on a view it made leaves the next test a + // pointer to a view that dies with this one. + class FocusScope + { + public: + FocusScope() + : mKeyboard(gFocusMgr.getKeyboardFocus()), + mCapture(gFocusMgr.getMouseCapture()), + mTop(gFocusMgr.getTopCtrl()) + { + } + ~FocusScope() + { + if (gFocusMgr.getMouseCapture() != mCapture) + { + gFocusMgr.setMouseCapture(mCapture); + } + if (gFocusMgr.getKeyboardFocus() != mKeyboard) + { + gFocusMgr.setKeyboardFocus(mKeyboard); + } + if (gFocusMgr.getTopCtrl() != mTop) + { + gFocusMgr.setTopCtrl(mTop); + } + } + + FocusScope(const FocusScope&) = delete; + FocusScope& operator=(const FocusScope&) = delete; + + private: + LLFocusableElement* mKeyboard; + LLMouseHandler* mCapture; + LLUICtrl* mTop; + }; + + // What LLKeyboard names keys with, set for a test that reads an + // accelerator's name and put back after. The viewer sets one at start; + // without one, naming a key is an LL_ERRS. + class KeyNamesScope + { + public: + explicit KeyNamesScope(LLKeyStringTranslatorFunc* names) + : mPrevious(Keyboard::names()) + { + LLKeyboard::setStringTranslatorFunc(names); + } + ~KeyNamesScope() { LLKeyboard::setStringTranslatorFunc(mPrevious); } + + KeyNamesScope(const KeyNamesScope&) = delete; + KeyNamesScope& operator=(const KeyNamesScope&) = delete; + + private: + // LLKeyboard keeps its translator to itself and its subclasses. + struct Keyboard : public LLKeyboard + { + static LLKeyStringTranslatorFunc* names() { return mStringTranslator; } + }; + + LLKeyStringTranslatorFunc* mPrevious; + }; + + // The holder every menu shows itself in, set for a test that opens one. + class MenuContainerScope + { + public: + explicit MenuContainerScope(LLMenuHolderGL* holder) + : mPrevious(LLMenuGL::sMenuContainer) + { + LLMenuGL::sMenuContainer = holder; + } + ~MenuContainerScope() { LLMenuGL::sMenuContainer = mPrevious; } + + MenuContainerScope(const MenuContainerScope&) = delete; + MenuContainerScope& operator=(const MenuContainerScope&) = delete; + + private: + LLMenuHolderGL* mPrevious; + }; + + // Settings a test declares in a group that outlives it, as the HeadlessUI + // "config" group does, put back to their defaults when it ends. The group + // keeps the declarations, since a group has no way to forget one, but a + // later declaration of the same name, which keeps the value it finds, + // then finds the default. + class SettingsScope + { + public: + SettingsScope() = default; + ~SettingsScope() + { + for (const LLPointer& control : mKept) + { + control->resetToDefault(false); + } + } + + LLControlVariable* keep(LLControlVariable* control) + { + if (control) + { + mKept.emplace_back(control); + } + return control; + } + + SettingsScope(const SettingsScope&) = delete; + SettingsScope& operator=(const SettingsScope&) = delete; + + private: + std::vector> mKept; + }; + + // The colour table as a test found it. Its user layer always: a colour + // a test sets there is reset, and one it changed is set back. With the + // loaded layer too, a test that loads sheets of its own has what was + // loaded before read again in their place, since the table answers only + // to documents: rebuilt here from the sheet it had, a declaration at a + // time. + // + // A name, once in the table, stays there -- widgets hold pointers into + // it -- so a name a test loaded and the one before had not keeps a + // place, magenta, as any name a reload drops does. A table there was + // none of is left in being. + class ColorTableScope + { + public: + enum Layers + { + USER, + USER_AND_LOADED + }; + + explicit ColorTableScope(Layers layers = USER) + : mLoadedToo(layers == USER_AND_LOADED) + { + if (!LLUIColorTable::instanceExists()) + { + return; + } + const LLUIColorTable& table = LLUIColorTable::instance(); + for (const auto& [name, color] : table.getUserColors()) + { + mUser.emplace(name, color.get()); + } + if (mLoadedToo) + { + mLoadedSheet = table.getLoadedSheet(); + mUserSheet = table.getUserSheet(); + mLoaded = loadedColors(table); + } + } + + ~ColorTableScope() + { + if (!LLUIColorTable::instanceExists()) + { + return; + } + LLUIColorTable& table = LLUIColorTable::instance(); + if (mLoadedToo && loadedColors(table) != mLoaded) + { + std::vector documents; + for (size_t file = 0; file < mLoadedSheet.files().size(); ++file) + { + documents.emplace_back(documentOf(mLoadedSheet, file), mLoadedSheet.files()[file]); + } + const bool user = !mUserSheet.files().empty(); + table.load(documents, user ? documentOf(mUserSheet, 0) : LLXMLNodePtr(), + user ? mUserSheet.files()[0] : std::string()); + } + + std::vector added; + for (const auto& [name, color] : table.getUserColors()) + { + if (mUser.find(name) == mUser.end()) + { + added.push_back(name); + } + } + for (const std::string& name : added) + { + table.resetToDefault(name); + } + for (const auto& [name, color] : mUser) + { + const auto now = table.getUserColors().find(name); + if (now == table.getUserColors().end() || now->second.get() != color) + { + table.setColor(name, color); + } + } + } + + ColorTableScope(const ColorTableScope&) = delete; + ColorTableScope& operator=(const ColorTableScope&) = delete; + + private: + static std::map loadedColors(const LLUIColorTable& table) + { + std::map colors; + for (const auto& [name, color] : table.getLoadedColors()) + { + colors.emplace(name, color.get()); + } + return colors; + } + + // One file's declarations, in the order it made them, as a document + // that reads back the same. + static LLXMLNodePtr documentOf(const ALColorSheet& sheet, size_t file) + { + std::string xml = "\n"; + for (const ALColorSheet::Declaration& declaration : sheet.declarations()) + { + if (declaration.file != file || declaration.kind == ALColorSheet::Declaration::Kind::Invalid) + { + continue; + } + xml += "\n"; + } + else + { + char value[128]; + std::snprintf(value, sizeof(value), "%.9g %.9g %.9g %.9g", declaration.value.mV[VRED], + declaration.value.mV[VGREEN], declaration.value.mV[VBLUE], declaration.value.mV[VALPHA]); + xml += std::string("value=\"") + value + "\"/>\n"; + } + } + xml += "\n"; + LLXMLNodePtr root; + LLXMLNode::parseBuffer(xml.data(), static_cast(xml.size()), root); + return root; + } + + bool mLoadedToo; + std::map mUser; + std::map mLoaded; + ALColorSheet mLoadedSheet; + ALColorSheet mUserSheet; + }; + + // Every popover still showing in the floater view, escaped, as a person + // leaving one open would at last: what it was opened from hears it was + // escaped and takes nothing from it, and keyboard focus goes back. A + // popover left open holds focus, and a test that looks for "the" + // popover showing would find this one first. + inline void escapePopovers() + { + if (!gFloaterView) + { + return; + } + std::vector> showing; + for (LLView* child : *gFloaterView->getChildList()) + { + ALPopover* popover = child->as(); + if (popover && popover->getVisible() && !popover->isDead()) + { + showing.push_back(popover->getDerivedHandle()); + } + } + for (const LLHandle& handle : showing) + { + if (ALPopover* popover = handle.get()) + { + popover->escape(); + } + } + } +} + +#endif // AL_ALUISTATESCOPE_H diff --git a/indra/llui/tests/alundostack_test.cpp b/indra/llui/tests/alundostack_test.cpp index e30c1bd20ec..5800abfb952 100644 --- a/indra/llui/tests/alundostack_test.cpp +++ b/indra/llui/tests/alundostack_test.cpp @@ -35,12 +35,15 @@ namespace tut { - // A step here is the names a change touched. - struct Step + namespace { - std::vector mNames; - std::string mLabel; - }; + // A step here is the names a change touched. + struct Step + { + std::vector mNames; + std::string mLabel; + }; + } struct alundostack_data { diff --git a/indra/llui/tests/alviewtype_test.cpp b/indra/llui/tests/alviewtype_test.cpp index 5666a54ba39..dc20a3f2d71 100644 --- a/indra/llui/tests/alviewtype_test.cpp +++ b/indra/llui/tests/alviewtype_test.cpp @@ -42,50 +42,45 @@ #include #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gViewKindTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gViewKindTestAnonName; -} - namespace tut { - struct TestView : public LLView + namespace { - TestView(const LLView::Params& p) : LLView(p) {} - }; + struct TestView : public LLView + { + TestView(const LLView::Params& p) : LLView(p) {} + }; - struct TestCtrl : public LLUICtrl - { - AL_VIEW_TYPE(TestCtrl, LLUICtrl); - TestCtrl(const LLUICtrl::Params& p) : LLUICtrl(p) {} - }; + struct TestCtrl : public LLUICtrl + { + AL_VIEW_TYPE(TestCtrl, LLUICtrl); + TestCtrl(const LLUICtrl::Params& p) : LLUICtrl(p) {} + }; - struct TestFloater : public LLFloater - { - TestFloater(const LLFloater::Params& p) : LLFloater(LLSD(), p) {} - }; + struct TestFloater : public LLFloater + { + TestFloater(const LLFloater::Params& p) : LLFloater(LLSD(), p) {} + }; - // A parent that asks what a child is as it leaves. ~LLView removes a - // child from its parent after every derived destructor has run. - struct AskingView : public LLView - { - AskingView(const LLView::Params& p) : LLView(p) {} + // A parent that asks what a child is as it leaves. ~LLView removes a + // child from its parent after every derived destructor has run. + struct AskingView : public LLView + { + AskingView(const LLView::Params& p) : LLView(p) {} - bool mLeaverWasCtrl { false }; + bool mLeaverWasCtrl { false }; - void removeChild(LLView* child) override - { - mLeaverWasCtrl = child->as() != nullptr; - LLView::removeChild(child); - } - }; + void removeChild(LLView* child) override + { + mLeaverWasCtrl = child->as() != nullptr; + LLView::removeChild(child); + } + }; + } struct alviewtype_data { @@ -156,7 +151,7 @@ namespace tut } std::unique_ptr fv(floaterView()); - gFloaterView = fv.get(); + ll_test::FloaterViewScope floater_view(fv.get()); TestFloater* f = floater(); // parents itself into gFloaterView ensure("a floater is a floater", f->as() == f); @@ -194,7 +189,6 @@ namespace tut ensure("its parent is its stack", lp->getParentAs() == s.get()); fv.reset(); - gFloaterView = nullptr; } // The parent as a kind, and the nearest ancestor of a kind. @@ -207,7 +201,7 @@ namespace tut } std::unique_ptr fv(floaterView()); - gFloaterView = fv.get(); + ll_test::FloaterViewScope floater_view(fv.get()); TestFloater* f = floater(); TestView* inner = view("inner"); f->addChild(inner); @@ -221,7 +215,6 @@ namespace tut ensure("no ancestor is a type with no kind", inner->getParentByType() == nullptr); fv.reset(); - gFloaterView = nullptr; } // A view being destroyed answers for the base that is left, as diff --git a/indra/llui/tests/alvimkeymap_test.cpp b/indra/llui/tests/alvimkeymap_test.cpp index 2b61b4247c1..95f4d83d77c 100644 --- a/indra/llui/tests/alvimkeymap_test.cpp +++ b/indra/llui/tests/alvimkeymap_test.cpp @@ -32,27 +32,12 @@ #include "llsd.h" #include "alheadlessui_fixture.h" +#include "altextviewprobe.h" #include "../test/lltut.h" #include -class LLAvatarName; -const std::string gVimTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gVimTestAnonName; -} - -namespace ll_test -{ - // How long a search for a misspelling may check lines for. - struct TextViewProbe - { - static void misspellingBudget(ALTextView& view, F32 seconds) { view.mMisspellingBudget = seconds; } - }; -} - namespace tut { struct alvimkeymap_data @@ -68,6 +53,14 @@ namespace tut { editor->die(); } + // Reaped now, as the next frame would: test 73 empties the + // graveyard itself, and then finds only its own views there. + LLMortician::updateClass(); + // What a test yanked or deleted is not the next one's to put. + if (LLClipboard::instanceExists()) + { + LLClipboard::instance().reset(); + } } ALCodeEditor& make(const char* text, const char* syntax = "lsl", bool auto_complete = false) diff --git a/indra/llui/tests/alxuicatalog_test.cpp b/indra/llui/tests/alxuicatalog_test.cpp index acfe5a79b4d..829f7575edc 100644 --- a/indra/llui/tests/alxuicatalog_test.cpp +++ b/indra/llui/tests/alxuicatalog_test.cpp @@ -31,6 +31,7 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "altesttempdir.h" #include "lldir.h" #include "llfile.h" @@ -39,13 +40,6 @@ #include -class LLAvatarName; -const std::string gCatalogTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gCatalogTestAnonName; -} - namespace tut { // A skin tree of two skins and two languages: one floater in every @@ -55,12 +49,12 @@ namespace tut { ll_test::HeadlessUI& ui = ll_test::HeadlessUI::get(); - std::string mSkins; + // A tree of its own for each test, removed as it ends. + ll_test::TempDir mTemp{ "alxuicatalog_test" }; + std::string mSkins = mTemp.string(); alxuicatalog_data() { - mSkins = gDirUtilp->add(gDirUtilp->getTempDir(), "alxuicatalog_test"); - gDirUtilp->deleteDirAndContents(mSkins); write("default/xui/en/floater_a.xml", "\n" " Hello\n" @@ -87,11 +81,6 @@ namespace tut "\n\n"); } - ~alxuicatalog_data() - { - gDirUtilp->deleteDirAndContents(mSkins); - } - void write(const std::string& relative, const std::string& text) { std::string path = mSkins; diff --git a/indra/llui/tests/alxuidiagnostics_test.cpp b/indra/llui/tests/alxuidiagnostics_test.cpp index 86573dbdf69..9b29a9ad746 100644 --- a/indra/llui/tests/alxuidiagnostics_test.cpp +++ b/indra/llui/tests/alxuidiagnostics_test.cpp @@ -29,20 +29,12 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gDiagTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gDiagTestAnonName; -} - namespace tut { struct alxuidiagnostics_data @@ -127,7 +119,7 @@ namespace tut } std::unique_ptr fv(floaterView()); - gFloaterView = fv.get(); + ll_test::FloaterViewScope floater_view(fv.get()); { ALXUIDiagnostics sink; @@ -164,7 +156,6 @@ namespace tut ensure("the sink detaches when it dies", ALXUIDiagnostics::active() == nullptr); fv.reset(); - gFloaterView = nullptr; } // The attributes of a child widget fail against the parent's parameter @@ -180,7 +171,7 @@ namespace tut } std::unique_ptr fv(floaterView()); - gFloaterView = fv.get(); + ll_test::FloaterViewScope floater_view(fv.get()); ALXUIDiagnostics sink; LLFloater* view = build(fv.get()); @@ -192,6 +183,5 @@ namespace tut !has(sink, ALXUIDiagnostics::Kind::UnknownAttribute, 0, "name")); fv.reset(); - gFloaterView = nullptr; } } diff --git a/indra/llui/tests/alxuidocuments_test.cpp b/indra/llui/tests/alxuidocuments_test.cpp index b77569276df..fcf58835eb8 100644 --- a/indra/llui/tests/alxuidocuments_test.cpp +++ b/indra/llui/tests/alxuidocuments_test.cpp @@ -29,34 +29,19 @@ #include "lldir.h" #include "llfile.h" +#include "altesttempdir.h" + #include "../test/lltut.h" #include -class LLAvatarName; -const std::string gDocumentsTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gDocumentsTestAnonName; -} - namespace tut { struct alxuidocuments_data { - std::string mDir; - - alxuidocuments_data() - { - mDir = gDirUtilp->add(gDirUtilp->getTempDir(), "alxuidocuments_test"); - gDirUtilp->deleteDirAndContents(mDir); - LLFile::mkdir(mDir); - } - - ~alxuidocuments_data() - { - gDirUtilp->deleteDirAndContents(mDir); - } + // A directory of its own for each test, removed as it ends. + ll_test::TempDir mTemp{ "alxuidocuments_test" }; + std::string mDir = mTemp.string(); std::string write(const std::string& name, const std::string& text) const { diff --git a/indra/llui/tests/alxuiedit_test.cpp b/indra/llui/tests/alxuiedit_test.cpp index 86dbe159927..e1805054d2c 100644 --- a/indra/llui/tests/alxuiedit_test.cpp +++ b/indra/llui/tests/alxuiedit_test.cpp @@ -41,13 +41,6 @@ #include "../test/lltut.h" -class LLAvatarName; -const std::string gEditTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gEditTestAnonName; -} - namespace tut { // Every element here is a panel: a widget that draws text cannot be diff --git a/indra/llui/tests/alxuifields_test.cpp b/indra/llui/tests/alxuifields_test.cpp index 8bd02199f96..5da4fafce93 100644 --- a/indra/llui/tests/alxuifields_test.cpp +++ b/indra/llui/tests/alxuifields_test.cpp @@ -33,13 +33,6 @@ #include -class LLAvatarName; -const std::string gXUIFieldsTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gXUIFieldsTestAnonName; -} - namespace tut { struct alxuifields_data diff --git a/indra/llui/tests/alxuifill_test.cpp b/indra/llui/tests/alxuifill_test.cpp index 5458275a4a0..4952f06f43f 100644 --- a/indra/llui/tests/alxuifill_test.cpp +++ b/indra/llui/tests/alxuifill_test.cpp @@ -47,15 +47,6 @@ #include #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gFillTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gFillTestAnonName; -} - namespace tut { struct alxuifill_data diff --git a/indra/llui/tests/alxuifindings_test.cpp b/indra/llui/tests/alxuifindings_test.cpp index dc8d9a77a3b..e1a8ba67e03 100644 --- a/indra/llui/tests/alxuifindings_test.cpp +++ b/indra/llui/tests/alxuifindings_test.cpp @@ -28,13 +28,6 @@ #include "../test/lltut.h" -class LLAvatarName; -const std::string gFindingsTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gFindingsTestAnonName; -} - namespace tut { struct alxuifindings_data diff --git a/indra/llui/tests/alxuilint_test.cpp b/indra/llui/tests/alxuilint_test.cpp index bc4ec32ff26..5603733f0f5 100644 --- a/indra/llui/tests/alxuilint_test.cpp +++ b/indra/llui/tests/alxuilint_test.cpp @@ -35,6 +35,7 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "altesttempdir.h" #include "alxmldocument.h" @@ -46,13 +47,6 @@ #include #include -class LLAvatarName; -const std::string gLintTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gLintTestAnonName; -} - namespace tut { // Every element here is a panel. The rules that read the document @@ -374,8 +368,9 @@ namespace tut template<> template<> void alxuilint_object::test<6>() { - const std::string skins = gDirUtilp->add(gDirUtilp->getTempDir(), "alxuilint_catalog_test"); - gDirUtilp->deleteDirAndContents(skins); + // A tree of the test's own, removed as it ends. + const ll_test::TempDir temp("alxuilint_catalog_test"); + const std::string skins = temp.string(); auto write = [&](const std::string& relative, const std::string& text) { std::string dir = skins; @@ -419,8 +414,6 @@ namespace tut } ensure_equals("one template written under another tag", mismatches, 1); ensure_equals("and one layer that did not parse", parse_errors, 1); - - gDirUtilp->deleteDirAndContents(skins); } // A layout panel has one dimension the stack reads -- the one along the @@ -762,8 +755,9 @@ namespace tut { skip("no UI: LLUI_TEST_APP_DIR does not point at the source tree"); } - const std::string skins = gDirUtilp->add(gDirUtilp->getTempDir(), "alxuilint_file_test"); - gDirUtilp->deleteDirAndContents(skins); + // A tree of the test's own, removed as it ends. + const ll_test::TempDir temp("alxuilint_file_test"); + const std::string skins = temp.string(); std::string dir = skins; for (const char* step : { "default", "xui", "en" }) { @@ -797,7 +791,6 @@ namespace tut ensure_equals("and one it has not: " + run.describe(), run.count(ALXUILint::Rule::FileMissing), 1); ensure_equals("named", run.first(ALXUILint::Rule::FileMissing)->what, std::string("filename")); } - gDirUtilp->deleteDirAndContents(skins); } // Every finding names its sentence and the names in it, and says it diff --git a/indra/llui/tests/alxuinotes_test.cpp b/indra/llui/tests/alxuinotes_test.cpp index de9e6e11494..0a50b7e6138 100644 --- a/indra/llui/tests/alxuinotes_test.cpp +++ b/indra/llui/tests/alxuinotes_test.cpp @@ -32,13 +32,6 @@ #include "../test/lltut.h" -class LLAvatarName; -const std::string gXUINotesTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gXUINotesTestAnonName; -} - namespace tut { struct alxuinotes_data diff --git a/indra/llui/tests/alxuioverlay_test.cpp b/indra/llui/tests/alxuioverlay_test.cpp index 3436fdaad59..f91c21b8560 100644 --- a/indra/llui/tests/alxuioverlay_test.cpp +++ b/indra/llui/tests/alxuioverlay_test.cpp @@ -33,15 +33,6 @@ #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gOverlayTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gOverlayTestAnonName; -} - namespace tut { struct alxuioverlay_data diff --git a/indra/llui/tests/alxuischema_test.cpp b/indra/llui/tests/alxuischema_test.cpp index 958d94d1434..64aec7dc940 100644 --- a/indra/llui/tests/alxuischema_test.cpp +++ b/indra/llui/tests/alxuischema_test.cpp @@ -46,13 +46,6 @@ #include -class LLAvatarName; -const std::string gSchemaTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gSchemaTestAnonName; -} - namespace tut { struct alxuischema_data diff --git a/indra/llui/tests/alxuiselection_test.cpp b/indra/llui/tests/alxuiselection_test.cpp index ff99aed38d6..403c87b3949 100644 --- a/indra/llui/tests/alxuiselection_test.cpp +++ b/indra/llui/tests/alxuiselection_test.cpp @@ -32,13 +32,6 @@ #include "../test/lltut.h" -class LLAvatarName; -const std::string gSelectionTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gSelectionTestAnonName; -} - namespace tut { struct alxuiselection_data diff --git a/indra/llui/tests/alxuiservice_test.cpp b/indra/llui/tests/alxuiservice_test.cpp index 2d34d3f9b73..cc4ee9920e7 100644 --- a/indra/llui/tests/alxuiservice_test.cpp +++ b/indra/llui/tests/alxuiservice_test.cpp @@ -41,13 +41,6 @@ #include -class LLAvatarName; -const std::string gXUIServiceTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gXUIServiceTestAnonName; -} - namespace tut { struct alxuiservice_data diff --git a/indra/llui/tests/alxuishellbuild_test.cpp b/indra/llui/tests/alxuishellbuild_test.cpp index 1de234a2475..b0e3c021707 100644 --- a/indra/llui/tests/alxuishellbuild_test.cpp +++ b/indra/llui/tests/alxuishellbuild_test.cpp @@ -34,30 +34,24 @@ #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gShellTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gShellTestAnonName; -} - namespace tut { - // A registered panel class that counts its constructions, standing in - // for the viewer classes whose constructors have side effects. - static int gProbeClassBuilt = 0; - - struct ProbeClassPanel : public LLPanel + namespace { - ProbeClassPanel() { ++gProbeClassBuilt; } - }; + // A registered panel class that counts its constructions, standing in + // for the viewer classes whose constructors have side effects. + int gProbeClassBuilt = 0; - struct TestPanel : public LLPanel - { - TestPanel(const LLPanel::Params& p) : LLPanel(p) {} - }; + struct ProbeClassPanel : public LLPanel + { + ProbeClassPanel() { ++gProbeClassBuilt; } + }; + + struct TestPanel : public LLPanel + { + TestPanel(const LLPanel::Params& p) : LLPanel(p) {} + }; + } struct alxuishellbuild_data { diff --git a/indra/llui/tests/alxuisourcemap_test.cpp b/indra/llui/tests/alxuisourcemap_test.cpp index dde7f462c8f..7f7f1724b92 100644 --- a/indra/llui/tests/alxuisourcemap_test.cpp +++ b/indra/llui/tests/alxuisourcemap_test.cpp @@ -31,18 +31,12 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" #include -class LLAvatarName; -const std::string gSourceMapTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gSourceMapTestAnonName; -} - namespace tut { struct alxuisourcemap_data @@ -151,7 +145,7 @@ namespace tut } std::unique_ptr fv(floaterView()); - gFloaterView = fv.get(); + ll_test::FloaterViewScope floater_view(fv.get()); LLXMLNodePtr root; LLFloater* floater = build(fv.get(), root); @@ -203,6 +197,5 @@ namespace tut delete floater; fv.reset(); - gFloaterView = nullptr; } } diff --git a/indra/llui/tests/alxuitranslate_test.cpp b/indra/llui/tests/alxuitranslate_test.cpp index f9cc3cd55a4..c7d15466adc 100644 --- a/indra/llui/tests/alxuitranslate_test.cpp +++ b/indra/llui/tests/alxuitranslate_test.cpp @@ -32,13 +32,6 @@ #include -class LLAvatarName; -const std::string gTranslateTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gTranslateTestAnonName; -} - namespace tut { struct alxuitranslate_data diff --git a/indra/llui/tests/alxuitreemodel_test.cpp b/indra/llui/tests/alxuitreemodel_test.cpp index 7e15dace345..c22d1723819 100644 --- a/indra/llui/tests/alxuitreemodel_test.cpp +++ b/indra/llui/tests/alxuitreemodel_test.cpp @@ -34,6 +34,7 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -42,13 +43,6 @@ #include -class LLAvatarName; -const std::string gTreeModelTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gTreeModelTestAnonName; -} - // Two functors the folder view declares and the viewer's inventory code // defines; linking the folder view pulls them, and nothing here runs them. void LLOpenFilteredFolders::doFolder(LLFolderViewFolder* folder) {} @@ -182,7 +176,7 @@ namespace tut skip("no UI: LLUI_TEST_APP_DIR does not point at the source tree"); } std::unique_ptr fv(floaterView()); - gFloaterView = fv.get(); + ll_test::FloaterViewScope floater_view(fv.get()); LLXMLNodePtr root; LLFloater* floater = build(fv.get(), root); @@ -218,7 +212,6 @@ namespace tut delete floater; fv.reset(); - gFloaterView = nullptr; } // The filter matches by name and by tag and hides code-built rows on @@ -232,7 +225,7 @@ namespace tut skip("no UI: LLUI_TEST_APP_DIR does not point at the source tree"); } std::unique_ptr fv(floaterView()); - gFloaterView = fv.get(); + ll_test::FloaterViewScope floater_view(fv.get()); LLXMLNodePtr root; LLFloater* floater = build(fv.get(), root); @@ -297,7 +290,6 @@ namespace tut delete floater2; fv.reset(); - gFloaterView = nullptr; } // An item told to contain itself is refused. Taken as its own child it @@ -313,7 +305,7 @@ namespace tut skip("no UI: LLUI_TEST_APP_DIR does not point at the source tree"); } std::unique_ptr fv(floaterView()); - gFloaterView = fv.get(); + ll_test::FloaterViewScope floater_view(fv.get()); LLXMLNodePtr root; LLFloater* floater = build(fv.get(), root); @@ -340,7 +332,6 @@ namespace tut delete floater; fv.reset(); - gFloaterView = nullptr; } // A drag over a row lands before it, into it or after it by where on the @@ -356,7 +347,7 @@ namespace tut skip("no UI: LLUI_TEST_APP_DIR does not point at the source tree"); } std::unique_ptr fv(floaterView()); - gFloaterView = fv.get(); + ll_test::FloaterViewScope floater_view(fv.get()); // A combo box builds a line editor and a button of its own, so its // row is a folder holding rows no element describes. @@ -511,7 +502,6 @@ namespace tut delete host; delete floater; fv.reset(); - gFloaterView = nullptr; } template<> template<> @@ -523,7 +513,7 @@ namespace tut skip("no UI: LLUI_TEST_APP_DIR does not point at the source tree"); } std::unique_ptr fv(floaterView()); - gFloaterView = fv.get(); + ll_test::FloaterViewScope floater_view(fv.get()); LLXMLNodePtr root; LLFloater* floater = build(fv.get(), root); ALXUISourceMap map; @@ -555,6 +545,5 @@ namespace tut filter.resetTime(0); ensure("a pass out of time stops", !top->filter(filter)); ensure("before the top is reached", top->getLastFilterGeneration() < filter.getCurrentGeneration()); - gFloaterView = nullptr; } } diff --git a/indra/llui/tests/llaccordionctrltab_test.cpp b/indra/llui/tests/llaccordionctrltab_test.cpp index 723f10ba831..ec697d9d811 100644 --- a/indra/llui/tests/llaccordionctrltab_test.cpp +++ b/indra/llui/tests/llaccordionctrltab_test.cpp @@ -37,15 +37,6 @@ #include "../test/lltut.h" -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gAccordionTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gAccordionTestAnonName; -} - namespace tut { struct llaccordionctrltab_data diff --git a/indra/llui/tests/llcombobox_test.cpp b/indra/llui/tests/llcombobox_test.cpp index 5415e28e821..378a1c38516 100644 --- a/indra/llui/tests/llcombobox_test.cpp +++ b/indra/llui/tests/llcombobox_test.cpp @@ -36,15 +36,6 @@ #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gComboBoxTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gComboBoxTestAnonName; -} - namespace tut { struct llcombobox_data diff --git a/indra/llui/tests/llemojidictionary_test.cpp b/indra/llui/tests/llemojidictionary_test.cpp index 73a7e805978..23b57632eea 100644 --- a/indra/llui/tests/llemojidictionary_test.cpp +++ b/indra/llui/tests/llemojidictionary_test.cpp @@ -96,15 +96,25 @@ LLEmojiDescriptor make_astronaut_descriptor() namespace tut { + // A dictionary for each test, and none left after it -- unless one was + // there already, which is used as it is and left. struct llemojidictionary_data { + const bool mOwn = !LLEmojiDictionary::instanceExists(); + llemojidictionary_data() { - LLEmojiDictionary::initParamSingleton(); + if (mOwn) + { + LLEmojiDictionary::initParamSingleton(); + } } ~llemojidictionary_data() { - LLEmojiDictionary::deleteSingleton(); + if (mOwn) + { + LLEmojiDictionary::deleteSingleton(); + } } }; typedef test_group factory; diff --git a/indra/llui/tests/llfloater_test.cpp b/indra/llui/tests/llfloater_test.cpp index 61eeb350389..2bc0b8ff203 100644 --- a/indra/llui/tests/llfloater_test.cpp +++ b/indra/llui/tests/llfloater_test.cpp @@ -29,29 +29,24 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gFloaterTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gFloaterTestAnonName; -} - namespace tut { - struct TestFloater : public LLFloater + namespace { - TestFloater(const LLFloater::Params& p) : LLFloater(LLSD(), p) {} - }; + struct TestFloater : public LLFloater + { + TestFloater(const LLFloater::Params& p) : LLFloater(LLSD(), p) {} + }; - struct TestView : public LLView - { - TestView(const LLView::Params& p) : LLView(p) {} - }; + struct TestView : public LLView + { + TestView(const LLView::Params& p) : LLView(p) {} + }; + } struct llfloater_data { @@ -82,7 +77,7 @@ namespace tut } std::unique_ptr fv(floaterView()); - gFloaterView = fv.get(); + ll_test::FloaterViewScope floater_view(fv.get()); LLFloater::Params fp; fp.name = "floater"; @@ -100,7 +95,6 @@ namespace tut delete v; fv.reset(); - gFloaterView = nullptr; } // Built from a tree the caller already holds rather than from the file @@ -118,7 +112,7 @@ namespace tut } std::unique_ptr fv(floaterView()); - gFloaterView = fv.get(); + ll_test::FloaterViewScope floater_view(fv.get()); const std::string xml = ""; @@ -134,7 +128,6 @@ namespace tut ensure_equals("and titled what it says", f->getTitle(), std::string("Held in memory")); fv.reset(); - gFloaterView = nullptr; } // A floater's strings are parameters written as elements, and every @@ -155,7 +148,7 @@ namespace tut } std::unique_ptr fv(floaterView()); - gFloaterView = fv.get(); + ll_test::FloaterViewScope floater_view(fv.get()); const std::string xml = "" @@ -204,6 +197,5 @@ namespace tut ensure_equals("with nothing left under it", panel_node->getChildCount(), 0u); fv.reset(); - gFloaterView = nullptr; } } diff --git a/indra/llui/tests/llfocusmgr_test.cpp b/indra/llui/tests/llfocusmgr_test.cpp index 2872d29b518..c6707f48c39 100644 --- a/indra/llui/tests/llfocusmgr_test.cpp +++ b/indra/llui/tests/llfocusmgr_test.cpp @@ -29,26 +29,20 @@ #include "../test/lltut.h" -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gFocusMgrTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gFocusMgrTestAnonName; -} - namespace tut { - struct TestView : public LLView + namespace { - TestView(const LLView::Params& p) : LLView(p) {} - }; + struct TestView : public LLView + { + TestView(const LLView::Params& p) : LLView(p) {} + }; - // A focusable that is not a view. The viewer has one: media focus. - struct TestFocusable : public LLFocusableElement - { - }; + // A focusable that is not a view. The viewer has one: media focus. + struct TestFocusable : public LLFocusableElement + { + }; + } struct llfocusmgr_data { diff --git a/indra/llui/tests/lllayoutstack_test.cpp b/indra/llui/tests/lllayoutstack_test.cpp index 3fd36f6ad94..2035779009a 100644 --- a/indra/llui/tests/lllayoutstack_test.cpp +++ b/indra/llui/tests/lllayoutstack_test.cpp @@ -36,15 +36,6 @@ #include "../test/lltut.h" -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gLayoutStackTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gLayoutStackTestAnonName; -} - namespace tut { struct lllayoutstack_data diff --git a/indra/llui/tests/llradiogroup_test.cpp b/indra/llui/tests/llradiogroup_test.cpp index 328bd339afd..bbee08cea23 100644 --- a/indra/llui/tests/llradiogroup_test.cpp +++ b/indra/llui/tests/llradiogroup_test.cpp @@ -33,20 +33,12 @@ #include "llcontrol.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gRadioGroupTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gRadioGroupTestAnonName; -} - namespace tut { struct llradiogroup_data @@ -117,7 +109,11 @@ namespace tut skip("no UI: LLUI_TEST_APP_DIR does not point at the source tree"); } - LLControlVariable* setting = config().declareBOOL("RadioGroupTestChoice", false, std::string("A yes or no")); + // Back to its default when the test ends: declared again, a setting + // keeps the value it has, so a second run would start where this + // one left it. + ll_test::SettingsScope settings; + LLControlVariable* setting = settings.keep(config().declareBOOL("RadioGroupTestChoice", false, std::string("A yes or no"))); LLRadioGroup* group = choice("stay", "open"); group->setControlName("RadioGroupTestChoice"); ensure_equals("no is the first item", group->getSelectedIndex(), 0); @@ -143,7 +139,11 @@ namespace tut skip("no UI: LLUI_TEST_APP_DIR does not point at the source tree"); } - LLControlVariable* setting = config().declareBOOL("RadioGroupTestNamed", true, std::string("A yes or no")); + // Back to its default when the test ends: declared again, a setting + // keeps the value it has, so a second run would start where this + // one left it. + ll_test::SettingsScope settings; + LLControlVariable* setting = settings.keep(config().declareBOOL("RadioGroupTestNamed", true, std::string("A yes or no"))); LLRadioGroup* group = choice("false", "true"); group->setControlName("RadioGroupTestNamed"); ensure_equals("yes is the item named true", group->getSelectedIndex(), 1); diff --git a/indra/llui/tests/llresizebar_test.cpp b/indra/llui/tests/llresizebar_test.cpp index 2a6879e1ce7..96f997e4353 100644 --- a/indra/llui/tests/llresizebar_test.cpp +++ b/indra/llui/tests/llresizebar_test.cpp @@ -34,22 +34,16 @@ #include "../test/lltut.h" -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gResizeBarTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gResizeBarTestAnonName; -} - namespace tut { // LLView's constructor is protected, for LLUICtrlFactory. - struct TestView : public LLView + namespace { - TestView(const LLView::Params& p) : LLView(p) {} - }; + struct TestView : public LLView + { + TestView(const LLView::Params& p) : LLView(p) {} + }; + } struct llresizebar_data { @@ -174,10 +168,13 @@ namespace tut // Being disabled is a reason focus cannot arrive, not a reason it cannot // leave. A control disabled while it held focus would otherwise keep it, // with nothing able to ask for it back. - struct PlainCtrl : public LLUICtrl + namespace { - PlainCtrl(const LLUICtrl::Params& p) : LLUICtrl(p) {} - }; + struct PlainCtrl : public LLUICtrl + { + PlainCtrl(const LLUICtrl::Params& p) : LLUICtrl(p) {} + }; + } template<> template<> void llresizebar_object::test<5>() @@ -217,6 +214,12 @@ namespace tut } LLUICtrlFactory::instance().pushFileName("panel_people.xml"); + // Popped however the test ends: the factory's stack of file names + // is the process's, and the next file built would be named by this. + struct Popped + { + ~Popped() { LLUICtrlFactory::instance().popFileName(); } + } popped; const std::string before = LLUICtrlFactory::instance().getCurFileName(); LLXMLNodePtr node; @@ -230,7 +233,6 @@ namespace tut !panel->initPanelXML(node, nullptr, LLUICtrlFactory::getDefaultParams())); const std::string after = LLUICtrlFactory::instance().getCurFileName(); - LLUICtrlFactory::instance().popFileName(); ensure_equals("and left the file name where it found it", after, before); } } diff --git a/indra/llui/tests/llspinctrl_test.cpp b/indra/llui/tests/llspinctrl_test.cpp index 1ff3c1198c2..b41f940f37f 100644 --- a/indra/llui/tests/llspinctrl_test.cpp +++ b/indra/llui/tests/llspinctrl_test.cpp @@ -37,15 +37,6 @@ #include "../test/lltut.h" -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gSpinCtrlTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gSpinCtrlTestAnonName; -} - namespace tut { struct llspinctrl_data diff --git a/indra/llui/tests/llstatbar_test.cpp b/indra/llui/tests/llstatbar_test.cpp index 32b48b4f4da..20d0d9e192f 100644 --- a/indra/llui/tests/llstatbar_test.cpp +++ b/indra/llui/tests/llstatbar_test.cpp @@ -32,15 +32,6 @@ #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gStatBarAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gStatBarAnonName; -} - namespace tut { struct llstatbar_data diff --git a/indra/llui/tests/lltabcontainer_test.cpp b/indra/llui/tests/lltabcontainer_test.cpp index a3c2070edfb..e84214f3ba0 100644 --- a/indra/llui/tests/lltabcontainer_test.cpp +++ b/indra/llui/tests/lltabcontainer_test.cpp @@ -37,25 +37,20 @@ #include "lluiimage.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gTabContainerTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gTabContainerTestAnonName; -} - namespace tut { struct lltabcontainer_data { ll_test::HeadlessUI& ui = ll_test::HeadlessUI::get(); + // Focus and capture as the test found them, whatever it leaves + // them on. + ll_test::FocusScope focus; static LLTabContainer* build(LLTabContainer::TabPosition position = LLTabContainer::TOP) { diff --git a/indra/llui/tests/llui_test_fakes.cpp b/indra/llui/tests/llui_test_fakes.cpp new file mode 100644 index 00000000000..9cdbd8b1c0c --- /dev/null +++ b/indra/llui/tests/llui_test_fakes.cpp @@ -0,0 +1,42 @@ +/** + * @file llui_test_fakes.cpp + * @brief What llui's tests link in place of the viewer + * + * $LicenseInfo:firstyear=2026&license=viewerlgpl$ + * Second Life 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$ + */ + +// One definition each of what llui asks the viewer for, linked into every +// test and benchmark that links the library. A test file holds none of its +// own: two copies in one binary would not link. + +#include "linden_common.h" + +#include + +class LLAvatarName; + +// llurlentry.cpp asks RLVa for the name an anonymised avatar is shown under, +// and linking any of the library pulls the object that asks. Nothing under +// test goes near it. +const std::string& rlvGetAnonym(const LLAvatarName&) +{ + static const std::string anon("Anon"); + return anon; +} diff --git a/indra/llui/tests/lluicolortable_test.cpp b/indra/llui/tests/lluicolortable_test.cpp index 2849ec93605..8ec9ec1f7b0 100644 --- a/indra/llui/tests/lluicolortable_test.cpp +++ b/indra/llui/tests/lluicolortable_test.cpp @@ -26,23 +26,20 @@ #include "../lluicolortable.h" +#include "aluistatescope.h" + #include "../test/lltut.h" #include -class LLAvatarName; -const std::string gColorTableTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gColorTableTestAnonName; -} - namespace tut { // The table is a singleton and lives across the tests; each uses names - // of its own. + // of its own, and what it loads and sets is put back as it found it. struct lluicolortable_data { + ll_test::ColorTableScope colours{ ll_test::ColorTableScope::USER_AND_LOADED }; + static LLXMLNodePtr parse(const std::string& body) { const std::string xml = "\n" + body + "\n"; diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp index ec4d8c3daac..77d8a3846fc 100644 --- a/indra/llui/tests/llurlentry_test.cpp +++ b/indra/llui/tests/llurlentry_test.cpp @@ -33,62 +33,6 @@ #include "../llrender/lluiimage.h" #include "../llmessage/llexperiencecache.h" - -// namespace LLExperienceCache -// { -// const LLSD& get( const LLUUID& key) -// { -// static LLSD boo; -// return boo; -// } -// -// void get( const LLUUID& key, callback_slot_t slot ){} -// -// } - -/*==========================================================================*| -typedef std::map settings_map_t; -settings_map_t LLUI::sSettingGroups; - -bool LLControlGroup::getBOOL(const std::string& name) -{ - return false; -} - -LLUIColor LLUIColorTable::getColor(const std::string& name, const LLColor4& default_color) const -{ - return LLUIColor(); -} - -LLUIColor::LLUIColor() : mColorPtr(NULL) {} - -LLUIImage::LLUIImage(const std::string& name, LLPointer image) -{ -} - -LLUIImage::~LLUIImage() -{ -} - -//virtual -S32 LLUIImage::getWidth() const -{ - return 0; -} - -//virtual -S32 LLUIImage::getHeight() const -{ - return 0; -} -|*==========================================================================*/ - -const std::string anonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& avName) -{ - return anonName; -} - namespace tut { struct LLUrlEntryData @@ -106,55 +50,57 @@ namespace namespace tut { - void testRegex(const std::string &testname, LLUrlEntryBase &entry, - const char *text, const std::string &expected) + namespace { - std::string url = ""; - ALRegexMatch result; - const U32 group = entry.getUrlGroup(); - bool found = entry.getPattern().search(text, &result) && result.matched(group); - if (found) + void testRegex(const std::string &testname, LLUrlEntryBase &entry, + const char *text, const std::string &expected) { - url = entry.getUrl(result.str(group)); + std::string url = ""; + ALRegexMatch result; + const U32 group = entry.getUrlGroup(); + bool found = entry.getPattern().search(text, &result) && result.matched(group); + if (found) + { + url = entry.getUrl(result.str(group)); + } + ensure_equals(testname, url, expected); } - ensure_equals(testname, url, expected); - } - void dummyCallback(const std::string &url, const std::string &label, const std::string& icon) - { - } + void dummyCallback(const std::string &url, const std::string &label, const std::string& icon) + { + } - void testLabel(const std::string &testname, LLUrlEntryBase &entry, - const char *text, const std::string &expected) - { - std::string label = ""; - ALRegexMatch result; - const U32 group = entry.getUrlGroup(); - bool found = entry.getPattern().search(text, &result) && result.matched(group); - if (found) + void testLabel(const std::string &testname, LLUrlEntryBase &entry, + const char *text, const std::string &expected) { - std::string url = result.str(group); - label = entry.getLabel(url, boost::bind(dummyCallback, _1, _2, _3)); + std::string label = ""; + ALRegexMatch result; + const U32 group = entry.getUrlGroup(); + bool found = entry.getPattern().search(text, &result) && result.matched(group); + if (found) + { + std::string url = result.str(group); + label = entry.getLabel(url, boost::bind(dummyCallback, _1, _2, _3)); + } + ensure_equals(testname, label, expected); } - ensure_equals(testname, label, expected); - } - void testLocation(const std::string &testname, LLUrlEntryBase &entry, - const char *text, const std::string &expected) - { - std::string location = ""; - ALRegexMatch result; - const U32 group = entry.getUrlGroup(); - bool found = entry.getPattern().search(text, &result) && result.matched(group); - if (found) + void testLocation(const std::string &testname, LLUrlEntryBase &entry, + const char *text, const std::string &expected) { - std::string url = result.str(group); - location = entry.getLocation(url); + std::string location = ""; + ALRegexMatch result; + const U32 group = entry.getUrlGroup(); + bool found = entry.getPattern().search(text, &result) && result.matched(group); + if (found) + { + std::string url = result.str(group); + location = entry.getLocation(url); + } + ensure_equals(testname, location, expected); } - ensure_equals(testname, location, expected); } - template<> template<> void object::test<1>() { diff --git a/indra/llui/tests/llurlmatch_test.cpp b/indra/llui/tests/llurlmatch_test.cpp index d03efbc1c90..0eebc4d5617 100644 --- a/indra/llui/tests/llurlmatch_test.cpp +++ b/indra/llui/tests/llurlmatch_test.cpp @@ -29,106 +29,18 @@ #include "../llurlmatch.h" #include "../llrender/lluiimage.h" -#include "lltut.h" - -// link seams - -LLUIColor::LLUIColor() - : mColorPtr(NULL) -{} - -LLStyle::Params::Params() -{ -} -LLUIImage::LLUIImage(const std::string& name, LLPointer image) -{ -} - -LLUIImage::~LLUIImage() -{ -} - -//virtual -S32 LLUIImage::getWidth() const -{ - return 0; -} - -//virtual -S32 LLUIImage::getHeight() const -{ - return 0; -} - -namespace LLInitParam -{ - ParamValue::ParamValue(const LLUIColor& color) - : super_t(color) - {} - - void ParamValue::updateValueFromBlock() - {} - - void ParamValue::updateBlockFromValue(bool) - {} - - bool ParamCompare::equals(const LLFontGL* a, const LLFontGL* b) - { - return false; - } - - - ParamValue::ParamValue(const LLFontGL* fontp) - : super_t(fontp) - {} - - void ParamValue::updateValueFromBlock() - {} - - void ParamValue::updateBlockFromValue(bool) - {} - - void TypeValues::declareValues() - {} - - void TypeValues::declareValues() - {} - - void TypeValues::declareValues() - {} - - void ParamValue::updateValueFromBlock() - {} - - void ParamValue::updateBlockFromValue(bool) - {} - - bool ParamCompare::equals( - LLUIImage* const &a, - LLUIImage* const &b) - { - return false; - } - - bool ParamCompare::equals(const LLUIColor &a, const LLUIColor &b) - { - return false; - } - -} - -//static -LLFontGL* LLFontGL::getFontDefault() -{ - return NULL; -} +#include "alheadlessui_fixture.h" +#include "lltut.h" namespace tut { + // A match is given a style, and a style's parameters start from the + // default font, so the fonts are loaded first. struct LLUrlMatchData { + ll_test::HeadlessUI& ui = ll_test::HeadlessUI::get(); }; typedef test_group factory; @@ -145,6 +57,10 @@ namespace tut template<> template<> void object::test<1>() { + if (!ui.ok()) + { + skip("no UI: LLUI_TEST_APP_DIR does not point at the source tree"); + } // // test the empty() method // @@ -158,6 +74,10 @@ namespace tut template<> template<> void object::test<2>() { + if (!ui.ok()) + { + skip("no UI: LLUI_TEST_APP_DIR does not point at the source tree"); + } // // test the getStart() method // @@ -171,6 +91,10 @@ namespace tut template<> template<> void object::test<3>() { + if (!ui.ok()) + { + skip("no UI: LLUI_TEST_APP_DIR does not point at the source tree"); + } // // test the getEnd() method // @@ -184,6 +108,10 @@ namespace tut template<> template<> void object::test<4>() { + if (!ui.ok()) + { + skip("no UI: LLUI_TEST_APP_DIR does not point at the source tree"); + } // // test the getUrl() method // @@ -200,6 +128,10 @@ namespace tut template<> template<> void object::test<5>() { + if (!ui.ok()) + { + skip("no UI: LLUI_TEST_APP_DIR does not point at the source tree"); + } // // test the getLabel() method // @@ -216,6 +148,10 @@ namespace tut template<> template<> void object::test<6>() { + if (!ui.ok()) + { + skip("no UI: LLUI_TEST_APP_DIR does not point at the source tree"); + } // // test the getTooltip() method // @@ -232,6 +168,10 @@ namespace tut template<> template<> void object::test<7>() { + if (!ui.ok()) + { + skip("no UI: LLUI_TEST_APP_DIR does not point at the source tree"); + } // // test the getIcon() method // @@ -248,6 +188,10 @@ namespace tut template<> template<> void object::test<8>() { + if (!ui.ok()) + { + skip("no UI: LLUI_TEST_APP_DIR does not point at the source tree"); + } // // test the getMenuName() method // @@ -264,6 +208,10 @@ namespace tut template<> template<> void object::test<9>() { + if (!ui.ok()) + { + skip("no UI: LLUI_TEST_APP_DIR does not point at the source tree"); + } // // test the getLocation() method // diff --git a/indra/llui/tests/llurlregistry_test.cpp b/indra/llui/tests/llurlregistry_test.cpp index 92ba6ace54c..98d662ba383 100644 --- a/indra/llui/tests/llurlregistry_test.cpp +++ b/indra/llui/tests/llurlregistry_test.cpp @@ -30,13 +30,6 @@ #include "../test/lltut.h" -class LLAvatarName; -const std::string gUrlRegistryTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gUrlRegistryTestAnonName; -} - namespace tut { struct llurlregistry_data diff --git a/indra/llui/tests/llview_test.cpp b/indra/llui/tests/llview_test.cpp index c1435b03b5e..d2468b76247 100644 --- a/indra/llui/tests/llview_test.cpp +++ b/indra/llui/tests/llview_test.cpp @@ -27,25 +27,21 @@ #include "../llview.h" #include "../llfocusmgr.h" -#include "../test/lltut.h" +#include "aluistatescope.h" -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gViewTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gViewTestAnonName; -} +#include "../test/lltut.h" namespace tut { - // LLView's constructor is protected, for LLUICtrlFactory. A test builds - // views the same way a widget does. - struct TestView : public LLView + namespace { - TestView(const LLView::Params& p) : LLView(p) {} - }; + // LLView's constructor is protected, for LLUICtrlFactory. A test builds + // views the same way a widget does. + struct TestView : public LLView + { + TestView(const LLView::Params& p) : LLView(p) {} + }; + } struct llview_data { @@ -316,16 +312,19 @@ namespace tut // it handled the message is the answer to give back, not a reason to stop: // a panel telling each of its accordions to store its state has as many // handlers as it has accordions. - struct CountingView : public LLView + namespace { - CountingView(const LLView::Params& p) : LLView(p) {} - bool notifyChildren(const LLSD& info) override + struct CountingView : public LLView { - ++mHeard; - return true; - } - S32 mHeard = 0; - }; + CountingView(const LLView::Params& p) : LLView(p) {} + bool notifyChildren(const LLSD& info) override + { + ++mHeard; + return true; + } + S32 mHeard = 0; + }; + } template<> template<> void llview_object::test<12>() @@ -389,6 +388,9 @@ namespace tut TestView* their_combo = view("combo"); mine->addChild(my_combo); theirs->addChild(their_combo); + // Let go of before the views it is given to are deleted, a failed + // check included: a plain view does not let go of it itself. + ll_test::FocusScope focus; gFocusMgr.setKeyboardFocus(my_combo); ensure("the view holding it owns the child", mine->childHasKeyboardFocus("combo")); diff --git a/indra/llui/tests/llxuiparser_test.cpp b/indra/llui/tests/llxuiparser_test.cpp index 7d714f39a5f..e3fb7a31f7b 100644 --- a/indra/llui/tests/llxuiparser_test.cpp +++ b/indra/llui/tests/llxuiparser_test.cpp @@ -36,6 +36,8 @@ #include "lluuid.h" #include "v4color.h" +#include "altesttempdir.h" + #include "../test/lltut.h" #include @@ -47,75 +49,72 @@ // could only be checked by reading it. // -struct XuiLeaf : public LLInitParam::Block +namespace { - Optional depth; - Optional label; + struct XuiLeaf : public LLInitParam::Block + { + Optional depth; + Optional label; - XuiLeaf() - : depth("depth", 0), - label("label", "") - {} -}; + XuiLeaf() + : depth("depth", 0), + label("label", "") + {} + }; -struct XuiMiddle : public LLInitParam::Block -{ - Optional leaf; - Optional depth; + struct XuiMiddle : public LLInitParam::Block + { + Optional leaf; + Optional depth; - XuiMiddle() - : leaf("leaf"), - depth("depth", 0) - {} -}; + XuiMiddle() + : leaf("leaf"), + depth("depth", 0) + {} + }; -struct XuiRoot : public LLInitParam::Block -{ - Optional middle; - Optional flag; - Optional text; - Optional small; - Optional count; - Optional ratio; - Optional tint; - Optional id; - - XuiRoot() - : middle("middle"), - flag("flag", false), - text("text", ""), - small("small", 0), - count("count", 0), - ratio("ratio", 0.f), - tint("tint", LLColor4::black), - id("id") - {} -}; + struct XuiRoot : public LLInitParam::Block + { + Optional middle; + Optional flag; + Optional text; + Optional small_number; + Optional count; + Optional ratio; + Optional tint; + Optional id; + + XuiRoot() + : middle("middle"), + flag("flag", false), + text("text", ""), + small_number("small", 0), + count("count", 0), + ratio("ratio", 0.f), + tint("tint", LLColor4::black), + id("id") + {} + }; -namespace -{ // LLSimpleXUIParser reads a file rather than a buffer, so every case here - // needs one on disk. Named per case so a crashed run leaves something - // identifiable behind rather than clobbering the next test's input. + // needs one on disk: in a directory of its own, since a fixed name is + // shared by two runs at once. Named per case within it, so a crashed run + // leaves something identifiable behind. class ScratchXui { public: ScratchXui(const char* tag, const std::string& contents) - : mPath(std::filesystem::temp_directory_path() / (std::string("al_xuiparser_") + tag + ".xml")) + : mDir("al_xuiparser"), + mPath(mDir.path() / (std::string("al_xuiparser_") + tag + ".xml")) { std::ofstream out(mPath, std::ios::binary); out << contents; } - ~ScratchXui() - { - std::error_code ignored; - std::filesystem::remove(mPath, ignored); - } - std::string name() const { return mPath.string(); } private: + ll_test::TempDir mDir; std::filesystem::path mPath; }; @@ -158,7 +157,7 @@ namespace tut ensure("flag provided", block.flag.isProvided()); ensure_equals("flag", block.flag(), true); ensure_equals("text", block.text(), std::string("hello")); - ensure_equals("small", (S32)block.small(), 200); + ensure_equals("small", (S32)block.small_number(), 200); ensure_equals("count", block.count(), -17); ensure_equals("ratio", block.ratio(), 0.5f); ensure_equals("tint red", block.tint().mV[VRED], 1.f); @@ -195,8 +194,8 @@ namespace tut XuiRoot block; parse("narrow", "", block, true); - ensure("out of range is refused", !block.small.isProvided()); - ensure_equals("and the default stands", (S32)block.small(), 0); + ensure("out of range is refused", !block.small_number.isProvided()); + ensure_equals("and the default stands", (S32)block.small_number(), 0); } template<> template<> @@ -272,9 +271,12 @@ namespace tut { set_test_name("a missing file is reported, not assumed empty"); + // In a directory of the test's own, not the working one, where a + // file of that name could be. + const ll_test::TempDir dir("al_xuiparser"); XuiRoot block; LLSimpleXUIParser parser; ensure("reading a file that is not there fails", - !parser.readXUI("al_xuiparser_no_such_file.xml", block, true)); + !parser.readXUI((dir.path() / "al_xuiparser_no_such_file.xml").string(), block, true)); } } diff --git a/indra/llui/text/altextfind.cpp b/indra/llui/text/altextfind.cpp index 5bd479b74ca..9d88a2da18d 100644 --- a/indra/llui/text/altextfind.cpp +++ b/indra/llui/text/altextfind.cpp @@ -63,6 +63,15 @@ namespace } // Closing, it passes over what waits for it. bool closing() const { return mThread && mThread->closing(); } + // Closed and waited for, and let go of: the next post starts another. + void stop() + { + if (mThread) + { + mThread->close(); + mThread.reset(); + } + } private: std::unique_ptr mThread; @@ -93,6 +102,15 @@ ALTextFind::~ALTextFind() letGo(); } +// static +void ALTextFind::closeWorker() +{ + if (ALTextFindThread::instanceExists()) + { + ALTextFindThread::instance().stop(); + } +} + void ALTextFind::letGo() { if (!mWorking) diff --git a/indra/llui/text/altextfind.h b/indra/llui/text/altextfind.h index 77e55a99a23..a8bd813d0c6 100644 --- a/indra/llui/text/altextfind.h +++ b/indra/llui/text/altextfind.h @@ -70,6 +70,10 @@ class ALTextFind bool searching() const { return mWorking != nullptr; } // Past this many bytes, a text is looked through on a worker. static constexpr size_t ON_A_WORKER = 256 * 1024; + // The worker's thread closed and waited for, as the viewer going closes + // it; the next search to need it starts another. For whoever must leave + // nothing of its own running when it is done: a test. + static void closeWorker(); // Nothing found, and nothing to look through again: the find put away, // and with it the stretch a find in a selection kept to. void clear(); diff --git a/indra/llxml/CMakeLists.txt b/indra/llxml/CMakeLists.txt index f9e26cf4caa..b1ca7f927f9 100644 --- a/indra/llxml/CMakeLists.txt +++ b/indra/llxml/CMakeLists.txt @@ -25,7 +25,7 @@ target_precompile_headers(llxml REUSE_FROM llprecompiled) # tests if(AL_BUILD_TESTS) - al_add_test(llxmlnode UNIT PROJECT llxml SOURCES alxmldocument.cpp LIBRARIES ll::pugixml) + al_add_test(llxmlnode PROJECT llxml LIBRARIES llxml llmath llcommon) al_add_test(llcontrol PROJECT llxml LIBRARIES llxml llmath llcommon) al_add_test(alxmllayermerge PROJECT llxml LIBRARIES llxml llmath llcommon) endif() diff --git a/indra/llxml/tests/alxmllayermerge_test.cpp b/indra/llxml/tests/alxmllayermerge_test.cpp index 667fc7ceb34..16eb13f899a 100644 --- a/indra/llxml/tests/alxmllayermerge_test.cpp +++ b/indra/llxml/tests/alxmllayermerge_test.cpp @@ -28,6 +28,7 @@ #include "lldir.h" #include "llfile.h" +#include "lluuid.h" #include "../test/lltut.h" @@ -35,6 +36,8 @@ #include namespace tut +{ +namespace { // Every event, as one line: the kind, the layer, and the element or // attribute it was about, so a run can be compared with what the @@ -213,10 +216,10 @@ namespace tut std::string mDir; + // A folder of its own each time, so two runs at once never share one. alxmllayermerge_data() { - mDir = gDirUtilp->add(gDirUtilp->getTempDir(), "alxmllayermerge_test"); - gDirUtilp->deleteDirAndContents(mDir); + mDir = gDirUtilp->add(gDirUtilp->getTempDir(), "alxmllayermerge_test_" + LLUUID::generateNewID().asString()); LLFile::mkdir(mDir); } @@ -237,6 +240,7 @@ namespace tut typedef test_group alxmllayermerge_test; typedef alxmllayermerge_test::object alxmllayermerge_object; tut::alxmllayermerge_test alxmllayermerge_testgroup("alxmllayermerge"); +} // One event per decision: repeated names in order, text overwritten // where the overlay has text and kept where it has none, a value= diff --git a/indra/llxml/tests/llcontrol_test.cpp b/indra/llxml/tests/llcontrol_test.cpp index 0b28e20ff55..5f8811824b4 100644 --- a/indra/llxml/tests/llcontrol_test.cpp +++ b/indra/llxml/tests/llcontrol_test.cpp @@ -37,6 +37,8 @@ #include namespace tut +{ +namespace { struct control_group { @@ -92,6 +94,7 @@ namespace tut typedef test_group control_group_test; typedef control_group_test::object control_group_t; control_group_test tut_control_group("control_group"); +} //load settings from files - LLSD template<> template<> diff --git a/indra/llxml/tests/llxmlnode_test.cpp b/indra/llxml/tests/llxmlnode_test.cpp index 9b38d064098..29c46e3f701 100644 --- a/indra/llxml/tests/llxmlnode_test.cpp +++ b/indra/llxml/tests/llxmlnode_test.cpp @@ -35,6 +35,8 @@ #include namespace tut +{ +namespace { struct llxmlnode_data { @@ -42,6 +44,7 @@ namespace tut typedef test_group llxmlnode_test; typedef llxmlnode_test::object llxmlnode_object; tut::llxmlnode_test llxmlnode_testcase("llxmlnode"); +} // Element name, id, attributes (string + int) and text value round-trip. template<> template<> diff --git a/indra/lscript/tests/allslreference_test.cpp b/indra/lscript/tests/allslreference_test.cpp index 181641fad83..62ecb0b9b27 100644 --- a/indra/lscript/tests/allslreference_test.cpp +++ b/indra/lscript/tests/allslreference_test.cpp @@ -66,6 +66,26 @@ namespace gSaid.emplace_back(args[1].mString ? args[1].mString : ""); } + // A library function answered by a stand-in while this lives, and as it + // was after, however the test ends: the library is the process's. + struct StandIn + { + using exec_t = decltype(LLScriptLibraryFunction::mExecFunc); + + StandIn(LLScriptLibraryFunction& function, exec_t by) + : mFunction(function), + mWas(function.mExecFunc) + { + mFunction.mExecFunc = by; + } + ~StandIn() { mFunction.mExecFunc = mWas; } + StandIn(const StandIn&) = delete; + StandIn& operator=(const StandIn&) = delete; + + LLScriptLibraryFunction& mFunction; + const exec_t mWas; + }; + // Where LSO keeps the version, the top of memory and the default // state's handlers (gLSCRIPTRegisterAddresses). constexpr size_t TM = 0; @@ -218,20 +238,19 @@ namespace tut const ALLSLReference::Result lso = ALLSLReference::compile(text, Target::LSO); ensure("LSO: " + lso.messages, lso.ok); - // llSay is function 23: heard here for the test, as it was. - LLScriptLibraryFunction& say = gScriptLibrary.mFunctions[23]; - const auto was = say.mExecFunc; - say.mExecFunc = heardSay; + // llSay is function 23: heard here for the run, as it was after. gSaid.clear(); - LLScriptExecuteLSL2 vm(lso.image.data(), static_cast(lso.image.size())); - LLTimer timer; - const char* error = nullptr; - U32 events = 0; - for (S32 quanta = 0; quanta < 100 && gSaid.empty() && !error; ++quanta) + const char* error = nullptr; + U32 events = 0; { - vm.runQuanta(FALSE, LLUUID::null, &error, 1.f, events, timer); + const StandIn heard(gScriptLibrary.mFunctions[23], heardSay); + LLScriptExecuteLSL2 vm(lso.image.data(), static_cast(lso.image.size())); + LLTimer timer; + for (S32 quanta = 0; quanta < 100 && gSaid.empty() && !error; ++quanta) + { + vm.runQuanta(FALSE, LLUUID::null, &error, 1.f, events, timer); + } } - say.mExecFunc = was; ensure("no fault: " + std::string(error ? error : ""), !error); ensure_equals("one handler run", events, (U32)1); ensure_equals("said once", gSaid.size(), (size_t)1); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index b6c55226022..009fcb15205 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -2476,8 +2476,8 @@ if(AL_BUILD_TESTS) alscriptstudioviewer.cpp alscriptstudioweighing.cpp alscriptstudiowords.cpp + alscripttypes.cpp alscriptweightspane.cpp - tests/alscriptpaths_stub.cpp tests/alscriptstudio_stubs.cpp ) get_property(studio_test_includes TARGET ${test_project} PROPERTY INTERFACE_INCLUDE_DIRECTORIES) @@ -2492,6 +2492,18 @@ if(AL_BUILD_TESTS) target_link_libraries(al_studio_test_common PRIVATE al::flags PUBLIC llui alscript ${test_libs}) set_target_properties(al_studio_test_common PROPERTIES FOLDER "Tests/${test_project}") + # What the tests take of the rest of the viewer, faked once. Objects, which + # a test links whole, since what reaches for them is a library the linker + # may read after an archive of them; a test names them where it needs them. + add_library(newview_test_fakes OBJECT tests/newview_test_fakes.cpp) + target_link_libraries(newview_test_fakes PRIVATE al::flags llcommon) + set_target_properties(newview_test_fakes PROPERTIES FOLDER "Tests/${test_project}") + # The viewer's settings group, gSavedSettings, for a test whose code reads + # it: a group made as the process starts, so linked by those tests alone. + add_library(newview_test_settings OBJECT tests/newview_test_settings.cpp) + target_link_libraries(newview_test_settings PRIVATE al::flags llxml) + set_target_properties(newview_test_settings PROPERTIES FOLDER "Tests/${test_project}") + # A unit test compiles one viewer source file on its own. Each line is one # such file and its test under tests/. foreach( @@ -2508,9 +2520,7 @@ if(AL_BUILD_TESTS) almotionpredictor alsceneexplorerpredicate alwhitebalancesolver - llagentaccess lldateutil - llremoteparcelrequest llviewerhelputil llversioninfo llvocache @@ -2519,6 +2529,17 @@ if(AL_BUILD_TESTS) ) al_add_test(${test} UNIT PROJECT ${test_project} LIBRARIES ${test_libs}) endforeach() + # A parcel's information handed to its observers and to the URL entries + # that name it: llui's own, rather than stubs of them. + al_add_test( + llremoteparcelrequest + UNIT + PROJECT ${test_project} + LIBRARIES newview_test_fakes llui ${test_libs} + ) + # The agent's maturity, read and written through a settings group of each + # test's own. + al_add_test(llagentaccess UNIT PROJECT ${test_project} LIBRARIES llxml ${test_libs}) # The vectorscope bins on the colour wheels' own chroma plane, so the scope # data test links the model that defines it rather than keeping a second copy # of the basis that could drift from it. @@ -2584,7 +2605,7 @@ if(AL_BUILD_TESTS) alscriptitemdrop UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2593,7 +2614,7 @@ if(AL_BUILD_TESTS) alnotecardembedded UNIT PROJECT ${test_project} - LIBRARIES llui alscript ${test_libs} + LIBRARIES newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2616,7 +2637,7 @@ if(AL_BUILD_TESTS) alscriptexplorermodel UNIT PROJECT ${test_project} - LIBRARIES llui alscript ${test_libs} + LIBRARIES newview_test_fakes llui alscript ${test_libs} SOURCES alscriptcontentsindex.cpp ) @@ -2626,7 +2647,7 @@ if(AL_BUILD_TESTS) alscriptexplorertree UNIT PROJECT ${test_project} - LIBRARIES llui alscript ${test_libs} + LIBRARIES newview_test_fakes llui alscript ${test_libs} SOURCES alscriptexplorermodel.cpp alscriptcontentsindex.cpp DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2637,7 +2658,7 @@ if(AL_BUILD_TESTS) alscriptoutputpane UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2647,7 +2668,7 @@ if(AL_BUILD_TESTS) alscriptproblemspane UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2657,7 +2678,7 @@ if(AL_BUILD_TESTS) alscriptsearchpane UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} SOURCES alscriptsearch.cpp DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2667,7 +2688,7 @@ if(AL_BUILD_TESTS) alrecoverykeeper UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} ) # A Script Studio window's saves kept in a history of the test's, listed, @@ -2676,7 +2697,7 @@ if(AL_BUILD_TESTS) alscriptstudiohistory UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2687,7 +2708,7 @@ if(AL_BUILD_TESTS) UNIT PROJECT ${test_project} SOURCES alscriptstudiohistory.cpp - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2698,7 +2719,7 @@ if(AL_BUILD_TESTS) alscriptstudiocomparepairs UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2708,7 +2729,7 @@ if(AL_BUILD_TESTS) alscriptstudioexpandedcompare UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2718,7 +2739,7 @@ if(AL_BUILD_TESTS) alscriptstudiomerging UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2728,7 +2749,7 @@ if(AL_BUILD_TESTS) alscriptstudioselections UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2738,7 +2759,7 @@ if(AL_BUILD_TESTS) alscriptstudiorecovery UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2758,7 +2779,7 @@ if(AL_BUILD_TESTS) alscripttheme UNIT PROJECT ${test_project} - LIBRARIES llui ${test_libs} + LIBRARIES newview_test_fakes llui ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2772,7 +2793,7 @@ if(AL_BUILD_TESTS) UNIT PROJECT ${test_project} SOURCES alscriptkeypresets.cpp - LIBRARIES llui ${test_libs} + LIBRARIES newview_test_fakes newview_test_settings llui ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2786,7 +2807,7 @@ if(AL_BUILD_TESTS) alscriptstudiosaving UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2796,7 +2817,7 @@ if(AL_BUILD_TESTS) alscriptstudiofiles UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2808,7 +2829,7 @@ if(AL_BUILD_TESTS) alscriptstudiomasters UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2822,7 +2843,7 @@ if(AL_BUILD_TESTS) alscriptnavigation UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2832,7 +2853,7 @@ if(AL_BUILD_TESTS) alscriptlookup UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2842,7 +2863,7 @@ if(AL_BUILD_TESTS) alscriptreferencespane UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2852,7 +2873,7 @@ if(AL_BUILD_TESTS) alscriptoutlinepane UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2862,7 +2883,7 @@ if(AL_BUILD_TESTS) alscriptcrumbsbar UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2872,7 +2893,7 @@ if(AL_BUILD_TESTS) alscriptinspectorpane UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2882,7 +2903,7 @@ if(AL_BUILD_TESTS) alscriptstudiocaret UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2906,7 +2927,7 @@ if(AL_BUILD_TESTS) alscriptanalyzers UNIT PROJECT ${test_project} - LIBRARIES alscript llui ${test_libs} + LIBRARIES newview_test_fakes alscript llui ${test_libs} DEFINES "AL_LSL_DEFINITIONS_DIR=\"${LSL_DEFINITIONS_DIR}\"" ) @@ -2916,7 +2937,7 @@ if(AL_BUILD_TESTS) alscriptstudiochecking UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2926,7 +2947,7 @@ if(AL_BUILD_TESTS) alscriptobjectcheck UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} SOURCES alscriptstudiochecking.cpp DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2942,7 +2963,7 @@ if(AL_BUILD_TESTS) alscriptrecompile UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2952,7 +2973,7 @@ if(AL_BUILD_TESTS) alscriptstudioorphans UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2962,7 +2983,7 @@ if(AL_BUILD_TESTS) alscriptstudiowords UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2972,7 +2993,7 @@ if(AL_BUILD_TESTS) alscriptstudioweighing UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2982,7 +3003,7 @@ if(AL_BUILD_TESTS) alscriptexternaleditor UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2992,7 +3013,7 @@ if(AL_BUILD_TESTS) alscriptstudiovim UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} SOURCES alscriptstudiocommands.cpp DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -3003,7 +3024,7 @@ if(AL_BUILD_TESTS) alscriptstudiodoc UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -3013,7 +3034,7 @@ if(AL_BUILD_TESTS) alscriptweightspane UNIT PROJECT ${test_project} - LIBRARIES al_studio_test_common llui alscript ${test_libs} + LIBRARIES al_studio_test_common newview_test_fakes llui alscript ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -3038,13 +3059,8 @@ if(AL_BUILD_TESTS) al_add_test( llviewercontrollistener PROJECT ${test_project} - LIBRARIES ${test_libs} - SOURCES - llviewercontrollistener.cpp - ../llxml/llcontrol.cpp - ../llxml/llxmltree.cpp - ../llxml/alxmldocument.cpp - ../llcommon/commoncontrol.cpp + LIBRARIES llxml ${test_libs} + SOURCES llviewercontrollistener.cpp ) al_add_test( llviewernetwork @@ -3067,12 +3083,7 @@ if(AL_BUILD_TESTS) al_add_test( lltextureinfo PROJECT ${test_project} - LIBRARIES ${test_libs} - SOURCES - lltextureinfo.cpp - ../llxml/llcontrol.cpp - ../llxml/llxmltree.cpp - ../llxml/alxmldocument.cpp - ../llcommon/commoncontrol.cpp + LIBRARIES newview_test_settings llxml ${test_libs} + SOURCES lltextureinfo.cpp ) endif() diff --git a/indra/newview/tests/alcrashreporter_test.cpp b/indra/newview/tests/alcrashreporter_test.cpp index a011318510e..0c06ff7690b 100644 --- a/indra/newview/tests/alcrashreporter_test.cpp +++ b/indra/newview/tests/alcrashreporter_test.cpp @@ -27,6 +27,7 @@ #include "../alcrashreporter.h" #include "llsd.h" +#include "lluuid.h" #include "v3math.h" #include "../test/lltut.h" @@ -112,8 +113,9 @@ namespace tut void object::test<7>() { set_test_name("consent is a sentinel file: recorded, seen, withdrawn"); + // A name of this run's own: another run of the tests may be at it. const std::string sentinel = - (std::filesystem::temp_directory_path() / "alcrashreporter_test_consent").string(); + (std::filesystem::temp_directory_path() / ("alcrashreporter_test_consent_" + LLUUID::generateNewID().asString())).string(); ALCrashReporter::recordConsent(sentinel, false); ensure("absent to begin with", !ALCrashReporter::consentRecorded(sentinel)); @@ -167,8 +169,9 @@ namespace tut void object::test<10>() { set_test_name("a freeze's report is recorded for the next run, which takes it once"); + // Names of this run's own, here and below: another run of the tests may be at it. const std::string record = - (std::filesystem::temp_directory_path() / "alcrashreporter_test_last_report").string(); + (std::filesystem::temp_directory_path() / ("alcrashreporter_test_last_report_" + LLUUID::generateNewID().asString())).string(); ALCrashReporter::takeRecordedReport(record); ensure("nothing to begin with", !ALCrashReporter::takeRecordedReport(record)); @@ -197,7 +200,7 @@ namespace tut "00000000000000000000000000000001"); const std::string record = - (std::filesystem::temp_directory_path() / "alcrashreporter_test_nil_report").string(); + (std::filesystem::temp_directory_path() / ("alcrashreporter_test_nil_report_" + LLUUID::generateNewID().asString())).string(); ALCrashReporter::recordReport(record, { "freeze", "00000000000000000000000000000000" }); ensure("a nil report is not taken", !ALCrashReporter::takeRecordedReport(record)); } @@ -228,8 +231,8 @@ namespace tut void object::test<13>() { set_test_name("a record is written whole over the last, and nothing is left beside it"); - const std::filesystem::path dir = std::filesystem::temp_directory_path() / "alcrashreporter_test_records"; - std::filesystem::remove_all(dir); + const std::filesystem::path dir = + std::filesystem::temp_directory_path() / ("alcrashreporter_test_records_" + LLUUID::generateNewID().asString()); std::filesystem::create_directories(dir); const auto entries = [&dir]() { diff --git a/indra/newview/tests/alhuddepth_gl_test.cpp b/indra/newview/tests/alhuddepth_gl_test.cpp index de870c55480..502aa365a8a 100644 --- a/indra/newview/tests/alhuddepth_gl_test.cpp +++ b/indra/newview/tests/alhuddepth_gl_test.cpp @@ -93,27 +93,21 @@ namespace tut return LLColor4U((U8)ll_round(c.mV[0] * 255.f), (U8)ll_round(c.mV[1] * 255.f), (U8)ll_round(c.mV[2] * 255.f), (U8)ll_round(c.mV[3] * 255.f)); } - } struct alhuddepth_data { - static ll_test::HeadlessGL& gl() - { - static ll_test::HeadlessGL instance(true, true, true, false); - return instance; - } - + // Each test starts forward, as the window does, whatever ran before. alhuddepth_data() : mShaders(std::string(AL_TEST_SHADER_DIR) + "/class") { - gl(); + setConvention(false); } + // The convention, viewport, shader and clear colour the tests set go + // back with mGL. ~alhuddepth_data() { tearDown(); - setConvention(false); - glViewport(0, 0, ll_test::HeadlessGL::WIDTH, ll_test::HeadlessGL::HEIGHT); } void tearDown() @@ -390,6 +384,7 @@ namespace tut return false; } + ll_test::SharedGLScope mGL; ll_test::TestShaderMgr mShaders; LLGLSLShader mBlitProgram; LLGLSLShader mCopyDepthProgram; @@ -408,6 +403,7 @@ namespace tut typedef test_group alhuddepth_t; typedef alhuddepth_t::object alhuddepth_object_t; tut::alhuddepth_t alhuddepth_testcase("ALHUDDepth"); + } // The final blit's copy fills the window's depth with the scene's, as the window's 24 bits hold it. template<> template<> diff --git a/indra/newview/tests/alnotecardembedded_test.cpp b/indra/newview/tests/alnotecardembedded_test.cpp index a3f85e3905e..995e21ea109 100644 --- a/indra/newview/tests/alnotecardembedded_test.cpp +++ b/indra/newview/tests/alnotecardembedded_test.cpp @@ -47,15 +47,6 @@ #include #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. -class LLAvatarName; -const std::string gEmbeddedTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gEmbeddedTestAnonName; -} - namespace { // The viewer's side, answered as a test says, and what was asked of it diff --git a/indra/newview/tests/alrecoverykeeper_test.cpp b/indra/newview/tests/alrecoverykeeper_test.cpp index 6f841e5e435..a81229a4cdf 100644 --- a/indra/newview/tests/alrecoverykeeper_test.cpp +++ b/indra/newview/tests/alrecoverykeeper_test.cpp @@ -36,15 +36,6 @@ #include #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. -class LLAvatarName; -const std::string gKeeperTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gKeeperTestAnonName; -} - namespace tut { struct alrecoverykeeper_data diff --git a/indra/newview/tests/alscriptanalysislane_test.cpp b/indra/newview/tests/alscriptanalysislane_test.cpp index 1ab4490c71b..1bbdf7d2b1c 100644 --- a/indra/newview/tests/alscriptanalysislane_test.cpp +++ b/indra/newview/tests/alscriptanalysislane_test.cpp @@ -39,6 +39,8 @@ #include namespace tut +{ +namespace { // What the lanes answered, in the order they did, from their threads. struct Answers @@ -149,6 +151,7 @@ namespace tut bool mStops = false; bool mStopped = false; }; +} // anonymous namespace struct alscriptanalysislane_data { diff --git a/indra/newview/tests/alscriptanalyzers_test.cpp b/indra/newview/tests/alscriptanalyzers_test.cpp index 2802497566b..e9e21ce2466 100644 --- a/indra/newview/tests/alscriptanalyzers_test.cpp +++ b/indra/newview/tests/alscriptanalyzers_test.cpp @@ -28,15 +28,6 @@ #include "../test/lltut.h" -// llui reaches the viewer for this one, and on Linux the link pulls the -// object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gAnalyzersTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gAnalyzersTestAnonName; -} - namespace tut { struct alscriptanalyzers_data diff --git a/indra/newview/tests/alscriptcrumbsbar_test.cpp b/indra/newview/tests/alscriptcrumbsbar_test.cpp index 01abac2242b..ee586112d62 100644 --- a/indra/newview/tests/alscriptcrumbsbar_test.cpp +++ b/indra/newview/tests/alscriptcrumbsbar_test.cpp @@ -41,6 +41,8 @@ #include "../test/lltut.h" +#include + namespace { typedef ALScriptStudioDoc Doc; @@ -103,6 +105,39 @@ namespace } const std::string SCRIPT = "default\n{\n state_entry()\n {\n }\n touch_start(integer n)\n {\n }\n}\n\nf()\n{\n}\n" "g(){}h(){}\nstate other\n{\n timer()\n {\n }\n}\n"; + + // A colour of the table set while this lives, and put back as it was + // after -- a person's own again, or none -- for the UI tests after it. + class HeldColor + { + public: + HeldColor(const std::string& name, const LLColor4& color) : mName(name) + { + const auto& own = LLUIColorTable::instance().getUserColors(); + if (const auto found = own.find(name); found != own.end()) + { + mWas = found->second.get(); + } + LLUIColorTable::instance().setColor(name, color); + } + ~HeldColor() + { + if (mWas) + { + LLUIColorTable::instance().setColor(mName, *mWas); + } + else + { + LLUIColorTable::instance().resetToDefault(mName); + } + } + HeldColor(const HeldColor&) = delete; + HeldColor& operator=(const HeldColor&) = delete; + + private: + std::string mName; + std::optional mWas; + }; } namespace tut @@ -110,14 +145,11 @@ namespace tut struct alscriptcrumbsbar_data { al_studio_test::StudioWindowOf window; - // The marks' colours told apart, as the skin's are: with none, every // mark is one red. - alscriptcrumbsbar_data() - { - LLUIColorTable::instance().setColor("CodeMarkWarning", LLColor4::yellow); - LLUIColorTable::instance().setColor("CodeMarkError", LLColor4::red); - } + HeldColor warningMark{ "CodeMarkWarning", LLColor4::yellow }; + HeldColor errorMark{ "CodeMarkError", LLColor4::red }; + ~alscriptcrumbsbar_data() { gFocusMgr.setKeyboardFocus(nullptr); } ALScriptCrumbsBar* bar() { diff --git a/indra/newview/tests/alscriptexplorermodel_test.cpp b/indra/newview/tests/alscriptexplorermodel_test.cpp index bf54165f627..82b3fbbaa63 100644 --- a/indra/newview/tests/alscriptexplorermodel_test.cpp +++ b/indra/newview/tests/alscriptexplorermodel_test.cpp @@ -34,15 +34,6 @@ #include #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gExplorerTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gExplorerTestAnonName; -} - namespace tut { struct alscriptexplorermodel_data diff --git a/indra/newview/tests/alscriptexplorertree_test.cpp b/indra/newview/tests/alscriptexplorertree_test.cpp index 30270ec3d05..0af65611784 100644 --- a/indra/newview/tests/alscriptexplorertree_test.cpp +++ b/indra/newview/tests/alscriptexplorertree_test.cpp @@ -39,15 +39,6 @@ #include #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. -class LLAvatarName; -const std::string gExplorerTreeTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gExplorerTreeTestAnonName; -} - // Two functors the folder view declares and the viewer's inventory code // defines; linking the folder view pulls them, and nothing here runs them. void LLOpenFilteredFolders::doFolder(LLFolderViewFolder* folder) {} diff --git a/indra/newview/tests/alscriptkeymap_test.cpp b/indra/newview/tests/alscriptkeymap_test.cpp index 2f620fe8bb8..4a75dec821b 100644 --- a/indra/newview/tests/alscriptkeymap_test.cpp +++ b/indra/newview/tests/alscriptkeymap_test.cpp @@ -26,6 +26,9 @@ #include "../alscriptkeymap.h" #include "../alscriptkeypresets.h" +// The settings the keymap is kept in, the viewer's group, which the test +// links (newview_test_settings.cpp). +#include "../llviewercontrol.h" #include "llcontrol.h" #include "llkeyboard.h" @@ -37,16 +40,14 @@ #include #include -// The setting the keymap is kept in, in a group of the test's own. -LLControlGroup gSavedSettings("Global"); - -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. -class LLAvatarName; -const std::string gKeymapTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) +namespace { - return gKeymapTestAnonName; + // What names keys as a person reads them, which LLKeyboard keeps + // without saying. + struct KeyTranslator : LLKeyboard + { + static LLKeyStringTranslatorFunc* current() { return mStringTranslator; } + }; } namespace tut @@ -55,6 +56,12 @@ namespace tut { typedef ALEditorCommand C; + // What the fixture sets, as it was: put back as each test ends, for + // whatever runs after it. + LLKeyStringTranslatorFunc* translatorWas = KeyTranslator::current(); + LLSD keysWas; + std::string presetWas; + alscriptkeymap_data() { LLKeyboard::setStringTranslatorFunc([](std::string_view name) { return std::string(name); }); @@ -66,9 +73,19 @@ namespace tut { gSavedSettings.declareString("ALScriptStudioKeymapPreset", "studio", "another editor's keys", LLControlVariable::PERSIST_NO); } + keysWas = gSavedSettings.getLLSD("ALScriptStudioKeymap"); + presetWas = gSavedSettings.getString("ALScriptStudioKeymapPreset"); ALScriptKeymap::setPreset(ALScriptKeyPresets::STANDARD); ALScriptKeymap::restoreAll(); } + ~alscriptkeymap_data() + { + // The keymap hears its settings change, and builds again from + // them when next asked. + gSavedSettings.setString("ALScriptStudioKeymapPreset", presetWas); + gSavedSettings.setLLSD("ALScriptStudioKeymap", keysWas); + LLKeyboard::setStringTranslatorFunc(translatorWas); + } static bool has(const ALScriptKeymap::chords_t& keys, const ALKeyChord& chord) { diff --git a/indra/newview/tests/alscriptlookup_test.cpp b/indra/newview/tests/alscriptlookup_test.cpp index 72a9f821923..1997e4ac15a 100644 --- a/indra/newview/tests/alscriptlookup_test.cpp +++ b/indra/newview/tests/alscriptlookup_test.cpp @@ -36,6 +36,7 @@ #include "../alscriptstudiowords.h" #include "alscriptstudio_fixture.h" #include "llfocusmgr.h" +#include "newview_test_fakes.h" #include "../test/lltut.h" @@ -941,7 +942,7 @@ namespace tut // A folder of the scripter's, as the window lists it, where it // stands: what the walk names each file by. namespace fs = std::filesystem; - const fs::path made = fs::temp_directory_path() / ("alscriptlookup_" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count())); + const fs::path made = fs::temp_directory_path() / ("alscriptlookup_" + LLUUID::generateNewID().asString()); fs::create_directories(made); const fs::path root = fs::canonical(made); const std::string LIB_TEXT = "local M = {}\nfunction M.twice(n) return n * 2 end\nreturn M\n"; @@ -1031,7 +1032,7 @@ namespace tut set_test_name("a file open here by another way to its path is the one file: looked through once, as its tab has it, and found to be where the name is declared"); make(); namespace fs = std::filesystem; - const fs::path made = fs::temp_directory_path() / ("alscriptlookup_" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count())); + const fs::path made = fs::temp_directory_path() / ("alscriptlookup_" + LLUUID::generateNewID().asString()); fs::create_directories(made / "sub"); const fs::path root = fs::canonical(made); const std::string LIB_TEXT = "local M = {}\nfunction M.twice(n) return n * 2 end\nreturn M\n"; @@ -1068,7 +1069,7 @@ namespace tut set_test_name("with a main loop to hand them back to, the folders on disk are walked and read off the main thread, the lookup held open for them; nothing comes of a walk once the lookups are gone"); make(); namespace fs = std::filesystem; - const fs::path made = fs::temp_directory_path() / ("alscriptlookup_" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count())); + const fs::path made = fs::temp_directory_path() / ("alscriptlookup_" + LLUUID::generateNewID().asString()); fs::create_directories(made); const fs::path root = fs::canonical(made); const std::string B_LUA = "local lib = require(\"./lib\")\nprint(lib.twice(4))\n"; @@ -1076,8 +1077,8 @@ namespace tut studio.disk = { root.string() }; // The main loop's queue, as the viewer's is: kept, since a walk // let go of may hand back to it after the test. - static LL::WorkQueue main_loop("mainloop", 1024); - const auto pumped = [](const std::function& until, std::chrono::milliseconds within) { + LL::WorkQueue& main_loop = newview_test::mainLoop(); + const auto pumped = [&main_loop](const std::function& until, std::chrono::milliseconds within) { const auto deadline = std::chrono::steady_clock::now() + within; while (std::chrono::steady_clock::now() < deadline) { diff --git a/indra/newview/tests/alscriptobjectcheck_test.cpp b/indra/newview/tests/alscriptobjectcheck_test.cpp index 447731a8730..3a6ebb765c6 100644 --- a/indra/newview/tests/alscriptobjectcheck_test.cpp +++ b/indra/newview/tests/alscriptobjectcheck_test.cpp @@ -40,40 +40,15 @@ #include #include -// The lints as a scripter chose them are the viewer's settings: here, every -// warning named "off" dropped. -void ALScriptLints::apply(ALScriptProblems& problems) -{ - problems.erase(std::remove_if(problems.begin(), problems.end(), [](const ALScriptProblem& p) { return p.code == "off"; }), problems.end()); -} -ALLuauConfig ALScriptLints::luauBase() -{ - return ALLuauConfig(); -} -std::string alScriptKeyedWords(const std::string&, const std::vector&, const std::string& english) -{ - return english; -} -bool ALScriptPreprocessor::fileOf(const std::string& path, std::string& file) +namespace { - if (path.rfind("disk:", 0) != 0) + // The lints as a scripter chose them are the viewer's settings: here, + // every warning named "off" dropped. + void applyLints(ALScriptProblems& problems) { - return false; + problems.erase(std::remove_if(problems.begin(), problems.end(), [](const ALScriptProblem& p) { return p.code == "off"; }), problems.end()); } - file = path.substr(5); - return !file.empty(); -} -std::string ALScriptPreprocessor::pathOf(const ALScriptRef& ref) -{ - return "object:" + ref.object.asString() + ":" + ref.item.asString(); -} -std::string ALScriptModules::identity(const std::string& path) -{ - return path; -} -namespace -{ typedef ALScriptObjectCheck Check; typedef Check::Window Window; @@ -173,6 +148,7 @@ namespace tut { struct alscriptobjectcheck_data { + al_studio_test::ChosenLints lints{ applyLints }; al_studio_test::FakeServices services; FakeWindow window; Check check{ services, window, window }; diff --git a/indra/newview/tests/alscriptpaths_stub.cpp b/indra/newview/tests/alscriptpaths_stub.cpp deleted file mode 100644 index 33bf3fcd597..00000000000 --- a/indra/newview/tests/alscriptpaths_stub.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/** - * @file alscriptpaths_stub.cpp - * @brief A script's id, and the preprocessor's names for scripts and files, stubbed for Script Studio's tests. - * - * $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" - -// The preprocessor's header reaches the inventory model's, which does not -// include what it uses. -#include - -#include "../alscriptpreprocessor.h" -#include "../alscripttypes.h" - -// Apart from the other stubs (alscriptstudio_stubs.cpp): a test that spells -// these itself takes nothing from here, and one that needs them takes -// them alone. - -// A script's id is the bridge's name for it, and the bridge is the viewer's. -std::string ALScriptRef::id() const -{ - return object.asString() + "_" + item.asString(); -} - -// What the preprocessor calls a script and a file, as it spells them; the -// preprocessor itself is the viewer's. -bool ALScriptPreprocessor::refOf(const std::string& path, ALScriptRef& ref) -{ - if (path.rfind("object:", 0) == 0) - { - const size_t colon = path.find(':', 7); - if (colon == std::string::npos) - { - return false; - } - ref.object.set(path.substr(7, colon - 7)); - ref.item.set(path.substr(colon + 1)); - return ref.object.notNull() && ref.item.notNull(); - } - if (path.rfind("inventory:", 0) == 0) - { - ref.object.setNull(); - ref.item.set(path.substr(10)); - return ref.item.notNull(); - } - return false; -} - -std::string ALScriptPreprocessor::pathOf(const ALScriptRef& ref) -{ - return ref.inInventory() ? "inventory:" + ref.item.asString() : "object:" + ref.object.asString() + ":" + ref.item.asString(); -} - -bool ALScriptPreprocessor::fileOf(const std::string& path, std::string& file) -{ - if (path.rfind("disk:", 0) != 0) - { - return false; - } - file = path.substr(5); - return !file.empty(); -} diff --git a/indra/newview/tests/alscriptstudio_fixture.h b/indra/newview/tests/alscriptstudio_fixture.h index badb8b026da..bf3a330679e 100644 --- a/indra/newview/tests/alscriptstudio_fixture.h +++ b/indra/newview/tests/alscriptstudio_fixture.h @@ -56,6 +56,7 @@ #include "llxmlnode.h" #include "../../llui/tests/alheadlessui_fixture.h" +#include "alscriptstudio_stubs.h" #include #include @@ -64,16 +65,6 @@ #include #include -// llui reaches the viewer for this one, and linking any of the library pulls -// the object that calls it. Nothing under test goes near it. One test binary -// includes this once. -class LLAvatarName; -const std::string gStudioTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gStudioTestAnonName; -} - namespace al_studio_test { // A viewer of the test's for the studio's units (ALScriptStudioViewer): diff --git a/indra/newview/tests/alscriptstudio_stubs.cpp b/indra/newview/tests/alscriptstudio_stubs.cpp index 244a9a585f2..146a94c1d89 100644 --- a/indra/newview/tests/alscriptstudio_stubs.cpp +++ b/indra/newview/tests/alscriptstudio_stubs.cpp @@ -25,8 +25,21 @@ #include "../llviewerprecompiledheaders.h" +#include "alscriptstudio_stubs.h" + +// The preprocessor's header reaches the inventory model's, which does not +// include what it uses. +#include + +#include "../alscriptanalysis.h" +#include "../alscriptmodules.h" +#include "../alscriptpreprocessor.h" +#include "../alscripttypes.h" #include "../lllogchat.h" +// One fake of each, for every test: where two tests want one to behave +// differently, each chooses (alscriptstudio_stubs.h). + // The external editor's log says when on its first line, by the chat log's // clock, which is the viewer's: a fixed word here, which a test can look // for. @@ -34,3 +47,76 @@ std::string LLLogChat::timestamp2LogString(U32, bool, bool) { return "[when]"; } + +// What the preprocessor calls a script and a file, as it spells them; the +// preprocessor itself is the viewer's. +bool ALScriptPreprocessor::refOf(const std::string& path, ALScriptRef& ref) +{ + if (path.rfind("object:", 0) == 0) + { + const size_t colon = path.find(':', 7); + if (colon == std::string::npos) + { + return false; + } + ref.object.set(path.substr(7, colon - 7)); + ref.item.set(path.substr(colon + 1)); + return ref.object.notNull() && ref.item.notNull(); + } + if (path.rfind("inventory:", 0) == 0) + { + ref.object.setNull(); + ref.item.set(path.substr(10)); + return ref.item.notNull(); + } + return false; +} + +std::string ALScriptPreprocessor::pathOf(const ALScriptRef& ref) +{ + return ref.inInventory() ? "inventory:" + ref.item.asString() : "object:" + ref.object.asString() + ":" + ref.item.asString(); +} + +bool ALScriptPreprocessor::fileOf(const std::string& path, std::string& file) +{ + if (path.rfind("disk:", 0) != 0) + { + return false; + } + file = path.substr(5); + return !file.empty(); +} + +// A module's identity is the path it was reached by; the modules index is +// the viewer's. +std::string ALScriptModules::identity(const std::string& path) +{ + return path; +} + +// The skin's words for a key are the viewer's: the English they came with. +std::string alScriptKeyedWords(const std::string&, const std::vector&, const std::string& english) +{ + return english; +} + +// The lints as a scripter chose them are the viewer's settings: what a test +// chose, and an SLua script checked with the defaults. +std::function& al_studio_test::chosenLints() +{ + static std::function chosen; + return chosen; +} + +void ALScriptLints::apply(ALScriptProblems& problems) +{ + if (const std::function& chosen = al_studio_test::chosenLints()) + { + chosen(problems); + } +} + +ALLuauConfig ALScriptLints::luauBase() +{ + return ALLuauConfig(); +} diff --git a/indra/newview/tests/alscriptstudio_stubs.h b/indra/newview/tests/alscriptstudio_stubs.h new file mode 100644 index 00000000000..914225140df --- /dev/null +++ b/indra/newview/tests/alscriptstudio_stubs.h @@ -0,0 +1,55 @@ +/** + * @file alscriptstudio_stubs.h + * @brief What a test chooses of the stubs Script Studio's units are tested over. + * + * $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_ALSCRIPTSTUDIO_STUBS_H +#define AL_ALSCRIPTSTUDIO_STUBS_H + +#include "alscriptproblem.h" + +#include +#include + +namespace al_studio_test +{ + // What the stub of ALScriptLints::apply does with a check's problems: + // the lints as a scripter chose them, which are the viewer's settings. + // Nothing, until a test chooses. + std::function& chosenLints(); + + // A test's choice of the lints while this lives, and the choice before + // it put back after, however the test ends. + class ChosenLints + { + public: + explicit ChosenLints(std::function apply) : mWas(std::exchange(chosenLints(), std::move(apply))) {} + ~ChosenLints() { chosenLints() = std::move(mWas); } + ChosenLints(const ChosenLints&) = delete; + ChosenLints& operator=(const ChosenLints&) = delete; + + private: + std::function mWas; + }; +} + +#endif // AL_ALSCRIPTSTUDIO_STUBS_H diff --git a/indra/newview/tests/alscriptstudiochecking_test.cpp b/indra/newview/tests/alscriptstudiochecking_test.cpp index e19cf29753e..3f07e834996 100644 --- a/indra/newview/tests/alscriptstudiochecking_test.cpp +++ b/indra/newview/tests/alscriptstudiochecking_test.cpp @@ -45,50 +45,24 @@ #include #include -// The lints as a scripter chose them, and the skin's words for a key, are -// the viewer's settings. +// The lints as a scripter chose them are the viewer's settings: here, +// counted, and every warning dropped while they drop warnings. namespace { S32 gLintsApplied = 0; // Whether the lints as chosen drop every warning. bool gWarningsOff = false; -} -void ALScriptLints::apply(ALScriptProblems& problems) -{ - ++gLintsApplied; - if (gWarningsOff) - { - problems.erase(std::remove_if(problems.begin(), problems.end(), - [](const ALScriptProblem& p) { return p.severity == ALScriptProblem::Severity::Warning; }), - problems.end()); - } -} -ALLuauConfig ALScriptLints::luauBase() -{ - return ALLuauConfig(); -} -std::string alScriptKeyedWords(const std::string&, const std::vector&, const std::string& english) -{ - return english; -} -// What the preprocessor and the modules index call a script and a file, -// as they spell them; both are the viewer's. -bool ALScriptPreprocessor::fileOf(const std::string& path, std::string& file) -{ - if (path.rfind("disk:", 0) != 0) + + void applyLints(ALScriptProblems& problems) { - return false; + ++gLintsApplied; + if (gWarningsOff) + { + problems.erase(std::remove_if(problems.begin(), problems.end(), + [](const ALScriptProblem& p) { return p.severity == ALScriptProblem::Severity::Warning; }), + problems.end()); + } } - file = path.substr(5); - return !file.empty(); -} -std::string ALScriptPreprocessor::pathOf(const ALScriptRef& ref) -{ - return "object:" + ref.object.asString() + ":" + ref.item.asString(); -} -std::string ALScriptModules::identity(const std::string& path) -{ - return path; } namespace @@ -237,10 +211,7 @@ namespace return out; } const std::string SCRIPT = "integer count;\ndefault\n{\n state_entry() { count = 1; }\n}\n"; -} -namespace tut -{ // A notecard's items need a world to be made with; this one answers // nothing. class NoWorld final : public ALNotecardEmbedded::World @@ -258,9 +229,13 @@ namespace tut bool pastDragStart(S32, S32) override { return false; } void dragOut(const LLInventoryItem&, const ALScriptRef&) override {} }; +} +namespace tut +{ struct alscriptstudiochecking_data { + al_studio_test::ChosenLints lints{ applyLints }; // The viewer the units ask, attached first and let go of last. al_studio_test::StudioViewer viewer; al_studio_test::StudioWindow window; diff --git a/indra/newview/tests/alscriptstudiofiles_test.cpp b/indra/newview/tests/alscriptstudiofiles_test.cpp index 916e23201e4..4d27ef2112c 100644 --- a/indra/newview/tests/alscriptstudiofiles_test.cpp +++ b/indra/newview/tests/alscriptstudiofiles_test.cpp @@ -38,12 +38,6 @@ #include #include -// A script's id is the bridge's name for it, and the bridge is the viewer's. -std::string ALScriptRef::id() const -{ - return object.asString() + "_" + item.asString(); -} - namespace { typedef ALScriptStudioDoc Doc; diff --git a/indra/newview/tests/alscriptstudiomasters_test.cpp b/indra/newview/tests/alscriptstudiomasters_test.cpp index 08fc2c7616c..8c163bbcf2c 100644 --- a/indra/newview/tests/alscriptstudiomasters_test.cpp +++ b/indra/newview/tests/alscriptstudiomasters_test.cpp @@ -32,6 +32,7 @@ #include "alwatchedfile.h" #include "fsyspath.h" #include "llfile.h" +#include "newview_test_fakes.h" #include "workqueue.h" #include "../test/lltut.h" @@ -242,13 +243,8 @@ namespace tut } // The main loop's queue, as the viewer's is, which a look at the - // tabs waits for: kept for the rest of the run, since what was posted - // to it for a test gone finds nobody. - static LL::WorkQueue& mainLoop() - { - static LL::WorkQueue queue("mainloop", 1024); - return queue; - } + // tabs waits for: the process's, kept for the rest of the run. + static LL::WorkQueue& mainLoop() { return newview_test::mainLoop(); } // Whatever was waiting for the main loop, done. static void settle() { mainLoop().runPending(); } diff --git a/indra/newview/tests/alscriptstudiorecovery_test.cpp b/indra/newview/tests/alscriptstudiorecovery_test.cpp index 1c3c0f0088b..47d64566309 100644 --- a/indra/newview/tests/alscriptstudiorecovery_test.cpp +++ b/indra/newview/tests/alscriptstudiorecovery_test.cpp @@ -666,6 +666,19 @@ namespace tut const std::string folder = fsyspath(std::filesystem::temp_directory_path() / fsyspath("alsavehistory_" + LLUUID::generateNewID().asString())).string(); auto history = std::make_shared(folder); + // The history is the process's: the one before put back, and the + // folder taken out of the way, however this ends. + struct HistoryBack + { + std::shared_ptr was; + std::string folder; + ~HistoryBack() + { + ALRecovery::useHistory(was); + std::error_code ec; + std::filesystem::remove_all(fsyspath(folder), ec); + } + } history_back{ ALRecovery::history(), folder }; ALRecovery::useHistory(history); // Everything posted before this, done: the writer is one thread. const auto written = []() { @@ -694,8 +707,5 @@ namespace tut ensure_equals("behind the new one's", moved.size(), 2U); ALRecovery::keepSaved(save("", "nobody's", 5.0)); written(); - ALRecovery::useHistory(nullptr); - std::error_code ec; - std::filesystem::remove_all(fsyspath(folder), ec); } } diff --git a/indra/newview/tests/alscripttheme_test.cpp b/indra/newview/tests/alscripttheme_test.cpp index f738138cabe..b7092e39bca 100644 --- a/indra/newview/tests/alscripttheme_test.cpp +++ b/indra/newview/tests/alscripttheme_test.cpp @@ -41,13 +41,6 @@ #include #include -class LLAvatarName; -const std::string gScriptThemeTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gScriptThemeTestAnonName; -} - namespace tut { struct alscripttheme_data @@ -121,6 +114,13 @@ namespace tut { config->declareString("ALScriptStudioTheme", std::string(), "The script editor's theme"); } + // The UI's settings are every UI test's: the theme chosen before put + // back however this ends. + struct ChosenBack + { + const std::string was = ALScriptTheme::chosen(); + ~ChosenBack() { ALScriptTheme::setChosen(was); } + } chosen_back; ALScriptTheme::setChosen("Monokai"); ensure_equals("the setting says it", config->getString("ALScriptStudioTheme"), std::string("Monokai")); ensure_equals("and so does the theme", ALScriptTheme::chosen(), std::string("Monokai")); diff --git a/indra/newview/tests/alselectionoutline_gl_test.cpp b/indra/newview/tests/alselectionoutline_gl_test.cpp index 16fc6d5abfd..ca5d9ac9d31 100644 --- a/indra/newview/tests/alselectionoutline_gl_test.cpp +++ b/indra/newview/tests/alselectionoutline_gl_test.cpp @@ -161,33 +161,22 @@ namespace tut { return al_ortho(0.f, (F32)W, 0.f, (F32)H, 0.f, 10.f); } - } struct alselectionoutline_data { - static ll_test::HeadlessGL& gl() - { - static ll_test::HeadlessGL instance(true, true, true, false); - return instance; - } - + // Each test starts forward, as the window does, whatever ran before: + // the ones that set no convention of their own draw under it. alselectionoutline_data() : mShaders(std::string(AL_TEST_SHADER_DIR) + "/class") { - gl(); + setConvention(false); } + // The convention, matrices, viewport and shader the tests set go back + // with mGL. ~alselectionoutline_data() { tearDown(); - setConvention(false); - gGL.matrixMode(LLRender::MM_PROJECTION); - gGL.loadIdentity(); - gGL.matrixMode(LLRender::MM_TEXTURE0); - gGL.loadIdentity(); - gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.loadIdentity(); - glViewport(0, 0, ll_test::HeadlessGL::WIDTH, ll_test::HeadlessGL::HEIGHT); } // Unloading a program deletes the shared objects attached to it, so a new convention compiles them again. @@ -885,6 +874,7 @@ namespace tut return rect; } + ll_test::SharedGLScope mGL; ll_test::TestShaderMgr mShaders; LLGLSLShader mIdProgram; LLGLSLShader mJumpProgram; @@ -926,6 +916,7 @@ namespace tut typedef test_group alselectionoutline_t; typedef alselectionoutline_t::object alselectionoutline_object_t; tut::alselectionoutline_t alselectionoutline_testcase("ALSelectionOutline"); + } // Two objects side by side: each is outlined around its outer contour in its own colour, a solid line two // pixels wide at a UI scale of 1. The edge between them is drawn once, thin and faint, on the side of the one @@ -2376,8 +2367,12 @@ namespace tut const LLUUID b("c0ffee00-0000-4000-8000-00000000000b"); std::vector alive = { a, b }; auto is_alive = [&alive](const LLUUID& id) { return std::find(alive.begin(), alive.end(), id) != alive.end(); }; - // A fresh singleton for this case, given up at its end. - ALHoverGlow::deleteSingleton(); + // A fresh singleton for this case, given up at its end however it ends. + struct FreshGlow + { + FreshGlow() { ALHoverGlow::deleteSingleton(); } + ~FreshGlow() { ALHoverGlow::deleteSingleton(); } + } fresh_glow; ALHoverGlow& glow = ALHoverGlow::instance(); auto fade = [&glow](const LLUUID& id) { @@ -2453,7 +2448,6 @@ namespace tut glow.hover(b, 100); ensure("named a frame ago, it is still named", glow.hovered(101) == b); ensure("unnamed for longer, the name lapses", glow.hovered(102).isNull()); - ALHoverGlow::deleteSingleton(); const F64 kept = Outline::TARGET_KEEP_SECONDS; ensure("a target drawn with this frame is kept", !Outline::stale(5, 100.0, 5, 100.0 + kept * 2.0)); diff --git a/indra/newview/tests/cppfeatures_test.cpp b/indra/newview/tests/cppfeatures_test.cpp index ca94dcfc95e..82d697f3328 100644 --- a/indra/newview/tests/cppfeatures_test.cpp +++ b/indra/newview/tests/cppfeatures_test.cpp @@ -134,6 +134,9 @@ void cpp_features_test_object_t::test<3>() // * Some compilers require that any class using override must use it consistently for all functions. // This makes switching a class to use override a lot more work. +namespace +{ + class Foo { public: @@ -150,6 +153,8 @@ class Bar: public Foo // bool is_happx() override { return true; } }; +} // anonymous namespace + template<> template<> void cpp_features_test_object_t::test<4>() { @@ -163,6 +168,9 @@ void cpp_features_test_object_t::test<4>() // virtual function cannot be overridden in a derived class or that a // class cannot be inherited from." +namespace +{ + class Vehicle { public: @@ -182,6 +190,8 @@ class Bicycle: public WheeledVehicle // virtual bool has_wheels() override const { return true; } }; +} // anonymous namespace + template<> template<> void cpp_features_test_object_t::test<5>() { @@ -203,6 +213,9 @@ void cpp_features_test_object_t::test<5>() // * Makes the intention clear // * Creates an error sooner, at compile time +namespace +{ + class DoNotCopy { public: @@ -210,6 +223,8 @@ class DoNotCopy DoNotCopy(const DoNotCopy& ref) = delete; }; +} // anonymous namespace + template<> template<> void cpp_features_test_object_t::test<6>() { @@ -226,6 +241,9 @@ void cpp_features_test_object_t::test<6>() // and are happy with the default implementation the compiler will make (memberwise copy). // Now you can explicitly declare that too. // Usage: I guess it makes the intent clearer, but otherwise not obviously useful. +namespace +{ + class DefaultCopyOK { public: @@ -236,6 +254,8 @@ class DefaultCopyOK S32 mVal; }; +} // anonymous namespace + template<> template<> void cpp_features_test_object_t::test<7>() { @@ -256,6 +276,9 @@ void cpp_features_test_object_t::test<7>() // It is preferred to skip creating a constructor if all the work can be done by inline initialization: // http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines.html#c45-dont-define-a-default-constructor-that-only-initializes-data-members-use-in-class-member-initializers-instead // +namespace +{ + class InitInline { public: @@ -276,6 +299,8 @@ class InitInlineWithConstructor S32 mBar = 20; }; +} // anonymous namespace + template<> template<> void cpp_features_test_object_t::test<8>() { @@ -292,6 +317,9 @@ void cpp_features_test_object_t::test<8>() // https://en.cppreference.com/w/cpp/language/constexpr // // Various things can be computed at compile time, and flagged as constexpr. +namespace +{ + constexpr S32 compute2() { return 2; } constexpr S32 ce_factorial(S32 n) @@ -306,6 +334,8 @@ constexpr S32 ce_factorial(S32 n) } } +} // anonymous namespace + template<> template<> void cpp_features_test_object_t::test<9>() { diff --git a/indra/newview/tests/llagentaccess_test.cpp b/indra/newview/tests/llagentaccess_test.cpp index 6f5b3a97216..747157a808e 100644 --- a/indra/newview/tests/llagentaccess_test.cpp +++ b/indra/newview/tests/llagentaccess_test.cpp @@ -34,38 +34,6 @@ #include -//---------------------------------------------------------------------------- -// Implementation of enough of LLControlGroup to support the tests: - -static U32 test_preferred_maturity = SIM_ACCESS_PG; - -LLControlGroup::LLControlGroup(const std::string& name) -: LLInstanceTracker(name) -{ -} - -LLControlGroup::~LLControlGroup() -{ -} - -// Implementation of just the LLControlGroup methods we requre -LLControlVariable* LLControlGroup::declareU32(const std::string& name, U32 initial_val, const std::string& comment, LLControlVariable::ePersist persist) -{ - test_preferred_maturity = initial_val; - return NULL; -} - -void LLControlGroup::setU32(std::string_view name, U32 val) -{ - test_preferred_maturity = val; -} - -U32 LLControlGroup::getU32(std::string_view name) -{ - return test_preferred_maturity; -} -//---------------------------------------------------------------------------- - namespace tut { struct agentaccess @@ -79,7 +47,7 @@ namespace tut template<> template<> void agentaccess_object_t::test<1>() { - LLControlGroup cgr("test"); + LLControlGroup cgr("llagentaccess"); cgr.declareU32("PreferredMaturity", SIM_ACCESS_PG, "declared_for_test", LLControlVariable::PERSIST_NO); LLAgentAccess aa(cgr); @@ -108,7 +76,7 @@ namespace tut template<> template<> void agentaccess_object_t::test<2>() { - LLControlGroup cgr("test"); + LLControlGroup cgr("llagentaccess"); cgr.declareU32("PreferredMaturity", SIM_ACCESS_PG, "declared_for_test", LLControlVariable::PERSIST_NO); LLAgentAccess aa(cgr); @@ -156,7 +124,7 @@ namespace tut template<> template<> void agentaccess_object_t::test<3>() { - LLControlGroup cgr("test"); + LLControlGroup cgr("llagentaccess"); cgr.declareU32("PreferredMaturity", SIM_ACCESS_PG, "declared_for_test", LLControlVariable::PERSIST_NO); LLAgentAccess aa(cgr); @@ -194,7 +162,7 @@ namespace tut template<> template<> void agentaccess_object_t::test<4>() { - LLControlGroup cgr("test"); + LLControlGroup cgr("llagentaccess"); cgr.declareU32("PreferredMaturity", SIM_ACCESS_PG, "declared_for_test", LLControlVariable::PERSIST_NO); LLAgentAccess aa(cgr); @@ -271,7 +239,7 @@ namespace tut template<> template<> void agentaccess_object_t::test<5>() { - LLControlGroup cgr("test"); + LLControlGroup cgr("llagentaccess"); cgr.declareU32("PreferredMaturity", SIM_ACCESS_PG, "declared_for_test", LLControlVariable::PERSIST_NO); LLAgentAccess aa(cgr); diff --git a/indra/newview/tests/lldateutil_test.cpp b/indra/newview/tests/lldateutil_test.cpp index 79f1a2791d2..cbe8b250a79 100644 --- a/indra/newview/tests/lldateutil_test.cpp +++ b/indra/newview/tests/lldateutil_test.cpp @@ -37,6 +37,8 @@ #include +namespace +{ // Baked-in return values for getString() boost::unordered_map< std::string, std::string, ll::string_hash, std::equal_to<>> gString; @@ -44,6 +46,7 @@ boost::unordered_map< std::string, std::string, ll::string_hash, std::equal_to<> // map of pairs of input xml_desc and integer count typedef std::pair< std::string, int > count_string_t; std::map gCountString; +} // anonymous namespace std::string LLTrans::getString(const std::string_view xml_desc, const LLStringUtil::format_map_t& args, bool def_string) { diff --git a/indra/newview/tests/llhttpretrypolicy_test.cpp b/indra/newview/tests/llhttpretrypolicy_test.cpp index 5c995c807b8..a261ec86997 100644 --- a/indra/newview/tests/llhttpretrypolicy_test.cpp +++ b/indra/newview/tests/llhttpretrypolicy_test.cpp @@ -30,11 +30,11 @@ namespace tut { -struct TestData +struct llhttpretrypolicy_data { }; -typedef test_group RetryPolicyTestGroup; +typedef test_group RetryPolicyTestGroup; typedef RetryPolicyTestGroup::object RetryPolicyTestObject; RetryPolicyTestGroup retryPolicyTestGroup("retry_policy"); diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp index 612b285a3a4..b1a2db70654 100644 --- a/indra/newview/tests/lllogininstance_test.cpp +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -60,7 +60,13 @@ const std::string VIEWERLOGIN_VERSION("invalid_version"); // Link seams. //----------------------------------------------------------------------------- -static LLEventStream gTestPump("test_pump"); +// The login's event pump, made the first time it is asked for rather than as +// the process starts: a pump's name is its key among LLEventPumps'. +static LLEventPump& testPump() +{ + static LLEventStream pump("test_pump"); + return pump; +} #include "../llslurl.h" #include "../llstartup.h" @@ -90,7 +96,7 @@ class LLLogin::Impl }; LLLogin::LLLogin() {} LLLogin::~LLLogin() {} -LLEventPump& LLLogin::getEventPump() { return gTestPump; } +LLEventPump& LLLogin::getEventPump() { return testPump(); } void LLLogin::connect(const std::string& uri, const LLSD& credentials) { gLoginURI = uri; @@ -194,7 +200,7 @@ std::string LLGridManager::getGridId(const std::string& grid) //----------------------------------------------------------------------------- #include "../llviewercontrol.h" -LLControlGroup gSavedSettings("Global"); +LLControlGroup gSavedSettings("lllogininstance"); LLControlGroup::LLControlGroup(const std::string& name) : LLInstanceTracker(name){} @@ -249,6 +255,8 @@ void LLProgressView::setMessage(std::string const &){} //----------------------------------------------------------------------------- // LLNotifications +namespace +{ class MockNotifications : public LLNotificationsInterface { std::function mResponder; @@ -300,6 +308,7 @@ class MockNotifications : public LLNotificationsInterface int addedCount() { return mAddedCount; } }; +} // anonymous namespace S32 LLNotification::getSelectedOption(const LLSD& notification, const LLSD& response) { @@ -372,6 +381,12 @@ namespace tut logininstance->setNotificationsInterface(¬ifications); logininstance->setPlatformInfo("win", "1.3.5", "Windows Bogus Version 100.6.6.6"); } + ~lllogininstance_data() + { + // The login instance outlives the test: not left holding the + // fixture's notifications once they are gone. + logininstance->setNotificationsInterface(nullptr); + } LLLoginInstance* logininstance; LLPointer agentCredential; @@ -401,7 +416,7 @@ namespace tut response["transfer_rate"] = 7; response["data"] = "test_data"; - gTestPump.post(response); + testPump().post(response); ensure("Success response", logininstance->authSuccess()); ensure_equals("Test Response Data", logininstance->getResponse().asString(), "test_data"); @@ -417,7 +432,7 @@ namespace tut response["transfer_rate"] = 0; response["data"] = "test_data"; - gTestPump.post(response); + testPump().post(response); ensure("Disconnected", !(logininstance->authSuccess())); } @@ -444,7 +459,7 @@ namespace tut response["progress"] = 0.0; response["transfer_rate"] = 7; response["data"]["reason"] = "tos"; - gTestPump.post(response); + testPump().post(response); ensure_equals("TOS Dialog type", gTOSType, "message_tos"); ensure("TOS callback given", gTOSReplyPump != 0); @@ -453,7 +468,7 @@ namespace tut // Start again. logininstance->connect(test_uri, agentCredential); - gTestPump.post(response); // Fail for tos again. + testPump().post(response); // Fail for tos again. gTOSReplyPump->post(true); // Accept tos, should reconnect w/ agree_to_tos. ensure_equals("Accepted agree to tos", gLoginCreds["params"]["agree_to_tos"].asBoolean(), true); ensure("Incomplete login status", !logininstance->authFailure() && !logininstance->authSuccess()); @@ -461,7 +476,7 @@ namespace tut // Fail connection, attempt connect again. // The new request should have reset agree to tos to default. response["data"]["reason"] = "key"; // bad creds. - gTestPump.post(response); + testPump().post(response); ensure("TOS auth failure", logininstance->authFailure()); logininstance->connect(test_uri, agentCredential); @@ -470,7 +485,7 @@ namespace tut // Critical Message failure response. logininstance->connect(test_uri, agentCredential); response["data"]["reason"] = "critical"; // Change response to "critical message" - gTestPump.post(response); + testPump().post(response); ensure_equals("TOS Dialog type", gTOSType, "message_critical"); ensure("TOS callback given", gTOSReplyPump != 0); @@ -480,7 +495,7 @@ namespace tut // Fail then attempt new connection response["data"]["reason"] = "key"; // bad creds. - gTestPump.post(response); + testPump().post(response); ensure("TOS auth failure", logininstance->authFailure()); logininstance->connect(test_uri, agentCredential); ensure_equals("Default for agree to tos", gLoginCreds["params"]["read_critical"].asBoolean(), false); diff --git a/indra/newview/tests/llremoteparcelrequest_test.cpp b/indra/newview/tests/llremoteparcelrequest_test.cpp index 0a829d9208d..d1eae88f460 100644 --- a/indra/newview/tests/llremoteparcelrequest_test.cpp +++ b/indra/newview/tests/llremoteparcelrequest_test.cpp @@ -31,7 +31,6 @@ #include "../llremoteparcelrequest.h" #include "../llagent.h" -#include "llurlentry.h" namespace { const LLUUID TEST_PARCEL_ID("11111111-1111-1111-1111-111111111111"); @@ -52,12 +51,10 @@ LLAgent::~LLAgent() { } void LLAgent::sendReliableMessage(void) { } LLUUID gAgentSessionID; LLUUID gAgentID; -LLUIColor::LLUIColor(void) { } -LLControlGroup::LLControlGroup(std::string const & name) : LLInstanceTracker(name) { } -LLControlGroup::~LLControlGroup(void) { } -void LLUrlEntryParcel::processParcelInfo(const LLUrlEntryParcel::LLParcelData& parcel_data) { } namespace tut +{ +namespace { struct TestObserver : public LLRemoteParcelInfoObserver { TestObserver() : mProcessed(false) { } @@ -73,6 +70,7 @@ namespace tut bool mProcessed; }; +} // anonymous namespace struct RemoteParcelRequestData { diff --git a/indra/newview/tests/llsecapi_test.cpp b/indra/newview/tests/llsecapi_test.cpp index 1a2fa7d8f6f..f065d29458f 100644 --- a/indra/newview/tests/llsecapi_test.cpp +++ b/indra/newview/tests/llsecapi_test.cpp @@ -50,7 +50,7 @@ std::string LLControlGroup::getString(std::string_view name) } -LLControlGroup gSavedSettings("test"); +LLControlGroup gSavedSettings("llsecapi"); LLSecAPIBasicHandler::LLSecAPIBasicHandler() {} void LLSecAPIBasicHandler::init() {} @@ -98,7 +98,7 @@ namespace tut // Tut templating thingamagic: test group, object and test instance typedef test_group secapiTestFactory; typedef secapiTestFactory::object secapiTestObject; - tut::secapiTestFactory tut_test("LLSecAPI"); + tut::secapiTestFactory tut_secapi("LLSecAPI"); // --------------------------------------------------------------------------------------- // Test functions diff --git a/indra/newview/tests/llsechandler_basic_test.cpp b/indra/newview/tests/llsechandler_basic_test.cpp index fc8d2192423..b9a51c67b30 100644 --- a/indra/newview/tests/llsechandler_basic_test.cpp +++ b/indra/newview/tests/llsechandler_basic_test.cpp @@ -70,8 +70,8 @@ extern bool _cert_hostname_wildcard_match(const std::string& hostname, const std //---------------------------------------------------------------------------- // Mock objects for the dependencies of the code we're testing -std::string gFirstName; -std::string gLastName; +static std::string gFirstName; +static std::string gLastName; LLControlGroup::LLControlGroup(const std::string& name) : LLInstanceTracker(name) {} LLControlGroup::~LLControlGroup() {} @@ -113,7 +113,7 @@ void LLCredential::authenticatorType(std::string &idType) } -LLControlGroup gSavedSettings("test"); +LLControlGroup gSavedSettings("llsechandler_basic"); unsigned char gMACAddress[MAC_ADDRESS_BYTES] = {77,21,46,31,89,2}; @@ -705,7 +705,7 @@ namespace tut // Tut templating thingamagic: test group, object and test instance typedef test_group sechandler_basic_test_factory; typedef sechandler_basic_test_factory::object sechandler_basic_test_object; - tut::sechandler_basic_test_factory tut_test("LLSecHandler"); + tut::sechandler_basic_test_factory tut_sechandler_basic("LLSecHandler"); // --------------------------------------------------------------------------------------- // Test functions diff --git a/indra/newview/tests/llslurl_test.cpp b/indra/newview/tests/llslurl_test.cpp index eae2646a619..2e0f4488425 100644 --- a/indra/newview/tests/llslurl_test.cpp +++ b/indra/newview/tests/llslurl_test.cpp @@ -102,11 +102,11 @@ LLControlVariable* LLControlGroup::declareString(const std::string& name, LLControlVariable::ePersist persist) {return NULL;} void LLControlGroup::setString(std::string_view name, const std::string& val){} -std::string gCmdLineLoginURI; -std::string gCmdLineGridChoice; -std::string gCmdLineHelperURI; -std::string gLoginPage; -std::string gCurrentGrid; +static std::string gCmdLineLoginURI; +static std::string gCmdLineGridChoice; +static std::string gCmdLineHelperURI; +static std::string gLoginPage; +static std::string gCurrentGrid; std::string LLControlGroup::getString(std::string_view name) { if (name == "CmdLineGridChoice") @@ -138,8 +138,8 @@ LLPointer LLControlGroup::getControl(std::string_view name) return iter == mNameTable.end() ? LLPointer() : iter->second; } -LLControlGroup gSavedSettings("test"); -const char *gSampleGridFile = +LLControlGroup gSavedSettings("llslurl"); +static const char *gSampleGridFile = "" "" " " @@ -195,7 +195,7 @@ namespace tut // Tut templating thingamagic: test group, object and test instance typedef test_group slurlTestFactory; typedef slurlTestFactory::object slurlTestObject; - tut::slurlTestFactory tut_test("LLSlurl"); + tut::slurlTestFactory tut_slurl("LLSlurl"); // --------------------------------------------------------------------------------------- // Test functions diff --git a/indra/newview/tests/lltextureinfo_test.cpp b/indra/newview/tests/lltextureinfo_test.cpp index 17222794059..2dcab18c142 100644 --- a/indra/newview/tests/lltextureinfo_test.cpp +++ b/indra/newview/tests/lltextureinfo_test.cpp @@ -50,8 +50,6 @@ // * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code) // * A simulator for a class can be implemented here. Please comment and document thoroughly. -LLControlGroup gSavedSettings("Global"); - void send_texture_stats_to_sim(const LLSD& texture_stats) {} LLAgent gAgent; diff --git a/indra/newview/tests/llversioninfo_test.cpp b/indra/newview/tests/llversioninfo_test.cpp index a78235c5f7c..2e217c7a193 100644 --- a/indra/newview/tests/llversioninfo_test.cpp +++ b/indra/newview/tests/llversioninfo_test.cpp @@ -111,6 +111,12 @@ namespace tut LLVersionInfo::instance().getChannelAndVersion(), mVersionAndChannel); + // The channel is the process's: put back however this ends. + struct ChannelBack + { + const std::string was = LLVersionInfo::instance().getChannel(); + ~ChannelBack() { LLVersionInfo::instance().resetChannel(was); } + } channel_back; LLVersionInfo::instance().resetChannel(mResetChannel); ensure_equals("Reset channel version", LLVersionInfo::instance().getChannel(), diff --git a/indra/newview/tests/llviewerassetstats_test.cpp b/indra/newview/tests/llviewerassetstats_test.cpp index 10c68432a15..323affadeda 100644 --- a/indra/newview/tests/llviewerassetstats_test.cpp +++ b/indra/newview/tests/llviewerassetstats_test.cpp @@ -239,6 +239,39 @@ get_region(const LLSD & sd, U64 region_handle1) namespace tut { + namespace + { + // The fixture's ThreadRecorder makes itself this thread's recorder, + // with its own timer stack and current accumulators, and leaves all + // of them null when it goes; the fixture makes it the master recorder + // too. This takes the runner's as they were before the test's + // recorder, and puts them back after it. + struct RecorderRestore + { + template + using Current = LLThreadLocalSingletonPointer; + + LLTrace::ThreadRecorder* mMaster{ LLTrace::get_master_thread_recorder() }; + LLTrace::ThreadRecorder* mRecorder{ LLTrace::get_thread_recorder() }; + LLTrace::BlockTimerStackRecord* mTimerStack{ Current::getInstance() }; + LLTrace::CountAccumulator* mCounts{ Current::getInstance() }; + LLTrace::SampleAccumulator* mSamples{ Current::getInstance() }; + LLTrace::EventAccumulator* mEvents{ Current::getInstance() }; + LLTrace::TimeBlockAccumulator* mTimers{ Current::getInstance() }; + + ~RecorderRestore() + { + LLTrace::set_master_thread_recorder(mMaster); + LLTrace::set_thread_recorder(mRecorder); + Current::setInstance(mTimerStack); + Current::setInstance(mCounts); + Current::setInstance(mSamples); + Current::setInstance(mEvents); + Current::setInstance(mTimers); + } + }; + } // anonymous namespace + struct tst_viewerassetstats_index { tst_viewerassetstats_index() @@ -251,6 +284,8 @@ namespace tut LLTrace::set_master_thread_recorder(NULL); } + // before mThreadRecorder, so it is put back after mThreadRecorder is gone + RecorderRestore mRestore; LLTrace::ThreadRecorder mThreadRecorder; }; typedef test_group tst_viewerassetstats_index_t; diff --git a/indra/newview/tests/llviewercontrollistener_test.cpp b/indra/newview/tests/llviewercontrollistener_test.cpp index f261a045447..8fb71b726d4 100644 --- a/indra/newview/tests/llviewercontrollistener_test.cpp +++ b/indra/newview/tests/llviewercontrollistener_test.cpp @@ -22,15 +22,20 @@ #include "commoncontrol.h" #include "llcontrol.h" // LLControlGroup +#include + /***************************************************************************** * TUT *****************************************************************************/ namespace tut +{ +namespace { void ensure_contains(const std::string& msg, const std::string& substr) { - ensure_contains("Exception does not contain " + substr, msg, substr); + tut::ensure_contains("Exception does not contain " + substr, msg, substr); } +} // anonymous namespace struct llviewercontrollistener_data { @@ -139,9 +144,18 @@ namespace tut void object::test<9>() { set_test_name("CommonControl get_groups"); - std::vector groups{ LL::CommonControl::get_groups() }; - ensure_equals(groups.size(), 1); - ensure_equals(groups[0], "FakeGlobal"); + // Every group the process has: the fixture's, and whatever else has + // been made -- HeadlessUI's, another test's gSavedSettings. So the + // fixture's is there once, and one more group made adds its name. + const std::vector groups{ LL::CommonControl::get_groups() }; + ensure_equals("FakeGlobal", std::count(groups.begin(), groups.end(), "FakeGlobal"), std::ptrdiff_t(1)); + { + LLControlGroup another{ "FakeAnother" }; + const std::vector more{ LL::CommonControl::get_groups() }; + ensure_equals("one more", more.size(), groups.size() + 1); + ensure_equals("FakeAnother", std::count(more.begin(), more.end(), "FakeAnother"), std::ptrdiff_t(1)); + } + ensure_equals("one fewer once it goes", LL::CommonControl::get_groups().size(), groups.size()); } template<> template<> diff --git a/indra/newview/tests/llviewerhelputil_test.cpp b/indra/newview/tests/llviewerhelputil_test.cpp index 9ee6625bf11..9c9ae0e083e 100644 --- a/indra/newview/tests/llviewerhelputil_test.cpp +++ b/indra/newview/tests/llviewerhelputil_test.cpp @@ -60,7 +60,7 @@ std::string LLControlGroup::getString(std::string_view name) return gHelpURL; return ""; } -LLControlGroup gSavedSettings("test"); +LLControlGroup gSavedSettings("llviewerhelputil"); static void substitute_string(std::string &input, const std::string &search, const std::string &replace) { diff --git a/indra/newview/tests/llviewernetwork_test.cpp b/indra/newview/tests/llviewernetwork_test.cpp index 6190d7052b1..63501b09dd5 100644 --- a/indra/newview/tests/llviewernetwork_test.cpp +++ b/indra/newview/tests/llviewernetwork_test.cpp @@ -75,11 +75,11 @@ LLControlVariable* LLControlGroup::declareString(const std::string& name, LLControlVariable::ePersist persist) {return NULL;} void LLControlGroup::setString(std::string_view name, const std::string& val){} -std::string gCmdLineLoginURI; -std::string gCmdLineGridChoice; -std::string gCmdLineHelperURI; -std::string gLoginPage; -std::string gCurrentGrid; +static std::string gCmdLineLoginURI; +static std::string gCmdLineGridChoice; +static std::string gCmdLineHelperURI; +static std::string gLoginPage; +static std::string gCurrentGrid; std::string LLControlGroup::getString(std::string_view name) { if (name == "CmdLineGridChoice") @@ -111,9 +111,9 @@ LLPointer LLControlGroup::getControl(std::string_view name) return iter == mNameTable.end() ? LLPointer() : iter->second; } -LLControlGroup gSavedSettings("test"); +LLControlGroup gSavedSettings("llviewernetwork"); -const char *gSampleGridFile = +static const char *gSampleGridFile = "" "" " " @@ -193,7 +193,7 @@ namespace tut // Tut templating thingamagic: test group, object and test instance typedef test_group viewerNetworkTestFactory; typedef viewerNetworkTestFactory::object viewerNetworkTestObject; - tut::viewerNetworkTestFactory tut_test("LLViewerNetwork"); + tut::viewerNetworkTestFactory tut_viewernetwork("LLViewerNetwork"); // --------------------------------------------------------------------------------------- // Test functions diff --git a/indra/newview/tests/llvocache_test.cpp b/indra/newview/tests/llvocache_test.cpp index d8d159d77e3..d4076625e07 100644 --- a/indra/newview/tests/llvocache_test.cpp +++ b/indra/newview/tests/llvocache_test.cpp @@ -278,7 +278,7 @@ namespace tut }; typedef vocacheTestFactory::object vocacheTestObject; - tut::vocacheTestFactory tut_test; + tut::vocacheTestFactory tut_vocache; // --------------------------------------------------------------------------------------- // Test functions diff --git a/indra/newview/tests/llworldmap_test.cpp b/indra/newview/tests/llworldmap_test.cpp index 01b0153c00f..655ebb50521 100644 --- a/indra/newview/tests/llworldmap_test.cpp +++ b/indra/newview/tests/llworldmap_test.cpp @@ -54,12 +54,15 @@ LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTexture(const LLUUID&, // Stub related map calls +namespace +{ // Records each sendMapBlockRequest call for inspection in tests struct MapBlockRequest { U16 min_x, min_y, max_x, max_y; }; -static std::vector gMapBlockRequests; +std::vector gMapBlockRequests; +} // anonymous namespace LLWorldMapMessage::LLWorldMapMessage() { } LLWorldMapMessage::~LLWorldMapMessage() { } @@ -86,7 +89,7 @@ void LLUIString::assign(std::string_view ) { } LLControlGroup::LLControlGroup(const std::string& name) : LLInstanceTracker(name) {} LLControlGroup::~LLControlGroup() {} bool LLControlGroup::getBOOL(std::string_view) { return true; } -LLControlGroup gSavedSettings("test_settings"); +LLControlGroup gSavedSettings("llworldmap"); // End Stubbing // ------------------------------------------------------------------------------------------- diff --git a/indra/newview/tests/llworldmipmap_test.cpp b/indra/newview/tests/llworldmipmap_test.cpp index 0a5de18e0f4..76b50c4c5b3 100644 --- a/indra/newview/tests/llworldmipmap_test.cpp +++ b/indra/newview/tests/llworldmipmap_test.cpp @@ -49,7 +49,7 @@ LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromUrl(const s LLControlGroup::LLControlGroup(const std::string& name) : LLInstanceTracker(name) { } LLControlGroup::~LLControlGroup() { } std::string LLControlGroup::getString(std::string_view) { return std::string("test_url"); } -LLControlGroup gSavedSettings("test_settings"); +LLControlGroup gSavedSettings("llworldmipmap"); // End Stubbing // ------------------------------------------------------------------------------------------- diff --git a/indra/newview/tests/newview_test_fakes.cpp b/indra/newview/tests/newview_test_fakes.cpp new file mode 100644 index 00000000000..517c4b7b715 --- /dev/null +++ b/indra/newview/tests/newview_test_fakes.cpp @@ -0,0 +1,51 @@ +/** + * @file newview_test_fakes.cpp + * @brief What newview's tests take of the viewer, faked once for every test that links it. + * + * $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 "newview_test_fakes.h" + +#include "workqueue.h" + +#include + +// An object, linked whole into each test that names it, rather than an +// archive's member, so each test binary has one definition of each. + +// llui reaches the viewer for this one, and linking any of the library pulls +// the object that calls it. Nothing under test goes near it. A linker that +// reads its archives once, in order, would have passed an archive listed +// before llui by the time llui asked. +class LLAvatarName; +const std::string& rlvGetAnonym(const LLAvatarName&) +{ + static const std::string anon("Anon"); + return anon; +} + +LL::WorkQueue& newview_test::mainLoop() +{ + static LL::WorkQueue queue("mainloop", 1024); + return queue; +} diff --git a/indra/newview/tests/newview_test_fakes.h b/indra/newview/tests/newview_test_fakes.h new file mode 100644 index 00000000000..3c0fd0ddfb5 --- /dev/null +++ b/indra/newview/tests/newview_test_fakes.h @@ -0,0 +1,42 @@ +/** + * @file newview_test_fakes.h + * @brief What newview's tests take of the viewer, faked once for every test that links it. + * + * $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_NEWVIEW_TEST_FAKES_H +#define AL_NEWVIEW_TEST_FAKES_H + +namespace LL +{ + class WorkQueue; +} + +namespace newview_test +{ + // The main loop's queue, as the viewer's is: made the first time a test + // asks for it, never while the process starts, and kept for the rest of + // the run, since what was posted to it for a test gone finds nobody. A + // queue's name is its key in a process, so every test shares this one. + LL::WorkQueue& mainLoop(); +} + +#endif // AL_NEWVIEW_TEST_FAKES_H diff --git a/indra/newview/tests/newview_test_settings.cpp b/indra/newview/tests/newview_test_settings.cpp new file mode 100644 index 00000000000..565e44aa964 --- /dev/null +++ b/indra/newview/tests/newview_test_settings.cpp @@ -0,0 +1,33 @@ +/** + * @file newview_test_settings.cpp + * @brief The viewer's settings group, for the tests whose code reads it. + * + * $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 "llcontrol.h" + +// The code under test names gSavedSettings, so it is an object from the +// start, as the viewer's is, and a group's name is its key in a process: one +// definition for every test that links this, named as the viewer names it, +// and only those tests link it. Each test declares the settings it reads. +LLControlGroup gSavedSettings("Global"); diff --git a/indra/test/alrandomreseed.h b/indra/test/alrandomreseed.h new file mode 100644 index 00000000000..5acad0c897e --- /dev/null +++ b/indra/test/alrandomreseed.h @@ -0,0 +1,51 @@ +/** + * @file alrandomreseed.h + * @brief llrand's generator seeded from entropy again, for a test that + * seeded it + * + * $LicenseInfo:firstyear=2026&license=viewerlgpl$ + * Second Life 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_ALRANDOMRESEED_H +#define AL_ALRANDOMRESEED_H + +#include "linden_common.h" + +#include "llrand.h" + +#include + +namespace ll_test +{ + // A test that seeds the thread's generator, so that what it draws is the + // same on every run, seeds it from entropy again as it ends, as a thread's + // first draw does. Left seeded, everything drawn after it on the thread + // -- by later tests, and by what they call, such as LLUUID's clock + // sequence -- would be one fixed sequence that hangs on which tests ran + // before. llrand cannot hand back the state it had, so this is the + // nearest to putting it back. + inline void reseedRandomFromEntropy() + { + std::random_device entropy; + ll_rand_seed((U64(entropy()) << 32) | entropy()); + } +} + +#endif // AL_ALRANDOMRESEED_H diff --git a/indra/viewer_components/login/CMakeLists.txt b/indra/viewer_components/login/CMakeLists.txt index 26d15b46341..7096951959b 100644 --- a/indra/viewer_components/login/CMakeLists.txt +++ b/indra/viewer_components/login/CMakeLists.txt @@ -11,5 +11,5 @@ target_link_libraries( ) if(AL_BUILD_TESTS) - al_add_test(lllogin UNIT PROJECT lllogin LIBRARIES llmessage llcorehttp llcommon) + al_add_test(lllogin PROJECT lllogin LIBRARIES lllogin) endif() diff --git a/indra/viewer_components/login/tests/lllogin_test.cpp b/indra/viewer_components/login/tests/lllogin_test.cpp index c21deecb205..1c8a7f7303b 100644 --- a/indra/viewer_components/login/tests/lllogin_test.cpp +++ b/indra/viewer_components/login/tests/lllogin_test.cpp @@ -65,6 +65,8 @@ /***************************************************************************** * Helper classes *****************************************************************************/ +namespace +{ // This is a listener to receive results from lllogin. class LoginListener: public LLEventTrackable { @@ -137,7 +139,8 @@ class LoginListener: public LLEventTrackable } }; -class LLXMLRPCListener: public LLEventTrackable +// Answers on the XMLRPC transaction pump in place of the viewer's XMLRPC code. +class FakeXMLRPCListener: public LLEventTrackable { std::string mName; LLSD mEvent; @@ -146,10 +149,10 @@ class LLXMLRPCListener: public LLEventTrackable Debug mDebug; public: - LLXMLRPCListener(const std::string& name, - bool i = false, - const LLSD& response = LLSD() - ) : + FakeXMLRPCListener(const std::string& name, + bool i = false, + const LLSD& response = LLSD() + ) : mName(name), mImmediateResponse(i), mResponse(response), @@ -172,7 +175,7 @@ class LLXMLRPCListener: public LLEventTrackable bool handle_event(const LLSD& event) { - mDebug(STRINGIZE("LLXMLRPCListener called!: " << event)); + mDebug(STRINGIZE("FakeXMLRPCListener called!: " << event)); mEvent = event; if(mImmediateResponse) { @@ -188,19 +191,22 @@ class LLXMLRPCListener: public LLEventTrackable LLBoundListener listenTo(LLEventPump& pump) { - return pump.listen(mName, boost::bind(&LLXMLRPCListener::handle_event, this, _1)); + return pump.listen(mName, boost::bind(&FakeXMLRPCListener::handle_event, this, _1)); } - friend std::ostream& operator<<(std::ostream& out, const LLXMLRPCListener& listener) + friend std::ostream& operator<<(std::ostream& out, const FakeXMLRPCListener& listener) { - return out << "LLXMLRPCListener(" << listener.mName << ')'; + return out << "FakeXMLRPCListener(" << listener.mName << ')'; } }; +} /***************************************************************************** * TUT *****************************************************************************/ namespace tut +{ +namespace { struct llviewerlogin_data { @@ -218,6 +224,7 @@ namespace tut typedef test_group llviewerlogin_group; typedef llviewerlogin_group::object llviewerlogin_object; llviewerlogin_group llviewerlogingrp("LLViewerLogin"); +} template<> template<> void llviewerlogin_object::test<1>() @@ -231,7 +238,7 @@ namespace tut bool respond_immediately = true; // Have dummy XMLRPC respond immediately. - LLXMLRPCListener dummyXMLRPC("dummy_xmlrpc", respond_immediately); + FakeXMLRPCListener dummyXMLRPC("dummy_xmlrpc", respond_immediately); LLTempBoundListener conn1 = dummyXMLRPC.listenTo(xmlrpcPump); LLLogin login; @@ -260,7 +267,7 @@ namespace tut // Testing normal login procedure. LLEventStream xmlrpcPump("LLXMLRPCTransaction"); // Dummy XMLRPC pump - LLXMLRPCListener dummyXMLRPC("dummy_xmlrpc"); + FakeXMLRPCListener dummyXMLRPC("dummy_xmlrpc"); LLTempBoundListener conn1 = dummyXMLRPC.listenTo(xmlrpcPump); LLLogin login; @@ -306,7 +313,7 @@ namespace tut // Testing normal login procedure. LLEventStream xmlrpcPump("LLXMLRPCTransaction"); // Dummy XMLRPC pump - LLXMLRPCListener dummyXMLRPC("dummy_xmlrpc"); + FakeXMLRPCListener dummyXMLRPC("dummy_xmlrpc"); LLTempBoundListener conn1 = dummyXMLRPC.listenTo(xmlrpcPump); LLLogin login;