From 4e2f031243c8cb575324a46edff2ae5c09420636 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 22:56:03 -0400 Subject: [PATCH 01/67] llmath's tests link llmath instead of compiling their own copies of it Seven unit tests compiled the file they test in place of the library, and seven more listed one of the library's files in SOURCES while linking the library as well, so llquaternion.cpp, for one, was built three times. Every llmath test now links llmath and compiles only its own test file, so the code under test is the code the viewer runs, and two tests can share a binary without defining the same symbols twice. The unit tests' targets are INTEGRATION_TEST_ now, like the rest. Co-Authored-By: Claude Opus 5.5 --- indra/llmath/CMakeLists.txt | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt index 87ff6123fd..a5dbe13fd9 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. From a30f63fdd99eba3b417cc472d48b796cb9d4b58f Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 22:56:03 -0400 Subject: [PATCH 02/67] mathmisc's UUID group and llsdutil_math's group object have names of their own llcommon's lluuid test registers a TUT group named "LLUUID" too, and TUT throws on a second group of one name from a static initializer, so the two in one binary would end before main. mathmisc's is "mathmisc LLUUID" now. llsdutil_math's group object was tut::tutil, the name llcommon's llsdutil test gives its own. Co-Authored-By: Claude Opus 5.5 --- indra/llmath/tests/llsdutil_math_test.cpp | 2 +- indra/llmath/tests/mathmisc_test.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/llmath/tests/llsdutil_math_test.cpp b/indra/llmath/tests/llsdutil_math_test.cpp index f7f096e59a..4d1995efac 100644 --- a/indra/llmath/tests/llsdutil_math_test.cpp +++ b/indra/llmath/tests/llsdutil_math_test.cpp @@ -77,7 +77,7 @@ 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/mathmisc_test.cpp b/indra/llmath/tests/mathmisc_test.cpp index 917d7cded3..9ec13ab33a 100644 --- a/indra/llmath/tests/mathmisc_test.cpp +++ b/indra/llmath/tests/mathmisc_test.cpp @@ -179,7 +179,7 @@ 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>() From ea34c2fff7389447b07b1598dfa0b526e0df49fd Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 22:56:25 -0400 Subject: [PATCH 03/67] alsimdkernels' denormal test puts back the FP mode it found Test 10 calls set_thread_fp_mode(), as the render thread does, to check a texture index carried in w as a denormal survives flush-to-zero. That mode is the main thread's MXCSR or FPCR, and nothing reset it, so every test run after it in the same process would flush denormals too. A guard in the test saves the control register and writes it back when the test ends, passed or failed. Co-Authored-By: Claude Opus 5.5 --- indra/llmath/tests/alsimdkernels_test.cpp | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/indra/llmath/tests/alsimdkernels_test.cpp b/indra/llmath/tests/alsimdkernels_test.cpp index eeff0e5722..f7b920f543 100644 --- a/indra/llmath/tests/alsimdkernels_test.cpp +++ b/indra/llmath/tests/alsimdkernels_test.cpp @@ -135,6 +135,41 @@ 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 @@ -683,6 +718,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}) From 8c84f5a4838ef1ae1475319bd9a0e50fccc068f2 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 22:56:35 -0400 Subject: [PATCH 04/67] llmath's test fixtures, groups and helpers have internal linkage Each test's TUT data struct, group object and file-local helpers sit in an anonymous namespace inside tut, so the test bodies TUT instantiates for them are internal too. Two tests in one binary could otherwise share a fixture name and run each other's code with no diagnostic. llmatrix4a's same_bytes and ensure_matrix_close were strong and external, and llrender's llrendermatrix test defines both; mathmisc's radius globals and alignment's aligned types were external as well. The test specializations stay in tut, where TUT declares the template. Co-Authored-By: Claude Opus 5.5 --- indra/llmath/tests/alignment_test.cpp | 3 +++ indra/llmath/tests/alplaneset_test.cpp | 3 +++ indra/llmath/tests/alsimdkernels_test.cpp | 3 +++ indra/llmath/tests/llbbox_test.cpp | 3 +++ indra/llmath/tests/llbboxlocal_test.cpp | 3 +++ indra/llmath/tests/llmatrix3a_test.cpp | 3 +++ indra/llmath/tests/llmatrix4a_test.cpp | 3 +++ indra/llmath/tests/llmodularmath_test.cpp | 3 +++ indra/llmath/tests/llquaternion2_test.cpp | 3 +++ indra/llmath/tests/llquaternion_test.cpp | 3 +++ indra/llmath/tests/llrect_test.cpp | 3 +++ indra/llmath/tests/llsdutil_math_test.cpp | 3 +++ indra/llmath/tests/llvector4a_test.cpp | 3 +++ indra/llmath/tests/m3math_test.cpp | 3 +++ indra/llmath/tests/mathmisc_test.cpp | 16 ++++++++++++++++ indra/llmath/tests/v2math_test.cpp | 3 +++ indra/llmath/tests/v3color_test.cpp | 3 +++ indra/llmath/tests/v3dmath_test.cpp | 3 +++ indra/llmath/tests/v3math_test.cpp | 3 +++ indra/llmath/tests/v4color_test.cpp | 3 +++ indra/llmath/tests/v4coloru_test.cpp | 3 +++ indra/llmath/tests/v4math_test.cpp | 3 +++ indra/llmath/tests/xform_test.cpp | 3 +++ 23 files changed, 82 insertions(+) diff --git a/indra/llmath/tests/alignment_test.cpp b/indra/llmath/tests/alignment_test.cpp index 0d77896958..3e4a25c66f 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 7baa4b3fd8..f39e19482d 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 f7b920f543..ccf24a3dea 100644 --- a/indra/llmath/tests/alsimdkernels_test.cpp +++ b/indra/llmath/tests/alsimdkernels_test.cpp @@ -174,6 +174,8 @@ namespace } // namespace tut namespace tut +{ +namespace { struct alsimdkernels_data { @@ -181,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 diff --git a/indra/llmath/tests/llbbox_test.cpp b/indra/llmath/tests/llbbox_test.cpp index bd6bd9e4b0..8b2c2bfaa2 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 120e18cadf..41d6326bc5 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 d1c99e70d3..b0da1591dd 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 74455efe8d..32192faead 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 399b3af05d..c9d23c07e0 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 7f5bf09ab4..db80e82076 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 cc09d59f25..a826eafa13 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 0fd5602d4a..a98907140c 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 4d1995efac..8c79d002da 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 { @@ -78,6 +80,7 @@ namespace tut typedef test_group llsdutil_math_test; typedef llsdutil_math_test::object llsdutil_math_object; 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 dcd73b96d8..18b34bb974 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 ab71ec6039..6a9a841436 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 9ec13ab33a..b303249fef 100644 --- a/indra/llmath/tests/mathmisc_test.cpp +++ b/indra/llmath/tests/mathmisc_test.cpp @@ -40,6 +40,8 @@ #include namespace tut +{ +namespace { struct math_data { @@ -47,6 +49,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 +175,8 @@ namespace tut } namespace tut +{ +namespace { struct uuid_data { @@ -180,6 +185,7 @@ namespace tut typedef test_group uuid_test; typedef uuid_test::object uuid_object; tut::uuid_test tu("mathmisc LLUUID"); +} template<> template<> void uuid_object::test<1>() @@ -228,6 +234,8 @@ namespace tut } namespace tut +{ +namespace { struct crc_data { @@ -235,6 +243,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 +289,8 @@ namespace tut } namespace tut +{ +namespace { F32 SMALL_RADIUS = 1.0f; F32 MEDIUM_RADIUS = 5.0f; @@ -291,6 +302,7 @@ namespace tut 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 +478,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 860e3ef672..7bce7d5fe7 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 8897d48365..0003c3cd60 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 f66962eaf7..10ee4c3523 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 6d95a9e5b7..805d692114 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 e5d914a065..b70a43f62f 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 ad181e76b2..1dd48d801a 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 236585f13b..aba953563f 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 6d6a64ec4c..39a72a1cc5 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<> From e9d26910dd5f244bece0ba52ef18fa7529de99ba Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:02:30 -0400 Subject: [PATCH 05/67] llxmlnode's test links llxml instead of compiling llxmlnode.cpp and alxmldocument.cpp As a unit test it built its own copies of two of llxml's files and linked pugixml for them. It links the library now, like llxml's other tests, so it tests the code the viewer runs and could share a binary with them. Co-Authored-By: Claude Opus 5.5 --- indra/llxml/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llxml/CMakeLists.txt b/indra/llxml/CMakeLists.txt index f9e26cf4ca..b1ca7f927f 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() From 6ebb10d77aacffcc26ac0b3c27df3113dc269cfc Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:02:36 -0400 Subject: [PATCH 06/67] alxmllayermerge's test works in a temp folder of its own Every fixture deleted and re-created /alxmllayermerge_test, so two runs of the test at once, as parallel CI shards or a per-file and a combined run would be, deleted each other's files. The folder's name now carries a fresh UUID, as llcontrol's test already does. Co-Authored-By: Claude Opus 5.5 --- indra/llxml/tests/alxmllayermerge_test.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/indra/llxml/tests/alxmllayermerge_test.cpp b/indra/llxml/tests/alxmllayermerge_test.cpp index 667fc7ceb3..efd11cd5dd 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" @@ -213,10 +214,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); } From 71926f8f9369452642eb75d49a522e8afc6dfe0b Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:02:36 -0400 Subject: [PATCH 07/67] llxml's test fixtures, groups and helpers have internal linkage Each test's TUT data struct, group object and helpers, alxmllayermerge's RecordingObserver among them, sit in an anonymous namespace inside tut, so the test bodies TUT instantiates for them are internal too, and no two tests sharing a binary can meet over a name. Co-Authored-By: Claude Opus 5.5 --- indra/llxml/tests/alxmllayermerge_test.cpp | 3 +++ indra/llxml/tests/llcontrol_test.cpp | 3 +++ indra/llxml/tests/llxmlnode_test.cpp | 3 +++ 3 files changed, 9 insertions(+) diff --git a/indra/llxml/tests/alxmllayermerge_test.cpp b/indra/llxml/tests/alxmllayermerge_test.cpp index efd11cd5dd..16eb13f899 100644 --- a/indra/llxml/tests/alxmllayermerge_test.cpp +++ b/indra/llxml/tests/alxmllayermerge_test.cpp @@ -36,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 @@ -238,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 0b28e20ff5..5f8811824b 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 9b38d06409..29c46e3f70 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<> From 91ed57f606969f4c91506d48f51a42ff09a2ef19 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:02:44 -0400 Subject: [PATCH 08/67] lljoint's test links llcharacter instead of compiling lljoint.cpp It listed lljoint.cpp in SOURCES and linked llcommon and llmath, so it tested its own build of the file. It links llcharacter now, as the rest of llcharacter's tests do. Co-Authored-By: Claude Opus 5.5 --- indra/llcharacter/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/indra/llcharacter/CMakeLists.txt b/indra/llcharacter/CMakeLists.txt index 51fcebbd12..6f88b72dc5 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 From ba90fc695ec6fb4b1b8eb4e949dee63e2750bbc5 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:02:44 -0400 Subject: [PATCH 09/67] llcharacter's test fixtures and groups have internal linkage Each test's TUT data struct and group object, and llpose's joints base that four of its groups share, sit in an anonymous namespace inside tut, so the test bodies TUT instantiates for them are internal too. The shared ALTestCharacter and ALTestMotion headers keep their one inline definition each. Co-Authored-By: Claude Opus 5.5 --- indra/llcharacter/tests/llbvhloader_test.cpp | 3 +++ indra/llcharacter/tests/llcharacter_test.cpp | 3 +++ indra/llcharacter/tests/lljoint_test.cpp | 3 +++ indra/llcharacter/tests/llkeyframemotion_test.cpp | 3 +++ .../llcharacter/tests/llmotioncontroller_test.cpp | 3 +++ indra/llcharacter/tests/llmultigesture_test.cpp | 3 +++ indra/llcharacter/tests/llpose_test.cpp | 15 +++++++++++++++ 7 files changed, 33 insertions(+) diff --git a/indra/llcharacter/tests/llbvhloader_test.cpp b/indra/llcharacter/tests/llbvhloader_test.cpp index c282e549b8..9aaf370aed 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 557904c431..ab4dd2b519 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 62f44b7cda..3c92936e6e 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 0e25dc82ab..2406d4595a 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 2bae0e0226..ceece16b05 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 a6ab7a53b8..20a20db4e7 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 5239046ba9..a722691dd1 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>() From bb621cd8c24cdcd9234e28d070a324c61e7f5e5d Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:02:44 -0400 Subject: [PATCH 10/67] llprimitive's unit tests link llprimitive instead of compiling the file they test llmediaentry, llgltfmaterial, llmaterialid and llmaterial each built their own copy of the file under test. They link the library now; the one tinygltf implementation they need still comes from llgltfmaterial's test. llprimitive's own test and llimageworker's stay unit tests, since they stub classes their libraries define, and their CMake says so. Co-Authored-By: Claude Opus 5.5 --- indra/llimage/CMakeLists.txt | 2 ++ indra/llprimitive/CMakeLists.txt | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/indra/llimage/CMakeLists.txt b/indra/llimage/CMakeLists.txt index 5976f054e3..5d9d9642ab 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/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt index 10c25f7798..109b537ef2 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() From 6e0469dda88143d81af1e28a354568f6be81a91a Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:02:53 -0400 Subject: [PATCH 11/67] llprimitive's test fixtures, groups and helpers have internal linkage Each test's TUT data struct, group object and helpers sit in an anonymous namespace inside tut, and llprimitive's DummyVolumeMgr and PRIMITIVE_TEST_SETUP in one at file scope, so nothing a test defines for itself is visible to another test in the same binary. llmediaentry's ensure_*, whitelist_test and URL constants were strong tut:: symbols; two of those helpers were never called, which the compiler now says, and they are gone. llprimitive's stubs of library members stay as they were. Co-Authored-By: Claude Opus 5.5 --- .../llprimitive/tests/llgltfmaterial_test.cpp | 6 +++++ indra/llprimitive/tests/llmaterial_test.cpp | 3 +++ indra/llprimitive/tests/llmaterialid_test.cpp | 3 +++ indra/llprimitive/tests/llmediaentry_test.cpp | 27 +++++++------------ indra/llprimitive/tests/llprimitive_test.cpp | 9 +++++++ 5 files changed, 30 insertions(+), 18 deletions(-) diff --git a/indra/llprimitive/tests/llgltfmaterial_test.cpp b/indra/llprimitive/tests/llgltfmaterial_test.cpp index dbeea1a1f5..479f3fc0cd 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 dfc44840ff..ce9168dea1 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 89c5883287..1060aaff41 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 414df66680..cb4c17e061 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 75c3613ece..d2008f43b0 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>() From e3f77f9cdafcf4a95329e5673c1fd78f9fcbce17 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:02:53 -0400 Subject: [PATCH 12/67] lldiriterator's test links llfilesystem instead of compiling lldiriterator.cpp As a unit test it built its own copy of the file it tests. It links the library now, like lldir's test, so the two could share a binary. Co-Authored-By: Claude Opus 5.5 --- indra/llfilesystem/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llfilesystem/CMakeLists.txt b/indra/llfilesystem/CMakeLists.txt index ecebdd1030..dce31cb115 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() From 7de527840509033bda0224f6f2434a458a5e8b7f Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:02:53 -0400 Subject: [PATCH 13/67] llfilesystem's test fixtures, groups and helpers have internal linkage lldiriterator's group object was the strong tut::tf and its CHOP-662 check the strong tut::test_chop_662(); lldir's makeTestFile, makeTestDir and scanTest were strong tut:: functions, and its LLDir_Dummy and LLDir_Relocated global types. All of them, with each TUT data struct and group, sit in anonymous namespaces now. lldir still gets a strong operator<< for vectors of strings from llcommon's StringVec.h, which is llcommon's to make inline. Co-Authored-By: Claude Opus 5.5 --- indra/llfilesystem/tests/lldir_test.cpp | 9 +++++++++ indra/llfilesystem/tests/lldiriterator_test.cpp | 3 +++ 2 files changed, 12 insertions(+) diff --git a/indra/llfilesystem/tests/lldir_test.cpp b/indra/llfilesystem/tests/lldir_test.cpp index 4c928e5e0e..3f243d1e3b 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 dc573b5555..15470bd966 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>() From addf95e08795b433fc93ef69741d2c195f9dc386 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:03:18 -0400 Subject: [PATCH 14/67] inventorymisc's tests write their file to a temp file of their own Tests 7 and 13 wrote linden_file.dat into the working directory and never removed it, so two runs at once wrote over each other's file and the build tree kept it. Each uses a NamedTempFile now, removed when the test ends. Co-Authored-By: Claude Opus 5.5 --- indra/llinventory/tests/inventorymisc_test.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/indra/llinventory/tests/inventorymisc_test.cpp b/indra/llinventory/tests/inventorymisc_test.cpp index f11a4c3bf7..7769ca5b53 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 @@ -351,7 +352,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 +486,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()) { From 83c61e4b6fcf9f8e5ee6d4276a7f9aa621fdbc9d Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:03:18 -0400 Subject: [PATCH 15/67] llinventory's test fixtures, groups and helpers have internal linkage Each test's TUT data struct and group object, inventorymisc's strong tut::inv among them, sit in an anonymous namespace inside tut, and inventorymisc's create_random_inventory_item, create_random_inventory_cat and set_random_inventory_metadata, strong global functions, in one at file scope. Co-Authored-By: Claude Opus 5.5 --- indra/llinventory/tests/inventorymisc_test.cpp | 6 ++++++ indra/llinventory/tests/llparcel_test.cpp | 3 +++ indra/llinventory/tests/llpermissions_test.cpp | 3 +++ indra/llinventory/tests/llsaleinfo_test.cpp | 3 +++ indra/llinventory/tests/lluserrelations_test.cpp | 3 +++ 5 files changed, 18 insertions(+) diff --git a/indra/llinventory/tests/inventorymisc_test.cpp b/indra/llinventory/tests/inventorymisc_test.cpp index 7769ca5b53..1a7eeeb26b 100644 --- a/indra/llinventory/tests/inventorymisc_test.cpp +++ b/indra/llinventory/tests/inventorymisc_test.cpp @@ -40,6 +40,8 @@ #pragma warning(disable: 4702) #endif +namespace +{ void set_random_inventory_metadata(LLInventoryObject* obj) { S32 extra = rand() % 4; @@ -123,8 +125,11 @@ LLPointer create_random_inventory_cat() set_random_inventory_metadata(cat); return cat; } +} namespace tut +{ +namespace { struct inventory_data { @@ -132,6 +137,7 @@ namespace tut typedef test_group inventory_test; typedef inventory_test::object inventory_object; tut::inventory_test inv("LLInventory"); +} //***class LLInventoryType***// diff --git a/indra/llinventory/tests/llparcel_test.cpp b/indra/llinventory/tests/llparcel_test.cpp index c6b433d468..c32d17b449 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 1328939004..c600e3a45d 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 1da7101aa3..44c47c2662 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/lluserrelations_test.cpp b/indra/llinventory/tests/lluserrelations_test.cpp index 80a659da65..0b0706c9b5 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>() From 73e5dbbb106e65d4f3eb8f4fad75de0453b113a1 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:03:29 -0400 Subject: [PATCH 16/67] llimageworker's test responder and fixture have internal linkage The responder class and the TUT data struct and group object sit in an anonymous namespace inside tut. The image class stubs stay external: they stand in for libllimage's members, which is why the test is still a unit test of its own. Co-Authored-By: Claude Opus 5.5 --- indra/llimage/tests/llimageworker_test.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/indra/llimage/tests/llimageworker_test.cpp b/indra/llimage/tests/llimageworker_test.cpp index 28c3feb2ab..663142d79d 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 From b30d035eacd4817b0efba8f9f26470e035d00c6b Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:03:29 -0400 Subject: [PATCH 17/67] lllogin's test links lllogin instead of compiling lllogin.cpp As a unit test it built its own copy of the file and named the libraries that copy needed. It links the library now, which brings those with it. Co-Authored-By: Claude Opus 5.5 --- indra/viewer_components/login/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/viewer_components/login/CMakeLists.txt b/indra/viewer_components/login/CMakeLists.txt index 26d15b4634..7096951959 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() From 90a945f5a1debe1c30642c2b3fa2a2981ba00a84 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:03:29 -0400 Subject: [PATCH 18/67] lllogin's test listeners have internal linkage, and none shares a viewer name The fake XMLRPC responder was a global class named LLXMLRPCListener, the name of a different class in newview's llxmlrpclistener.h; in one link with that, one would quietly have replaced the other's inline members and vtable. It is FakeXMLRPCListener now, and it, LoginListener and the TUT data struct and group sit in anonymous namespaces. The fixture still makes an LLTestApp and clears the event pumps as before. Co-Authored-By: Claude Opus 5.5 --- .../login/tests/lllogin_test.cpp | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/indra/viewer_components/login/tests/lllogin_test.cpp b/indra/viewer_components/login/tests/lllogin_test.cpp index c21deecb20..1c8a7f7303 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; From 41ff036fb8a2c127765d64785ae458d435f9bfd9 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 22:56:12 -0400 Subject: [PATCH 19/67] llcommon's tests define each shared helper once, and no two of them one name differently Each test is its own link today, so a duplicate never meets its twin; the moment two of these tests share a binary, each of the following is a link error or, worse, links and runs the other test's code. - The operator<< in StringVec.h and listener.h is inline, as a header's definitions must be. apply_test drops its own copy for StringVec.h's. - The three test groups named tut_singleton (llsingleton, lltrace, llunits) take their own names. - lleventcoro and lleventfilter each had a function template tut::test with a body of its own, instantiated for the same pumps: one group's tests 6 and 7 would have run the other's. Each is now static and says what it checks. - classic_callback and llpounceable each defined a ::Data of a different layout, and their other helpers, at global scope: they move into an anonymous namespace. Co-Authored-By: Claude Opus 5.5 --- indra/llcommon/tests/StringVec.h | 2 +- indra/llcommon/tests/apply_test.cpp | 16 ++-------------- indra/llcommon/tests/classic_callback_test.cpp | 5 +++++ indra/llcommon/tests/listener.h | 4 ++-- indra/llcommon/tests/lleventcoro_test.cpp | 7 ++++--- indra/llcommon/tests/lleventfilter_test.cpp | 7 ++++--- indra/llcommon/tests/llpounceable_test.cpp | 9 +++++++-- indra/llcommon/tests/llsingleton_test.cpp | 2 +- indra/llcommon/tests/lltrace_test.cpp | 2 +- indra/llcommon/tests/llunits_test.cpp | 2 +- 10 files changed, 28 insertions(+), 28 deletions(-) diff --git a/indra/llcommon/tests/StringVec.h b/indra/llcommon/tests/StringVec.h index 4311cba992..1c5341d1a4 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/apply_test.cpp b/indra/llcommon/tests/apply_test.cpp index 6d213c2db9..f1e4a23b1e 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" /***************************************************************************** diff --git a/indra/llcommon/tests/classic_callback_test.cpp b/indra/llcommon/tests/classic_callback_test.cpp index fe79179ce6..ec255ed1c7 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/listener.h b/indra/llcommon/tests/listener.h index 887a2c9082..9baa4c5627 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/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index 05c25e6627..94b3a72a1e 100644 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -258,8 +258,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 +325,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/lleventfilter_test.cpp b/indra/llcommon/tests/lleventfilter_test.cpp index 2a93d1946c..75926fab1f 100644 --- a/indra/llcommon/tests/lleventfilter_test.cpp +++ b/indra/llcommon/tests/lleventfilter_test.cpp @@ -405,8 +405,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 +468,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/llpounceable_test.cpp b/indra/llcommon/tests/llpounceable_test.cpp index b3024966c5..4c0b9aeb51 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 *****************************************************************************/ diff --git a/indra/llcommon/tests/llsingleton_test.cpp b/indra/llcommon/tests/llsingleton_test.cpp index adf5804272..0417ff4627 100644 --- a/indra/llcommon/tests/llsingleton_test.cpp +++ b/indra/llcommon/tests/llsingleton_test.cpp @@ -101,7 +101,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>() diff --git a/indra/llcommon/tests/lltrace_test.cpp b/indra/llcommon/tests/lltrace_test.cpp index 923a67ac8e..d40c076cda 100644 --- a/indra/llcommon/tests/lltrace_test.cpp +++ b/indra/llcommon/tests/lltrace_test.cpp @@ -66,7 +66,7 @@ namespace tut 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"); diff --git a/indra/llcommon/tests/llunits_test.cpp b/indra/llcommon/tests/llunits_test.cpp index 98a58eb47e..f90bb8e866 100644 --- a/indra/llcommon/tests/llunits_test.cpp +++ b/indra/llcommon/tests/llunits_test.cpp @@ -63,7 +63,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<> From 4be21ae121228e7b0fed9f42adb8f71b4e0af2d1 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:07:51 -0400 Subject: [PATCH 20/67] What an llcommon test defines for itself has internal linkage Only the names TUT needs stay external: each group's data struct, its test group object and the test specialisations. Every other type, function and variable a test file defines for itself moves into an anonymous namespace or becomes static, so two tests in one binary can no longer clash at link time or, silently, share one weak definition of a same-named type. The helpers of the shared headers (listener.h, wrapllerrs.h, the indra/test ones) keep one inline definition each. - llerror keeps TestAlpha, TestBeta, logFromGlobal and Foo::logFromNamespace as they are: log filtering matches their qualified names, and the global function is what tells it apart from the static and anonymous ones. - An anonymous namespace does not make a type's name unique: LockStatic, LLSingleton and LLInstanceTracker find their statics by typeid(T).name(), the same for every TU's (anonymous namespace)::X. llmainthreadtask's LockStatic key, a generic StaticData, takes a name of its own. - lleventcoro's data struct, the generic tut::test_data, is lleventcoro_data. - Internal linkage shows what nothing calls: llevents' chainEvents and llunits' accept_implicit_quatloos go; lltreeiterators' fallback strings and node printer, there for templates nothing instantiates, are [[maybe_unused]]. Co-Authored-By: Claude Opus 5.5 --- indra/llcommon/tests/apply_test.cpp | 20 +- indra/llcommon/tests/lazyeventapi_test.cpp | 6 + indra/llcommon/tests/lldependencies_test.cpp | 10 + .../llcommon/tests/lldoubledispatch_test.cpp | 4 + indra/llcommon/tests/llerror_test.cpp | 58 +++-- indra/llcommon/tests/lleventcoro_test.cpp | 19 +- .../llcommon/tests/lleventdispatcher_test.cpp | 95 ++++--- indra/llcommon/tests/lleventfilter_test.cpp | 5 + indra/llcommon/tests/llevents_test.cpp | 28 +- indra/llcommon/tests/llheteromap_test.cpp | 9 +- indra/llcommon/tests/llhttpdate_test.cpp | 4 +- indra/llcommon/tests/llinitparam_test.cpp | 10 + .../llcommon/tests/llinstancetracker_test.cpp | 5 + indra/llcommon/tests/llleap_test.cpp | 239 +++++++++--------- .../llcommon/tests/llmainthreadtask_test.cpp | 15 +- indra/llcommon/tests/llprocess_test.cpp | 168 ++++++------ indra/llcommon/tests/llrand_test.cpp | 4 +- indra/llcommon/tests/llsd_test.cpp | 63 ++--- indra/llcommon/tests/llsdserialize_test.cpp | 18 +- indra/llcommon/tests/llsingleton_test.cpp | 50 ++-- indra/llcommon/tests/llstring_test.cpp | 22 +- indra/llcommon/tests/lltrace_test.cpp | 21 +- indra/llcommon/tests/lltreeiterators_test.cpp | 19 +- indra/llcommon/tests/llunits_test.cpp | 29 ++- indra/llcommon/tests/lluuid_test.cpp | 49 ++-- 25 files changed, 555 insertions(+), 415 deletions(-) diff --git a/indra/llcommon/tests/apply_test.cpp b/indra/llcommon/tests/apply_test.cpp index f1e4a23b1e..6bb4e23b00 100644 --- a/indra/llcommon/tests/apply_test.cpp +++ b/indra/llcommon/tests/apply_test.cpp @@ -59,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) { @@ -79,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]); @@ -89,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]); @@ -99,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/lazyeventapi_test.cpp b/indra/llcommon/tests/lazyeventapi_test.cpp index f3fcf84e49..f2c2bf168e 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/lldependencies_test.cpp b/indra/llcommon/tests/lldependencies_test.cpp index fd554005a3..b13576a985 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 79c4edc058..bafa7a7404 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 e44cbf94e9..ce48c34a5a 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 94b3a72a1e..a170134c39 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,12 +88,14 @@ class ImmediateAPI Sync& mSync; }; +} // anonymous namespace + /***************************************************************************** * TUT *****************************************************************************/ namespace tut { - struct test_data + struct lleventcoro_data { Sync mSync; ImmediateAPI immediateAPI{mSync}; @@ -105,11 +110,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 +164,7 @@ namespace tut ensure_equals(stringdata, "received"); } - void test_data::waitForEventOn1() + void lleventcoro_data::waitForEventOn1() { BEGIN { @@ -185,7 +190,7 @@ namespace tut ensure_equals(result.asString(), "received"); } - void test_data::coroPump() + void lleventcoro_data::coroPump() { BEGIN { @@ -213,7 +218,7 @@ namespace tut ensure_equals(result.asString(), "received"); } - void test_data::postAndWait1() + void lleventcoro_data::postAndWait1() { BEGIN { @@ -236,7 +241,7 @@ namespace tut ensure_equals(result.asInteger(), 18); } - void test_data::coroPumpPost() + void lleventcoro_data::coroPumpPost() { BEGIN { diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp index 8b206f7b14..46daafedc3 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. @@ -312,19 +315,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 +338,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 +1085,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 +1178,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 +1339,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 75926fab1f..e8dd37ee60 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 *****************************************************************************/ diff --git a/indra/llcommon/tests/llevents_test.cpp b/indra/llcommon/tests/llevents_test.cpp index c5d5dcb9d4..a288cd0ee0 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; } @@ -187,20 +187,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 +357,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 +386,9 @@ void events_object::test<8>() ensure("threw Empty", !threw.empty()); } +namespace +{ + class TempListener: public Listener { public: @@ -418,6 +407,8 @@ class TempListener: public Listener bool& mLiveFlag; }; +} // anonymous namespace + template<> template<> void events_object::test<9>() { @@ -447,6 +438,9 @@ void events_object::test<9>() heaptest.stopListening("temp"); } +namespace +{ + class TempTrackableListener: public TempListener, public LLEventTrackable { public: @@ -455,6 +449,8 @@ class TempTrackableListener: public TempListener, public LLEventTrackable {} }; +} // anonymous namespace + template<> template<> void events_object::test<10>() { diff --git a/indra/llcommon/tests/llheteromap_test.cpp b/indra/llcommon/tests/llheteromap_test.cpp index cabfb91593..7d7a3f96f0 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 b580b09a9f..f56744bd08 100644 --- a/indra/llcommon/tests/llhttpdate_test.cpp +++ b/indra/llcommon/tests/llhttpdate_test.cpp @@ -99,8 +99,8 @@ 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) + 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); diff --git a/indra/llcommon/tests/llinitparam_test.cpp b/indra/llcommon/tests/llinitparam_test.cpp index 7d9bee378e..51c0196231 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 { @@ -151,6 +154,8 @@ struct TestPoint S32 mY; }; +} // anonymous namespace + namespace LLInitParam { template<> @@ -188,6 +193,9 @@ namespace LLInitParam }; } +namespace +{ + struct PointBlock : public LLInitParam::Block { Optional point; @@ -210,6 +218,8 @@ struct PointChoice : public LLInitParam::ChoiceBlock {} }; +} // anonymous namespace + namespace tut { struct llinitparam_data diff --git a/indra/llcommon/tests/llinstancetracker_test.cpp b/indra/llcommon/tests/llinstancetracker_test.cpp index bf661dc051..ae315e45c4 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 ae20e7ed27..d833e6185f 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 9ccf391327..a4fab4ba2f 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/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp index 43d1b243f2..9bd4680b4c 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,33 @@ 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) + { + 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; + }; + } // 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 fd64a6b3a2..5e7523578a 100644 --- a/indra/llcommon/tests/llrand_test.cpp +++ b/indra/llcommon/tests/llrand_test.cpp @@ -45,8 +45,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)); diff --git a/indra/llcommon/tests/llsd_test.cpp b/indra/llcommon/tests/llsd_test.cpp index cafdb051fb..6fe75d3bac 100644 --- a/indra/llcommon/tests/llsd_test.cpp +++ b/indra/llcommon/tests/llsd_test.cpp @@ -37,38 +37,41 @@ using std::fpclassify; namespace tut { - class SDCleanupCheck + namespace { - private: - U32 mOutstandingAtStart; - public: - SDCleanupCheck() : mOutstandingAtStart(llsd::outstandingCount()) { } - ~SDCleanupCheck() + class SDCleanupCheck { - ensure_equals("SDCleanupCheck", - llsd::outstandingCount(), mOutstandingAtStart); - } - }; + private: + U32 mOutstandingAtStart; + public: + SDCleanupCheck() : mOutstandingAtStart(llsd::outstandingCount()) { } + ~SDCleanupCheck() + { + ensure_equals("SDCleanupCheck", + llsd::outstandingCount(), mOutstandingAtStart); + } + }; - 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() + class SDAllocationCheck : public SDCleanupCheck { - ensure_equals(mMessage + " SDAllocationCheck", - llsd::allocationCount() - mAllocationAtStart, - mExpectedAllocations); - } - }; + private: + std::string mMessage; + U32 mExpectedAllocations; + U32 mAllocationAtStart; + public: + SDAllocationCheck(const std::string& message, int expectedAllocations) + : mMessage(message), + mExpectedAllocations(expectedAllocations), + mAllocationAtStart(llsd::allocationCount()) + { } + ~SDAllocationCheck() + { + ensure_equals(mMessage + " SDAllocationCheck", + llsd::allocationCount() - mAllocationAtStart, + mExpectedAllocations); + } + }; + } // anonymous namespace struct SDTestData { template @@ -218,7 +221,7 @@ namespace tut 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) { @@ -290,7 +293,7 @@ namespace tut } 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(); diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp index f8222692e9..189c18420c 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/llsingleton_test.cpp b/indra/llcommon/tests/llsingleton_test.cpp index 0417ff4627..51f2b92f12 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 @@ -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 ef3fa86eec..44a1d3f087 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 d40c076cda..e48029e65a 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); @@ -75,7 +78,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 6734596d25..44932ce7f6 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 f90bb8e866..cb7924f630 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); @@ -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 be1304c851..45da7e3705 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 From 0de917432889910627b4ab2576f48f0b887f8cf8 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:09:49 -0400 Subject: [PATCH 21/67] alwatchedfile's main loop queue lives only as long as the test that pumps it A function-static WorkQueue("mainloop") lived from the first test that pumped to the end of the process. In a binary shared with other tests it would stand in for the viewer's main loop for all of them: a second "mainloop" queue anywhere is LL_ERRS, workers that hand back to the main loop would post into it, and almasterwatch test 11 fails once one exists. The fixture now makes the queue the first time a test pumps and lets it go with the test. The watched files go before the fixture, and with the last of them the watcher's worker is joined, so nothing posts into a dead queue. Co-Authored-By: Claude Opus 5.5 --- indra/llcommon/tests/alwatchedfile_test.cpp | 23 ++++++++++++--------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/indra/llcommon/tests/alwatchedfile_test.cpp b/indra/llcommon/tests/alwatchedfile_test.cpp index 7b62c024de..852488ccc9 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; From c2b148f209b6c74dcabad6d40a615f113b0365ae Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:16:03 -0400 Subject: [PATCH 22/67] llcommon's tests put back the process state they change Each of these left something behind that a later test in the same process would read, or crash on. Each is now restored as the test or its fixture ends, however it ends. - lltrace's fixture ThreadRecorder made itself the main thread's recorder and left it, its timer stack and the current accumulators null: a later Recording on the main thread dereferenced null. They are put back. - llevents left listeners bound to dead fixtures and stack objects on per-frame, login, filter0 (disabled too), filter1 and button. The fixture now deletes the pumps its tests obtain, with their listeners, so each test also finds them new: test 1, which counts the pumps obtain() adds, no longer needs to run before test 2. - llprocess test 24 left its PostendListener on postend; tests 22 and 23 run after it crashed. The listener now holds its connection. - llhttpdate test 4 restored LC_TIME and LLStringUtil's locale only on success, and never the locale LLDate had last set LC_TIME for, so the next date formatted anywhere set LC_TIME to the environment's. A guard restores all three. - lleventcoro built and destroyed an LLApp in every fixture, and ~LLApp ends LLCommon: the APR pool and the LLTrace master recorder were null for every test after it. One app now serves the process and is never destroyed; each test still sees it start running and stop. - llpounceable test 1 left gForward and static_check pointing at a dead local. Co-Authored-By: Claude Opus 5.5 --- indra/llcommon/tests/lleventcoro_test.cpp | 29 +++++++++++++++++++- indra/llcommon/tests/llevents_test.cpp | 17 ++++++++++++ indra/llcommon/tests/llhttpdate_test.cpp | 30 ++++++++++++++------ indra/llcommon/tests/llpounceable_test.cpp | 10 +++++++ indra/llcommon/tests/llprocess_test.cpp | 5 +++- indra/llcommon/tests/lltrace_test.cpp | 32 +++++++++++++++++++++- 6 files changed, 112 insertions(+), 11 deletions(-) diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index a170134c39..a16acb3307 100644 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -88,6 +88,33 @@ 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 /***************************************************************************** @@ -102,7 +129,7 @@ namespace tut 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(); diff --git a/indra/llcommon/tests/llevents_test.cpp b/indra/llcommon/tests/llevents_test.cpp index a288cd0ee0..4232efd9ca 100644 --- a/indra/llcommon/tests/llevents_test.cpp +++ b/indra/llcommon/tests/llevents_test.cpp @@ -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; diff --git a/indra/llcommon/tests/llhttpdate_test.cpp b/indra/llcommon/tests/llhttpdate_test.cpp index f56744bd08..023987f171 100644 --- a/indra/llcommon/tests/llhttpdate_test.cpp +++ b/indra/llcommon/tests/llhttpdate_test.cpp @@ -99,6 +99,27 @@ namespace tut ensure("Current time in RFC 1123", (strcmp(expected, actual.c_str()) == 0)); } + 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) { @@ -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/llpounceable_test.cpp b/indra/llcommon/tests/llpounceable_test.cpp index 4c0b9aeb51..20954be5d0 100644 --- a/indra/llcommon/tests/llpounceable_test.cpp +++ b/indra/llcommon/tests/llpounceable_test.cpp @@ -93,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 9bd4680b4c..a17247d232 100644 --- a/indra/llcommon/tests/llprocess_test.cpp +++ b/indra/llcommon/tests/llprocess_test.cpp @@ -1657,7 +1657,9 @@ namespace tut mExpect(expect), mTriggered(false) { - LLEventPumps::instance().obtain(pumpname) + // 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)); } @@ -1671,6 +1673,7 @@ namespace tut LLProcess::ReadPipe& mReadPipe; std::string mExpect; bool mTriggered; + LLTempBoundListener mConnection; }; } // anonymous namespace diff --git a/indra/llcommon/tests/lltrace_test.cpp b/indra/llcommon/tests/lltrace_test.cpp index e48029e65a..af8fe63911 100644 --- a/indra/llcommon/tests/lltrace_test.cpp +++ b/indra/llcommon/tests/lltrace_test.cpp @@ -62,9 +62,39 @@ 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; From e2c35fd633d2096d88cadba16c6281b614cc5f5d Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:18:42 -0400 Subject: [PATCH 23/67] llsd's tests check LLSD's counts outside a destructor, and the Python peers write no bytecode - llsd checked LLSD's live and allocated counts in the destructors of SDCleanupCheck and SDAllocationCheck. A destructor is noexcept, so a failed ensure() there ended the process rather than failing the test. Each check now runs the test's body and checks once what it made is gone, in the same order and with the same messages. - llleap's peers import a reader module the test writes to the temp folder, and each run left its bytecode behind in __pycache__ there. The three Python peer tests run with PYTHONDONTWRITEBYTECODE=1. Co-Authored-By: Claude Opus 5.5 --- indra/llcommon/CMakeLists.txt | 11 +- indra/llcommon/tests/llsd_test.cpp | 1168 ++++++++++++++-------------- 2 files changed, 596 insertions(+), 583 deletions(-) diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 3927400b6a..d9724dbb60 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/llsd_test.cpp b/indra/llcommon/tests/llsd_test.cpp index 6fe75d3bac..184874fc16 100644 --- a/indra/llcommon/tests/llsd_test.cpp +++ b/indra/llcommon/tests/llsd_test.cpp @@ -39,38 +39,32 @@ namespace tut { namespace { - class 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) { - private: - U32 mOutstandingAtStart; - public: - SDCleanupCheck() : mOutstandingAtStart(llsd::outstandingCount()) { } - ~SDCleanupCheck() - { - ensure_equals("SDCleanupCheck", - llsd::outstandingCount(), mOutstandingAtStart); - } - }; + const U32 outstanding = llsd::outstandingCount(); + body(); + ensure_equals("SDCleanupCheck", + llsd::outstandingCount(), outstanding); + } - class SDAllocationCheck : public SDCleanupCheck + // ...and the body allocated exactly as many LLSD values as expected. + template + void ensure_allocations(const std::string& message, U32 expectedAllocations, BODY&& body) { - private: - std::string mMessage; - U32 mExpectedAllocations; - U32 mAllocationAtStart; - public: - SDAllocationCheck(const std::string& message, int expectedAllocations) - : mMessage(message), - mExpectedAllocations(expectedAllocations), - mAllocationAtStart(llsd::allocationCount()) - { } - ~SDAllocationCheck() - { - 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 { @@ -118,58 +112,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<> @@ -177,48 +172,49 @@ 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); + }); } static void checkConversions(const char* msg, const LLSD& v, @@ -254,42 +250,43 @@ 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 @@ -313,38 +310,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<> @@ -353,74 +351,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 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"); - } + { + 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"); + } + }); } @@ -430,49 +429,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<> @@ -482,49 +482,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); + }); } @@ -533,86 +534,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"); + }); } @@ -620,71 +623,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); + }); } @@ -692,126 +696,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; - + ensure_cleaned_up([&] { - SDAllocationCheck check("copy construct undefinded", 0); - LLSD v; - LLSD w = v; - } + ensure_allocations("copy construct undefinded", 0, [] + { + LLSD v; + LLSD w = v; + }); - { - SDAllocationCheck check("assign undefined", 0); - LLSD v; - LLSD w; - 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; - } + ensure_allocations("assign integer value", 0, [] + { + LLSD v = 45; + v = 33; + v = 0; + }); - { - SDAllocationCheck check("copy construct integer", 0); - LLSD v = 45; - LLSD w = v; - } + ensure_allocations("copy construct integer", 0, [] + { + LLSD v = 45; + LLSD w = v; + }); - { - SDAllocationCheck check("assign integer", 0); - LLSD v = 45; - LLSD w; - w = v; - } + ensure_allocations("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("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<> @@ -827,34 +832,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: From e50aa5b6018830602584bdf705302e99c1d4738f Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:19:51 -0400 Subject: [PATCH 24/67] llinitparam test 9 and llfile tests 3 and 4 pass whatever ran before them - llinitparam test 9 checks that a choice block starts with its first alternative chosen. Only the first block of a type does, the one whose construction builds the descriptor, so test 9 passed only while nothing had built a ChoiceOfThings before it; test 19 builds one. It now has a type of its own. - llfile test 3 wrote into the folder test 2 made, and test 4 needed the file test 3 wrote. Each now makes what it needs. The folders were fixed paths in the temp folder that test 2 left behind; each test now has its own, named by a UUID, and its fixture removes them. Co-Authored-By: Claude Opus 5.5 --- indra/llcommon/tests/llfile_test.cpp | 30 ++++++++++++++++++----- indra/llcommon/tests/llinitparam_test.cpp | 17 ++++++++++++- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/indra/llcommon/tests/llfile_test.cpp b/indra/llcommon/tests/llfile_test.cpp index cf39294882..46ea565b45 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/llinitparam_test.cpp b/indra/llcommon/tests/llinitparam_test.cpp index 51c0196231..0096b98fe3 100644 --- a/indra/llcommon/tests/llinitparam_test.cpp +++ b/indra/llcommon/tests/llinitparam_test.cpp @@ -129,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; @@ -366,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"); From 258fe645a0152025a1c050bdc557c9f8917541fc Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:20:42 -0400 Subject: [PATCH 25/67] llcommon's llsdutil test group takes a name llmath's does not share Both llsdutil_test and llmath's llsdutil_math_test named their test group object tut::tutil, a duplicate symbol the moment the two share a binary. Co-Authored-By: Claude Opus 5.5 --- indra/llcommon/tests/llsdutil_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcommon/tests/llsdutil_test.cpp b/indra/llcommon/tests/llsdutil_test.cpp index de6e0bede5..1209bb3807 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>() From e1a42fb6c53d9c117a971fab2ab2346a1f12f942 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:27:50 -0400 Subject: [PATCH 26/67] What an alscript test defines for itself has internal linkage Nearly every alscript test already kept its helpers in an anonymous namespace or in its fixture; two did not. alincludesearch's RequireCase moves into one, and alpreprocessor's gRuns is static. alincludesearch's fixture keeps its name and namespace: alincludesearch.h befriends tut::alincludesearch_data. Co-Authored-By: Claude Opus 5.5 --- indra/alscript/tests/alincludesearch_test.cpp | 35 ++++++++++--------- indra/alscript/tests/alpreprocessor_test.cpp | 2 +- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/indra/alscript/tests/alincludesearch_test.cpp b/indra/alscript/tests/alincludesearch_test.cpp index 367b234976..cc5875d713 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/alpreprocessor_test.cpp b/indra/alscript/tests/alpreprocessor_test.cpp index 944b0e0748..3633c978a6 100644 --- a/indra/alscript/tests/alpreprocessor_test.cpp +++ b/indra/alscript/tests/alpreprocessor_test.cpp @@ -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>() From 6041c0fc523e63a816702f95d37dc0803e57ca82 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:28:05 -0400 Subject: [PATCH 27/67] alpreprocessor tests 39 and 40 and alluauconfig test 9 pass whatever ran before them - alpreprocessor tests 39 and 40 need the grid's builtins, for the optimizer and for const folding, and passed only because test 13 had loaded them into Tailslide's table, which keeps them for the process. Every test that needs them now asks through one helper. - alluauconfig test 9 times a run stopped by the deadline, and such a run is kept a while by its text: the same text asked in the process in the last ten seconds, by another test or by this one run again, answered at once and failed the test. Its text is now its own, each run. - allslservice test 21 wrote its region's builtins to a fixed name in the temp folder; the name is now its own. Co-Authored-By: Claude Opus 5.5 --- indra/alscript/tests/allslservice_test.cpp | 4 ++- indra/alscript/tests/alluauconfig_test.cpp | 12 ++++++-- indra/alscript/tests/alpreprocessor_test.cpp | 30 ++++++++++---------- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/indra/alscript/tests/allslservice_test.cpp b/indra/alscript/tests/allslservice_test.cpp index 2df9e973bc..bfff29bfd8 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 e313a66a03..3ba1ed2935 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 3633c978a6..b19d3d871e 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; From 1bc5ef46aa0cce6a9bb691444f191a451d941116 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:29:11 -0400 Subject: [PATCH 28/67] allslreference test 6 puts LL's llSay back however the test ends The test answers llSay with a stand-in by patching the process's script library, and put the real one back only if the run returned. A guard now restores it, and holds the stand-in for the run alone. Co-Authored-By: Claude Opus 5.5 --- indra/lscript/tests/allslreference_test.cpp | 41 +++++++++++++++------ 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/indra/lscript/tests/allslreference_test.cpp b/indra/lscript/tests/allslreference_test.cpp index 181641fad8..62ecb0b9b2 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); From 099b15ea7c803ff17d31815a12ec41dc381d8025 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:07:03 -0400 Subject: [PATCH 29/67] llui's tests share one rlvGetAnonym llurlentry.cpp asks the viewer for an anonymised avatar name, so every binary that pulls it needs a definition, and 75 tests and two benchmarks each carried one, alquickopen a second, dead one inside namespace tut. Two of them in one binary would not link. They go, for one definition in llui_test_fakes.cpp, an object library compiled once and linked into every test and benchmark that links llui. alquickopen also loses the includes it had twice. Co-Authored-By: Claude Opus 5.5 --- indra/llui/CMakeLists.txt | 9 ++++- indra/llui/tests/alangledial_test.cpp | 9 ----- indra/llui/tests/alcanvasview_test.cpp | 9 ----- indra/llui/tests/alchangepeek_test.cpp | 7 ---- indra/llui/tests/alchoicelist_test.cpp | 7 ---- indra/llui/tests/alcodeeditor_test.cpp | 7 ---- indra/llui/tests/alcolorfield_test.cpp | 7 ---- indra/llui/tests/alcolorpicker_test.cpp | 7 ---- indra/llui/tests/alcolorsheet_test.cpp | 7 ---- indra/llui/tests/alcolortablepanel_test.cpp | 7 ---- indra/llui/tests/alcornerfield_test.cpp | 9 ----- indra/llui/tests/aldiffview_gl_test.cpp | 7 ---- indra/llui/tests/aldiffview_test.cpp | 7 ---- indra/llui/tests/aldockpanel_test.cpp | 9 ----- indra/llui/tests/alemptystate_test.cpp | 9 ----- indra/llui/tests/alfindbar_test.cpp | 7 ---- indra/llui/tests/alflagsfield_test.cpp | 7 ---- indra/llui/tests/alfollowscontrol_test.cpp | 9 ----- indra/llui/tests/alfontfield_test.cpp | 7 ---- indra/llui/tests/alhistorylist_test.cpp | 7 ---- indra/llui/tests/alimagefield_test.cpp | 7 ---- indra/llui/tests/aljumpbar_test.cpp | 7 ---- indra/llui/tests/alkeycapture_test.cpp | 7 ---- indra/llui/tests/almenuslot_test.cpp | 7 ---- indra/llui/tests/aloffsetpad_test.cpp | 9 ----- indra/llui/tests/aloutputview_test.cpp | 7 ---- indra/llui/tests/alpanefolds_test.cpp | 9 ----- indra/llui/tests/alpanelist_test.cpp | 7 ---- indra/llui/tests/alpopover_test.cpp | 7 ---- indra/llui/tests/alpropertygrid_test.cpp | 9 ----- indra/llui/tests/alquickopen_test.cpp | 22 +--------- indra/llui/tests/alscopebar_test.cpp | 7 ---- indra/llui/tests/alsettingrow_test.cpp | 9 ----- indra/llui/tests/alspecimenlist_test.cpp | 7 ---- indra/llui/tests/alstudiofloater_test.cpp | 7 ---- indra/llui/tests/alsurface_gl_test.cpp | 7 ---- indra/llui/tests/alsurface_test.cpp | 7 ---- indra/llui/tests/alsyntaxgrammars_test.cpp | 10 ----- indra/llui/tests/altabstrip_test.cpp | 7 ---- indra/llui/tests/altextdraw_bench.cpp | 7 ---- indra/llui/tests/altextengine_bench.cpp | 7 ---- indra/llui/tests/altextgotoline_test.cpp | 7 ---- indra/llui/tests/altextlayout_test.cpp | 9 ----- indra/llui/tests/altextview_gl_test.cpp | 7 ---- indra/llui/tests/altextview_test.cpp | 9 ----- indra/llui/tests/alviewtype_test.cpp | 9 ----- indra/llui/tests/alvimkeymap_test.cpp | 7 ---- indra/llui/tests/alxuicatalog_test.cpp | 7 ---- indra/llui/tests/alxuidiagnostics_test.cpp | 9 ----- indra/llui/tests/alxuidocuments_test.cpp | 7 ---- indra/llui/tests/alxuiedit_test.cpp | 7 ---- indra/llui/tests/alxuifields_test.cpp | 7 ---- indra/llui/tests/alxuifill_test.cpp | 9 ----- indra/llui/tests/alxuifindings_test.cpp | 7 ---- indra/llui/tests/alxuilint_test.cpp | 7 ---- indra/llui/tests/alxuinotes_test.cpp | 7 ---- indra/llui/tests/alxuioverlay_test.cpp | 9 ----- indra/llui/tests/alxuischema_test.cpp | 7 ---- indra/llui/tests/alxuiselection_test.cpp | 7 ---- indra/llui/tests/alxuiservice_test.cpp | 7 ---- indra/llui/tests/alxuishellbuild_test.cpp | 9 ----- indra/llui/tests/alxuisourcemap_test.cpp | 7 ---- indra/llui/tests/alxuitranslate_test.cpp | 7 ---- indra/llui/tests/alxuitreemodel_test.cpp | 7 ---- indra/llui/tests/llaccordionctrltab_test.cpp | 9 ----- indra/llui/tests/llcombobox_test.cpp | 9 ----- indra/llui/tests/llfloater_test.cpp | 9 ----- indra/llui/tests/llfocusmgr_test.cpp | 9 ----- indra/llui/tests/lllayoutstack_test.cpp | 9 ----- indra/llui/tests/llradiogroup_test.cpp | 9 ----- indra/llui/tests/llresizebar_test.cpp | 9 ----- indra/llui/tests/llspinctrl_test.cpp | 9 ----- indra/llui/tests/llstatbar_test.cpp | 9 ----- indra/llui/tests/lltabcontainer_test.cpp | 9 ----- indra/llui/tests/llui_test_fakes.cpp | 42 ++++++++++++++++++++ indra/llui/tests/lluicolortable_test.cpp | 7 ---- indra/llui/tests/llurlentry_test.cpp | 6 --- indra/llui/tests/llurlregistry_test.cpp | 7 ---- indra/llui/tests/llview_test.cpp | 9 ----- 79 files changed, 51 insertions(+), 612 deletions(-) create mode 100644 indra/llui/tests/llui_test_fakes.cpp diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 94d69fba30..49c6e53030 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -658,7 +658,14 @@ if(AL_BUILD_TESTS) LIBRARIES ${test_libs} ) - set(test_libs llui ${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 diff --git a/indra/llui/tests/alangledial_test.cpp b/indra/llui/tests/alangledial_test.cpp index bd92e67f78..a0a74ef37f 100644 --- a/indra/llui/tests/alangledial_test.cpp +++ b/indra/llui/tests/alangledial_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 gAngleDialTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gAngleDialTestAnonName; -} - namespace tut { struct alangledial_data diff --git a/indra/llui/tests/alcanvasview_test.cpp b/indra/llui/tests/alcanvasview_test.cpp index 202eb5afe3..f167da52a9 100644 --- a/indra/llui/tests/alcanvasview_test.cpp +++ b/indra/llui/tests/alcanvasview_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 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 diff --git a/indra/llui/tests/alchangepeek_test.cpp b/indra/llui/tests/alchangepeek_test.cpp index 5d986630d5..9e70599d3e 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 e03ec3c14c..ef0d6d06be 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 d9687412e3..25a00b90df 100644 --- a/indra/llui/tests/alcodeeditor_test.cpp +++ b/indra/llui/tests/alcodeeditor_test.cpp @@ -46,13 +46,6 @@ #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. diff --git a/indra/llui/tests/alcolorfield_test.cpp b/indra/llui/tests/alcolorfield_test.cpp index faf0fc3505..32a61468f1 100644 --- a/indra/llui/tests/alcolorfield_test.cpp +++ b/indra/llui/tests/alcolorfield_test.cpp @@ -39,13 +39,6 @@ #include "../test/lltut.h" -class LLAvatarName; -const std::string gColorFieldTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gColorFieldTestAnonName; -} - namespace tut { struct alcolorfield_data diff --git a/indra/llui/tests/alcolorpicker_test.cpp b/indra/llui/tests/alcolorpicker_test.cpp index 5b1a0f5d67..c8bd826f64 100644 --- a/indra/llui/tests/alcolorpicker_test.cpp +++ b/indra/llui/tests/alcolorpicker_test.cpp @@ -33,13 +33,6 @@ #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 diff --git a/indra/llui/tests/alcolorsheet_test.cpp b/indra/llui/tests/alcolorsheet_test.cpp index cab4e06971..b6029ec05a 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 1a3a1413f4..432d6782a9 100644 --- a/indra/llui/tests/alcolortablepanel_test.cpp +++ b/indra/llui/tests/alcolortablepanel_test.cpp @@ -36,13 +36,6 @@ #include "../test/lltut.h" -class LLAvatarName; -const std::string gColorTablePanelTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gColorTablePanelTestAnonName; -} - namespace tut { struct alcolortablepanel_data diff --git a/indra/llui/tests/alcornerfield_test.cpp b/indra/llui/tests/alcornerfield_test.cpp index 82e2a9a972..08967fb19d 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 ffe514d75e..82e1c8831b 100644 --- a/indra/llui/tests/aldiffview_gl_test.cpp +++ b/indra/llui/tests/aldiffview_gl_test.cpp @@ -41,13 +41,6 @@ #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; diff --git a/indra/llui/tests/aldiffview_test.cpp b/indra/llui/tests/aldiffview_test.cpp index 7fae7d9edf..4b391417b5 100644 --- a/indra/llui/tests/aldiffview_test.cpp +++ b/indra/llui/tests/aldiffview_test.cpp @@ -46,13 +46,6 @@ #include #include -class LLAvatarName; -const std::string gDiffTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gDiffTestAnonName; -} - namespace tut { struct aldiffview_data diff --git a/indra/llui/tests/aldockpanel_test.cpp b/indra/llui/tests/aldockpanel_test.cpp index bcdfb1121f..c509fc1f7b 100644 --- a/indra/llui/tests/aldockpanel_test.cpp +++ b/indra/llui/tests/aldockpanel_test.cpp @@ -42,15 +42,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 gDockTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gDockTestAnonName; -} - namespace tut { struct aldockpanel_data diff --git a/indra/llui/tests/alemptystate_test.cpp b/indra/llui/tests/alemptystate_test.cpp index 1d0fd7ce93..55e75c426b 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 3b11081754..ba20c3e9ab 100644 --- a/indra/llui/tests/alfindbar_test.cpp +++ b/indra/llui/tests/alfindbar_test.cpp @@ -34,13 +34,6 @@ #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 diff --git a/indra/llui/tests/alflagsfield_test.cpp b/indra/llui/tests/alflagsfield_test.cpp index b49210a20c..2211c284cb 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 6368f8a80a..b906adaed7 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 6f1d6e693a..85c962174f 100644 --- a/indra/llui/tests/alfontfield_test.cpp +++ b/indra/llui/tests/alfontfield_test.cpp @@ -42,13 +42,6 @@ #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 diff --git a/indra/llui/tests/alhistorylist_test.cpp b/indra/llui/tests/alhistorylist_test.cpp index b5b8595c9c..f083a33ef4 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 4be4f14483..e34d22e291 100644 --- a/indra/llui/tests/alimagefield_test.cpp +++ b/indra/llui/tests/alimagefield_test.cpp @@ -39,13 +39,6 @@ #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 diff --git a/indra/llui/tests/aljumpbar_test.cpp b/indra/llui/tests/aljumpbar_test.cpp index a5c740957d..5ed95542a5 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 4137b75a4e..61ab2fb7bd 100644 --- a/indra/llui/tests/alkeycapture_test.cpp +++ b/indra/llui/tests/alkeycapture_test.cpp @@ -42,13 +42,6 @@ #include #include -class LLAvatarName; -const std::string gKeyCaptureTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gKeyCaptureTestAnonName; -} - namespace tut { struct alkeycapture_data diff --git a/indra/llui/tests/almenuslot_test.cpp b/indra/llui/tests/almenuslot_test.cpp index fc8df030f7..aaa7a46d62 100644 --- a/indra/llui/tests/almenuslot_test.cpp +++ b/indra/llui/tests/almenuslot_test.cpp @@ -37,13 +37,6 @@ #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 diff --git a/indra/llui/tests/aloffsetpad_test.cpp b/indra/llui/tests/aloffsetpad_test.cpp index 0b9778f116..57bbf6f9df 100644 --- a/indra/llui/tests/aloffsetpad_test.cpp +++ b/indra/llui/tests/aloffsetpad_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 gOffsetPadTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gOffsetPadTestAnonName; -} - namespace tut { struct aloffsetpad_data diff --git a/indra/llui/tests/aloutputview_test.cpp b/indra/llui/tests/aloutputview_test.cpp index 3cbeb15105..5637562121 100644 --- a/indra/llui/tests/aloutputview_test.cpp +++ b/indra/llui/tests/aloutputview_test.cpp @@ -36,13 +36,6 @@ #include -class LLAvatarName; -const std::string gOutputTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gOutputTestAnonName; -} - namespace tut { struct aloutputview_data diff --git a/indra/llui/tests/alpanefolds_test.cpp b/indra/llui/tests/alpanefolds_test.cpp index 2ac55e564f..9da610e49c 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 84db8ce7d1..f8b2c401e9 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 3713badd40..eb254b209e 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 diff --git a/indra/llui/tests/alpropertygrid_test.cpp b/indra/llui/tests/alpropertygrid_test.cpp index 9af623951c..1a2fa17a37 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 967c387a0c..1490123733 100644 --- a/indra/llui/tests/alquickopen_test.cpp +++ b/indra/llui/tests/alquickopen_test.cpp @@ -30,28 +30,16 @@ #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 "../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 @@ -79,14 +67,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 3b26fd126b..02107abbac 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 af6914ecd6..fe41269461 100644 --- a/indra/llui/tests/alsettingrow_test.cpp +++ b/indra/llui/tests/alsettingrow_test.cpp @@ -49,15 +49,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 gSettingRowTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gSettingRowTestAnonName; -} - namespace tut { struct alsettingrow_data diff --git a/indra/llui/tests/alspecimenlist_test.cpp b/indra/llui/tests/alspecimenlist_test.cpp index 04ed6429cf..d6ce8dd2e4 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 7369d90298..aaedb43e4f 100644 --- a/indra/llui/tests/alstudiofloater_test.cpp +++ b/indra/llui/tests/alstudiofloater_test.cpp @@ -46,13 +46,6 @@ #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. diff --git a/indra/llui/tests/alsurface_gl_test.cpp b/indra/llui/tests/alsurface_gl_test.cpp index d9c1eab33c..d838986b22 100644 --- a/indra/llui/tests/alsurface_gl_test.cpp +++ b/indra/llui/tests/alsurface_gl_test.cpp @@ -41,13 +41,6 @@ #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; diff --git a/indra/llui/tests/alsurface_test.cpp b/indra/llui/tests/alsurface_test.cpp index 64f85df2ea..e5e922a153 100644 --- a/indra/llui/tests/alsurface_test.cpp +++ b/indra/llui/tests/alsurface_test.cpp @@ -35,13 +35,6 @@ #include #include -class LLAvatarName; -const std::string gSurfaceTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gSurfaceTestAnonName; -} - namespace tut { struct alsurface_data diff --git a/indra/llui/tests/alsyntaxgrammars_test.cpp b/indra/llui/tests/alsyntaxgrammars_test.cpp index 16373145c8..22bbd4e51e 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 771ae9f198..6dcfc572a7 100644 --- a/indra/llui/tests/altabstrip_test.cpp +++ b/indra/llui/tests/altabstrip_test.cpp @@ -36,13 +36,6 @@ #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 diff --git a/indra/llui/tests/altextdraw_bench.cpp b/indra/llui/tests/altextdraw_bench.cpp index 9d5f72b548..45bcf7c8bb 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 7e9307eea1..2045b3e2fe 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/altextgotoline_test.cpp b/indra/llui/tests/altextgotoline_test.cpp index 7922888bfd..d72a0bafb3 100644 --- a/indra/llui/tests/altextgotoline_test.cpp +++ b/indra/llui/tests/altextgotoline_test.cpp @@ -41,13 +41,6 @@ #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) { diff --git a/indra/llui/tests/altextlayout_test.cpp b/indra/llui/tests/altextlayout_test.cpp index cfef07a561..f9777e22ea 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 diff --git a/indra/llui/tests/altextview_gl_test.cpp b/indra/llui/tests/altextview_gl_test.cpp index d211eeb1ba..db134c4ca6 100644 --- a/indra/llui/tests/altextview_gl_test.cpp +++ b/indra/llui/tests/altextview_gl_test.cpp @@ -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. diff --git a/indra/llui/tests/altextview_test.cpp b/indra/llui/tests/altextview_test.cpp index 3bad854ef3..6f5d085b32 100644 --- a/indra/llui/tests/altextview_test.cpp +++ b/indra/llui/tests/altextview_test.cpp @@ -49,15 +49,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 diff --git a/indra/llui/tests/alviewtype_test.cpp b/indra/llui/tests/alviewtype_test.cpp index 5666a54ba3..850e93a4b6 100644 --- a/indra/llui/tests/alviewtype_test.cpp +++ b/indra/llui/tests/alviewtype_test.cpp @@ -45,15 +45,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 gViewKindTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gViewKindTestAnonName; -} - namespace tut { struct TestView : public LLView diff --git a/indra/llui/tests/alvimkeymap_test.cpp b/indra/llui/tests/alvimkeymap_test.cpp index 2b61b4247c..534f09daf4 100644 --- a/indra/llui/tests/alvimkeymap_test.cpp +++ b/indra/llui/tests/alvimkeymap_test.cpp @@ -37,13 +37,6 @@ #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. diff --git a/indra/llui/tests/alxuicatalog_test.cpp b/indra/llui/tests/alxuicatalog_test.cpp index acfe5a79b4..cc9e3c82c7 100644 --- a/indra/llui/tests/alxuicatalog_test.cpp +++ b/indra/llui/tests/alxuicatalog_test.cpp @@ -39,13 +39,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 diff --git a/indra/llui/tests/alxuidiagnostics_test.cpp b/indra/llui/tests/alxuidiagnostics_test.cpp index 86573dbdf6..813dbaff5e 100644 --- a/indra/llui/tests/alxuidiagnostics_test.cpp +++ b/indra/llui/tests/alxuidiagnostics_test.cpp @@ -34,15 +34,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 gDiagTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gDiagTestAnonName; -} - namespace tut { struct alxuidiagnostics_data diff --git a/indra/llui/tests/alxuidocuments_test.cpp b/indra/llui/tests/alxuidocuments_test.cpp index b77569276d..066c4a3119 100644 --- a/indra/llui/tests/alxuidocuments_test.cpp +++ b/indra/llui/tests/alxuidocuments_test.cpp @@ -33,13 +33,6 @@ #include -class LLAvatarName; -const std::string gDocumentsTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gDocumentsTestAnonName; -} - namespace tut { struct alxuidocuments_data diff --git a/indra/llui/tests/alxuiedit_test.cpp b/indra/llui/tests/alxuiedit_test.cpp index 86dbe15992..e1805054d2 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 8bd02199f9..5da4fafce9 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 5458275a4a..4952f06f43 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 dc8d9a77a3..e1a8ba67e0 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 bc4ec32ff2..d4c97f12c0 100644 --- a/indra/llui/tests/alxuilint_test.cpp +++ b/indra/llui/tests/alxuilint_test.cpp @@ -46,13 +46,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 diff --git a/indra/llui/tests/alxuinotes_test.cpp b/indra/llui/tests/alxuinotes_test.cpp index de9e6e1149..0a50b7e613 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 3436fdaad5..f91c21b856 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 958d94d143..64aec7dc94 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 ff99aed38d..403c87b394 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 2d34d3f9b7..cc4ee9920e 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 1de234a247..e8d8f65dfe 100644 --- a/indra/llui/tests/alxuishellbuild_test.cpp +++ b/indra/llui/tests/alxuishellbuild_test.cpp @@ -34,15 +34,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 gShellTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gShellTestAnonName; -} - namespace tut { // A registered panel class that counts its constructions, standing in diff --git a/indra/llui/tests/alxuisourcemap_test.cpp b/indra/llui/tests/alxuisourcemap_test.cpp index dde7f462c8..9d1c6ab7c7 100644 --- a/indra/llui/tests/alxuisourcemap_test.cpp +++ b/indra/llui/tests/alxuisourcemap_test.cpp @@ -36,13 +36,6 @@ #include -class LLAvatarName; -const std::string gSourceMapTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gSourceMapTestAnonName; -} - namespace tut { struct alxuisourcemap_data diff --git a/indra/llui/tests/alxuitranslate_test.cpp b/indra/llui/tests/alxuitranslate_test.cpp index f9cc3cd55a..c7d15466ad 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 7e15dace34..9d8223e1c6 100644 --- a/indra/llui/tests/alxuitreemodel_test.cpp +++ b/indra/llui/tests/alxuitreemodel_test.cpp @@ -42,13 +42,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) {} diff --git a/indra/llui/tests/llaccordionctrltab_test.cpp b/indra/llui/tests/llaccordionctrltab_test.cpp index 723f10ba83..ec697d9d81 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 5415e28e82..378a1c3851 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/llfloater_test.cpp b/indra/llui/tests/llfloater_test.cpp index 61eeb35038..cb5bad1703 100644 --- a/indra/llui/tests/llfloater_test.cpp +++ b/indra/llui/tests/llfloater_test.cpp @@ -32,15 +32,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 gFloaterTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gFloaterTestAnonName; -} - namespace tut { struct TestFloater : public LLFloater diff --git a/indra/llui/tests/llfocusmgr_test.cpp b/indra/llui/tests/llfocusmgr_test.cpp index 2872d29b51..e763c90f17 100644 --- a/indra/llui/tests/llfocusmgr_test.cpp +++ b/indra/llui/tests/llfocusmgr_test.cpp @@ -29,15 +29,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 gFocusMgrTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gFocusMgrTestAnonName; -} - namespace tut { struct TestView : public LLView diff --git a/indra/llui/tests/lllayoutstack_test.cpp b/indra/llui/tests/lllayoutstack_test.cpp index 3fd36f6ad9..2035779009 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 328bd339af..e7f3a10583 100644 --- a/indra/llui/tests/llradiogroup_test.cpp +++ b/indra/llui/tests/llradiogroup_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 gRadioGroupTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gRadioGroupTestAnonName; -} - namespace tut { struct llradiogroup_data diff --git a/indra/llui/tests/llresizebar_test.cpp b/indra/llui/tests/llresizebar_test.cpp index 2a6879e1ce..cbf98f4fca 100644 --- a/indra/llui/tests/llresizebar_test.cpp +++ b/indra/llui/tests/llresizebar_test.cpp @@ -34,15 +34,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 gResizeBarTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gResizeBarTestAnonName; -} - namespace tut { // LLView's constructor is protected, for LLUICtrlFactory. diff --git a/indra/llui/tests/llspinctrl_test.cpp b/indra/llui/tests/llspinctrl_test.cpp index 1ff3c1198c..b41f940f37 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 32b48b4f4d..20d0d9e192 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 a3c2070edf..1fb02910bc 100644 --- a/indra/llui/tests/lltabcontainer_test.cpp +++ b/indra/llui/tests/lltabcontainer_test.cpp @@ -42,15 +42,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 gTabContainerTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gTabContainerTestAnonName; -} - namespace tut { struct lltabcontainer_data diff --git a/indra/llui/tests/llui_test_fakes.cpp b/indra/llui/tests/llui_test_fakes.cpp new file mode 100644 index 0000000000..9cdbd8b1c0 --- /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 2849ec9360..3a75ca5a34 100644 --- a/indra/llui/tests/lluicolortable_test.cpp +++ b/indra/llui/tests/lluicolortable_test.cpp @@ -30,13 +30,6 @@ #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 diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp index ec4d8c3daa..4dc046e263 100644 --- a/indra/llui/tests/llurlentry_test.cpp +++ b/indra/llui/tests/llurlentry_test.cpp @@ -83,12 +83,6 @@ S32 LLUIImage::getHeight() const } |*==========================================================================*/ -const std::string anonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& avName) -{ - return anonName; -} - namespace tut { struct LLUrlEntryData diff --git a/indra/llui/tests/llurlregistry_test.cpp b/indra/llui/tests/llurlregistry_test.cpp index 92ba6ace54..98d662ba38 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 c1435b03b5..06b2507356 100644 --- a/indra/llui/tests/llview_test.cpp +++ b/indra/llui/tests/llview_test.cpp @@ -29,15 +29,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 gViewTestAnonName("Anon"); -const std::string& rlvGetAnonym(const LLAvatarName& av_name) -{ - return gViewTestAnonName; -} - namespace tut { // LLView's constructor is protected, for LLUICtrlFactory. A test builds From 6c135614593257486ccf70d9708f7a574c2fd923 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:08:41 -0400 Subject: [PATCH 30/67] ALTextView's tests share one TextViewProbe ALTextView befriends ll_test::TextViewProbe by name, and altextview, alcodeeditor and alvimkeymap each defined it, with different members. In one binary the linker keeps one copy of an inline function, so a test would run the other file's probe without a word said. altextviewprobe.h now holds the one definition, the union of the three, and each test includes it. Co-Authored-By: Claude Opus 5.5 --- indra/llui/tests/alcodeeditor_test.cpp | 16 +----- indra/llui/tests/altextview_test.cpp | 29 +--------- indra/llui/tests/altextviewprobe.h | 74 ++++++++++++++++++++++++++ indra/llui/tests/alvimkeymap_test.cpp | 10 +--- 4 files changed, 77 insertions(+), 52 deletions(-) create mode 100644 indra/llui/tests/altextviewprobe.h diff --git a/indra/llui/tests/alcodeeditor_test.cpp b/indra/llui/tests/alcodeeditor_test.cpp index 25a00b90df..f532bfe60c 100644 --- a/indra/llui/tests/alcodeeditor_test.cpp +++ b/indra/llui/tests/alcodeeditor_test.cpp @@ -39,6 +39,7 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "altextviewprobe.h" #include "../test/lltut.h" @@ -46,21 +47,6 @@ #include #include -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 diff --git a/indra/llui/tests/altextview_test.cpp b/indra/llui/tests/altextview_test.cpp index 6f5d085b32..eb39ceb8cd 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,34 +50,6 @@ #include #include -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 diff --git a/indra/llui/tests/altextviewprobe.h b/indra/llui/tests/altextviewprobe.h new file mode 100644 index 0000000000..626ac74cee --- /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/alvimkeymap_test.cpp b/indra/llui/tests/alvimkeymap_test.cpp index 534f09daf4..ae12d1b471 100644 --- a/indra/llui/tests/alvimkeymap_test.cpp +++ b/indra/llui/tests/alvimkeymap_test.cpp @@ -32,20 +32,12 @@ #include "llsd.h" #include "alheadlessui_fixture.h" +#include "altextviewprobe.h" #include "../test/lltut.h" #include -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 From fd0cb344bfa8333ed5fc157c895090a9dab80970 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:11:18 -0400 Subject: [PATCH 31/67] llui's unit tests link llui rather than compiling its files again Thirteen unit tests, and alanchoredranges, llurlentry and llemojidictionary, compiled the llui files they test into themselves, while libllui.a holds the same objects: altextdocument.cpp five times over, altextdiff.cpp and the eleven diff sources five times, alsyntaxgrammar.cpp four. No llui file changes under LL_TEST, so the copies were the library's own; they cost a compile each, and two tests sharing a binary would have defined them twice. Each now links llui and the shared fakes. Being no longer unit tests, they are INTEGRATION_TEST_ and run as INTEGRATION_TEST_RUNNER_. llurlmatch defined 21 of llui's and llrender's symbols itself, among them LLFontGL::getFontDefault() returning null, and never linked the library they came from. They go, and the test stands up the headless UI's fonts instead, since a style's parameters start from the default font; it checks what it did. llurlentry loses the stubs it kept in comments. Co-Authored-By: Claude Opus 5.5 --- indra/llui/CMakeLists.txt | 139 +++++++++------------------ indra/llui/tests/llurlentry_test.cpp | 50 ---------- indra/llui/tests/llurlmatch_test.cpp | 134 ++++++++------------------ 3 files changed, 87 insertions(+), 236 deletions(-) diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 49c6e53030..a5348deb66 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -562,101 +562,12 @@ 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 - ) - 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} - ) - # 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\"" - ) - # A grammar's regions for a comparison, read again only where a text - # 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}) + # 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}) - 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} - ) # What llui asks the viewer for, defined once: compiled here and linked # into every test and benchmark below, which link the library. A test @@ -681,13 +592,55 @@ if(AL_BUILD_TESTS) ) 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 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 + PROJECT llui + LIBRARIES ${test_libs} + DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" + ) + al_add_test( + aldiffmodel + PROJECT llui + LIBRARIES ${test_libs} + DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" + ) + # A grammar's regions for a comparison, read again only where a text + # changed: over the grammars the viewer ships. + al_add_test( + aldifflexer + PROJECT llui + LIBRARIES ${test_libs} + DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" + ) # 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/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp index 4dc046e263..f094100b7c 100644 --- a/indra/llui/tests/llurlentry_test.cpp +++ b/indra/llui/tests/llurlentry_test.cpp @@ -33,56 +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; -} -|*==========================================================================*/ - namespace tut { struct LLUrlEntryData diff --git a/indra/llui/tests/llurlmatch_test.cpp b/indra/llui/tests/llurlmatch_test.cpp index d03efbc1c9..0eebc4d561 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 // From 008a098c17b358627d3c4ccc4d57872c78df36c8 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:14:28 -0400 Subject: [PATCH 32/67] llui's test-local types and helpers have internal linkage TestView was defined in four files and TestFloater in two, identical today by luck, and llview's CountingView, llresizebar's PlainCtrl, llfocusmgr's TestFocusable, alviewtype's TestCtrl and AskingView, alxuishellbuild's panels, alundostack's Step, llxuiparser's three blocks at global scope, llurlentry's four helpers and altextgotoline's printer for ALTextPos were all external. In one binary two same-named classes would share one vtable and one set of inline functions with no diagnostic. They are now in anonymous namespaces; the printer is static instead, since tut's ensure_equals finds it by argument-dependent lookup, which does not look into a nested namespace. TUT's own data structs, group objects and test specialisations stay in namespace tut, and ll_test::TextViewProbe keeps its name for ALTextView's friend declaration. nm finds no other external symbol of a test's own. llxuiparser's block member small is small_number, since small is a macro in Windows' rpcndr.h; its XUI name is still small. Co-Authored-By: Claude Opus 5.5 --- indra/llui/tests/altextgotoline_test.cpp | 6 +- indra/llui/tests/alundostack_test.cpp | 13 ++-- indra/llui/tests/alviewtype_test.cpp | 51 ++++++------- indra/llui/tests/alxuishellbuild_test.cpp | 25 ++++--- indra/llui/tests/llfloater_test.cpp | 17 +++-- indra/llui/tests/llfocusmgr_test.cpp | 17 +++-- indra/llui/tests/llresizebar_test.cpp | 18 +++-- indra/llui/tests/llurlentry_test.cpp | 76 ++++++++++---------- indra/llui/tests/llview_test.cpp | 32 +++++---- indra/llui/tests/llxuiparser_test.cpp | 88 +++++++++++------------ 10 files changed, 187 insertions(+), 156 deletions(-) diff --git a/indra/llui/tests/altextgotoline_test.cpp b/indra/llui/tests/altextgotoline_test.cpp index d72a0bafb3..1ca504dc55 100644 --- a/indra/llui/tests/altextgotoline_test.cpp +++ b/indra/llui/tests/altextgotoline_test.cpp @@ -41,8 +41,10 @@ #include #include -// 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; } diff --git a/indra/llui/tests/alundostack_test.cpp b/indra/llui/tests/alundostack_test.cpp index e30c1bd20e..5800abfb95 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 850e93a4b6..6fffcde328 100644 --- a/indra/llui/tests/alviewtype_test.cpp +++ b/indra/llui/tests/alviewtype_test.cpp @@ -47,36 +47,39 @@ 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 { diff --git a/indra/llui/tests/alxuishellbuild_test.cpp b/indra/llui/tests/alxuishellbuild_test.cpp index e8d8f65dfe..b0e3c02170 100644 --- a/indra/llui/tests/alxuishellbuild_test.cpp +++ b/indra/llui/tests/alxuishellbuild_test.cpp @@ -36,19 +36,22 @@ 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/llfloater_test.cpp b/indra/llui/tests/llfloater_test.cpp index cb5bad1703..5f85696013 100644 --- a/indra/llui/tests/llfloater_test.cpp +++ b/indra/llui/tests/llfloater_test.cpp @@ -34,15 +34,18 @@ 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 { diff --git a/indra/llui/tests/llfocusmgr_test.cpp b/indra/llui/tests/llfocusmgr_test.cpp index e763c90f17..c6707f48c3 100644 --- a/indra/llui/tests/llfocusmgr_test.cpp +++ b/indra/llui/tests/llfocusmgr_test.cpp @@ -31,15 +31,18 @@ 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/llresizebar_test.cpp b/indra/llui/tests/llresizebar_test.cpp index cbf98f4fca..75a139a575 100644 --- a/indra/llui/tests/llresizebar_test.cpp +++ b/indra/llui/tests/llresizebar_test.cpp @@ -37,10 +37,13 @@ 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 { @@ -165,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>() diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp index f094100b7c..77d8a3846f 100644 --- a/indra/llui/tests/llurlentry_test.cpp +++ b/indra/llui/tests/llurlentry_test.cpp @@ -50,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/llview_test.cpp b/indra/llui/tests/llview_test.cpp index 06b2507356..e5703d43b0 100644 --- a/indra/llui/tests/llview_test.cpp +++ b/indra/llui/tests/llview_test.cpp @@ -31,12 +31,15 @@ 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 { @@ -307,16 +310,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>() diff --git a/indra/llui/tests/llxuiparser_test.cpp b/indra/llui/tests/llxuiparser_test.cpp index 7d714f39a5..c76d8c4f78 100644 --- a/indra/llui/tests/llxuiparser_test.cpp +++ b/indra/llui/tests/llxuiparser_test.cpp @@ -47,53 +47,53 @@ // 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. @@ -158,7 +158,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 +195,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<> From 30ef5c8424c6674ece34a15ceba0b6a8e5769087 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:29:49 -0400 Subject: [PATCH 33/67] ALSurface's skin colours follow the table rather than its first answer well(), rim(), handle(), text() and quiet() each kept, in a static, what the colour table said the first time it was asked. Asked before the skin's colours were in, the answer was the fallback colour for good; asked while a name was there, it stayed a pointer into the table after the name was reset. In the viewer the skin is loaded before any control draws, so nothing showed it. In tests, whichever test first built a property grid, a tab strip or a field decided what alsurface's test of these colours would see, which is why it could only pass as the first. Each is now asked again whenever the table's generation has moved on, which is one comparison otherwise, and every caller is still handed the same object. Co-Authored-By: Claude Opus 5.5 --- indra/llui/base/alsurface.cpp | 49 ++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/indra/llui/base/alsurface.cpp b/indra/llui/base/alsurface.cpp index 54074a01c0..ac77f8cfc6 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); } } From 6f4135d8b86e6daef2a53a2fd6217f2b51d2267f Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:29:49 -0400 Subject: [PATCH 34/67] llui's tests put back the process state they change A test binary per file hides what a test leaves behind; in one process the next test reads it. Each of these is now restored by the test or its fixture, a failed check included, through scopes in aluistatescope.h: - gFloaterView: llfloater, alviewtype, alxuitreemodel, alxuidiagnostics and alxuisourcemap set it to a floater view of their own and then to null, so every later floater would have been built into nothing. - LLKeyboard's key names: aldiffview, alfindbar, alkeycapture and alstudiofloater installed a translator and left it. - The colour table's user layer: alcodeeditor 100, alsurface 2, alcolorfield and alcolortablepanel set colours every later widget reads. lluicolortable, which loads sheets of its own in place of the skin's, puts the loaded layer back as well. - Settings: llradiogroup's two and alstudiofloater's saved state go back to their defaults, so a second run no longer starts where the first left them, and alsettingrow's go back as well. alsettingrow gives LLTrans its one string for each test that needs it and takes it back after, rather than once for the process, since any other parse of strings would wipe it. - alpopover 7 asks for popover sizes under kinds of its own each run: the sizes are kept for the process. - llemojidictionary makes and deletes its dictionary only where none was there already. - Focus and capture: llview 14 and the fixtures of lltabcontainer, alcanvasview, alquickopen, alimagefield, alangledial, aloffsetpad, alcolorpicker, altextgotoline, altabstrip, aloutputview, aldockpanel and alfontfield put them back as they found them. alcolorfield, alfontfield and alimagefield escape the popovers their tests leave open, which held the keys and were what alcolorfield's next test found showing; aldockpanel closes the windows its panes are out in; altabstrip's tests 12 and 13 die their strips, and alkeycapture takes its anchors out of the floater view. - The menu container: almenuslot and aldiffview 25 put back the one they found rather than null. - alfontfield, alimagefield, aldockpanel, aldiffview and alvimkeymap empty the LLMortician graveyard as each test ends, as a frame would, so the flushes some of their tests make delete only that test's views rather than every earlier test's. - llresizebar pops the file name it pushed, altextview 20 uninstalls its LLUrlAction callbacks, alvimkeymap empties the clipboard, and alcodeeditor dies every editor a test made, not just the last. - altextlayout 13 counts the shape cache's growth from empty: full from earlier tests, it would only trade one piece for another. Co-Authored-By: Claude Opus 5.5 --- indra/llui/tests/alangledial_test.cpp | 4 + indra/llui/tests/alcanvasview_test.cpp | 4 + indra/llui/tests/alcodeeditor_test.cpp | 13 +- indra/llui/tests/alcolorfield_test.cpp | 13 + indra/llui/tests/alcolorpicker_test.cpp | 4 + indra/llui/tests/alcolortablepanel_test.cpp | 8 + indra/llui/tests/aldiffview_test.cpp | 12 +- indra/llui/tests/aldockpanel_test.cpp | 28 ++ indra/llui/tests/alfindbar_test.cpp | 5 +- indra/llui/tests/alfontfield_test.cpp | 14 + indra/llui/tests/alimagefield_test.cpp | 14 + indra/llui/tests/alkeycapture_test.cpp | 16 +- indra/llui/tests/almenuslot_test.cpp | 4 +- indra/llui/tests/aloffsetpad_test.cpp | 4 + indra/llui/tests/aloutputview_test.cpp | 4 + indra/llui/tests/alpopover_test.cpp | 11 +- indra/llui/tests/alquickopen_test.cpp | 5 + indra/llui/tests/alsettingrow_test.cpp | 44 ++- indra/llui/tests/alstudiofloater_test.cpp | 7 +- indra/llui/tests/alsurface_test.cpp | 6 +- indra/llui/tests/altabstrip_test.cpp | 6 + indra/llui/tests/altextgotoline_test.cpp | 4 + indra/llui/tests/altextlayout_test.cpp | 3 + indra/llui/tests/altextview_test.cpp | 15 +- indra/llui/tests/aluistatescope.h | 349 ++++++++++++++++++++ indra/llui/tests/alviewtype_test.cpp | 7 +- indra/llui/tests/alvimkeymap_test.cpp | 8 + indra/llui/tests/alxuidiagnostics_test.cpp | 7 +- indra/llui/tests/alxuisourcemap_test.cpp | 4 +- indra/llui/tests/alxuitreemodel_test.cpp | 16 +- indra/llui/tests/llemojidictionary_test.cpp | 14 +- indra/llui/tests/llfloater_test.cpp | 10 +- indra/llui/tests/llradiogroup_test.cpp | 13 +- indra/llui/tests/llresizebar_test.cpp | 7 +- indra/llui/tests/lltabcontainer_test.cpp | 4 + indra/llui/tests/lluicolortable_test.cpp | 6 +- indra/llui/tests/llview_test.cpp | 5 + 37 files changed, 633 insertions(+), 65 deletions(-) create mode 100644 indra/llui/tests/aluistatescope.h diff --git a/indra/llui/tests/alangledial_test.cpp b/indra/llui/tests/alangledial_test.cpp index a0a74ef37f..fef9b389f3 100644 --- a/indra/llui/tests/alangledial_test.cpp +++ b/indra/llui/tests/alangledial_test.cpp @@ -31,6 +31,7 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -41,6 +42,9 @@ 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 f167da52a9..0271c4e73b 100644 --- a/indra/llui/tests/alcanvasview_test.cpp +++ b/indra/llui/tests/alcanvasview_test.cpp @@ -33,6 +33,7 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -81,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/alcodeeditor_test.cpp b/indra/llui/tests/alcodeeditor_test.cpp index f532bfe60c..3f80cb842b 100644 --- a/indra/llui/tests/alcodeeditor_test.cpp +++ b/indra/llui/tests/alcodeeditor_test.cpp @@ -39,6 +39,7 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "altextviewprobe.h" #include "../test/lltut.h" @@ -53,13 +54,16 @@ namespace tut { 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(); } } @@ -75,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; @@ -3604,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 32a61468f1..5b1bdba4d8 100644 --- a/indra/llui/tests/alcolorfield_test.cpp +++ b/indra/llui/tests/alcolorfield_test.cpp @@ -36,9 +36,12 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" +#include + namespace tut { struct alcolorfield_data @@ -54,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 c8bd826f64..9e3675c806 100644 --- a/indra/llui/tests/alcolorpicker_test.cpp +++ b/indra/llui/tests/alcolorpicker_test.cpp @@ -30,6 +30,7 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -38,6 +39,9 @@ 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/alcolortablepanel_test.cpp b/indra/llui/tests/alcolortablepanel_test.cpp index 432d6782a9..ff1d177bbe 100644 --- a/indra/llui/tests/alcolortablepanel_test.cpp +++ b/indra/llui/tests/alcolortablepanel_test.cpp @@ -33,9 +33,12 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" +#include + namespace tut { struct alcolortablepanel_data @@ -51,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/aldiffview_test.cpp b/indra/llui/tests/aldiffview_test.cpp index 4b391417b5..7fa6d6edf4 100644 --- a/indra/llui/tests/aldiffview_test.cpp +++ b/indra/llui/tests/aldiffview_test.cpp @@ -37,12 +37,14 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" #include #include #include +#include #include #include @@ -63,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) @@ -869,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); @@ -886,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; } @@ -1459,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 c509fc1f7b..62a3006139 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" @@ -47,6 +48,33 @@ 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/alfindbar_test.cpp b/indra/llui/tests/alfindbar_test.cpp index ba20c3e9ab..5814ab691e 100644 --- a/indra/llui/tests/alfindbar_test.cpp +++ b/indra/llui/tests/alfindbar_test.cpp @@ -31,6 +31,7 @@ #include "llkeyboard.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -122,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(); @@ -156,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/alfontfield_test.cpp b/indra/llui/tests/alfontfield_test.cpp index 85c962174f..78a1b9f8cb 100644 --- a/indra/llui/tests/alfontfield_test.cpp +++ b/indra/llui/tests/alfontfield_test.cpp @@ -39,6 +39,7 @@ #include "llmortician.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -47,6 +48,19 @@ 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/alimagefield_test.cpp b/indra/llui/tests/alimagefield_test.cpp index e34d22e291..2cee7790b3 100644 --- a/indra/llui/tests/alimagefield_test.cpp +++ b/indra/llui/tests/alimagefield_test.cpp @@ -36,6 +36,7 @@ #include "llmortician.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -44,6 +45,19 @@ 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/alkeycapture_test.cpp b/indra/llui/tests/alkeycapture_test.cpp index 61ab2fb7bd..51ce735c2c 100644 --- a/indra/llui/tests/alkeycapture_test.cpp +++ b/indra/llui/tests/alkeycapture_test.cpp @@ -36,6 +36,7 @@ #include "llkeyboard.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -55,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() @@ -70,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 aaa7a46d62..de80e60ac5 100644 --- a/indra/llui/tests/almenuslot_test.cpp +++ b/indra/llui/tests/almenuslot_test.cpp @@ -43,6 +43,8 @@ namespace tut { 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() { @@ -61,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 57bbf6f9df..38bbd3fdf9 100644 --- a/indra/llui/tests/aloffsetpad_test.cpp +++ b/indra/llui/tests/aloffsetpad_test.cpp @@ -31,6 +31,7 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -41,6 +42,9 @@ 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 5637562121..a4320825c4 100644 --- a/indra/llui/tests/aloutputview_test.cpp +++ b/indra/llui/tests/aloutputview_test.cpp @@ -31,6 +31,7 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -41,6 +42,9 @@ 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/alpopover_test.cpp b/indra/llui/tests/alpopover_test.cpp index eb254b209e..53ba3dcead 100644 --- a/indra/llui/tests/alpopover_test.cpp +++ b/indra/llui/tests/alpopover_test.cpp @@ -276,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/alquickopen_test.cpp b/indra/llui/tests/alquickopen_test.cpp index 1490123733..f8b6d6608d 100644 --- a/indra/llui/tests/alquickopen_test.cpp +++ b/indra/llui/tests/alquickopen_test.cpp @@ -37,6 +37,7 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -44,6 +45,10 @@ 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; diff --git a/indra/llui/tests/alsettingrow_test.cpp b/indra/llui/tests/alsettingrow_test.cpp index fe41269461..1e32b439cb 100644 --- a/indra/llui/tests/alsettingrow_test.cpp +++ b/indra/llui/tests/alsettingrow_test.cpp @@ -43,6 +43,7 @@ #include "v3math.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -54,6 +55,26 @@ 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() { @@ -62,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) @@ -70,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, @@ -99,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) @@ -120,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/alstudiofloater_test.cpp b/indra/llui/tests/alstudiofloater_test.cpp index aaedb43e4f..09eb42cca4 100644 --- a/indra/llui/tests/alstudiofloater_test.cpp +++ b/indra/llui/tests/alstudiofloater_test.cpp @@ -40,6 +40,7 @@ #include "llcontrol.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -244,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(); @@ -457,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_test.cpp b/indra/llui/tests/alsurface_test.cpp index e5e922a153..2df26c7d58 100644 --- a/indra/llui/tests/alsurface_test.cpp +++ b/indra/llui/tests/alsurface_test.cpp @@ -29,6 +29,7 @@ #include "../lluicolortable.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -89,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/altabstrip_test.cpp b/indra/llui/tests/altabstrip_test.cpp index 6dcfc572a7..382ea3a80d 100644 --- a/indra/llui/tests/altabstrip_test.cpp +++ b/indra/llui/tests/altabstrip_test.cpp @@ -33,6 +33,7 @@ #include "llfontgl.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -41,6 +42,9 @@ 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; @@ -632,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: @@ -652,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/altextgotoline_test.cpp b/indra/llui/tests/altextgotoline_test.cpp index 1ca504dc55..280659eaf6 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" @@ -64,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 f9777e22ea..5eaa2d7ef8 100644 --- a/indra/llui/tests/altextlayout_test.cpp +++ b/indra/llui/tests/altextlayout_test.cpp @@ -564,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_test.cpp b/indra/llui/tests/altextview_test.cpp index eb39ceb8cd..ff266f7a70 100644 --- a/indra/llui/tests/altextview_test.cpp +++ b/indra/llui/tests/altextview_test.cpp @@ -716,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); @@ -737,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<> diff --git a/indra/llui/tests/aluistatescope.h b/indra/llui/tests/aluistatescope.h new file mode 100644 index 0000000000..f54b2b4e47 --- /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/alviewtype_test.cpp b/indra/llui/tests/alviewtype_test.cpp index 6fffcde328..dc20a3f2d7 100644 --- a/indra/llui/tests/alviewtype_test.cpp +++ b/indra/llui/tests/alviewtype_test.cpp @@ -42,6 +42,7 @@ #include #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -150,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); @@ -188,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. @@ -201,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); @@ -215,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 ae12d1b471..95f4d83d77 100644 --- a/indra/llui/tests/alvimkeymap_test.cpp +++ b/indra/llui/tests/alvimkeymap_test.cpp @@ -53,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/alxuidiagnostics_test.cpp b/indra/llui/tests/alxuidiagnostics_test.cpp index 813dbaff5e..9b29a9ad74 100644 --- a/indra/llui/tests/alxuidiagnostics_test.cpp +++ b/indra/llui/tests/alxuidiagnostics_test.cpp @@ -29,6 +29,7 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -118,7 +119,7 @@ namespace tut } std::unique_ptr fv(floaterView()); - gFloaterView = fv.get(); + ll_test::FloaterViewScope floater_view(fv.get()); { ALXUIDiagnostics sink; @@ -155,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 @@ -171,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()); @@ -183,6 +183,5 @@ namespace tut !has(sink, ALXUIDiagnostics::Kind::UnknownAttribute, 0, "name")); fv.reset(); - gFloaterView = nullptr; } } diff --git a/indra/llui/tests/alxuisourcemap_test.cpp b/indra/llui/tests/alxuisourcemap_test.cpp index 9d1c6ab7c7..7f7f1724b9 100644 --- a/indra/llui/tests/alxuisourcemap_test.cpp +++ b/indra/llui/tests/alxuisourcemap_test.cpp @@ -31,6 +31,7 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -144,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); @@ -196,6 +197,5 @@ namespace tut delete floater; fv.reset(); - gFloaterView = nullptr; } } diff --git a/indra/llui/tests/alxuitreemodel_test.cpp b/indra/llui/tests/alxuitreemodel_test.cpp index 9d8223e1c6..c22d172381 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" @@ -175,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); @@ -211,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 @@ -225,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); @@ -290,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 @@ -306,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); @@ -333,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 @@ -349,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. @@ -504,7 +502,6 @@ namespace tut delete host; delete floater; fv.reset(); - gFloaterView = nullptr; } template<> template<> @@ -516,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; @@ -548,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/llemojidictionary_test.cpp b/indra/llui/tests/llemojidictionary_test.cpp index 73a7e80597..23b57632ee 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 5f85696013..2bc0b8ff20 100644 --- a/indra/llui/tests/llfloater_test.cpp +++ b/indra/llui/tests/llfloater_test.cpp @@ -29,6 +29,7 @@ #include "../lluictrlfactory.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -76,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"; @@ -94,7 +95,6 @@ namespace tut delete v; fv.reset(); - gFloaterView = nullptr; } // Built from a tree the caller already holds rather than from the file @@ -112,7 +112,7 @@ namespace tut } std::unique_ptr fv(floaterView()); - gFloaterView = fv.get(); + ll_test::FloaterViewScope floater_view(fv.get()); const std::string xml = ""; @@ -128,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 @@ -149,7 +148,7 @@ namespace tut } std::unique_ptr fv(floaterView()); - gFloaterView = fv.get(); + ll_test::FloaterViewScope floater_view(fv.get()); const std::string xml = "" @@ -198,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/llradiogroup_test.cpp b/indra/llui/tests/llradiogroup_test.cpp index e7f3a10583..bbee08cea2 100644 --- a/indra/llui/tests/llradiogroup_test.cpp +++ b/indra/llui/tests/llradiogroup_test.cpp @@ -33,6 +33,7 @@ #include "llcontrol.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -108,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); @@ -134,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 75a139a575..96f997e435 100644 --- a/indra/llui/tests/llresizebar_test.cpp +++ b/indra/llui/tests/llresizebar_test.cpp @@ -214,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; @@ -227,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/lltabcontainer_test.cpp b/indra/llui/tests/lltabcontainer_test.cpp index 1fb02910bc..e84214f3ba 100644 --- a/indra/llui/tests/lltabcontainer_test.cpp +++ b/indra/llui/tests/lltabcontainer_test.cpp @@ -37,6 +37,7 @@ #include "lluiimage.h" #include "alheadlessui_fixture.h" +#include "aluistatescope.h" #include "../test/lltut.h" @@ -47,6 +48,9 @@ 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/lluicolortable_test.cpp b/indra/llui/tests/lluicolortable_test.cpp index 3a75ca5a34..8ec9ec1f7b 100644 --- a/indra/llui/tests/lluicolortable_test.cpp +++ b/indra/llui/tests/lluicolortable_test.cpp @@ -26,6 +26,8 @@ #include "../lluicolortable.h" +#include "aluistatescope.h" + #include "../test/lltut.h" #include @@ -33,9 +35,11 @@ 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/llview_test.cpp b/indra/llui/tests/llview_test.cpp index e5703d43b0..d2468b7624 100644 --- a/indra/llui/tests/llview_test.cpp +++ b/indra/llui/tests/llview_test.cpp @@ -27,6 +27,8 @@ #include "../llview.h" #include "../llfocusmgr.h" +#include "aluistatescope.h" + #include "../test/lltut.h" namespace tut @@ -386,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")); From 901a51fd44eee8931b56883a4ebd2e8c78694ca4 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:41:28 -0400 Subject: [PATCH 35/67] llui's tests write their files in directories of their own alxuicatalog, alxuidocuments and alxuilint built their skin trees under LLDir's temp directory with fixed names; under the headless UI that directory is empty, so the trees landed in the working directory, and two runs at once -- shards of one binary, or two configurations -- shared them. llxuiparser wrote fixed names in the system's temp directory, and looked for a file that is not there in the working directory, where one could be. Each now works in a TempDir (altesttempdir.h): a new directory under the temp directory, as CI's runner gives it where it does, named by the test and a random suffix, and removed with what is in it when the test is done. Co-Authored-By: Claude Opus 5.5 --- indra/llui/tests/altesttempdir.h | 90 ++++++++++++++++++++++++ indra/llui/tests/alxuicatalog_test.cpp | 12 ++-- indra/llui/tests/alxuidocuments_test.cpp | 18 ++--- indra/llui/tests/alxuilint_test.cpp | 14 ++-- indra/llui/tests/llxuiparser_test.cpp | 22 +++--- 5 files changed, 118 insertions(+), 38 deletions(-) create mode 100644 indra/llui/tests/altesttempdir.h diff --git a/indra/llui/tests/altesttempdir.h b/indra/llui/tests/altesttempdir.h new file mode 100644 index 0000000000..294af9b448 --- /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/alxuicatalog_test.cpp b/indra/llui/tests/alxuicatalog_test.cpp index cc9e3c82c7..829f7575ed 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" @@ -48,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" @@ -80,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/alxuidocuments_test.cpp b/indra/llui/tests/alxuidocuments_test.cpp index 066c4a3119..fcf58835eb 100644 --- a/indra/llui/tests/alxuidocuments_test.cpp +++ b/indra/llui/tests/alxuidocuments_test.cpp @@ -29,6 +29,8 @@ #include "lldir.h" #include "llfile.h" +#include "altesttempdir.h" + #include "../test/lltut.h" #include @@ -37,19 +39,9 @@ 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/alxuilint_test.cpp b/indra/llui/tests/alxuilint_test.cpp index d4c97f12c0..5603733f0f 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" @@ -367,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; @@ -412,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 @@ -755,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" }) { @@ -790,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/llxuiparser_test.cpp b/indra/llui/tests/llxuiparser_test.cpp index c76d8c4f78..e3fb7a31f7 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 @@ -95,27 +97,24 @@ 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; }; @@ -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)); } } From 6bd0b12074ca2fe15cd63ac88742356304630090 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:41:29 -0400 Subject: [PATCH 36/67] The tests that start ALTextFind's worker close it and wait for it A find over a text past 256 KiB, or for a pattern across lines, runs on a worker thread that ALTextFind starts the first time and that lives until the viewer quits. altextfind tests 3 and 4 and altextview tests 50 and 98 started it and left it running, unjoined, for whatever ran after them in the process. ALTextFind::closeWorker() closes it and waits for its thread, as the viewer's going does, and lets it go, so the next long find starts another; the worker lives in altextfind.cpp, so nothing outside it could. Those tests call it as they end, a failed check included. Co-Authored-By: Claude Opus 5.5 --- indra/llui/tests/altextfind_test.cpp | 3 +++ indra/llui/tests/altextview_test.cpp | 12 ++++++++++++ indra/llui/text/altextfind.cpp | 18 ++++++++++++++++++ indra/llui/text/altextfind.h | 4 ++++ 4 files changed, 37 insertions(+) diff --git a/indra/llui/tests/altextfind_test.cpp b/indra/llui/tests/altextfind_test.cpp index d89764d0e9..9d0c793914 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/altextview_test.cpp b/indra/llui/tests/altextview_test.cpp index ff266f7a70..2eff75ad9f 100644 --- a/indra/llui/tests/altextview_test.cpp +++ b/indra/llui/tests/altextview_test.cpp @@ -1515,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) { @@ -3047,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/text/altextfind.cpp b/indra/llui/text/altextfind.cpp index 5bd479b74c..9d88a2da18 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 77e55a99a2..a8bd813d0c 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(); From 4e98dbbd44da6cc8cdfab57f4837387cca0002f5 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:50:48 -0400 Subject: [PATCH 37/67] llui's GL tests share one hidden window, and HeadlessUI holds to its mode altextview_gl, alsurface_gl and aldiffview_gl each kept a HeadlessGL in an anonymous-namespace gl() of their own. In one binary that is three hidden windows, each taking the context and putting gGL, the vertex buffers, the GL images and gUIProgram through their setup again, and at exit the first destroyed quits SDL under the other two. ll_test::uiGL() in aluigl_fixture.h is the one accessor, inline, so every file in a binary shares its window; it keeps today's lifetime until the test main can end it explicitly. HeadlessUI::get(gl_textures) made the process's UI with whichever mode its first caller asked for and handed it to every later caller, whatever they asked. A test that draws would get fonts with no GL textures, and a headless one fonts that need a context. A call asking for the other mode is now an LL_ERRS, which fails that test. The 34 newview tests that include the fixture, directly or through alscriptstudio_fixture.h, build and pass. Co-Authored-By: Claude Opus 5.5 --- indra/llui/tests/aldiffview_gl_test.cpp | 12 ++----- indra/llui/tests/alheadlessui_fixture.h | 14 +++++++- indra/llui/tests/alsurface_gl_test.cpp | 12 ++----- indra/llui/tests/altextview_gl_test.cpp | 36 +++++++++----------- indra/llui/tests/aluigl_fixture.h | 45 +++++++++++++++++++++++++ 5 files changed, 79 insertions(+), 40 deletions(-) create mode 100644 indra/llui/tests/aluigl_fixture.h diff --git a/indra/llui/tests/aldiffview_gl_test.cpp b/indra/llui/tests/aldiffview_gl_test.cpp index 82e1c8831b..bc69164109 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" @@ -46,16 +46,10 @@ 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(); @@ -96,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/alheadlessui_fixture.h b/indra/llui/tests/alheadlessui_fixture.h index 4f339d04ab..a21687f601 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/alsurface_gl_test.cpp b/indra/llui/tests/alsurface_gl_test.cpp index d838986b22..01448b020c 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" @@ -46,17 +46,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; - } - // 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(); @@ -81,7 +75,7 @@ namespace tut { alsurface_gl_data() { - gl(); + ll_test::uiGL(); ll_test::installWhiteTexture(); } }; diff --git a/indra/llui/tests/altextview_gl_test.cpp b/indra/llui/tests/altextview_gl_test.cpp index db134c4ca6..ce363f2ac4 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" @@ -58,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(); @@ -115,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(); @@ -151,7 +145,7 @@ namespace tut { altextview_gl_data() { - gl(); + ll_test::uiGL(); ll_test::installWhiteTexture(); } @@ -328,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(); @@ -386,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(); @@ -486,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(); @@ -587,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(); @@ -682,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(); @@ -944,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(); @@ -992,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(); @@ -1098,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(); @@ -1173,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(); @@ -1303,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(); @@ -1349,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/aluigl_fixture.h b/indra/llui/tests/aluigl_fixture.h new file mode 100644 index 0000000000..2303441f98 --- /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 From 251ac8704f14db74a970f64eb00c271f807d2afc Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:22:25 -0400 Subject: [PATCH 38/67] llrender's shared-context GL tests take one context, with every part up Six GL tests kept a function-static HeadlessGL of their own, each with its flags: in one binary that is six windows, whichever was made last current, and state one brought up that another tore down. ll_test::sharedHeadlessGL() is now the one, at the union of their flags, so a binary of them has one window and context. The window and tessellation tests had asked for less, and now run on a context with gUIProgram bound and LLVertexBuffer's arrays enabled for it. The tessellation fixture turns those arrays off for its own draws, as a draw with an enabled array and no buffer behind it fails, and puts them, the program and the buffer binding back; the window test puts back the texture binding it read through. The six tests that make a context per test keep doing so and link no static one; their comments no longer say they share. Co-Authored-By: Claude Opus 5.5 --- indra/llrender/tests/alfontcolrv1_gl_test.cpp | 2 +- .../llrender/tests/altessellation_gl_test.cpp | 22 ++++++++++------ .../tests/alwindowsdlheadless_test.cpp | 16 +++++++----- .../tests/llfontbitmapcache_gl_test.cpp | 4 +-- .../llrender/tests/llfontfreetype_gl_test.cpp | 4 +-- indra/llrender/tests/llfontgl_test.cpp | 19 ++++---------- indra/llrender/tests/llfonttextcache_test.cpp | 22 ++++------------ indra/llrender/tests/llheadlessgl_fixture.h | 25 +++++++++++++++---- .../tests/llrender2dutils_gl_test.cpp | 6 +---- indra/llrender/tests/lluiimage_test.cpp | 6 +---- 10 files changed, 62 insertions(+), 64 deletions(-) diff --git a/indra/llrender/tests/alfontcolrv1_gl_test.cpp b/indra/llrender/tests/alfontcolrv1_gl_test.cpp index b6f8d3c6ff..a2b107b3be 100644 --- a/indra/llrender/tests/alfontcolrv1_gl_test.cpp +++ b/indra/llrender/tests/alfontcolrv1_gl_test.cpp @@ -49,7 +49,7 @@ namespace tut // ------------------------------------------------------------- // 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. // ------------------------------------------------------------- diff --git a/indra/llrender/tests/altessellation_gl_test.cpp b/indra/llrender/tests/altessellation_gl_test.cpp index b9a0c512e8..2d89c8923b 100644 --- a/indra/llrender/tests/altessellation_gl_test.cpp +++ b/indra/llrender/tests/altessellation_gl_test.cpp @@ -101,16 +101,19 @@ namespace tut { 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 +159,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,6 +270,8 @@ namespace tut GLint mInner = -1; bool mCompiled = true; bool mLinked = false; + GLint mPriorProgram = 0; + U32 mPriorArrays = 0; }; typedef test_group altessellation_test; diff --git a/indra/llrender/tests/alwindowsdlheadless_test.cpp b/indra/llrender/tests/alwindowsdlheadless_test.cpp index 612e924c5e..86427f6808 100644 --- a/indra/llrender/tests/alwindowsdlheadless_test.cpp +++ b/indra/llrender/tests/alwindowsdlheadless_test.cpp @@ -39,13 +39,13 @@ namespace tut { 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(); } @@ -110,10 +110,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 e38f93569a..1957311d7e 100644 --- a/indra/llrender/tests/llfontbitmapcache_gl_test.cpp +++ b/indra/llrender/tests/llfontbitmapcache_gl_test.cpp @@ -37,8 +37,8 @@ namespace tut { 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 diff --git a/indra/llrender/tests/llfontfreetype_gl_test.cpp b/indra/llrender/tests/llfontfreetype_gl_test.cpp index 4704e797cc..19282a2de6 100644 --- a/indra/llrender/tests/llfontfreetype_gl_test.cpp +++ b/indra/llrender/tests/llfontfreetype_gl_test.cpp @@ -43,8 +43,8 @@ namespace tut 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. // ------------------------------------------------------------- diff --git a/indra/llrender/tests/llfontgl_test.cpp b/indra/llrender/tests/llfontgl_test.cpp index cf4af02606..8adde1b64f 100644 --- a/indra/llrender/tests/llfontgl_test.cpp +++ b/indra/llrender/tests/llfontgl_test.cpp @@ -73,29 +73,20 @@ namespace namespace tut { - // Shared GL fixture. TUT spins fixtures per-test method, but + // 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; }; @@ -1004,7 +995,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; }; diff --git a/indra/llrender/tests/llfonttextcache_test.cpp b/indra/llrender/tests/llfonttextcache_test.cpp index b4b92361de..e8506e2f76 100644 --- a/indra/llrender/tests/llfonttextcache_test.cpp +++ b/indra/llrender/tests/llfonttextcache_test.cpp @@ -82,20 +82,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 +99,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() { @@ -433,7 +421,7 @@ namespace tut struct llfonttextcache_render_data { - ll_test::HeadlessGL& gl = getSharedHeadlessGL(/*needs_render=*/true); + ll_test::HeadlessGL& gl = ll_test::sharedHeadlessGL(); llfonttextcache_render_data() { diff --git a/indra/llrender/tests/llheadlessgl_fixture.h b/indra/llrender/tests/llheadlessgl_fixture.h index 1576dbb20c..d51dc74550 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 @@ -81,7 +81,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 @@ -463,6 +463,21 @@ 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; + } } #endif // LL_LLHEADLESSGL_FIXTURE_H diff --git a/indra/llrender/tests/llrender2dutils_gl_test.cpp b/indra/llrender/tests/llrender2dutils_gl_test.cpp index d6f0b47ddc..250995ca8e 100644 --- a/indra/llrender/tests/llrender2dutils_gl_test.cpp +++ b/indra/llrender/tests/llrender2dutils_gl_test.cpp @@ -48,11 +48,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() diff --git a/indra/llrender/tests/lluiimage_test.cpp b/indra/llrender/tests/lluiimage_test.cpp index 6ab05b88fa..a4331e8689 100644 --- a/indra/llrender/tests/lluiimage_test.cpp +++ b/indra/llrender/tests/lluiimage_test.cpp @@ -96,11 +96,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() From 06b234ab56f73afb6b2f724071e9b2879d3e71a6 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:26:08 -0400 Subject: [PATCH 39/67] llrender's test fixtures, groups and helpers have internal linkage Each test's TUT data struct, group object and file-local helpers sit in an anonymous namespace inside tut, so the test bodies TUT instantiates for them are internal too. llrendermatrix's same_bytes and ensure_matrix_close were strong and external, and llmath's llmatrix4a test defined both as well. llfontregistry.h friends tut::llfontregistry_data and tut::llfontregistry_gl_data by name, so those two keep their names and namespace; only their group objects move. ll_test::UIImageProbe stays as lluiimage.h friends it. What stays external is the shared fixture headers' inline code, the same in every test. Co-Authored-By: Claude Opus 5.5 --- indra/llrender/tests/alfontcolrv1_gl_test.cpp | 3 +++ indra/llrender/tests/alfontcolrv1_test.cpp | 3 +++ indra/llrender/tests/alfontshaping_gl_test.cpp | 3 +++ indra/llrender/tests/alfontshaping_test.cpp | 3 +++ indra/llrender/tests/altessellation_gl_test.cpp | 2 +- indra/llrender/tests/alwindowsdlheadless_test.cpp | 3 +++ indra/llrender/tests/llfontbitmapcache_gl_test.cpp | 3 +++ indra/llrender/tests/llfontbitmapcache_test.cpp | 3 +++ indra/llrender/tests/llfontfreetype_gl_test.cpp | 3 +++ indra/llrender/tests/llfontfreetype_test.cpp | 6 ++++++ indra/llrender/tests/llfontgl_test.cpp | 6 ++++++ indra/llrender/tests/llfontregistry_gl_test.cpp | 7 ++++++- indra/llrender/tests/llfontregistry_test.cpp | 6 ++++++ indra/llrender/tests/llfonttextcache_test.cpp | 6 ++++++ indra/llrender/tests/llimagegl_test.cpp | 3 +++ indra/llrender/tests/llrender2dutils_gl_test.cpp | 3 +++ indra/llrender/tests/llrendermatrix_test.cpp | 3 +++ indra/llrender/tests/lluiimage_test.cpp | 3 +++ indra/llrender/tests/lluitransform_test.cpp | 3 +++ 19 files changed, 70 insertions(+), 2 deletions(-) diff --git a/indra/llrender/tests/alfontcolrv1_gl_test.cpp b/indra/llrender/tests/alfontcolrv1_gl_test.cpp index a2b107b3be..9312abb530 100644 --- a/indra/llrender/tests/alfontcolrv1_gl_test.cpp +++ b/indra/llrender/tests/alfontcolrv1_gl_test.cpp @@ -44,6 +44,8 @@ #include namespace tut +{ +namespace { using namespace ll_test; @@ -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 bf6436ddfa..ba95c9af03 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 410fc16819..5d9bd95e1e 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 77d5948fdc..5852663895 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/altessellation_gl_test.cpp b/indra/llrender/tests/altessellation_gl_test.cpp index 2d89c8923b..7e8073e894 100644 --- a/indra/llrender/tests/altessellation_gl_test.cpp +++ b/indra/llrender/tests/altessellation_gl_test.cpp @@ -95,7 +95,6 @@ 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 { @@ -277,6 +276,7 @@ namespace tut 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 86427f6808..145ec90e06 100644 --- a/indra/llrender/tests/alwindowsdlheadless_test.cpp +++ b/indra/llrender/tests/alwindowsdlheadless_test.cpp @@ -36,6 +36,8 @@ #include namespace tut +{ +namespace { struct alwindowsdlheadless_data { @@ -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. diff --git a/indra/llrender/tests/llfontbitmapcache_gl_test.cpp b/indra/llrender/tests/llfontbitmapcache_gl_test.cpp index 1957311d7e..de3ca03b41 100644 --- a/indra/llrender/tests/llfontbitmapcache_gl_test.cpp +++ b/indra/llrender/tests/llfontbitmapcache_gl_test.cpp @@ -34,6 +34,8 @@ #include "../test/lltut.h" namespace tut +{ +namespace { using namespace ll_test; @@ -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 a760cce0ce..745a211ca9 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 19282a2de6..3339f7b510 100644 --- a/indra/llrender/tests/llfontfreetype_gl_test.cpp +++ b/indra/llrender/tests/llfontfreetype_gl_test.cpp @@ -39,6 +39,8 @@ #include namespace tut +{ +namespace { using namespace ll_test; @@ -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 822f034483..6903243a8f 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 8adde1b64f..b95af706c9 100644 --- a/indra/llrender/tests/llfontgl_test.cpp +++ b/indra/llrender/tests/llfontgl_test.cpp @@ -72,6 +72,8 @@ namespace } namespace tut +{ +namespace { // The shared GL context. TUT spins fixtures per-test method, but // per-fixture HeadlessGL recreation poisons LLFontGL's per-getter @@ -94,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 @@ -982,6 +985,8 @@ namespace tut // static fontp cache after a few cycles (see vertexbuffer fixture). // =================================================================== +namespace +{ inline void ensureRenderLLFontGL() { static bool initialized = false; @@ -1003,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 1160ada76f..700ace4bbb 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 d9d1770f83..76e8174079 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 e8506e2f76..a5f7540dd1 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 @@ -121,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 @@ -419,6 +422,8 @@ namespace tut // framebuffer. // =================================================================== +namespace +{ struct llfonttextcache_render_data { ll_test::HeadlessGL& gl = ll_test::sharedHeadlessGL(); @@ -439,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/llimagegl_test.cpp b/indra/llrender/tests/llimagegl_test.cpp index 1287bc51c0..4e371c210d 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 diff --git a/indra/llrender/tests/llrender2dutils_gl_test.cpp b/indra/llrender/tests/llrender2dutils_gl_test.cpp index 250995ca8e..2132f31368 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 { @@ -129,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 40c497bbb3..28c170bcaf 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/lluiimage_test.cpp b/indra/llrender/tests/lluiimage_test.cpp index a4331e8689..d9d0ef772e 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 { @@ -145,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 49f6e1b9bd..7e0af297f1 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<> From 58c75720f34052cea06e6b6f8de1ac37d8c41baf Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:27:30 -0400 Subject: [PATCH 40/67] llimagegl's upload thread test ends the thread however it ends The test deleted LLImageGLThread only after its first checks had passed. One that failed left the thread running into the next test, which tears down the window its shared context hangs off, with work that wrote to the failed test's flags. A guard ends the thread on every way out, and the flags are declared before it so they outlive it. Co-Authored-By: Claude Opus 5.5 --- indra/llrender/tests/llimagegl_test.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/indra/llrender/tests/llimagegl_test.cpp b/indra/llrender/tests/llimagegl_test.cpp index 4e371c210d..4e6ba83e9e 100644 --- a/indra/llrender/tests/llimagegl_test.cpp +++ b/indra/llrender/tests/llimagegl_test.cpp @@ -816,7 +816,16 @@ namespace // 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", @@ -824,8 +833,6 @@ namespace 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( From e8ebb8f0f28cbc048cbe90bdb325dfe57408c59b Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:29:09 -0400 Subject: [PATCH 41/67] llnamevalue's and llsdmessagebuilder's tests link the libraries they test llnamevalue's test was a unit test that built its own llnamevalue.cpp; it links llmessage now, as the other integration tests here do. llsdmessagebuilder's test included llsdutil_math.cpp, defining twelve of llmath's functions strongly beside the llmath it links; it includes the header instead. The three tests left as units stub parts of llmessage and llcorehttp, which the CMake now says. Co-Authored-By: Claude Opus 5.5 --- indra/llmessage/CMakeLists.txt | 5 ++++- indra/llmessage/tests/llsdmessagebuilder_test.cpp | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 476b07b9df..018693a921 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/llsdmessagebuilder_test.cpp b/indra/llmessage/tests/llsdmessagebuilder_test.cpp index 7dcc03edc1..0ba0a1f182 100644 --- a/indra/llmessage/tests/llsdmessagebuilder_test.cpp +++ b/indra/llmessage/tests/llsdmessagebuilder_test.cpp @@ -40,8 +40,7 @@ #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 From ac13319856ffb150177f8b72b6b9c06c8eb53dda Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:32:21 -0400 Subject: [PATCH 42/67] llmessage's test fixtures, groups and helpers have internal linkage Each test's TUT data struct, group object and file-local helpers sit in an anonymous namespace inside tut: the node classes llhttpnode registers, llsdmessagereader's testType, the message maps and buffers builders and parsers keep, io's five groups and their io objects. llpipeutil's random generator is local to its file. The two tests that compile llhost.cpp and net.cpp, and the coprocedure manager's, keep their stubs as they are; what their doubles record and the dispatcher test's own helpers go in an anonymous namespace ahead of them. llpipeutil's pipes stay external, the interface of the one helper file io links. Co-Authored-By: Claude Opus 5.5 --- .../llmessage/tests/alnetimpairment_test.cpp | 6 ++++ indra/llmessage/tests/io_test.cpp | 15 +++++++++ .../tests/llavatarnamecache_test.cpp | 3 ++ indra/llmessage/tests/llbuffer_test.cpp | 3 ++ .../tests/llcoproceduremanager_test.cpp | 3 ++ indra/llmessage/tests/lldatapacker_test.cpp | 3 ++ indra/llmessage/tests/llhost_test.cpp | 3 ++ indra/llmessage/tests/llhttpnode_test.cpp | 9 ++++++ .../llmessage/tests/llmessageconfig_test.cpp | 3 ++ .../tests/llmessagetemplateparser_test.cpp | 3 ++ indra/llmessage/tests/llnamevalue_test.cpp | 3 ++ indra/llmessage/tests/llpartdata_test.cpp | 3 ++ indra/llmessage/tests/llpipeutil.cpp | 3 ++ .../tests/llsdmessagebuilder_test.cpp | 3 ++ .../tests/llsdmessagereader_test.cpp | 6 ++++ .../tests/lltemplatemessagebuilder_test.cpp | 3 ++ .../lltemplatemessagedispatcher_test.cpp | 32 ++++++++++++------- .../tests/lltrustedmessageservice_test.cpp | 17 +++++++--- indra/llmessage/tests/llxfer_file_test.cpp | 3 ++ indra/llmessage/tests/llxorcipher_test.cpp | 3 ++ indra/llmessage/tests/llzerocode_test.cpp | 3 ++ indra/llmessage/tests/message_test.cpp | 3 ++ 22 files changed, 116 insertions(+), 17 deletions(-) diff --git a/indra/llmessage/tests/alnetimpairment_test.cpp b/indra/llmessage/tests/alnetimpairment_test.cpp index 60ce0ae8c7..9dc0c39356 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 68a5c10060..54ffba490f 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. @@ -934,6 +948,7 @@ namespace tut 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>() diff --git a/indra/llmessage/tests/llavatarnamecache_test.cpp b/indra/llmessage/tests/llavatarnamecache_test.cpp index 3735f42c47..c45c9067c4 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 330a4f288f..26a00fb491 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 0d014b1ca2..d17f7730c4 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 cce5a23826..b8eccb0ca8 100644 --- a/indra/llmessage/tests/lldatapacker_test.cpp +++ b/indra/llmessage/tests/lldatapacker_test.cpp @@ -41,6 +41,8 @@ #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<> diff --git a/indra/llmessage/tests/llhost_test.cpp b/indra/llmessage/tests/llhost_test.cpp index a66655ad6c..8f4214e8bb 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 c38dee120b..a6c0f3da63 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 b8942e99b3..32baf761e5 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; @@ -80,6 +82,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/llmessagetemplateparser_test.cpp b/indra/llmessage/tests/llmessagetemplateparser_test.cpp index f665fdde4d..3c963955d4 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 f77cd102f4..c06abd41c9 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 c517232bcc..017fbbd0c4 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 1dd4bdfa3c..c95c459f44 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 0ba0a1f182..9e7bee3758 100644 --- a/indra/llmessage/tests/llsdmessagebuilder_test.cpp +++ b/indra/llmessage/tests/llsdmessagebuilder_test.cpp @@ -44,6 +44,8 @@ #include "lltemplatemessagebuilder.h" namespace tut +{ +namespace { static LLTemplateMessageBuilder::message_template_name_map_t templateNameMap; @@ -114,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 dd6520ea33..62ac6ed029 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 e2bf97803e..bbaa1f178b 100644 --- a/indra/llmessage/tests/lltemplatemessagebuilder_test.cpp +++ b/indra/llmessage/tests/lltemplatemessagebuilder_test.cpp @@ -42,6 +42,8 @@ #include "v4math.h" namespace tut +{ +namespace { static LLTemplateMessageBuilder::message_template_name_map_t nameMap; static LLTemplateMessageReader::message_template_number_map_t numberMap; @@ -121,6 +123,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 bb21336892..8169afd2c0 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 cc199141b8..aff273c755 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 358659a5b9..e7479f7402 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 72ae98afb2..992fe27f94 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 7a52c90b46..84426be147 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 6fb2b92803..3e8da523d9 100644 --- a/indra/llmessage/tests/message_test.cpp +++ b/indra/llmessage/tests/message_test.cpp @@ -51,6 +51,8 @@ namespace } namespace tut +{ +namespace { struct LLMessageSystemTestData { @@ -132,6 +134,7 @@ namespace tut typedef test_group LLMessageSystemTestGroup; typedef LLMessageSystemTestGroup::object LLMessageSystemTestObject; LLMessageSystemTestGroup messageTestGroup("LLMessageSystem"); +} template<> template<> void LLMessageSystemTestObject::test<1>() From 49f3257379dfb003fba7e3bda353f811bbb60089 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:37:18 -0400 Subject: [PATCH 43/67] The message system tests start and end their own, on a port the OS picks lltemplatemessagebuilder's test started a message system on UDP 13035 the first time a test asked, and left it in gMessageSystem for the rest of the process; message's started one on the same port for each test and deleted it, putting null where whatever was there had been. Both now hold an ll_test::MessageSystemScope for each test, which starts the system on a port the OS assigns and puts gMessageSystem back as it found it. The system these tests want has no template, so it is never OK, and ~LLMessageSystem closes the socket only of a system that is OK. The scope closes it, rather than leaving one bound port open per test. The tests' own ll_init_apr calls go: the runner has set APR up, and each call took another apr_initialize. Co-Authored-By: Claude Opus 5.5 --- .../llmessage/tests/llmessagesystem_fixture.h | 81 +++++++++++++++++++ .../tests/lltemplatemessagebuilder_test.cpp | 26 ++---- indra/llmessage/tests/message_test.cpp | 31 +------ 3 files changed, 89 insertions(+), 49 deletions(-) create mode 100644 indra/llmessage/tests/llmessagesystem_fixture.h diff --git a/indra/llmessage/tests/llmessagesystem_fixture.h b/indra/llmessage/tests/llmessagesystem_fixture.h new file mode 100644 index 0000000000..6e2b589290 --- /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/lltemplatemessagebuilder_test.cpp b/indra/llmessage/tests/lltemplatemessagebuilder_test.cpp index bbaa1f178b..5449f5a959 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" @@ -50,28 +50,12 @@ namespace 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); } diff --git a/indra/llmessage/tests/message_test.cpp b/indra/llmessage/tests/message_test.cpp index 3e8da523d9..0eb9d71660 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" @@ -56,34 +56,13 @@ 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 @@ -104,10 +83,6 @@ namespace ~LLMessageSystemTestData() { - // not end_messaging_system() - delete static_cast(gMessageSystem); - gMessageSystem = NULL; - // rm contents of temp dir std::ostringstream ostr; ostr << mTestConfigDir << mSep << "message.xml"; From 3f1b8682ce059147cc4c9158262ea04b2d0d65f2 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:40:36 -0400 Subject: [PATCH 44/67] io's server takes a port the OS picks, and llmessage's fixtures assert nothing as they end io's pipe and pump fitness tests listened on TCP 13050, so two runs at once failed each other's connects. LLSocket::create binds only a port it is given, so the fixture binds and listens on port 0 as it would and connects to the port the OS chose; two runs at once now pass. The message and llmessageconfig fixtures ensured, in their destructors, that their temporary directory came away. A destructor is noexcept, so a failure there ended the process; they now say what they could not remove. The waits stay: io's are its chains' expiry and response timeouts and a margin past them, and llmessageconfig's six seconds outlast the five the config file waits between looks at the disk. Co-Authored-By: Claude Opus 5.5 --- indra/llmessage/tests/io_test.cpp | 49 +++++++++++++------ .../llmessage/tests/llmessageconfig_test.cpp | 14 ++++-- indra/llmessage/tests/message_test.cpp | 14 ++++-- 3 files changed, 54 insertions(+), 23 deletions(-) diff --git a/indra/llmessage/tests/io_test.cpp b/indra/llmessage/tests/io_test.cpp index 54ffba490f..683142d18c 100644 --- a/indra/llmessage/tests/io_test.cpp +++ b/indra/llmessage/tests/io_test.cpp @@ -916,21 +916,39 @@ namespace 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() @@ -944,6 +962,7 @@ namespace 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; @@ -980,7 +999,7 @@ namespace //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; @@ -1023,7 +1042,7 @@ namespace // 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; @@ -1066,7 +1085,7 @@ namespace // 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; @@ -1109,7 +1128,7 @@ namespace // 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; @@ -1152,7 +1171,7 @@ namespace // 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/llmessageconfig_test.cpp b/indra/llmessage/tests/llmessageconfig_test.cpp index 32baf761e5..d886d248a4 100644 --- a/indra/llmessage/tests/llmessageconfig_test.cpp +++ b/indra/llmessage/tests/llmessageconfig_test.cpp @@ -58,14 +58,20 @@ namespace 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) diff --git a/indra/llmessage/tests/message_test.cpp b/indra/llmessage/tests/message_test.cpp index 0eb9d71660..2e3be7f899 100644 --- a/indra/llmessage/tests/message_test.cpp +++ b/indra/llmessage/tests/message_test.cpp @@ -81,17 +81,23 @@ namespace LLMessageConfig::initClass("simulator", ostr.str()); } + // A destructor cannot fail a test, so the clean-up only says what + // it could not remove. ~LLMessageSystemTestData() { // 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) From c97f72b5a9d1908e03cb1e3d489a0cba17a5db81 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:42:04 -0400 Subject: [PATCH 45/67] lldatapacker's file tests write a temporary file of their own The two LLDataPackerAsciiFile tests wrote datapacker_test.txt in the working directory, the same name for every run of the binary, and left it there. Each now writes a NamedTempFile, which no other run shares and which goes when the test ends. Co-Authored-By: Claude Opus 5.5 --- indra/llmessage/tests/lldatapacker_test.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/indra/llmessage/tests/lldatapacker_test.cpp b/indra/llmessage/tests/lldatapacker_test.cpp index b8eccb0ca8..2caeeb3fc8 100644 --- a/indra/llmessage/tests/lldatapacker_test.cpp +++ b/indra/llmessage/tests/lldatapacker_test.cpp @@ -37,8 +37,8 @@ #include "v3math.h" #include "v4math.h" #include "llsdserialize.h" +#include "../test/namedtempfile.h" -#define TEST_FILE_NAME "datapacker_test.txt" namespace tut { @@ -351,7 +351,10 @@ namespace { 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; @@ -406,7 +409,10 @@ namespace 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; From 56f2e093501943ba909679e06039356701d74993 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:47:21 -0400 Subject: [PATCH 46/67] llcorehttp's suites, fixtures and helpers have internal linkage The ten suites are .hpp files that llcorehttp_test.cpp includes, one translation unit: each suite's TUT data struct, handler and group object now sit in an anonymous namespace inside tut, and the runner's helpers (init_curl, term_curl, get_base_url, stop_thread, ScopedCurlInit), which the header declared extern, in an anonymous namespace of their own. Nothing in the binary takes a fixed port or file: the Python peer binds port 0 (a free one of 8000-8019 on Windows), and the WebSocket suite probes for a free port at random. Co-Authored-By: Claude Opus 5.5 --- indra/llcorehttp/tests/llcorehttp_test.cpp | 5 +++++ indra/llcorehttp/tests/llcorehttp_test.h | 15 +++++++++++---- indra/llcorehttp/tests/test_bufferarray.hpp | 3 +++ indra/llcorehttp/tests/test_bufferstream.hpp | 3 +++ indra/llcorehttp/tests/test_httpheaders.hpp | 3 +++ indra/llcorehttp/tests/test_httpoperation.hpp | 3 +++ indra/llcorehttp/tests/test_httprequest.hpp | 3 +++ indra/llcorehttp/tests/test_httprequestqueue.hpp | 3 +++ indra/llcorehttp/tests/test_httpstatus.hpp | 3 +++ indra/llcorehttp/tests/test_jsonrpcws.hpp | 3 +++ indra/llcorehttp/tests/test_refcounted.hpp | 3 +++ indra/llcorehttp/tests/test_websocketmgr.hpp | 3 +++ 12 files changed, 46 insertions(+), 4 deletions(-) diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index cc3c241a96..a503555602 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 133cb3121a..c0b575d1a5 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 2a272e73d7..da2b742350 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 556abf45a2..4bd4cba38a 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 d8eff78ca5..d2d76aa281 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 361091c7b2..2d17d00eab 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 5dacfc9e94..6d5b8ab0a3 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 c6fa1b7534..11a0016912 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 eac7ba8557..3a0ff04370 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 e87b2a19d4..5ae24a3181 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 eb23a25545..9b1a48b8c7 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 8f7737427c..9eae3ee311 100644 --- a/indra/llcorehttp/tests/test_websocketmgr.hpp +++ b/indra/llcorehttp/tests/test_websocketmgr.hpp @@ -424,6 +424,8 @@ namespace } namespace tut +{ +namespace { struct WebsocketMgrTestData { @@ -432,6 +434,7 @@ namespace tut typedef test_group WebsocketMgrTestGroupType; typedef WebsocketMgrTestGroupType::object WebsocketMgrTestObjectType; WebsocketMgrTestGroupType WebsocketMgrTestGroup("LLWebsocketMgr Tests"); +} template<> template<> void WebsocketMgrTestObjectType::test<1>() From 0cdb004e7fab562450b6768a174ca11b7f5f4eae Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:50:49 -0400 Subject: [PATCH 47/67] The WebSocket manager suite ends the manager with each test Each test adds its servers to LLWebsocketMgr and removes them at its end, so one that failed first left its servers listening, and their threads running, into every later test. The fixture deletes the manager as the test ends, which stops whatever it still holds; the next test makes a new one, as the first did. Co-Authored-By: Claude Opus 5.5 --- indra/llcorehttp/tests/test_websocketmgr.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/indra/llcorehttp/tests/test_websocketmgr.hpp b/indra/llcorehttp/tests/test_websocketmgr.hpp index 9eae3ee311..0f46d0fe99 100644 --- a/indra/llcorehttp/tests/test_websocketmgr.hpp +++ b/indra/llcorehttp/tests/test_websocketmgr.hpp @@ -429,6 +429,14 @@ 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; From 3b084331af2d1a18e3bd367a959fa6af3e366687 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:55:07 -0400 Subject: [PATCH 48/67] newview's tests define each shared fake once, and no two of them one name differently Each test is its own binary today, so a second definition never met the first; in one binary they are a link error, or one test silently runs the other's. - rlvGetAnonym, which llui reaches the viewer for, was defined by the studio fixture header, non-inline, in each of the 31 tests that include it, and again by 7 others. It is defined once, in newview_test_fakes, an object library each of the 38 tests links whole: an archive member could be passed over by a linker that reads llui after it. - alscriptstudiochecking and alscriptobjectcheck each stubbed ALScriptLints::apply and luauBase, alScriptKeyedWords, ALScriptModules::identity and the preprocessor's fileOf and pathOf, with apply doing different things in each. The studio's stub library holds one of each now, in alscriptstudio_stubs.cpp with the preprocessor's names that alscriptpaths_stub.cpp held; what apply does is the test's choice, through al_studio_test::ChosenLints, put back as the fixture ends. The two tests now spell an inventory script's path as the other tests do. - ALScriptRef::id was stubbed by alscriptstudiofiles' test and by the studio's stub library, and defined for real by alscripttypes.cpp. The stub library builds the real one now and the stubs go: the tests compare ids with id() itself, never with a spelling of their own. - tut::tut_test, the group object of five tests, takes each test's name, and llhttpretrypolicy's data struct, tut::TestData, is llhttpretrypolicy_data. Co-Authored-By: Claude Opus 5.5 --- indra/newview/CMakeLists.txt | 85 +++++++++--------- .../newview/tests/alnotecardembedded_test.cpp | 9 -- indra/newview/tests/alrecoverykeeper_test.cpp | 9 -- .../newview/tests/alscriptanalyzers_test.cpp | 9 -- .../tests/alscriptexplorermodel_test.cpp | 9 -- .../tests/alscriptexplorertree_test.cpp | 9 -- indra/newview/tests/alscriptkeymap_test.cpp | 9 -- .../tests/alscriptobjectcheck_test.cpp | 36 ++------ indra/newview/tests/alscriptpaths_stub.cpp | 82 ------------------ indra/newview/tests/alscriptstudio_fixture.h | 11 +-- indra/newview/tests/alscriptstudio_stubs.cpp | 86 +++++++++++++++++++ indra/newview/tests/alscriptstudio_stubs.h | 55 ++++++++++++ .../tests/alscriptstudiochecking_test.cpp | 49 +++-------- .../tests/alscriptstudiofiles_test.cpp | 6 -- indra/newview/tests/alscripttheme_test.cpp | 7 -- .../newview/tests/llhttpretrypolicy_test.cpp | 4 +- indra/newview/tests/llsecapi_test.cpp | 2 +- .../newview/tests/llsechandler_basic_test.cpp | 2 +- indra/newview/tests/llslurl_test.cpp | 2 +- indra/newview/tests/llviewernetwork_test.cpp | 2 +- indra/newview/tests/llvocache_test.cpp | 2 +- indra/newview/tests/newview_test_fakes.cpp | 41 +++++++++ 22 files changed, 254 insertions(+), 272 deletions(-) delete mode 100644 indra/newview/tests/alscriptpaths_stub.cpp create mode 100644 indra/newview/tests/alscriptstudio_stubs.h create mode 100644 indra/newview/tests/newview_test_fakes.cpp diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index b6c5522602..2d6b6f968f 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,13 @@ 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}") + # A unit test compiles one viewer source file on its own. Each line is one # such file and its test under tests/. foreach( @@ -2584,7 +2591,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 +2600,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 +2623,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 +2633,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 +2644,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 +2654,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 +2664,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 +2674,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 +2683,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 +2694,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 +2705,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 +2715,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 +2725,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 +2735,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 +2745,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 +2765,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 +2779,7 @@ if(AL_BUILD_TESTS) UNIT PROJECT ${test_project} SOURCES alscriptkeypresets.cpp - LIBRARIES llui ${test_libs} + LIBRARIES newview_test_fakes llui ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -2786,7 +2793,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 +2803,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 +2815,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 +2829,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 +2839,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 +2849,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 +2859,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 +2869,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 +2879,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 +2889,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 +2913,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 +2923,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 +2933,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 +2949,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 +2959,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 +2969,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 +2979,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 +2989,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 +2999,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 +3010,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 +3020,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\"" ) diff --git a/indra/newview/tests/alnotecardembedded_test.cpp b/indra/newview/tests/alnotecardembedded_test.cpp index a3f85e3905..995e21ea10 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 6f841e5e43..a81229a4cd 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/alscriptanalyzers_test.cpp b/indra/newview/tests/alscriptanalyzers_test.cpp index 2802497566..e9e21ce246 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/alscriptexplorermodel_test.cpp b/indra/newview/tests/alscriptexplorermodel_test.cpp index bf54165f62..82b3fbbaa6 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 30270ec3d0..0af6561178 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 2f620fe8bb..5264d3013b 100644 --- a/indra/newview/tests/alscriptkeymap_test.cpp +++ b/indra/newview/tests/alscriptkeymap_test.cpp @@ -40,15 +40,6 @@ // 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) -{ - return gKeymapTestAnonName; -} - namespace tut { struct alscriptkeymap_data diff --git a/indra/newview/tests/alscriptobjectcheck_test.cpp b/indra/newview/tests/alscriptobjectcheck_test.cpp index 447731a873..3a6ebb765c 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 33bf3fcd59..0000000000 --- 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 badb8b026d..bf3a330679 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 244a9a585f..146a94c1d8 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 0000000000..914225140d --- /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 e19cf29753..1af1d948da 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 @@ -261,6 +235,7 @@ 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 916e23201e..4d27ef2112 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/alscripttheme_test.cpp b/indra/newview/tests/alscripttheme_test.cpp index f738138cab..23cc721e92 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 diff --git a/indra/newview/tests/llhttpretrypolicy_test.cpp b/indra/newview/tests/llhttpretrypolicy_test.cpp index 5c995c807b..a261ec8699 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/llsecapi_test.cpp b/indra/newview/tests/llsecapi_test.cpp index 1a2fa7d8f6..a5d8a6d357 100644 --- a/indra/newview/tests/llsecapi_test.cpp +++ b/indra/newview/tests/llsecapi_test.cpp @@ -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 fc8d219242..578d1f6a3d 100644 --- a/indra/newview/tests/llsechandler_basic_test.cpp +++ b/indra/newview/tests/llsechandler_basic_test.cpp @@ -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 eae2646a61..051d85309b 100644 --- a/indra/newview/tests/llslurl_test.cpp +++ b/indra/newview/tests/llslurl_test.cpp @@ -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/llviewernetwork_test.cpp b/indra/newview/tests/llviewernetwork_test.cpp index 6190d7052b..c798b2f86f 100644 --- a/indra/newview/tests/llviewernetwork_test.cpp +++ b/indra/newview/tests/llviewernetwork_test.cpp @@ -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 d8d159d77e..d4076625e0 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/newview_test_fakes.cpp b/indra/newview/tests/newview_test_fakes.cpp new file mode 100644 index 0000000000..6585acb914 --- /dev/null +++ b/indra/newview/tests/newview_test_fakes.cpp @@ -0,0 +1,41 @@ +/** + * @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 + +// An object, linked whole into each test that names it, rather than an +// archive's member: llui is what reaches for these, and a linker that reads +// its archives once, in order, would have passed an archive listed before +// llui by the time llui asked. 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. +class LLAvatarName; +const std::string& rlvGetAnonym(const LLAvatarName&) +{ + static const std::string anon("Anon"); + return anon; +} From c1edb94cf164532fed48f8caa8e5a2720b2c0dd9 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 8 Oct 2026 23:59:21 -0400 Subject: [PATCH 49/67] What a newview test defines for itself has internal linkage Nearly every newview test already kept its helpers in an anonymous namespace or in its fixture. The rest move into one or become static, so that two tests in one binary can neither clash at link time nor silently share one weak definition of a same-named type: alscriptanalysislane's Answers and Held, alscriptstudiochecking's NoWorld, cppfeatures' example classes and constexpr functions, llviewercontrollistener's ensure_contains overload, llremoteparcelrequest's TestObserver, lllogininstance's MockNotifications, llworldmap's MapBlockRequest, and the globals lldateutil, llsechandler_basic, llslurl and llviewernetwork fill for their stubs to read. Only what TUT needs stays external -- each group's data struct, its group object and the test specialisations -- with the stubs of the viewer's own symbols, which the code under test calls by name. No newview test type is used with LLSingleton, LLInstanceTracker or LockStatic, which key on the type's name and would need a name unique across the tree too. Co-Authored-By: Claude Opus 5.5 --- .../tests/alscriptanalysislane_test.cpp | 3 ++ .../tests/alscriptstudiochecking_test.cpp | 6 ++-- indra/newview/tests/cppfeatures_test.cpp | 30 +++++++++++++++++++ indra/newview/tests/lldateutil_test.cpp | 3 ++ indra/newview/tests/lllogininstance_test.cpp | 3 ++ .../tests/llremoteparcelrequest_test.cpp | 3 ++ .../newview/tests/llsechandler_basic_test.cpp | 4 +-- indra/newview/tests/llslurl_test.cpp | 12 ++++---- .../tests/llviewercontrollistener_test.cpp | 5 +++- indra/newview/tests/llviewernetwork_test.cpp | 12 ++++---- indra/newview/tests/llworldmap_test.cpp | 5 +++- 11 files changed, 67 insertions(+), 19 deletions(-) diff --git a/indra/newview/tests/alscriptanalysislane_test.cpp b/indra/newview/tests/alscriptanalysislane_test.cpp index 1ab4490c71..1bbdf7d2b1 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/alscriptstudiochecking_test.cpp b/indra/newview/tests/alscriptstudiochecking_test.cpp index 1af1d948da..3f07e83499 100644 --- a/indra/newview/tests/alscriptstudiochecking_test.cpp +++ b/indra/newview/tests/alscriptstudiochecking_test.cpp @@ -211,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 @@ -232,7 +229,10 @@ 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 }; diff --git a/indra/newview/tests/cppfeatures_test.cpp b/indra/newview/tests/cppfeatures_test.cpp index ca94dcfc95..82d697f332 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/lldateutil_test.cpp b/indra/newview/tests/lldateutil_test.cpp index 79f1a2791d..cbe8b250a7 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/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp index 612b285a3a..e6b6b7861f 100644 --- a/indra/newview/tests/lllogininstance_test.cpp +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -249,6 +249,8 @@ void LLProgressView::setMessage(std::string const &){} //----------------------------------------------------------------------------- // LLNotifications +namespace +{ class MockNotifications : public LLNotificationsInterface { std::function mResponder; @@ -300,6 +302,7 @@ class MockNotifications : public LLNotificationsInterface int addedCount() { return mAddedCount; } }; +} // anonymous namespace S32 LLNotification::getSelectedOption(const LLSD& notification, const LLSD& response) { diff --git a/indra/newview/tests/llremoteparcelrequest_test.cpp b/indra/newview/tests/llremoteparcelrequest_test.cpp index 0a829d9208..8ce4b77796 100644 --- a/indra/newview/tests/llremoteparcelrequest_test.cpp +++ b/indra/newview/tests/llremoteparcelrequest_test.cpp @@ -58,6 +58,8 @@ LLControlGroup::~LLControlGroup(void) { } void LLUrlEntryParcel::processParcelInfo(const LLUrlEntryParcel::LLParcelData& parcel_data) { } namespace tut +{ +namespace { struct TestObserver : public LLRemoteParcelInfoObserver { TestObserver() : mProcessed(false) { } @@ -73,6 +75,7 @@ namespace tut bool mProcessed; }; +} // anonymous namespace struct RemoteParcelRequestData { diff --git a/indra/newview/tests/llsechandler_basic_test.cpp b/indra/newview/tests/llsechandler_basic_test.cpp index 578d1f6a3d..52bbd837d2 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() {} diff --git a/indra/newview/tests/llslurl_test.cpp b/indra/newview/tests/llslurl_test.cpp index 051d85309b..a211047b04 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") @@ -139,7 +139,7 @@ LLPointer LLControlGroup::getControl(std::string_view name) } LLControlGroup gSavedSettings("test"); -const char *gSampleGridFile = +static const char *gSampleGridFile = "" "" " " diff --git a/indra/newview/tests/llviewercontrollistener_test.cpp b/indra/newview/tests/llviewercontrollistener_test.cpp index f261a04544..07511bafca 100644 --- a/indra/newview/tests/llviewercontrollistener_test.cpp +++ b/indra/newview/tests/llviewercontrollistener_test.cpp @@ -26,11 +26,14 @@ * 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 { diff --git a/indra/newview/tests/llviewernetwork_test.cpp b/indra/newview/tests/llviewernetwork_test.cpp index c798b2f86f..b6af521100 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") @@ -113,7 +113,7 @@ LLPointer LLControlGroup::getControl(std::string_view name) LLControlGroup gSavedSettings("test"); -const char *gSampleGridFile = +static const char *gSampleGridFile = "" "" " " diff --git a/indra/newview/tests/llworldmap_test.cpp b/indra/newview/tests/llworldmap_test.cpp index 01b0153c00..e4615c882b 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() { } From 11c81acb24a6ba6e6af7f6b386752d89034f2db1 Mon Sep 17 00:00:00 2001 From: Rye Date: Fri, 9 Oct 2026 00:01:19 -0400 Subject: [PATCH 50/67] newview's tests link llxml rather than compiling its settings code, and llagentaccess tests over the real LLControlGroup llviewercontrollistener and lltextureinfo each compiled llcontrol.cpp, llxmltree.cpp and alxmldocument.cpp from llxml, and commoncontrol.cpp from llcommon, into themselves: the same objects libllxml and libllcommon hold, built three times over. They link llxml now; nothing in those files reads LL_TEST, so the code is the same. llagentaccess stubbed LLControlGroup's constructor, destructor and its U32 accessors over one file-static value, which in a binary beside any test that links llxml would replace the real class for both. It links llxml and declares its setting in a real group of each test's own, now named for the test rather than "test", the name five of the Linden-era tests' groups have. Co-Authored-By: Claude Opus 5.5 --- indra/newview/CMakeLists.txt | 22 ++++-------- indra/newview/tests/llagentaccess_test.cpp | 42 +++------------------- 2 files changed, 12 insertions(+), 52 deletions(-) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 2d6b6f968f..7432ef69a8 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -2515,7 +2515,6 @@ if(AL_BUILD_TESTS) almotionpredictor alsceneexplorerpredicate alwhitebalancesolver - llagentaccess lldateutil llremoteparcelrequest llviewerhelputil @@ -2526,6 +2525,9 @@ if(AL_BUILD_TESTS) ) al_add_test(${test} UNIT PROJECT ${test_project} LIBRARIES ${test_libs}) endforeach() + # 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. @@ -3045,13 +3047,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 @@ -3074,12 +3071,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 llxml ${test_libs} + SOURCES lltextureinfo.cpp ) endif() diff --git a/indra/newview/tests/llagentaccess_test.cpp b/indra/newview/tests/llagentaccess_test.cpp index 6f5b3a9721..747157a808 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); From b37805d0bd560c3cdcd64af6a0ce133ff95e80cd Mon Sep 17 00:00:00 2001 From: Rye Date: Fri, 9 Oct 2026 00:06:57 -0400 Subject: [PATCH 51/67] No two newview tests name a settings group or a main loop alike, and none makes a pump as it starts A settings group's name, a WorkQueue's and an event pump's are each a key in the process, and a second of one name is LL_ERRS: in one binary, most of these would have ended the run before main. - Five Linden-era tests named their stubbed gSavedSettings "test", two "test_settings" and lllogininstance "Global". Each group takes its test's name. - alscriptkeymap and lltextureinfo each defined a real gSavedSettings("Global"). newview_test_settings defines it once, linked by those two alone: it is made as the process starts, as the code under test needs, so no test that does not read it carries it. - alscriptlookup and alscriptstudiomasters each kept a function-static WorkQueue("mainloop"). They share newview_test::mainLoop(), in newview_test_fakes, made the first time a test asks and kept for the run, as both kept theirs: a walk or a look let go of may still hand back to it after its test. - lllogininstance's login pump was an LLEventStream at namespace scope; it is made the first time the login asks for it. Co-Authored-By: Claude Opus 5.5 --- indra/newview/CMakeLists.txt | 9 +++- indra/newview/tests/alscriptkeymap_test.cpp | 6 +-- indra/newview/tests/alscriptlookup_test.cpp | 5 ++- .../tests/alscriptstudiomasters_test.cpp | 10 ++--- indra/newview/tests/lllogininstance_test.cpp | 26 +++++++----- indra/newview/tests/llsecapi_test.cpp | 2 +- .../newview/tests/llsechandler_basic_test.cpp | 2 +- indra/newview/tests/llslurl_test.cpp | 2 +- indra/newview/tests/lltextureinfo_test.cpp | 2 - indra/newview/tests/llviewerhelputil_test.cpp | 2 +- indra/newview/tests/llviewernetwork_test.cpp | 2 +- indra/newview/tests/llworldmap_test.cpp | 2 +- indra/newview/tests/llworldmipmap_test.cpp | 2 +- indra/newview/tests/newview_test_fakes.cpp | 18 ++++++-- indra/newview/tests/newview_test_fakes.h | 42 +++++++++++++++++++ indra/newview/tests/newview_test_settings.cpp | 33 +++++++++++++++ 16 files changed, 128 insertions(+), 37 deletions(-) create mode 100644 indra/newview/tests/newview_test_fakes.h create mode 100644 indra/newview/tests/newview_test_settings.cpp diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 7432ef69a8..bad6abb2d7 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -2498,6 +2498,11 @@ if(AL_BUILD_TESTS) 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/. @@ -2781,7 +2786,7 @@ if(AL_BUILD_TESTS) UNIT PROJECT ${test_project} SOURCES alscriptkeypresets.cpp - LIBRARIES newview_test_fakes llui ${test_libs} + LIBRARIES newview_test_fakes newview_test_settings llui ${test_libs} DEFINES "LLUI_TEST_APP_DIR=\"${INDRA_SOURCE_DIR}/newview\"" ) @@ -3071,7 +3076,7 @@ if(AL_BUILD_TESTS) al_add_test( lltextureinfo PROJECT ${test_project} - LIBRARIES llxml ${test_libs} + LIBRARIES newview_test_settings llxml ${test_libs} SOURCES lltextureinfo.cpp ) endif() diff --git a/indra/newview/tests/alscriptkeymap_test.cpp b/indra/newview/tests/alscriptkeymap_test.cpp index 5264d3013b..672ab1797a 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,9 +40,6 @@ #include #include -// The setting the keymap is kept in, in a group of the test's own. -LLControlGroup gSavedSettings("Global"); - namespace tut { struct alscriptkeymap_data diff --git a/indra/newview/tests/alscriptlookup_test.cpp b/indra/newview/tests/alscriptlookup_test.cpp index 72a9f82192..b6f357a2cb 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" @@ -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/alscriptstudiomasters_test.cpp b/indra/newview/tests/alscriptstudiomasters_test.cpp index 08fc2c7616..8c163bbcf2 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/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp index e6b6b7861f..1e4621028d 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){} @@ -404,7 +410,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"); @@ -420,7 +426,7 @@ namespace tut response["transfer_rate"] = 0; response["data"] = "test_data"; - gTestPump.post(response); + testPump().post(response); ensure("Disconnected", !(logininstance->authSuccess())); } @@ -447,7 +453,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); @@ -456,7 +462,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()); @@ -464,7 +470,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); @@ -473,7 +479,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); @@ -483,7 +489,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/llsecapi_test.cpp b/indra/newview/tests/llsecapi_test.cpp index a5d8a6d357..f065d29458 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() {} diff --git a/indra/newview/tests/llsechandler_basic_test.cpp b/indra/newview/tests/llsechandler_basic_test.cpp index 52bbd837d2..b9a51c67b3 100644 --- a/indra/newview/tests/llsechandler_basic_test.cpp +++ b/indra/newview/tests/llsechandler_basic_test.cpp @@ -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}; diff --git a/indra/newview/tests/llslurl_test.cpp b/indra/newview/tests/llslurl_test.cpp index a211047b04..2e0f448842 100644 --- a/indra/newview/tests/llslurl_test.cpp +++ b/indra/newview/tests/llslurl_test.cpp @@ -138,7 +138,7 @@ LLPointer LLControlGroup::getControl(std::string_view name) return iter == mNameTable.end() ? LLPointer() : iter->second; } -LLControlGroup gSavedSettings("test"); +LLControlGroup gSavedSettings("llslurl"); static const char *gSampleGridFile = "" "" diff --git a/indra/newview/tests/lltextureinfo_test.cpp b/indra/newview/tests/lltextureinfo_test.cpp index 1722279405..2dcab18c14 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/llviewerhelputil_test.cpp b/indra/newview/tests/llviewerhelputil_test.cpp index 9ee6625bf1..9c9ae0e083 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 b6af521100..63501b09dd 100644 --- a/indra/newview/tests/llviewernetwork_test.cpp +++ b/indra/newview/tests/llviewernetwork_test.cpp @@ -111,7 +111,7 @@ LLPointer LLControlGroup::getControl(std::string_view name) return iter == mNameTable.end() ? LLPointer() : iter->second; } -LLControlGroup gSavedSettings("test"); +LLControlGroup gSavedSettings("llviewernetwork"); static const char *gSampleGridFile = "" diff --git a/indra/newview/tests/llworldmap_test.cpp b/indra/newview/tests/llworldmap_test.cpp index e4615c882b..655ebb5052 100644 --- a/indra/newview/tests/llworldmap_test.cpp +++ b/indra/newview/tests/llworldmap_test.cpp @@ -89,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 0a5de18e0f..76b50c4c5b 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 index 6585acb914..517c4b7b71 100644 --- a/indra/newview/tests/newview_test_fakes.cpp +++ b/indra/newview/tests/newview_test_fakes.cpp @@ -24,18 +24,28 @@ #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: llui is what reaches for these, and a linker that reads -// its archives once, in order, would have passed an archive listed before -// llui by the time llui asked. Each test binary has one definition of each. +// 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. +// 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 0000000000..3c0fd0ddfb --- /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 0000000000..565e44aa96 --- /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"); From 402277ae6f8737f9002f304bab2d757a6c8da7bd Mon Sep 17 00:00:00 2001 From: Rye Date: Fri, 9 Oct 2026 00:14:11 -0400 Subject: [PATCH 52/67] newview's tests put back the process state they change Each of these left something behind that a later test in the same process would read. Each is now restored as the test or its fixture ends, however it ends. - llviewerassetstats' fixture made its ThreadRecorder the master recorder and then set the master to null, and the recorder left this thread's recorder, timer stack and accumulators null: every LLTrace user after it ran without one. The runner's are put back. - alscriptcrumbsbar's fixture set the mark colours in the colour table and left them; each is now held for the test and put back to the person's own colour, or to none. - alscriptkeymap's fixture installed a key translator and set the keymap's preset and keys; the translator before it and both settings are put back. - alscripttheme test 3 chose a theme in the UI's settings, and cleared it only if it passed; alscriptstudiorecovery test 15 let go of its save history only if it passed; llversioninfo test 1 left the channel reset. - llviewercontrollistener test 9 asserted that its group was the only LLControlGroup in the process, which fails beside HeadlessUI's or any test's gSavedSettings. It checks that its group is listed once, and that one more group made adds exactly its name. Co-Authored-By: Claude Opus 5.5 --- .../newview/tests/alscriptcrumbsbar_test.cpp | 44 ++++++++++++++++--- indra/newview/tests/alscriptkeymap_test.cpp | 26 +++++++++++ .../tests/alscriptstudiorecovery_test.cpp | 16 +++++-- indra/newview/tests/alscripttheme_test.cpp | 7 +++ indra/newview/tests/llversioninfo_test.cpp | 6 +++ .../newview/tests/llviewerassetstats_test.cpp | 35 +++++++++++++++ .../tests/llviewercontrollistener_test.cpp | 17 +++++-- 7 files changed, 139 insertions(+), 12 deletions(-) diff --git a/indra/newview/tests/alscriptcrumbsbar_test.cpp b/indra/newview/tests/alscriptcrumbsbar_test.cpp index 01abac2242..ee586112d6 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/alscriptkeymap_test.cpp b/indra/newview/tests/alscriptkeymap_test.cpp index 672ab1797a..4a75dec821 100644 --- a/indra/newview/tests/alscriptkeymap_test.cpp +++ b/indra/newview/tests/alscriptkeymap_test.cpp @@ -40,12 +40,28 @@ #include #include +namespace +{ + // What names keys as a person reads them, which LLKeyboard keeps + // without saying. + struct KeyTranslator : LLKeyboard + { + static LLKeyStringTranslatorFunc* current() { return mStringTranslator; } + }; +} + namespace tut { struct alscriptkeymap_data { 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); }); @@ -57,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/alscriptstudiorecovery_test.cpp b/indra/newview/tests/alscriptstudiorecovery_test.cpp index 1c3c0f0088..47d6456630 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 23cc721e92..b7092e39bc 100644 --- a/indra/newview/tests/alscripttheme_test.cpp +++ b/indra/newview/tests/alscripttheme_test.cpp @@ -114,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/llversioninfo_test.cpp b/indra/newview/tests/llversioninfo_test.cpp index a78235c5f7..2e217c7a19 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 10c68432a1..323affaded 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 07511bafca..8fb71b726d 100644 --- a/indra/newview/tests/llviewercontrollistener_test.cpp +++ b/indra/newview/tests/llviewercontrollistener_test.cpp @@ -22,6 +22,8 @@ #include "commoncontrol.h" #include "llcontrol.h" // LLControlGroup +#include + /***************************************************************************** * TUT *****************************************************************************/ @@ -142,9 +144,18 @@ namespace 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<> From ff5a556020f7acc2f05cc8d12ce79ae6dd1a4604 Mon Sep 17 00:00:00 2001 From: Rye Date: Fri, 9 Oct 2026 00:16:16 -0400 Subject: [PATCH 53/67] alcrashreporter's consent file and alscriptlookup's folders take names of their run's own alcrashreporter test 7 wrote its consent sentinel at one fixed name in the temp directory, which two runs of the tests at once would share and each remove under the other. alscriptlookup named its folders on disk by the monotonic clock, unique only within one process; they take a new UUID, as every other studio test's folder does. Co-Authored-By: Claude Opus 5.5 --- indra/newview/tests/alcrashreporter_test.cpp | 4 +++- indra/newview/tests/alscriptlookup_test.cpp | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/indra/newview/tests/alcrashreporter_test.cpp b/indra/newview/tests/alcrashreporter_test.cpp index a011318510..073bd4ffe1 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)); diff --git a/indra/newview/tests/alscriptlookup_test.cpp b/indra/newview/tests/alscriptlookup_test.cpp index b6f357a2cb..1997e4ac15 100644 --- a/indra/newview/tests/alscriptlookup_test.cpp +++ b/indra/newview/tests/alscriptlookup_test.cpp @@ -942,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"; @@ -1032,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"; @@ -1069,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"; From 5fc89baabac9179ebf5471e341acfa22b2a5f921 Mon Sep 17 00:00:00 2001 From: Rye Date: Fri, 9 Oct 2026 00:20:33 -0400 Subject: [PATCH 54/67] lllogininstance's fixture does not leave the login instance holding its notifications LLLoginInstance is a singleton and outlives each test, and the fixture gave it the fixture's own MockNotifications: after every test it held a pointer to an object gone. The fixture now puts back the instance's default, none. Its two tests, which the audits listed as order-dependent, pass alone and in either order, as llworldmap's four do: each begins by setting up what it reads. Co-Authored-By: Claude Opus 5.5 --- indra/newview/tests/lllogininstance_test.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp index 1e4621028d..b1a2db7065 100644 --- a/indra/newview/tests/lllogininstance_test.cpp +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -381,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; From 5a65a8f1c5e4983844d0d7f192420f90be93e9c6 Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 10 Oct 2026 10:03:55 -0400 Subject: [PATCH 55/67] The tests that seed llrand seed it from entropy again as they end llrand_test's tests 8 to 12, 14, 17 and 18, and mathmisc's LLLine tests 1 and 3, seed the main thread's generator so that what they draw is the same on every run, and left it seeded. In one binary, everything drawn after them on that thread, by later tests and by what they call (LLUUID's clock sequence among it), would be one fixed sequence that hangs on which tests ran first. llrand cannot hand back the state it had, so each fixture seeds it from entropy again as it ends, as a thread's first draw does. The one helper for it is ll_test::reseedRandomFromEntropy(), in indra/test. Co-Authored-By: Claude Opus 5.5 --- indra/llcommon/tests/llrand_test.cpp | 3 ++ indra/llmath/tests/mathmisc_test.cpp | 3 ++ indra/test/alrandomreseed.h | 51 ++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 indra/test/alrandomreseed.h diff --git a/indra/llcommon/tests/llrand_test.cpp b/indra/llcommon/tests/llrand_test.cpp index 5e7523578a..fca8275c6c 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" @@ -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/llmath/tests/mathmisc_test.cpp b/indra/llmath/tests/mathmisc_test.cpp index b303249fef..c28d353ace 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" @@ -298,6 +299,8 @@ namespace 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; diff --git a/indra/test/alrandomreseed.h b/indra/test/alrandomreseed.h new file mode 100644 index 0000000000..5acad0c897 --- /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 From 8c4731c8f65d231c521e432dbedf8188927de112 Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 10 Oct 2026 10:04:04 -0400 Subject: [PATCH 56/67] llsettingssky's test fixture and group have internal linkage The test landed after llinventory's tests were made internal, with its data struct and group object external. They move into the anonymous namespace, as the rest of llinventory's tests have them. Co-Authored-By: Claude Opus 5.5 --- indra/llinventory/tests/llsettingssky_test.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/indra/llinventory/tests/llsettingssky_test.cpp b/indra/llinventory/tests/llsettingssky_test.cpp index 0e56ce3c98..9e64572853 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<> From 1927a3f57b3aed8ca6118c00ba7bcbc03322957b Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 10 Oct 2026 10:04:05 -0400 Subject: [PATCH 57/67] llrender's newer GL tests take the shared context, and put back what they change of it alinfiniteprojection_gl, alrenderstate_gl and llrendertarget_gl landed after the shared-context tests were given one context, and each kept a function-static HeadlessGL of its own: in one binary, more windows, and GL's classes set up again over the first. They take sharedHeadlessGL(), and their fixtures and groups move into the anonymous namespace with their helpers. On a shared context a test must leave it as it found it, and these left program 0 and vertex array 0 bound behind LLGLSLShader's and LLRender's caches, a texture slot out of step with GL, the clear colour, the scissor box, and reverse-Z forced off rather than put back. ll_test::SharedGLScope, the first member of each fixture, takes the context, reads what a test may change of it, and puts that back as the test ends, through the caches and behind them. The depth state each fixture put back by hand goes with it. The fixture itself: - A TestShaderMgr made while another is the instance, as a test's own over the shared context's, hands the instance back when it goes, rather than leaving none and LLShaderMgr::instance() an error. - gGLViewport holds the window's viewport, as the viewer's window records it: a render target's flush with no outer target set the viewport to the 0x0 it held. Linked into one binary, the six shared-context tests and these three pass, 89 tests with alinfiniteprojection's 5 skips (no clip control here). Co-Authored-By: Claude Opus 5.5 --- .../tests/alinfiniteprojection_gl_test.cpp | 25 +-- .../llrender/tests/alrenderstate_gl_test.cpp | 16 +- indra/llrender/tests/llheadlessgl_fixture.h | 152 +++++++++++++++++- .../llrender/tests/llrendertarget_gl_test.cpp | 17 +- 4 files changed, 161 insertions(+), 49 deletions(-) diff --git a/indra/llrender/tests/alinfiniteprojection_gl_test.cpp b/indra/llrender/tests/alinfiniteprojection_gl_test.cpp index d6a0b1394e..68d6ae21fe 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 91513340de..a11edb4584 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/llheadlessgl_fixture.h b/indra/llrender/tests/llheadlessgl_fixture.h index d51dc74550..55e88bd460 100644 --- a/indra/llrender/tests/llheadlessgl_fixture.h +++ b/indra/llrender/tests/llheadlessgl_fixture.h @@ -52,7 +52,9 @@ #include "llwindow.h" #include "llwindowcallbacks.h" +#include #include +#include #include #include @@ -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() @@ -478,6 +492,140 @@ namespace ll_test /*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/llrendertarget_gl_test.cpp b/indra/llrender/tests/llrendertarget_gl_test.cpp index 3546e7e8b0..7dcc963c09 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. From 8534c4a9dfc4f19e70426ef12b30c28b2ca67804 Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 10 Oct 2026 10:04:27 -0400 Subject: [PATCH 58/67] The HUD depth and selection outline tests take the shared context, and start forward whatever ran before Each kept a function-static HeadlessGL of its own with nothing rendering up, so in one binary with llrender's GL tests they would bring GL's classes up again on a second window. They take sharedHeadlessGL() through ll_test::SharedGLScope, which puts back what they change of it: the convention, viewport and depth state, the matrices (the selection outline's fixture loaded identity into all three, wiping the context's pixel ortho), and the shader, which their teardown left unbound under a gGL that asserts one is. Their own TestShaderMgr now hands the shared one back as it goes. Each fixture sets the forward convention as it starts. Selection outline tests 6 and 9 draw under the convention without setting one, and found it forward only because the test before them put it back. Their fixtures and groups move into the anonymous namespace with their helpers. Selection outline test 33 gives ALHoverGlow's singleton up however the test ends, rather than only when every check passed. Linked into one binary with the nine llrender shared-context tests, all 138 pass, with alinfiniteprojection's 5 skips. Co-Authored-By: Claude Opus 5.5 --- indra/newview/tests/alhuddepth_gl_test.cpp | 16 ++++------ .../tests/alselectionoutline_gl_test.cpp | 32 ++++++++----------- 2 files changed, 19 insertions(+), 29 deletions(-) diff --git a/indra/newview/tests/alhuddepth_gl_test.cpp b/indra/newview/tests/alhuddepth_gl_test.cpp index de870c5548..502aa365a8 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/alselectionoutline_gl_test.cpp b/indra/newview/tests/alselectionoutline_gl_test.cpp index 16fc6d5abf..ca5d9ac9d3 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)); From d24bf5d7a44b124c2acfc6315b5bb8ca54856c94 Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 10 Oct 2026 10:04:27 -0400 Subject: [PATCH 59/67] alcrashreporter's report files and records folder take names of their run's own Tests 10, 11 and 13 landed after test 7's consent file was given a name of its run's own, and used fixed names in the temp directory: two runs at once took each other's reports, and test 13's first act was to remove the other run's folder. Each name now carries a new UUID, as test 7's does. Co-Authored-By: Claude Opus 5.5 --- indra/newview/tests/alcrashreporter_test.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/indra/newview/tests/alcrashreporter_test.cpp b/indra/newview/tests/alcrashreporter_test.cpp index 073bd4ffe1..0c06ff7690 100644 --- a/indra/newview/tests/alcrashreporter_test.cpp +++ b/indra/newview/tests/alcrashreporter_test.cpp @@ -169,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)); @@ -199,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)); } @@ -230,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]() { From 5fd8c635a2b3270bdd44603c9543b668d5d140a6 Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 10 Oct 2026 10:04:27 -0400 Subject: [PATCH 60/67] llremoteparcelrequest's test links llui rather than stubbing it The test stubbed LLUIColor's constructor, LLControlGroup's constructor and destructor, and LLUrlEntryParcel::processParcelInfo, all of which llui and llxml define: in one binary with a test that links them, a duplicate symbol. It links llui, and newview_test_fakes for the rlvGetAnonym llui asks the viewer for, as llagentaccess came to link llxml rather than stub its settings group. The stubs of the viewer's own LLAgent and agent ids stay: the test keeps an executable of its own, with llviewerhelputil and lltextureinfo, until newview's tests share one. Co-Authored-By: Claude Opus 5.5 --- indra/newview/CMakeLists.txt | 9 ++++++++- indra/newview/tests/llremoteparcelrequest_test.cpp | 5 ----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index bad6abb2d7..009fcb1520 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -2521,7 +2521,6 @@ if(AL_BUILD_TESTS) alsceneexplorerpredicate alwhitebalancesolver lldateutil - llremoteparcelrequest llviewerhelputil llversioninfo llvocache @@ -2530,6 +2529,14 @@ 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}) diff --git a/indra/newview/tests/llremoteparcelrequest_test.cpp b/indra/newview/tests/llremoteparcelrequest_test.cpp index 8ce4b77796..d1eae88f46 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,10 +51,6 @@ 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 { From e60a50e87b626a0b6a9ebddf8115fa3a79b5efaf Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 10 Oct 2026 16:32:11 -0400 Subject: [PATCH 61/67] llsd's and lleventdispatcher's tests build with MSVC and GCC again Stage 1's own changes broke two tests that only Windows and Linux build, and this Mac's clang did not object to: - llsd's tests now run their bodies inside the lambdas they hand ensure_cleaned_up and ensure_allocations, and MSVC does not find SDTestData's static helpers, ensureTypeAndValue and ensureTypeAndRefValue, by their bare names there. They are static function templates in tut now, not members. Not in the anonymous namespace: TUT declares its ensure_equals there, and from inside it that would hide lltut.h's overloads for LLSD's binary type. - lleventdispatcher's Vars is internal now, and GCC's -Wunused-function, an error under -Werror, takes in an internal class's out-of-line member that nothing calls. Vars::smethod1 is kept with its set, marked [[maybe_unused]]. Clang's -Wunused-member-function, run over every test file this branch changes, finds no other out-of-line member nothing calls. Co-Authored-By: Claude Opus 5.5 --- .../llcommon/tests/lleventdispatcher_test.cpp | 4 +- indra/llcommon/tests/llsd_test.cpp | 45 +++++++++++-------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp index 46daafedc3..ada009b99b 100644 --- a/indra/llcommon/tests/lleventdispatcher_test.cpp +++ b/indra/llcommon/tests/lleventdispatcher_test.cpp @@ -175,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) diff --git a/indra/llcommon/tests/llsd_test.cpp b/indra/llcommon/tests/llsd_test.cpp index 184874fc16..900ba8c9d0 100644 --- a/indra/llcommon/tests/llsd_test.cpp +++ b/indra/llcommon/tests/llsd_test.cpp @@ -67,30 +67,37 @@ namespace tut } } // 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; From 994a5fb8d71014ecd8879c8aa4e2d549624e70ed Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 10 Oct 2026 17:47:25 -0400 Subject: [PATCH 62/67] The WebSocket manager suite keeps one manager for the run again 0cdb004e7f deleted LLWebsocketMgr as each test ended, so that a test that failed before removing its servers would not leave them listening into the next. On Linux x64 CI the suite then ended the binary: std::terminate inside it, with the runtime_error test 16's handlers throw as the current exception. That run was the only one to show it: macOS and Linux arm64 pass, and so did every Linux x64 run of the suite before, with the manager living for the process as it does again now. Why deleting a manager whose servers are already stopped and joined should do that is not found yet. A failed test leaving its servers listening is the lesser harm. The suite's teardown is left for the stage that converts llcorehttp, which can look for it with the crash in hand. Co-Authored-By: Claude Opus 5.5 --- indra/llcorehttp/tests/test_websocketmgr.hpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/indra/llcorehttp/tests/test_websocketmgr.hpp b/indra/llcorehttp/tests/test_websocketmgr.hpp index 0f46d0fe99..9eae3ee311 100644 --- a/indra/llcorehttp/tests/test_websocketmgr.hpp +++ b/indra/llcorehttp/tests/test_websocketmgr.hpp @@ -429,14 +429,6 @@ 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; From 66bba8be307c53c4fd3654d510288d1d53461b36 Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 10 Oct 2026 20:00:46 -0400 Subject: [PATCH 63/67] llcorehttp's tests are as develop has them, until a crash in their binary on Linux x64 is understood On Linux x64 CI, and only there, the llcorehttp test binary ended twice in the WebSocket manager suite with std::terminate, the runtime_error test 16's handlers throw being the exception named. Branches on the same develop pass it, and so did every Linux x64 run before this branch; macOS and Linux arm64 pass it here. Taking back the per-test manager deletion (994a5fb8d7) did not change it, which leaves this branch's other change to the binary: its suites, fixtures and helpers moved into anonymous namespaces (56f2e09350). Both of the handler's callers catch std::exception, and no library defines a name those helpers had, so how internal linkage would do it is not found. Until it is, llcorehttp's tests are exactly develop's, so this branch changes nothing that binary is built from; their linkage is left for the stage that converts llcorehttp, with this crash in hand. Co-Authored-By: Claude Opus 5.5 --- indra/llcorehttp/tests/llcorehttp_test.cpp | 5 ----- indra/llcorehttp/tests/llcorehttp_test.h | 15 ++++----------- indra/llcorehttp/tests/test_bufferarray.hpp | 3 --- indra/llcorehttp/tests/test_bufferstream.hpp | 3 --- indra/llcorehttp/tests/test_httpheaders.hpp | 3 --- indra/llcorehttp/tests/test_httpoperation.hpp | 3 --- indra/llcorehttp/tests/test_httprequest.hpp | 3 --- indra/llcorehttp/tests/test_httprequestqueue.hpp | 3 --- indra/llcorehttp/tests/test_httpstatus.hpp | 3 --- indra/llcorehttp/tests/test_jsonrpcws.hpp | 3 --- indra/llcorehttp/tests/test_refcounted.hpp | 3 --- indra/llcorehttp/tests/test_websocketmgr.hpp | 3 --- 12 files changed, 4 insertions(+), 46 deletions(-) diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index a503555602..cc3c241a96 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -75,9 +75,6 @@ int main() #endif // 0 -namespace -{ - void init_curl() { curl_global_init(CURL_GLOBAL_ALL); @@ -125,5 +122,3 @@ void stop_thread(LLCore::HttpRequest * req) } } } - -} diff --git a/indra/llcorehttp/tests/llcorehttp_test.h b/indra/llcorehttp/tests/llcorehttp_test.h index c0b575d1a5..133cb3121a 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.h +++ b/indra/llcorehttp/tests/llcorehttp_test.h @@ -36,20 +36,15 @@ #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. -void init_curl(); -void term_curl(); -std::string get_base_url(); -void stop_thread(LLCore::HttpRequest * req); +extern void init_curl(); +extern void term_curl(); +extern std::string get_base_url(); +extern void stop_thread(LLCore::HttpRequest * req); class ScopedCurlInit { @@ -65,7 +60,5 @@ class ScopedCurlInit } }; -} - #endif // _LLCOREHTTP_TEST_H_ diff --git a/indra/llcorehttp/tests/test_bufferarray.hpp b/indra/llcorehttp/tests/test_bufferarray.hpp index da2b742350..2a272e73d7 100644 --- a/indra/llcorehttp/tests/test_bufferarray.hpp +++ b/indra/llcorehttp/tests/test_bufferarray.hpp @@ -37,8 +37,6 @@ using namespace LLCore; namespace tut { -namespace -{ struct BufferArrayTestData { @@ -49,7 +47,6 @@ 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 4bd4cba38a..556abf45a2 100644 --- a/indra/llcorehttp/tests/test_bufferstream.hpp +++ b/indra/llcorehttp/tests/test_bufferstream.hpp @@ -39,8 +39,6 @@ using namespace LLCore; namespace tut { -namespace -{ struct BufferStreamTestData { @@ -52,7 +50,6 @@ 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 d2d76aa281..d8eff78ca5 100644 --- a/indra/llcorehttp/tests/test_httpheaders.hpp +++ b/indra/llcorehttp/tests/test_httpheaders.hpp @@ -36,8 +36,6 @@ using namespace LLCoreInt; namespace tut { -namespace -{ struct HttpHeadersTestData { @@ -48,7 +46,6 @@ 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 2d17d00eab..361091c7b2 100644 --- a/indra/llcorehttp/tests/test_httpoperation.hpp +++ b/indra/llcorehttp/tests/test_httpoperation.hpp @@ -53,8 +53,6 @@ class TestHandler : public LLCore::HttpHandler namespace tut -{ -namespace { struct HttpOperationTestData { @@ -65,7 +63,6 @@ namespace 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 6d5b8ab0a3..5dacfc9e94 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -67,8 +67,6 @@ void usleep(unsigned long usec); namespace tut { -namespace -{ typedef std::vector > regex_container_t; @@ -186,7 +184,6 @@ 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 11a0016912..c6fa1b7534 100644 --- a/indra/llcorehttp/tests/test_httprequestqueue.hpp +++ b/indra/llcorehttp/tests/test_httprequestqueue.hpp @@ -39,8 +39,6 @@ using namespace LLCoreInt; namespace tut { -namespace -{ struct HttpRequestqueueTestData { @@ -51,7 +49,6 @@ 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 3a0ff04370..eac7ba8557 100644 --- a/indra/llcorehttp/tests/test_httpstatus.hpp +++ b/indra/llcorehttp/tests/test_httpstatus.hpp @@ -36,8 +36,6 @@ using namespace LLCore; namespace tut { -namespace -{ struct HttpStatusTestData { @@ -49,7 +47,6 @@ 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 5ae24a3181..e87b2a19d4 100644 --- a/indra/llcorehttp/tests/test_jsonrpcws.hpp +++ b/indra/llcorehttp/tests/test_jsonrpcws.hpp @@ -29,8 +29,6 @@ class TestJSONRPCConnection : public LLJSONRPCConnection } namespace tut -{ -namespace { struct JSONRPCWSTestData { @@ -39,7 +37,6 @@ namespace 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 9b1a48b8c7..eb23a25545 100644 --- a/indra/llcorehttp/tests/test_refcounted.hpp +++ b/indra/llcorehttp/tests/test_refcounted.hpp @@ -31,8 +31,6 @@ using namespace LLCoreInt; namespace tut -{ -namespace { struct RefCountedTestData { @@ -43,7 +41,6 @@ namespace 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 9eae3ee311..8f7737427c 100644 --- a/indra/llcorehttp/tests/test_websocketmgr.hpp +++ b/indra/llcorehttp/tests/test_websocketmgr.hpp @@ -424,8 +424,6 @@ namespace } namespace tut -{ -namespace { struct WebsocketMgrTestData { @@ -434,7 +432,6 @@ namespace typedef test_group WebsocketMgrTestGroupType; typedef WebsocketMgrTestGroupType::object WebsocketMgrTestObjectType; WebsocketMgrTestGroupType WebsocketMgrTestGroup("LLWebsocketMgr Tests"); -} template<> template<> void WebsocketMgrTestObjectType::test<1>() From 8121cabf72f2dc71a65409eaa21d58933d287465 Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 10 Oct 2026 21:19:27 -0400 Subject: [PATCH 64/67] GCC splits no cold parts off where it links with a mold older than 2.42.1 On Linux x64 CI the llcorehttp test binary ended in std::terminate as test 16's async handler threw: the runtime_error was thrown with a catch for it two frames up, and the unwinder found no unwind entry (FDE) for the handler, so it never looked. GCC 15 split the cold blocks of Beast's buffers_cat_view iterator off into "[clone .cold]" parts that then optimised away, and emitted five of them empty, each with an FDE covering no bytes, at the address of what followed them in .text.unlikely: the handler's invoker, which only throws and so is cold whole. mold before 2.42.1 (rui314/mold#1648) puts those FDEs in .eh_frame_hdr's search table too, sorted on their address alone by an unstable parallel sort, so the six tie in an order of chance. libgcc takes the last entry at or before the PC; where that is an empty one it reports no FDE, phase one of the unwind ends, and __cxa_throw calls std::terminate. Develop's object has the same five empty FDEs at the same function. Its link happened to sort the real one last; the stage 1 branch's anonymous namespaces change which FDEs the link sorts, and wrapping test_httprequest.hpp's alone sorts an empty one last. Reordering only the tied table entries of the two binaries turns the abort on in develop's and off in the branch's. Without splitting there are no empty parts. Built with this under GCC 15.2 and mold 2.40.4, 994a5fb8d7's llcorehttp binary has no zero-length FDE, its WebSocket suite passes five runs in five, and the whole binary passes under its peer. It applies only where CMake finds a mold older than 2.42.1; from 2.42.1 mold writes such entries as tombstones no search lands on. The vcpkg ports keep their empty parts. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018TgkpVXucG17x8kCrsXMjy --- indra/cmake/00-Common.cmake | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index b8b51170f1..9b93220404 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( From d09b7cefb761d7705efb49ad125650b16f02c5f6 Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 10 Oct 2026 21:13:53 -0400 Subject: [PATCH 65/67] Linux builds link with mold 2.42.1 from its release, not the image's 2.40 mold before 2.42.1 puts the unwind entries GCC emits for empty cold parts into .eh_frame_hdr's search table, where one can stand in for the real entry of the function at the same address; an exception thrown through that function then ends the program in std::terminate (rui314/mold#1648). It took the llcorehttp test binary down on Linux x64, and could do the same to any function of the viewer or of a vcpkg port, which the GCC flag the build sets for its own code under such a mold does not reach. The release tarball, pinned by digest for x86-64 and arm64, goes under /usr/local, ahead of the image's on the path: GCC's -fuse-ld=mold and CMake's linker detection both find it, so the build sees 2.42.1 and leaves hot/cold splitting on. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018TgkpVXucG17x8kCrsXMjy --- .github/actions/setup-build/action.yaml | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/.github/actions/setup-build/action.yaml b/.github/actions/setup-build/action.yaml index 0f0c4e490b..8cc0810f1c 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 From 36a69cd7a4a6a99d2b55482db29f16c7099e3a7d Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 10 Oct 2026 23:03:37 -0400 Subject: [PATCH 66/67] llcorehttp's suites, fixtures and helpers have internal linkage again 66bba8be30 took them back out while the Linux x64 crash in their binary was unexplained. It was never their linkage: mold before 2.42.1 sorts the empty unwind entries GCC 15 leaves at the async handler's invoker into .eh_frame_hdr beside the real one, in an order of chance, and develop's link only escaped by its sort. 8121cabf72 and d09b7cefb7 take that away, and built with them the binary these namespaces make passes. This reverts commit 66bba8be307c53c4fd3654d510288d1d53461b36. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018TgkpVXucG17x8kCrsXMjy --- indra/llcorehttp/tests/llcorehttp_test.cpp | 5 +++++ indra/llcorehttp/tests/llcorehttp_test.h | 15 +++++++++++---- indra/llcorehttp/tests/test_bufferarray.hpp | 3 +++ indra/llcorehttp/tests/test_bufferstream.hpp | 3 +++ indra/llcorehttp/tests/test_httpheaders.hpp | 3 +++ indra/llcorehttp/tests/test_httpoperation.hpp | 3 +++ indra/llcorehttp/tests/test_httprequest.hpp | 3 +++ indra/llcorehttp/tests/test_httprequestqueue.hpp | 3 +++ indra/llcorehttp/tests/test_httpstatus.hpp | 3 +++ indra/llcorehttp/tests/test_jsonrpcws.hpp | 3 +++ indra/llcorehttp/tests/test_refcounted.hpp | 3 +++ indra/llcorehttp/tests/test_websocketmgr.hpp | 3 +++ 12 files changed, 46 insertions(+), 4 deletions(-) diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index cc3c241a96..a503555602 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 133cb3121a..c0b575d1a5 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 2a272e73d7..da2b742350 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 556abf45a2..4bd4cba38a 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 d8eff78ca5..d2d76aa281 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 361091c7b2..2d17d00eab 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 5dacfc9e94..6d5b8ab0a3 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 c6fa1b7534..11a0016912 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 eac7ba8557..3a0ff04370 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 e87b2a19d4..5ae24a3181 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 eb23a25545..9b1a48b8c7 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 8f7737427c..9eae3ee311 100644 --- a/indra/llcorehttp/tests/test_websocketmgr.hpp +++ b/indra/llcorehttp/tests/test_websocketmgr.hpp @@ -424,6 +424,8 @@ namespace } namespace tut +{ +namespace { struct WebsocketMgrTestData { @@ -432,6 +434,7 @@ namespace tut typedef test_group WebsocketMgrTestGroupType; typedef WebsocketMgrTestGroupType::object WebsocketMgrTestObjectType; WebsocketMgrTestGroupType WebsocketMgrTestGroup("LLWebsocketMgr Tests"); +} template<> template<> void WebsocketMgrTestObjectType::test<1>() From bc684175d1138d406775fd2053c4ff3f06f97701 Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 10 Oct 2026 23:03:37 -0400 Subject: [PATCH 67/67] The WebSocket manager suite deletes LLWebsocketMgr as each test ends again 994a5fb8d7 kept one manager for the run while the deletion was suspected of the std::terminate on Linux x64 CI. The crash came from the linker instead (8121cabf72), so a test that fails before removing its servers again leaves none listening into the next. This reverts commit 994a5fb8d71014ecd8879c8aa4e2d549624e70ed. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018TgkpVXucG17x8kCrsXMjy --- indra/llcorehttp/tests/test_websocketmgr.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/indra/llcorehttp/tests/test_websocketmgr.hpp b/indra/llcorehttp/tests/test_websocketmgr.hpp index 9eae3ee311..0f46d0fe99 100644 --- a/indra/llcorehttp/tests/test_websocketmgr.hpp +++ b/indra/llcorehttp/tests/test_websocketmgr.hpp @@ -429,6 +429,14 @@ 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;