From 32ca1c61356608a1645c54cb52b9a1571a136304 Mon Sep 17 00:00:00 2001 From: Rye Date: Mon, 12 Oct 2026 02:23:06 -0400 Subject: [PATCH 1/7] tut_parity.py numbers lines right past a string literal continued over several mask_source() turned the whole inside of a string literal into underscores, newlines too where the literal was continued with a backslash. tut_parity.py counts lines in that masked text, so every assertion after such a literal got a line number too low. It then said CHECKs that had run in passing cases never ran: llmediaentry's three LLSD macros span 93 lines, and parity reported 18 of its CHECKs that way. A line splice in a literal now stays as it is, backslash and newline, as one in code does. Lines then count as in the text, and a #define whose literal goes on over lines still reads as one directive. Over the tree's 379 TUT files, only llmediaentry and llmessagetemplateparser mask differently, and tut2catch.py converts both to the same bytes and the same mapping as before. Co-Authored-By: Claude Opus 5.5 --- scripts/testing/tut2catch.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/scripts/testing/tut2catch.py b/scripts/testing/tut2catch.py index 49e78fff33..e18de81228 100644 --- a/scripts/testing/tut2catch.py +++ b/scripts/testing/tut2catch.py @@ -198,10 +198,19 @@ class ConvertError(Exception): # -------------------------------------------------------------------------- # The source text # -------------------------------------------------------------------------- +def mask_literal(data, out, start, end): + """Masks a literal's inside, but for each line splice in it.""" + for k in range(start, end): + if data[k] == 0x0A or (data[k] == 0x5C and k + 1 < len(data) and data[k + 1] == 0x0A): + continue + out[k] = 0x5F + + def mask_source(data): """The text with comments blanked (newlines kept) and the insides of string and character literals replaced, so a search of it finds only code. Every - offset means what it meant.""" + offset means what it meant, and every line: a line splice in a literal, + a backslash and its newline, stays, as one in code does.""" out = bytearray(data) n = len(data) i = 0 @@ -247,8 +256,7 @@ def mask_source(data): if data[j] == 0x5C: j += 1 j += 1 - for k in range(i + 1, min(j, n)): - out[k] = 0x5F + mask_literal(data, out, i + 1, min(j, n)) i = j + 1 elif c == 0x27: # ' # A digit separator, inside a number, is no literal. @@ -263,8 +271,7 @@ def mask_source(data): if data[j] == 0x5C: j += 1 j += 1 - for k in range(i + 1, min(j, n)): - out[k] = 0x5F + mask_literal(data, out, i + 1, min(j, n)) i = j + 1 else: i += 1 From 843599a9c46180e0bd489c563136058c870157d6 Mon Sep 17 00:00:00 2001 From: Rye Date: Mon, 12 Oct 2026 02:24:02 -0400 Subject: [PATCH 2/7] The state guard takes a check's first description as the check is added A library's own guard checks will come from a listener of its own, which adds them when the run starts. Catch2 calls listeners in the order they registered, which across files is static-init order. Where the library's listener ran after the guard's, the guard's snapshot had already been taken. The new check had nothing to compare with, and the first case of every run was blamed for changing it. StateGuard::add now describes the check as it adds it, so the order of the listeners no longer matters. The harness's own tests have such a listener now, with a hidden case that changes its state. That entry names its case, and the entries that expect a clean exit pass whichever listener runs first. Without the change, four of them fail. Co-Authored-By: Claude Opus 5.5 --- indra/test/CMakeLists.txt | 10 ++++++++++ indra/test/alteststateguard.cpp | 3 ++- indra/test/alteststateguard.h | 4 ++++ indra/test/tests/altestsupport_test.cpp | 25 +++++++++++++++++++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt index ab1dce528a..ed03005deb 100644 --- a/indra/test/CMakeLists.txt +++ b/indra/test/CMakeLists.txt @@ -116,6 +116,16 @@ al_expect_from_test_support( EXIT 1 OUTPUT "a test case that leaves the environment changed. left the environment changed.*AL_TEST_SUPPORT_LEAK=1" ) +# A check a library's own listener adds as the run starts names its culprit +# too. That listener is in every run of this binary, so the entries that +# expect a clean exit show it blames no case that left its state alone, +# whichever listener runs first. +al_expect_from_test_support( + guard_checks_a_library + ARGS "[.library-leak]" + EXIT 1 + OUTPUT "a test case that leaves a library's state changed. left the library's state changed" +) al_expect_from_test_support( replay_on_failure ARGS "[.replay-fail]" diff --git a/indra/test/alteststateguard.cpp b/indra/test/alteststateguard.cpp index d2cc19e392..6edf258a60 100644 --- a/indra/test/alteststateguard.cpp +++ b/indra/test/alteststateguard.cpp @@ -220,7 +220,8 @@ namespace ll_test void StateGuard::add(std::string name, Describe describe, Restore restore) { - checks().push_back({ std::move(name), std::move(describe), std::move(restore), {} }); + std::string first = describe(); + checks().push_back({ std::move(name), std::move(describe), std::move(restore), std::move(first) }); } void StateGuard::addCommonChecks() diff --git a/indra/test/alteststateguard.h b/indra/test/alteststateguard.h index 59dc3a8fdb..5dd17cefc4 100644 --- a/indra/test/alteststateguard.h +++ b/indra/test/alteststateguard.h @@ -47,6 +47,10 @@ namespace ll_test using Describe = std::function; using Restore = std::function; + // Takes the check's first description as it is added. A library's + // own checks are added by a listener of its own when the run starts, + // before or after the guard's snapshot there: either way, no case is + // compared with nothing. static void add(std::string name, Describe describe, Restore restore = {}); // What every LL process has: the working directory, the environment, diff --git a/indra/test/tests/altestsupport_test.cpp b/indra/test/tests/altestsupport_test.cpp index d8c1283ad7..aeb355ecc0 100644 --- a/indra/test/tests/altestsupport_test.cpp +++ b/indra/test/tests/altestsupport_test.cpp @@ -39,6 +39,8 @@ #include #include #include +#include +#include #include #include @@ -63,8 +65,25 @@ namespace setenv(name, "1", 1); #endif } + + // A library's state, which its own listener adds a check for as the run + // starts, as a library's tests do. + int sLibraryState = 0; + + class LibraryChecks : public Catch::EventListenerBase + { + public: + using Catch::EventListenerBase::EventListenerBase; + + void testRunStarting(Catch::TestRunInfo const&) override + { + ll_test::StateGuard::add("the library's state", [] { return std::to_string(sLibraryState); }); + } + }; } +CATCH_REGISTER_LISTENER(LibraryChecks) + TEST_CASE("WithinBits holds to the bits asked for, and no further", "[altestsupport]") { // 2^-20 apart: equal to 16 bits of the fraction, not to 24. (Not "near": @@ -152,6 +171,12 @@ TEST_CASE("a test case that leaves the environment changed", "[.guard-leak]") SUCCEED(); } +TEST_CASE("a test case that leaves a library's state changed", "[.library-leak]") +{ + sLibraryState = 1; + SUCCEED(); +} + TEST_CASE("a failing test case that logged", "[.replay-fail]") { LL_INFOS("altestsupport") << "the line a failing case logged" << LL_ENDL; From 04caa02866648204495ae6207d3bd7ac089ae697 Mon Sep 17 00:00:00 2001 From: Rye Date: Mon, 12 Oct 2026 02:24:17 -0400 Subject: [PATCH 3/7] al_add_test_binary compiles a stubbing test's own file in, and leaves a one-file binary without a combined entry UNIT names files of the library compiled into the binary rather than linked, with the library's include directories, as al_add_test's UNIT did. It is for a test that stubs what its file calls, and so cannot link the library its stubs would duplicate: llprimitive_test, whose stubs replace LLMaterialID, LLPrimTextureList and LLMessageSystem, and llimageworker_test, whose stubs replace the image classes. Each is a binary of its own. A binary of one file had a combined entry that ran exactly what the file's entry ran, a second time. It no longer gets one. That drops test_support_tests' combined entry, and its COMBINED with it, and the combined entries of llmath's one-file ISA tier binaries. Co-Authored-By: Claude Opus 5.5 --- indra/cmake/ALAddTestBinary.cmake | 81 ++++++++++++++++++------------- indra/test/CMakeLists.txt | 1 - 2 files changed, 48 insertions(+), 34 deletions(-) diff --git a/indra/cmake/ALAddTestBinary.cmake b/indra/cmake/ALAddTestBinary.cmake index 655b43b4eb..02c82263b3 100644 --- a/indra/cmake/ALAddTestBinary.cmake +++ b/indra/cmake/ALAddTestBinary.cmake @@ -9,7 +9,7 @@ if(NOT AL_BUILD_TESTS) return() endif() -# al_add_test_binary( PROJECT SOURCES ... +# al_add_test_binary( PROJECT SOURCES ... [UNIT ...] # [LIBRARIES ...] [SUPPORT ...] # [INCLUDES ...] [DEFINES ...] [ENVIRONMENT ...] # [GL] [PYTHON] [ISA_TIER ] [RESOURCE_LOCK ...] [COMBINED]) @@ -22,6 +22,12 @@ endif() # with it, so the tests would vanish without a word. DEFINES and INCLUDES are # the binary's, for every file in it. # +# UNIT names files of PROJECT, the library, compiled into the binary instead +# of linked, for a test that stubs what they call: its stubs would duplicate +# the library's own definitions. The binary sees the library's include +# directories, and LIBRARIES is all it links. Such a test is a binary of its +# own, since every other test links the code it tests. +# # Each test file is a CTest entry, /, that runs the file's # test cases -- chosen by the tag Catch2's --filenames-as-tags gives each case # from the name of its file -- in one process, as an executable to a test file @@ -38,9 +44,10 @@ endif() # /combined runs all of them in one process. It is labelled # combined_pending and manual, which the default run leaves out, until the # binary's tests are known to share a process: then COMBINED labels it -# combined, and it runs with the rest. With AL_TEST_PER_CASE, each test case is -# an entry of its own instead, found when ctest runs, with the environment and -# labels a file's entry would have. +# combined, and it runs with the rest. A binary of one file has none: that +# file's entry already runs every case in one process. With AL_TEST_PER_CASE, +# each test case is an entry of its own instead, found when ctest runs, with +# the environment and labels a file's entry would have. # # ENVIRONMENT sets variables for the runs, VAR=value each. GL, PYTHON and # ISA_TIER mean what they mean to al_add_test: a GL binary is labelled gl and @@ -56,7 +63,7 @@ function(al_add_test_binary name) arg "GL;PYTHON;COMBINED" "PROJECT;ISA_TIER" - "SOURCES;LIBRARIES;SUPPORT;INCLUDES;DEFINES;ENVIRONMENT;RESOURCE_LOCK" + "SOURCES;UNIT;LIBRARIES;SUPPORT;INCLUDES;DEFINES;ENVIRONMENT;RESOURCE_LOCK" ) if(NOT arg_PROJECT) message(FATAL_ERROR "al_add_test_binary(${name}): PROJECT is required") @@ -109,9 +116,15 @@ function(al_add_test_binary name) # vcpkg's toolchain hangs an app-local copy step on every executable it # sees declared; a test runs from the build tree, as al_add_test explains. set(VCPKG_APPLOCAL_DEPS OFF) - add_executable(${target} ${arg_SOURCES}) + add_executable(${target} ${arg_SOURCES} ${arg_UNIT}) target_link_libraries(${target} PRIVATE al::flags al_test_support ${arg_SUPPORT} ${arg_LIBRARIES}) target_include_directories(${target} PRIVATE ${arg_INCLUDES} ${INDRA_SOURCE_DIR}/test) + if(arg_UNIT) + target_include_directories( + ${target} + PRIVATE $ + ) + endif() if(arg_DEFINES) target_compile_definitions(${target} PRIVATE ${arg_DEFINES}) endif() @@ -260,34 +273,36 @@ function(al_add_test_binary name) PROPERTIES ENVIRONMENT_MODIFICATION "${AL_TEST_ENVIRONMENT}" LABELS "${labels}" TIMEOUT 60 ) - # As long as the files take, but no longer than an hour: a hang, which - # the combined run is there to find, holds one CI row only so long. list(LENGTH stems file_count) - math(EXPR combined_timeout "${timeout} * ${file_count}") - if(combined_timeout GREATER 3600) - set(combined_timeout 3600) - endif() - set(combined_label "combined_pending;manual") - if(arg_COMBINED) - set(combined_label combined) - endif() - add_test( - NAME ${target}/combined - COMMAND $ ${arguments} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - ) - set_tests_properties( - ${target}/combined - PROPERTIES - ENVIRONMENT "${environment}" - ENVIRONMENT_MODIFICATION "${AL_TEST_ENVIRONMENT}" - LABELS "${labels};${combined_label}" - DISABLED ${disabled} - SKIP_RETURN_CODE 125 - TIMEOUT ${combined_timeout} - ) - if(arg_RESOURCE_LOCK) - set_tests_properties(${target}/combined PROPERTIES RESOURCE_LOCK "${arg_RESOURCE_LOCK}") + if(file_count GREATER 1) + # As long as the files take, but no longer than an hour: a hang, which + # the combined run is there to find, holds one CI row only so long. + math(EXPR combined_timeout "${timeout} * ${file_count}") + if(combined_timeout GREATER 3600) + set(combined_timeout 3600) + endif() + set(combined_label "combined_pending;manual") + if(arg_COMBINED) + set(combined_label combined) + endif() + add_test( + NAME ${target}/combined + COMMAND $ ${arguments} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties( + ${target}/combined + PROPERTIES + ENVIRONMENT "${environment}" + ENVIRONMENT_MODIFICATION "${AL_TEST_ENVIRONMENT}" + LABELS "${labels};${combined_label}" + DISABLED ${disabled} + SKIP_RETURN_CODE 125 + TIMEOUT ${combined_timeout} + ) + if(arg_RESOURCE_LOCK) + set_tests_properties(${target}/combined PROPERTIES RESOURCE_LOCK "${arg_RESOURCE_LOCK}") + endif() endif() endif() diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt index ed03005deb..ae25f9a371 100644 --- a/indra/test/CMakeLists.txt +++ b/indra/test/CMakeLists.txt @@ -76,7 +76,6 @@ al_add_test_binary( PROJECT test SOURCES tests/altestsupport_test.cpp LIBRARIES llcommon - COMBINED ) function(al_expect_from_test_support name) From 51b9e233707f87bfca982107b42d53c298402de7 Mon Sep 17 00:00:00 2001 From: Rye Date: Mon, 12 Oct 2026 02:25:29 -0400 Subject: [PATCH 4/7] The small libraries' tests are Catch2 binaries, converted by tut2catch.py TM18, the first wave of stage 3: 25 TUT files in seven libraries become eight Catch2 binaries, each file still run in a process of its own: - llxml_tests (3 files), llcharacter_tests (7), llinventory_tests (6), llfilesystem_tests (2) and llprimitive_tests (4), each linking its library; - llprimitive_test, llimageworker_test and lllogin_test alone. The first two stub what their file calls and so compile it in (UNIT). lllogin's cases make an LLApp, and clear every event pump as they end. llprimitive_tests' one tinygltf and stb implementation stays in llgltfmaterial_test.cpp. llbvhloader's paths to the shipped anim.ini and skeleton are defines of the whole of llcharacter_tests. The files are tut2catch.py's output, with the case names the idiom pass that follows chose given through --names. Two carry hand edits: - lldir_test.cpp: , which lltut.h used to bring, and the twelve ensure_equals() calls in its Linux-only case, which the converter met only as text and so marked. They compare LLDirXDG::Layout's std::string fields, so each is a CHECK(a == b). - lllogin_test.cpp: one LLTestApp for the process, made by the first case and ended by the harness's teardown. An LLApp sets up LLCommon as it is made and tears it down as it ends, APR and LLTrace's master recorder included. Under TUT each test's fixture made one and ended it, and so freed APR for whatever ran after it. The state guard reported that against the first case. tut_parity.py, against the TUT binaries' recorded results (290 tests, all passing): every file holds, 23 as the converter wrote them and lldir and lllogin under --recounted. lldir's four Linux-only cases and llmaterialid's three under !SHOW_ASSERT are not compiled here, and are listed. All 38 entries pass, and so do the five combined runs. Co-Authored-By: Claude Opus 5.5 --- indra/llcharacter/CMakeLists.txt | 33 +- indra/llcharacter/tests/llbvhloader_test.cpp | 535 ++++++++------ indra/llcharacter/tests/llcharacter_test.cpp | 559 ++++++++------ indra/llcharacter/tests/lljoint_test.cpp | 339 +++++---- .../tests/llkeyframemotion_test.cpp | 679 +++++++++-------- .../tests/llmotioncontroller_test.cpp | 125 ++-- .../llcharacter/tests/llmultigesture_test.cpp | 179 ++--- indra/llcharacter/tests/llpose_test.cpp | 695 +++++++++--------- indra/llfilesystem/CMakeLists.txt | 8 +- indra/llfilesystem/tests/lldir_test.cpp | 450 +++++++----- .../llfilesystem/tests/lldiriterator_test.cpp | 13 +- indra/llimage/CMakeLists.txt | 8 +- indra/llimage/tests/llimageworker_test.cpp | 21 +- indra/llinventory/CMakeLists.txt | 21 +- .../llinventory/tests/inventorymisc_test.cpp | 372 ++++++---- indra/llinventory/tests/llparcel_test.cpp | 36 +- .../llinventory/tests/llpermissions_test.cpp | 323 ++++---- indra/llinventory/tests/llsaleinfo_test.cpp | 148 ++-- .../llinventory/tests/llsettingssky_test.cpp | 94 +-- .../tests/lluserrelations_test.cpp | 102 ++- indra/llprimitive/CMakeLists.txt | 23 +- .../llprimitive/tests/llgltfmaterial_test.cpp | 126 ++-- indra/llprimitive/tests/llmaterial_test.cpp | 135 ++-- indra/llprimitive/tests/llmaterialid_test.cpp | 178 ++--- indra/llprimitive/tests/llmediaentry_test.cpp | 171 ++--- indra/llprimitive/tests/llprimitive_test.cpp | 94 +-- indra/llxml/CMakeLists.txt | 9 +- indra/llxml/tests/alxmllayermerge_test.cpp | 285 ++++--- indra/llxml/tests/llcontrol_test.cpp | 74 +- indra/llxml/tests/llxmlnode_test.cpp | 269 ++++--- indra/viewer_components/login/CMakeLists.txt | 4 +- .../login/tests/lllogin_test.cpp | 58 +- 32 files changed, 3370 insertions(+), 2796 deletions(-) diff --git a/indra/llcharacter/CMakeLists.txt b/indra/llcharacter/CMakeLists.txt index 6f88b72dc5..614387278e 100644 --- a/indra/llcharacter/CMakeLists.txt +++ b/indra/llcharacter/CMakeLists.txt @@ -63,25 +63,22 @@ target_link_libraries( target_precompile_headers(llcharacter REUSE_FROM llprecompiled) if(AL_BUILD_TESTS) - foreach( - test - lljoint - llpose - llcharacter - llmotioncontroller - llkeyframemotion - llmultigesture - ) - al_add_test(${test} PROJECT llcharacter LIBRARIES llcharacter) - endforeach() - - # The translation table this parser reads is shipped by newview, and one it - # refuses stops every BVH upload, so the test reads the shipped file itself. - # The joint names a .bvh may use come from the shipped skeleton, and the - # root joint check will not pass without them, so the test reads it too. - al_add_test( - llbvhloader + # The translation table the BVH parser reads is shipped by newview, and one + # it refuses stops every BVH upload, so llbvhloader's tests read the shipped + # file itself. The joint names a .bvh may use come from the shipped + # skeleton, and the root joint check will not pass without them, so the + # tests read it too. + al_add_test_binary( + llcharacter_tests PROJECT llcharacter + SOURCES + tests/llbvhloader_test.cpp + tests/llcharacter_test.cpp + tests/lljoint_test.cpp + tests/llkeyframemotion_test.cpp + tests/llmotioncontroller_test.cpp + tests/llmultigesture_test.cpp + tests/llpose_test.cpp LIBRARIES llcharacter DEFINES "AL_SHIPPED_ANIM_INI=\"${CMAKE_CURRENT_SOURCE_DIR}/../newview/app_settings/anim.ini\"" diff --git a/indra/llcharacter/tests/llbvhloader_test.cpp b/indra/llcharacter/tests/llbvhloader_test.cpp index 9aaf370aed..7076f1f0d8 100644 --- a/indra/llcharacter/tests/llbvhloader_test.cpp +++ b/indra/llcharacter/tests/llbvhloader_test.cpp @@ -39,7 +39,8 @@ #include #include -#include "../test/lltut.h" +#include +#include "altestmatchers.h" namespace { @@ -317,8 +318,6 @@ namespace } } -namespace tut -{ namespace { struct llbvhloader_data @@ -388,12 +387,15 @@ namespace WrittenAnimation build(const std::string& table, const std::vector& lines) { std::unique_ptr loader = makeLoader(); - ensure_equals("the table is read", readTable(*loader, table), E_ST_OK); - ensure_equals("the BVH is read", load(*loader, lines), E_ST_OK); + UNSCOPED_INFO("the table is read"); + REQUIRE(readTable(*loader, table) == E_ST_OK); + UNSCOPED_INFO("the BVH is read"); + REQUIRE(load(*loader, lines) == E_ST_OK); std::vector bytes = write(*loader); WrittenAnimation written; - ensure("the animation can be read back", read_written(bytes, written)); + UNSCOPED_INFO("the animation can be read back"); + REQUIRE(read_written(bytes, written)); return written; } @@ -409,57 +411,65 @@ namespace return motion.deserialize(dp, motion.getID()); } }; - typedef test_group llbvhloader_test; - typedef llbvhloader_test::object llbvhloader_object; - tut::llbvhloader_test llbvhloader_testcase("LLBVHLoader"); -} - template<> template<> - void llbvhloader_object::test<1>() + TEST_CASE_METHOD(llbvhloader_data, "LLBVHLoader: a well formed file is read and written as an animation", "[LLBVHLoader]") { // A well formed file, and what it says coming out the other side. std::unique_ptr loader = makeLoader(); - ensure_equals("a well formed BVH is read", load(*loader, sample_lines()), E_ST_OK); + UNSCOPED_INFO("a well formed BVH is read"); + CHECK(load(*loader, sample_lines()) == E_ST_OK); // Four frames, of which the first is a reference and the last is only // interpolated towards, so two of them are played. - ensure_approximately_equals("the duration is the frames that play", - loader->getDuration(), 2.f * 0.033333f, 16); + UNSCOPED_INFO("the duration is the frames that play"); + CHECK_THAT(loader->getDuration(), ll_test::WithinBits(2.f * 0.033333f, 16)); std::vector bytes = write(*loader); WrittenAnimation written; - ensure("the animation it writes can be read back", read_written(bytes, written)); - - ensure_equals("both joints are written", written.mNumJoints, 2u); - ensure_equals("under the names the table gave them", written.mJointNames[0], std::string("mPelvis")); - ensure_equals("and the second the same", written.mJointNames[1], std::string("mTorso")); - ensure("the root's positions are written", written.mNumPosKeys[0] > 0); - ensure_equals("a three channel joint has no positions to write", written.mNumPosKeys[1], 0); - ensure("both joints have rotations", written.mNumRotKeys[0] > 0 && written.mNumRotKeys[1] > 0); - ensure_equals("nothing asked for a constraint", written.mNumConstraints, 0); + UNSCOPED_INFO("the animation it writes can be read back"); + REQUIRE(read_written(bytes, written)); + + UNSCOPED_INFO("both joints are written"); + CHECK(written.mNumJoints == 2u); + UNSCOPED_INFO("under the names the table gave them"); + CHECK(written.mJointNames[0] == std::string("mPelvis")); + UNSCOPED_INFO("and the second the same"); + CHECK(written.mJointNames[1] == std::string("mTorso")); + UNSCOPED_INFO("the root's positions are written"); + CHECK(written.mNumPosKeys[0] > 0); + UNSCOPED_INFO("a three channel joint has no positions to write"); + CHECK(written.mNumPosKeys[1] == 0); + { + INFO("both joints have rotations"); + CHECK(written.mNumRotKeys[0] > 0); + CHECK(written.mNumRotKeys[1] > 0); + } + UNSCOPED_INFO("nothing asked for a constraint"); + CHECK(written.mNumConstraints == 0); // An animation shorter than the ease times it was given has them // scaled to fit rather than being left longer than itself. - ensure("the eases are scaled down to fit", - written.mEaseIn + written.mEaseOut <= written.mDuration + 1e-5f); + UNSCOPED_INFO("the eases are scaled down to fit"); + CHECK(written.mEaseIn + written.mEaseOut <= written.mDuration + 1e-5f); } - template<> template<> - void llbvhloader_object::test<2>() + TEST_CASE_METHOD(llbvhloader_data, "LLBVHLoader: LLKeyframeMotion reads what the loader writes", "[LLBVHLoader]") { // The bytes a BVH turns into are read by the same code that reads an // animation somebody sends you, so that is what they are given to. std::unique_ptr loader = makeLoader(); - ensure_equals("a well formed BVH is read", load(*loader, sample_lines()), E_ST_OK); + UNSCOPED_INFO("a well formed BVH is read"); + CHECK(load(*loader, sample_lines()) == E_ST_OK); std::vector bytes = write(*loader); LLKeyframeMotion motion(LLUUID::generateNewID()); - ensure("what the loader writes is what the reader takes", loadAsMotion(motion, bytes)); - ensure_equals("with both joints", motion.getNumJointMotions(), 2); + UNSCOPED_INFO("what the loader writes is what the reader takes"); + CHECK(loadAsMotion(motion, bytes)); + UNSCOPED_INFO("with both joints"); + CHECK(motion.getNumJointMotions() == 2); } - template<> template<> - void llbvhloader_object::test<3>() + TEST_CASE_METHOD(llbvhloader_data, "LLBVHLoader: a malformed hierarchy is refused", "[LLBVHLoader]") { // The hierarchy, one line wrong at a time. auto mutate = [&](const char* what, S32 index, const std::string& text, ELoadStatus expected) @@ -467,7 +477,8 @@ namespace std::vector lines = sample_lines(); lines[index] = text; std::unique_ptr loader = makeLoader(); - ensure_equals(what, load(*loader, lines), expected); + UNSCOPED_INFO(what); + REQUIRE(load(*loader, lines) == expected); }; mutate("a file that does not say HIERARCHY is refused", @@ -494,7 +505,8 @@ namespace lines[LINE_ROOT] = "ROOT Hips"; std::unique_ptr loader = makeLoader(); loader->makeTranslation("Hips", "mPelvis"); - ensure_equals("another name for the pelvis is a root", load(*loader, lines), E_ST_OK); + UNSCOPED_INFO("another name for the pelvis is a root"); + CHECK(load(*loader, lines) == E_ST_OK); } // The three lines of an end site are stepped over without looking at @@ -508,13 +520,12 @@ namespace std::vector lines = sample_lines(); lines.resize(length); std::unique_ptr loader = makeLoader(); - ensure_equals("a file that ends inside an end site is refused", - load(*loader, lines), E_ST_EOF); + UNSCOPED_INFO("a file that ends inside an end site is refused"); + REQUIRE(load(*loader, lines) == E_ST_EOF); } } - template<> template<> - void llbvhloader_object::test<4>() + TEST_CASE_METHOD(llbvhloader_data, "LLBVHLoader: a joint takes only three or six channels", "[LLBVHLoader]") { // Three rotations, or three positions and three rotations, is the // whole of what the frame reader takes off a line. Any other count and @@ -524,7 +535,8 @@ namespace std::vector lines = sample_lines(); lines[LINE_JOINT_CHANNELS] = text; std::unique_ptr loader = makeLoader(); - ensure_equals(what, load(*loader, lines), expected); + UNSCOPED_INFO(what); + REQUIRE(load(*loader, lines) == expected); }; channels("no channels at all is refused", @@ -557,12 +569,12 @@ namespace lines[LINE_FIRST_FRAME + frame] = frame_line(frame, 12); } std::unique_ptr loader = makeLoader(); - ensure_equals("a six channel child is read", load(*loader, lines), E_ST_OK); + UNSCOPED_INFO("a six channel child is read"); + CHECK(load(*loader, lines) == E_ST_OK); } } - template<> template<> - void llbvhloader_object::test<5>() + TEST_CASE_METHOD(llbvhloader_data, "LLBVHLoader: the rotation order is read from the rotation channel names", "[LLBVHLoader]") { // The rotation order is read as the letter in front of each of three // "rotation" words. @@ -571,7 +583,8 @@ namespace std::vector lines = sample_lines(); lines[LINE_JOINT_CHANNELS] = text; std::unique_ptr loader = makeLoader(); - ensure_equals(what, load(*loader, lines), expected); + UNSCOPED_INFO(what); + REQUIRE(load(*loader, lines) == expected); }; channels("two rotations where three are needed is refused", @@ -599,8 +612,7 @@ namespace " CHANNELS 3 Xrotation Xrotation Xrotation", E_ST_OK); } - template<> template<> - void llbvhloader_object::test<6>() + TEST_CASE_METHOD(llbvhloader_data, "LLBVHLoader: a bad frame count or frame time is refused", "[LLBVHLoader]") { // The frame count and the frame time, which together are the only // thing every key time is written as a fraction of. @@ -609,7 +621,8 @@ namespace std::vector lines = sample_lines(); lines[index] = text; std::unique_ptr loader = makeLoader(); - ensure_equals(what, load(*loader, lines), expected); + UNSCOPED_INFO(what); + REQUIRE(load(*loader, lines) == expected); }; mutate("a frame count that is not a number is refused", @@ -645,17 +658,17 @@ namespace std::vector lines = sample_lines(); lines[LINE_FRAME_TIME] = "Frame Time: 1e30"; std::unique_ptr loader = makeLoader(); - ensure_equals("an enormous frame time is read", load(*loader, lines), E_ST_OK); + UNSCOPED_INFO("an enormous frame time is read"); + CHECK(load(*loader, lines) == E_ST_OK); std::vector bytes = write(*loader); LLKeyframeMotion motion(LLUUID::generateNewID()); - ensure("but the animation it makes is too long to play", - !loadAsMotion(motion, bytes)); + UNSCOPED_INFO("but the animation it makes is too long to play"); + CHECK_FALSE(loadAsMotion(motion, bytes)); } } - template<> template<> - void llbvhloader_object::test<7>() + TEST_CASE_METHOD(llbvhloader_data, "LLBVHLoader: a malformed frame is refused and extra numbers are dropped", "[LLBVHLoader]") { // The frames themselves. auto mutate = [&](const char* what, S32 index, const std::string& text, ELoadStatus expected) @@ -663,7 +676,8 @@ namespace std::vector lines = sample_lines(); lines[index] = text; std::unique_ptr loader = makeLoader(); - ensure_equals(what, load(*loader, lines), expected); + UNSCOPED_INFO(what); + REQUIRE(load(*loader, lines) == expected); }; mutate("a frame with fewer numbers than the joints need is refused", @@ -690,13 +704,12 @@ namespace std::vector lines = sample_lines(); lines.pop_back(); std::unique_ptr loader = makeLoader(); - ensure_equals("a file with fewer frames than it claims is refused", - load(*loader, lines), E_ST_EOF); + UNSCOPED_INFO("a file with fewer frames than it claims is refused"); + CHECK(load(*loader, lines) == E_ST_EOF); } } - template<> template<> - void llbvhloader_object::test<8>() + TEST_CASE_METHOD(llbvhloader_data, "LLBVHLoader: every cut of a file is refused except within its last number", "[LLBVHLoader]") { // Cut anywhere and it is refused rather than half read. Every prefix // of a well formed file, so the cut lands in each line and in the @@ -708,28 +721,32 @@ namespace // between them is where the last number starts. const std::string whole = bvh_text(sample_lines()); const size_t last_number = whole.rfind(' ') + 1; - ensure("the file is worth cutting up", whole.size() > 200); - ensure("the last number was found", last_number > 1 && last_number < whole.size()); + UNSCOPED_INFO("the file is worth cutting up"); + REQUIRE(whole.size() > 200); + { + INFO("the last number was found"); + CHECK(last_number > 1); + REQUIRE(last_number < whole.size()); + } std::unique_ptr loader = makeLoader(); for (size_t length = 0; length <= last_number; ++length) { rearm(*loader); - ensure("a BVH cut short is refused", - loadText(*loader, whole.substr(0, length)) != E_ST_OK); + UNSCOPED_INFO("a BVH cut short is refused"); + REQUIRE(loadText(*loader, whole.substr(0, length)) != E_ST_OK); } for (size_t length = last_number + 1; length <= whole.size(); ++length) { rearm(*loader); - ensure_equals("a BVH whose last number is merely shorter is whole", - loadText(*loader, whole.substr(0, length)), E_ST_OK); + UNSCOPED_INFO("a BVH whose last number is merely shorter is whole"); + REQUIRE(loadText(*loader, whole.substr(0, length)) == E_ST_OK); } } - template<> template<> - void llbvhloader_object::test<9>() + TEST_CASE_METHOD(llbvhloader_data, "LLBVHLoader: a malformed table is refused and its globals are written", "[LLBVHLoader]") { // The translation table beside the BVH, which is where everything the // BVH itself does not say comes from. @@ -738,7 +755,8 @@ namespace auto table = [&](const char* what, const std::string& text, ELoadStatus expected) { std::unique_ptr loader = makeLoader(); - ensure_equals(what, readTable(*loader, text), expected); + UNSCOPED_INFO(what); + REQUIRE(readTable(*loader, text) == expected); }; table("an empty table is refused", "", E_ST_EOF); @@ -767,7 +785,8 @@ namespace // the table is read long before the loader knows. { std::unique_ptr loader = makeLoader(); - ensure_equals("the globals are read", readTable(*loader, + UNSCOPED_INFO("the globals are read"); + CHECK(readTable(*loader, header + "[GLOBALS]\n" "priority = 4\n" @@ -775,43 +794,54 @@ namespace "easein = 0.1 easeInOut\n" "easeout = 0.2 easeInOut\n" "hand = 3\n" - "emote = wave\n"), E_ST_OK); - ensure_equals("and then the BVH", load(*loader, sample_lines()), E_ST_OK); + "emote = wave\n") == E_ST_OK); + UNSCOPED_INFO("and then the BVH"); + CHECK(load(*loader, sample_lines()) == E_ST_OK); std::vector bytes = write(*loader); WrittenAnimation written; - ensure("the animation can be read back", read_written(bytes, written)); - - ensure_equals("the priority is the one the table gave", written.mBasePriority, 4); - ensure_equals("and every joint is given it", written.mJointPriorities[0], 4); - ensure_equals("the hand pose is the one the table gave", written.mHandPose, 3u); - ensure_equals("so is the emote", written.mEmoteName, std::string("wave")); - ensure_equals("the animation loops", written.mLoop, 1); - ensure_approximately_equals("the ease in is not scaled down under a loop", - written.mEaseIn, 0.1f, 16); - ensure_approximately_equals("nor the ease out", written.mEaseOut, 0.2f, 16); - ensure_approximately_equals("the loop starts a quarter of the way in", - written.mLoopInPoint, 0.25f * written.mDuration, 16); - ensure_approximately_equals("and ends three quarters of the way in", - written.mLoopOutPoint, 0.75f * written.mDuration, 16); - ensure("a loop point is somewhere, not nowhere", written.mLoopOutPoint > 0.f); + UNSCOPED_INFO("the animation can be read back"); + REQUIRE(read_written(bytes, written)); + + UNSCOPED_INFO("the priority is the one the table gave"); + CHECK(written.mBasePriority == 4); + UNSCOPED_INFO("and every joint is given it"); + CHECK(written.mJointPriorities[0] == 4); + UNSCOPED_INFO("the hand pose is the one the table gave"); + CHECK(written.mHandPose == 3u); + UNSCOPED_INFO("so is the emote"); + CHECK(written.mEmoteName == std::string("wave")); + UNSCOPED_INFO("the animation loops"); + CHECK(written.mLoop == 1); + UNSCOPED_INFO("the ease in is not scaled down under a loop"); + CHECK_THAT(written.mEaseIn, ll_test::WithinBits(0.1f, 16)); + UNSCOPED_INFO("nor the ease out"); + CHECK_THAT(written.mEaseOut, ll_test::WithinBits(0.2f, 16)); + UNSCOPED_INFO("the loop starts a quarter of the way in"); + CHECK_THAT(written.mLoopInPoint, ll_test::WithinBits(0.25f * written.mDuration, 16)); + UNSCOPED_INFO("and ends three quarters of the way in"); + CHECK_THAT(written.mLoopOutPoint, ll_test::WithinBits(0.75f * written.mDuration, 16)); + UNSCOPED_INFO("a loop point is somewhere, not nowhere"); + CHECK(written.mLoopOutPoint > 0.f); } // Without a table saying otherwise the loop runs the whole animation. { std::unique_ptr loader = makeLoader(); - ensure_equals("a plain BVH is read", load(*loader, sample_lines()), E_ST_OK); + UNSCOPED_INFO("a plain BVH is read"); + CHECK(load(*loader, sample_lines()) == E_ST_OK); std::vector bytes = write(*loader); WrittenAnimation written; - ensure("the animation can be read back", read_written(bytes, written)); - ensure_equals("it does not loop", written.mLoop, 0); - ensure_approximately_equals("and the loop still ends at the end", - written.mLoopOutPoint, written.mDuration, 16); + UNSCOPED_INFO("the animation can be read back"); + REQUIRE(read_written(bytes, written)); + UNSCOPED_INFO("it does not loop"); + CHECK(written.mLoop == 0); + UNSCOPED_INFO("and the loop still ends at the end"); + CHECK_THAT(written.mLoopOutPoint, ll_test::WithinBits(written.mDuration, 16)); } } - template<> template<> - void llbvhloader_object::test<10>() + TEST_CASE_METHOD(llbvhloader_data, "LLBVHLoader: a constraint line is written as given and nothing more", "[LLBVHLoader]") { // A constraint line is read by two formats, the second of which never // touches the target direction. Every byte of a constraint is written @@ -821,62 +851,70 @@ namespace { std::unique_ptr loader = makeLoader(); - ensure_equals("a constraint without a direction is read", readTable(*loader, - header + "constraint = 2 0.0 0.2 0.8 1.0 mFootRight 1 2 3 mGroundPlane 4 5 6\n"), + UNSCOPED_INFO("a constraint without a direction is read"); + CHECK(readTable(*loader, + header + "constraint = 2 0.0 0.2 0.8 1.0 mFootRight 1 2 3 mGroundPlane 4 5 6\n") == E_ST_OK); - ensure_equals("and then the BVH", load(*loader, sample_lines()), E_ST_OK); + UNSCOPED_INFO("and then the BVH"); + CHECK(load(*loader, sample_lines()) == E_ST_OK); std::vector bytes = write(*loader); WrittenAnimation written; - ensure("the animation can be read back", read_written(bytes, written)); - - ensure_equals("the constraint is written", written.mNumConstraints, 1); - ensure_equals("with its chain length", (S32)written.mChainLength, 2); - ensure_equals("as a point constraint", - (S32)written.mConstraintType, (S32)CONSTRAINT_TYPE_POINT); - ensure_equals("anchored where it said", - volume_name(written.mSourceVolume), std::string("mFootRight")); - ensure_equals("and aimed where it said", - volume_name(written.mTargetVolume), std::string("mGroundPlane")); - ensure_equals("the source offset is the one it gave", - written.mSourceOffset, LLVector3(1.f, 2.f, 3.f)); - ensure_equals("and the target offset", - written.mTargetOffset, LLVector3(4.f, 5.f, 6.f)); + UNSCOPED_INFO("the animation can be read back"); + REQUIRE(read_written(bytes, written)); + + UNSCOPED_INFO("the constraint is written"); + CHECK(written.mNumConstraints == 1); + UNSCOPED_INFO("with its chain length"); + CHECK((S32)written.mChainLength == 2); + UNSCOPED_INFO("as a point constraint"); + CHECK((S32)written.mConstraintType == (S32)CONSTRAINT_TYPE_POINT); + UNSCOPED_INFO("anchored where it said"); + CHECK(volume_name(written.mSourceVolume) == std::string("mFootRight")); + UNSCOPED_INFO("and aimed where it said"); + CHECK(volume_name(written.mTargetVolume) == std::string("mGroundPlane")); + UNSCOPED_INFO("the source offset is the one it gave"); + CHECK(written.mSourceOffset == LLVector3(1.f, 2.f, 3.f)); + UNSCOPED_INFO("and the target offset"); + CHECK(written.mTargetOffset == LLVector3(4.f, 5.f, 6.f)); // The line never said a direction, so nothing may be written as // one but nothing. - ensure_equals("a direction nobody gave is not a direction", - written.mTargetDir, LLVector3::zero); - ensure_equals("the bytes past the source name are empty", - trailing_zeroes(written.mSourceVolume), 6); - ensure_equals("and the bytes past the target name", - trailing_zeroes(written.mTargetVolume), 4); - - ensure_approximately_equals("the ease in starts where it said", - written.mEaseInStart, 0.f, 16); - ensure_approximately_equals("and stops where it said", - written.mEaseInStop, 0.2f, 16); - ensure_approximately_equals("the ease out starts where it said", - written.mEaseOutStart, 0.8f, 16); - ensure_approximately_equals("and stops where it said", - written.mEaseOutStop, 1.f, 16); + UNSCOPED_INFO("a direction nobody gave is not a direction"); + CHECK(written.mTargetDir == LLVector3::zero); + UNSCOPED_INFO("the bytes past the source name are empty"); + CHECK(trailing_zeroes(written.mSourceVolume) == 6); + UNSCOPED_INFO("and the bytes past the target name"); + CHECK(trailing_zeroes(written.mTargetVolume) == 4); + + UNSCOPED_INFO("the ease in starts where it said"); + CHECK_THAT(written.mEaseInStart, ll_test::WithinBits(0.f, 16)); + UNSCOPED_INFO("and stops where it said"); + CHECK_THAT(written.mEaseInStop, ll_test::WithinBits(0.2f, 16)); + UNSCOPED_INFO("the ease out starts where it said"); + CHECK_THAT(written.mEaseOutStart, ll_test::WithinBits(0.8f, 16)); + UNSCOPED_INFO("and stops where it said"); + CHECK_THAT(written.mEaseOutStop, ll_test::WithinBits(1.f, 16)); } // The longer format does give a direction, and it is normalized. { std::unique_ptr loader = makeLoader(); - ensure_equals("a planar constraint with a direction is read", readTable(*loader, - header + "planar_constraint = 2 0.0 0.2 0.8 1.0 mFootRight 0 0 0 mGroundPlane 0 0 0 0 0 4\n"), + UNSCOPED_INFO("a planar constraint with a direction is read"); + CHECK(readTable(*loader, + header + "planar_constraint = 2 0.0 0.2 0.8 1.0 mFootRight 0 0 0 mGroundPlane 0 0 0 0 0 4\n") == E_ST_OK); - ensure_equals("and then the BVH", load(*loader, sample_lines()), E_ST_OK); + UNSCOPED_INFO("and then the BVH"); + CHECK(load(*loader, sample_lines()) == E_ST_OK); std::vector bytes = write(*loader); WrittenAnimation written; - ensure("the animation can be read back", read_written(bytes, written)); - ensure_equals("as a plane constraint", - (S32)written.mConstraintType, (S32)CONSTRAINT_TYPE_PLANE); - ensure_equals("the direction it gave is normalized", - written.mTargetDir, LLVector3(0.f, 0.f, 1.f)); + UNSCOPED_INFO("the animation can be read back"); + REQUIRE(read_written(bytes, written)); + UNSCOPED_INFO("as a plane constraint"); + CHECK((S32)written.mConstraintType == (S32)CONSTRAINT_TYPE_PLANE); + UNSCOPED_INFO("the direction it gave is normalized"); + CHECK(written.mTargetDir == LLVector3(0.f, 0.f, 1.f)); } // A name is read into fifteen of the sixteen bytes and stops there. @@ -885,29 +923,32 @@ namespace // the name running past the field. { std::unique_ptr loader = makeLoader(); - ensure_equals("a name as long as the field is read", readTable(*loader, - header + "constraint = 2 0.0 0.2 0.8 1.0 mAJointWithAVer 0 0 0 mGroundPlane 0 0 0\n"), + UNSCOPED_INFO("a name as long as the field is read"); + CHECK(readTable(*loader, + header + "constraint = 2 0.0 0.2 0.8 1.0 mAJointWithAVer 0 0 0 mGroundPlane 0 0 0\n") == E_ST_OK); - ensure_equals("and then the BVH", load(*loader, sample_lines()), E_ST_OK); + UNSCOPED_INFO("and then the BVH"); + CHECK(load(*loader, sample_lines()) == E_ST_OK); std::vector bytes = write(*loader); WrittenAnimation written; - ensure("the animation can be read back", read_written(bytes, written)); - ensure_equals("the name fills the field", - volume_name(written.mSourceVolume), std::string("mAJointWithAVer")); - ensure_equals("with one byte left to end it", - trailing_zeroes(written.mSourceVolume), 1); + UNSCOPED_INFO("the animation can be read back"); + REQUIRE(read_written(bytes, written)); + UNSCOPED_INFO("the name fills the field"); + CHECK(volume_name(written.mSourceVolume) == std::string("mAJointWithAVer")); + UNSCOPED_INFO("with one byte left to end it"); + CHECK(trailing_zeroes(written.mSourceVolume) == 1); } { std::unique_ptr loader = makeLoader(); - ensure_equals("a name longer than the field is refused", readTable(*loader, - header + "constraint = 2 0.0 0.2 0.8 1.0 mAJointWithAVeryLongNameIndeed 0 0 0 mGroundPlane 0 0 0\n"), + UNSCOPED_INFO("a name longer than the field is refused"); + CHECK(readTable(*loader, + header + "constraint = 2 0.0 0.2 0.8 1.0 mAJointWithAVeryLongNameIndeed 0 0 0 mGroundPlane 0 0 0\n") == E_ST_NO_CONSTRAINT); } } - template<> template<> - void llbvhloader_object::test<11>() + TEST_CASE_METHOD(llbvhloader_data, "LLBVHLoader: a joint section renames or ignores a joint and adjusts its priority", "[LLBVHLoader]") { // A section named for a joint, and the keys under it that say what // becomes of that joint. None of these were read at all before. @@ -917,52 +958,58 @@ namespace // The table has the last word over the alias map, which is the // whole point of a table: it is read after the aliases now. WrittenAnimation written = build(header + "[abdomen]\noutname = mChest\n", sample_lines()); - ensure_equals("both joints are still written", written.mNumJoints, 2u); - ensure_equals("the root keeps the name the alias gave it", - written.mJointNames[0], std::string("mPelvis")); - ensure_equals("and the table renames the other", - written.mJointNames[1], std::string("mChest")); + UNSCOPED_INFO("both joints are still written"); + CHECK(written.mNumJoints == 2u); + UNSCOPED_INFO("the root keeps the name the alias gave it"); + CHECK(written.mJointNames[0] == std::string("mPelvis")); + UNSCOPED_INFO("and the table renames the other"); + CHECK(written.mJointNames[1] == std::string("mChest")); } { // And the reason it has to be read after them: an alias writes the // name and the frame whole, so one applied after a table takes the // table's outname away with it. std::unique_ptr loader = makeLoader(); - ensure_equals("the table is read", - readTable(*loader, header + "[abdomen]\noutname = mChest\n"), E_ST_OK); + UNSCOPED_INFO("the table is read"); + CHECK(readTable(*loader, header + "[abdomen]\noutname = mChest\n") == E_ST_OK); loader->makeTranslation("abdomen", "mTorso"); - ensure_equals("the BVH is read", load(*loader, sample_lines()), E_ST_OK); + UNSCOPED_INFO("the BVH is read"); + CHECK(load(*loader, sample_lines()) == E_ST_OK); std::vector bytes = write(*loader); WrittenAnimation written; - ensure("the animation can be read back", read_written(bytes, written)); - ensure_equals("an alias read after a table undoes what it said", - written.mJointNames[1], std::string("mTorso")); + UNSCOPED_INFO("the animation can be read back"); + REQUIRE(read_written(bytes, written)); + UNSCOPED_INFO("an alias read after a table undoes what it said"); + CHECK(written.mJointNames[1] == std::string("mTorso")); } { WrittenAnimation written = build(header + "[abdomen]\nignore = true\n", sample_lines()); - ensure_equals("an ignored joint is not written", written.mNumJoints, 1u); - ensure_equals("and the one left is the other one", - written.mJointNames[0], std::string("mPelvis")); + UNSCOPED_INFO("an ignored joint is not written"); + CHECK(written.mNumJoints == 1u); + UNSCOPED_INFO("and the one left is the other one"); + CHECK(written.mJointNames[0] == std::string("mPelvis")); } { WrittenAnimation written = build(header + "[abdomen]\nignore = false\n", sample_lines()); - ensure_equals("a joint told not to be ignored is written", written.mNumJoints, 2u); + UNSCOPED_INFO("a joint told not to be ignored is written"); + CHECK(written.mNumJoints == 2u); } { WrittenAnimation written = build( header + "[GLOBALS]\npriority = 3\n[abdomen]\npriority = 2\n", sample_lines()); - ensure_equals("the base priority is the global one", written.mBasePriority, 3); - ensure_equals("a joint with no section of its own takes it", - written.mJointPriorities[0], 3); - ensure_equals("and a joint with one adds its own to it", - written.mJointPriorities[1], 5); + UNSCOPED_INFO("the base priority is the global one"); + CHECK(written.mBasePriority == 3); + UNSCOPED_INFO("a joint with no section of its own takes it"); + CHECK(written.mJointPriorities[0] == 3); + UNSCOPED_INFO("and a joint with one adds its own to it"); + CHECK(written.mJointPriorities[1] == 5); } { WrittenAnimation written = build( header + "[GLOBALS]\npriority = 3\n[abdomen]\npriority = -2\n", sample_lines()); - ensure_equals("a modifier may take it down again", - written.mJointPriorities[1], 1); + UNSCOPED_INFO("a modifier may take it down again"); + CHECK(written.mJointPriorities[1] == 1); } { // Both halves come off a line of the file, so their sum is taken @@ -970,28 +1017,28 @@ namespace WrittenAnimation written = build( header + "[GLOBALS]\npriority = 2147483647\n[abdomen]\npriority = 2147483647\n", sample_lines()); - ensure_equals("the global priority is written as it was given", - written.mBasePriority, 2147483647); - ensure_equals("but a joint priority is brought into range", - written.mJointPriorities[1], LL_CHARACTER_MAX_PRIORITY); + UNSCOPED_INFO("the global priority is written as it was given"); + CHECK(written.mBasePriority == 2147483647); + UNSCOPED_INFO("but a joint priority is brought into range"); + CHECK(written.mJointPriorities[1] == LL_CHARACTER_MAX_PRIORITY); } { WrittenAnimation written = build( header + "[GLOBALS]\npriority = -2147483648\n[abdomen]\npriority = -2147483648\n", sample_lines()); - ensure_equals("from the other end too", - written.mJointPriorities[1], (S32)LLJoint::USE_MOTION_PRIORITY); + UNSCOPED_INFO("from the other end too"); + CHECK(written.mJointPriorities[1] == (S32)LLJoint::USE_MOTION_PRIORITY); } { // A key nobody knows is left alone, which is how a table written // for a later viewer still reads on this one. WrittenAnimation written = build(header + "[abdomen]\nwibble = 3\n", sample_lines()); - ensure_equals("an unknown key is ignored", written.mNumJoints, 2u); + UNSCOPED_INFO("an unknown key is ignored"); + CHECK(written.mNumJoints == 2u); } } - template<> template<> - void llbvhloader_object::test<12>() + TEST_CASE_METHOD(llbvhloader_data, "LLBVHLoader: a joint key with no joint or a bad value is refused", "[LLBVHLoader]") { // Every one of those keys with nothing after the equals, or with // something the key does not take. @@ -1000,7 +1047,8 @@ namespace auto table = [&](const char* what, const std::string& text, ELoadStatus expected) { std::unique_ptr loader = makeLoader(); - ensure_equals(what, readTable(*loader, text), expected); + UNSCOPED_INFO(what); + REQUIRE(readTable(*loader, text) == expected); }; // A key before any section belongs to no joint. @@ -1052,8 +1100,7 @@ namespace joint + "priority = x\n", E_ST_NO_XLT_PRIORITY); } - template<> template<> - void llbvhloader_object::test<13>() + TEST_CASE_METHOD(llbvhloader_data, "LLBVHLoader: relative and matrix keys change only the keyframes they apply to", "[LLBVHLoader]") { // The keys that change the keyframes rather than the joint. Each is // read against the same animation built without it, because what they @@ -1061,45 +1108,51 @@ namespace const std::string header = "Translations 1.0\n"; const std::vector lines = moving_lines(); const WrittenAnimation plain = build(header, lines); - ensure_equals("the animation to compare against has two joints", plain.mNumJoints, 2u); - ensure("and keys to compare", !plain.mRotKeys[1].empty() && !plain.mPosKeys[0].empty()); + UNSCOPED_INFO("the animation to compare against has two joints"); + CHECK(plain.mNumJoints == 2u); + { + INFO("and keys to compare"); + REQUIRE_FALSE(plain.mRotKeys[1].empty()); + REQUIRE_FALSE(plain.mPosKeys[0].empty()); + } { // Every position key is moved by this, and five metres is as far // as a position key goes, so a hundred puts them all at the end. WrittenAnimation written = build(header + "[hip]\nrelativepos = 100 0 0\n", lines); - ensure("a relative position moves the position keys", - written.mPosKeys[0] != plain.mPosKeys[0]); - ensure("as far as they go", written.mPosKeys[0][1] == 0); - ensure("the plain one is nowhere near there", plain.mPosKeys[0][1] > 30000); - ensure("and the rotations are left alone", - written.mRotKeys[0] == plain.mRotKeys[0]); + UNSCOPED_INFO("a relative position moves the position keys"); + CHECK(written.mPosKeys[0] != plain.mPosKeys[0]); + UNSCOPED_INFO("as far as they go"); + CHECK(written.mPosKeys[0][1] == 0); + UNSCOPED_INFO("the plain one is nowhere near there"); + CHECK(plain.mPosKeys[0][1] > 30000); + UNSCOPED_INFO("and the rotations are left alone"); + CHECK(written.mRotKeys[0] == plain.mRotKeys[0]); } { WrittenAnimation written = build(header + "[abdomen]\nrelativerot = firstkey\n", lines); - ensure("a rotation taken from the first key moves the rotation keys", - written.mRotKeys[1] != plain.mRotKeys[1]); - ensure("and leaves the other joint alone", - written.mRotKeys[0] == plain.mRotKeys[0]); + UNSCOPED_INFO("a rotation taken from the first key moves the rotation keys"); + CHECK(written.mRotKeys[1] != plain.mRotKeys[1]); + UNSCOPED_INFO("and leaves the other joint alone"); + CHECK(written.mRotKeys[0] == plain.mRotKeys[0]); } { WrittenAnimation written = build( header + "[abdomen]\nframe = 1 0 0, 0 1 0, 0 0 1\n", lines); - ensure("a frame matrix moves the rotation keys", - written.mRotKeys[1] != plain.mRotKeys[1]); - ensure("and leaves the other joint alone", - written.mRotKeys[0] == plain.mRotKeys[0]); + UNSCOPED_INFO("a frame matrix moves the rotation keys"); + CHECK(written.mRotKeys[1] != plain.mRotKeys[1]); + UNSCOPED_INFO("and leaves the other joint alone"); + CHECK(written.mRotKeys[0] == plain.mRotKeys[0]); } { WrittenAnimation written = build( header + "[abdomen]\noffset = 0 1 0, 0 0 1, 1 0 0\n", lines); - ensure("an offset matrix moves the rotation keys", - written.mRotKeys[1] != plain.mRotKeys[1]); + UNSCOPED_INFO("an offset matrix moves the rotation keys"); + CHECK(written.mRotKeys[1] != plain.mRotKeys[1]); } } - template<> template<> - void llbvhloader_object::test<14>() + TEST_CASE_METHOD(llbvhloader_data, "LLBVHLoader: merge keys fold another joint into a rotation", "[LLBVHLoader]") { // The merge keys, which name another joint whose rotation is folded // into this one. Nothing filled these in before, so the arm of the @@ -1110,22 +1163,22 @@ namespace { WrittenAnimation written = build(header + "[abdomen]\nmergeparent = hip\n", lines); - ensure("a merged parent moves the rotation keys", - written.mRotKeys[1] != plain.mRotKeys[1]); - ensure("and leaves the parent alone", - written.mRotKeys[0] == plain.mRotKeys[0]); + UNSCOPED_INFO("a merged parent moves the rotation keys"); + CHECK(written.mRotKeys[1] != plain.mRotKeys[1]); + UNSCOPED_INFO("and leaves the parent alone"); + CHECK(written.mRotKeys[0] == plain.mRotKeys[0]); } { WrittenAnimation written = build(header + "[hip]\nmergechild = abdomen\n", lines); - ensure("a merged child moves the rotation keys", - written.mRotKeys[0] != plain.mRotKeys[0]); + UNSCOPED_INFO("a merged child moves the rotation keys"); + CHECK(written.mRotKeys[0] != plain.mRotKeys[0]); } { // A name that is nobody is nobody, rather than an error: the table // is written once and the files come and go. WrittenAnimation written = build(header + "[abdomen]\nmergeparent = elbow\n", lines); - ensure("a merge with a joint the file does not have changes nothing", - written.mRotKeys[1] == plain.mRotKeys[1]); + UNSCOPED_INFO("a merge with a joint the file does not have changes nothing"); + CHECK(written.mRotKeys[1] == plain.mRotKeys[1]); } // The merged joint is read one frame behind, and the first frame of a @@ -1138,28 +1191,30 @@ namespace single.resize(LINE_FIRST_FRAME + 1); const WrittenAnimation one_frame = build(header, single); - ensure_equals("a single frame animation is written", one_frame.mNumJoints, 2u); - ensure_equals("with one rotation key", one_frame.mNumRotKeys[1], 1); + UNSCOPED_INFO("a single frame animation is written"); + CHECK(one_frame.mNumJoints == 2u); + UNSCOPED_INFO("with one rotation key"); + CHECK(one_frame.mNumRotKeys[1] == 1); WrittenAnimation merged = build(header + "[abdomen]\nmergeparent = hip\n", single); - ensure("a merge with no frame behind it is no merge", - merged.mRotKeys[1] == one_frame.mRotKeys[1]); + UNSCOPED_INFO("a merge with no frame behind it is no merge"); + CHECK(merged.mRotKeys[1] == one_frame.mRotKeys[1]); } } #ifdef AL_SHIPPED_ANIM_INI - template<> template<> - void llbvhloader_object::test<15>() + TEST_CASE_METHOD(llbvhloader_data, "LLBVHLoader: the shipped translation table reads and sets nothing", "[LLBVHLoader]") { // The table the viewer ships is read by this parser before every BVH // upload, and one it refuses stops the upload, so the file itself is // read here rather than something that looks like it. std::ifstream file(AL_SHIPPED_ANIM_INI); - ensure("the shipped translation table was found", file.good()); + UNSCOPED_INFO("the shipped translation table was found"); + CHECK(file.good()); std::unique_ptr loader = makeLoader(); - ensure_equals("the shipped translation table is read", - loader->loadTranslationTable(file), E_ST_OK); + UNSCOPED_INFO("the shipped translation table is read"); + CHECK(loader->loadTranslationTable(file) == E_ST_OK); // Nothing in it is a setting, so nothing in it may become one by // accident: an example that stopped being a comment would be read. @@ -1171,47 +1226,51 @@ namespace number++; if (number == 1) { - ensure_equals("the header is the first line of it", - line.compare(0, 16, "Translations 1.0"), 0); + UNSCOPED_INFO("the header is the first line of it"); + REQUIRE(line.compare(0, 16, "Translations 1.0") == 0); continue; } const size_t first = line.find_first_not_of(" \t\r"); - ensure("every line after it is blank or a comment", - first == std::string::npos || line[first] == '#'); + UNSCOPED_INFO("every line after it is blank or a comment"); + REQUIRE((first == std::string::npos || line[first] == '#')); } - ensure("the file has something in it", number > 1); + UNSCOPED_INFO("the file has something in it"); + CHECK(number > 1); } #endif #ifdef AL_SHIPPED_AVATAR_SKELETON - template<> template<> - void llbvhloader_object::test<16>() + TEST_CASE_METHOD(llbvhloader_data, "LLBVHLoader: a BVH reads against the aliases of the shipped skeleton", "[LLBVHLoader]") { // Every joint name a .bvh is allowed to use comes from the shipped // skeleton, and the root joint check turns away any file whose root is // not the pelvis under one of them. Nothing else stands between an // edit to that file and every BVH upload failing, so it is read here. LLXMLNodePtr root; - ensure("the shipped skeleton was read", - LLXMLNode::parseFile(AL_SHIPPED_AVATAR_SKELETON, root, nullptr)); - ensure("and it has a root", root.notNull()); + UNSCOPED_INFO("the shipped skeleton was read"); + REQUIRE(LLXMLNode::parseFile(AL_SHIPPED_AVATAR_SKELETON, root, nullptr)); + UNSCOPED_INFO("and it has a root"); + REQUIRE(root.notNull()); alias_map_t aliases; std::vector claimed_twice; collect_bone_aliases(root, aliases, claimed_twice); - ensure("the skeleton has its bones", aliases.size() > 100); + UNSCOPED_INFO("the skeleton has its bones"); + REQUIRE(aliases.size() > 100); // The name the BVH reader insists the root joint answers to. alias_map_t::iterator hip = aliases.find("hip"); - ensure("the skeleton still calls the pelvis hip", hip != aliases.end()); - ensure_equals("and means the pelvis by it", hip->second, std::string("mPelvis")); + UNSCOPED_INFO("the skeleton still calls the pelvis hip"); + REQUIRE(hip != aliases.end()); + UNSCOPED_INFO("and means the pelvis by it"); + CHECK(hip->second == std::string("mPelvis")); // A bone claiming a name another bone already has takes it, and only // says so in the log. - ensure_equals("no alias is claimed by two bones", - claimed_twice.empty() ? std::string() : claimed_twice.front(), + UNSCOPED_INFO("no alias is claimed by two bones"); + REQUIRE((claimed_twice.empty() ? std::string() : claimed_twice.front()) == std::string()); // The whole map through the whole reader, which is what the upload @@ -1223,17 +1282,19 @@ namespace loader->makeTranslation(alias, joint); } - ensure_equals("a BVH is read against the shipped skeleton", - load(*loader, sample_lines()), E_ST_OK); + UNSCOPED_INFO("a BVH is read against the shipped skeleton"); + CHECK(load(*loader, sample_lines()) == E_ST_OK); std::vector bytes = write(*loader); WrittenAnimation written; - ensure("the animation can be read back", read_written(bytes, written)); - ensure_equals("both joints are written", written.mNumJoints, 2u); - ensure_equals("the root under the name the skeleton gives it", - written.mJointNames[0], std::string("mPelvis")); - ensure_equals("and the other one too", - written.mJointNames[1], std::string("mTorso")); + UNSCOPED_INFO("the animation can be read back"); + REQUIRE(read_written(bytes, written)); + UNSCOPED_INFO("both joints are written"); + CHECK(written.mNumJoints == 2u); + UNSCOPED_INFO("the root under the name the skeleton gives it"); + CHECK(written.mJointNames[0] == std::string("mPelvis")); + UNSCOPED_INFO("and the other one too"); + CHECK(written.mJointNames[1] == std::string("mTorso")); } #endif } diff --git a/indra/llcharacter/tests/llcharacter_test.cpp b/indra/llcharacter/tests/llcharacter_test.cpp index ab4dd2b519..18f27fc1f6 100644 --- a/indra/llcharacter/tests/llcharacter_test.cpp +++ b/indra/llcharacter/tests/llcharacter_test.cpp @@ -41,14 +41,17 @@ #include #include -#include "../test/lltut.h" +#include +#include +#include "altestmatchers.h" namespace { // q and -q are the same rotation, so compare through the absolute dot product. void ensure_quat_equals(const char* msg, const LLQuaternion& actual, const LLQuaternion& expected) { - tut::ensure_approximately_equals_range(msg, fabsf(dot(actual, expected)), 1.f, 1e-5f); + UNSCOPED_INFO(msg); + REQUIRE_THAT(fabsf(dot(actual, expected)), Catch::Matchers::WithinAbs(1.f, 1e-5f)); } } @@ -105,8 +108,6 @@ namespace }; } -namespace tut -{ namespace { struct llcharacter_data @@ -121,7 +122,8 @@ namespace { mParamInfos.push_back(std::make_unique(id, name, sex)); ALTestVisualParam* param = new ALTestVisualParam(); - ensure("the parameter takes its info", param->setInfo(mParamInfos.back().get())); + UNSCOPED_INFO("the parameter takes its info"); + REQUIRE(param->setInfo(mParamInfos.back().get())); mCharacter.addVisualParam(param); return param; } @@ -131,10 +133,13 @@ namespace ALTestMotion* startFreshMotion(LLUUID& id, LLMotionConstructor create = ALTestMotion::create) { id = LLUUID::generateNewID(); - ensure("registerMotion", mCharacter.registerMotion(id, create)); - ensure("startMotion", mCharacter.startMotion(id)); + UNSCOPED_INFO("registerMotion"); + REQUIRE(mCharacter.registerMotion(id, create)); + UNSCOPED_INFO("startMotion"); + REQUIRE(mCharacter.startMotion(id)); LLMotion* motion = mCharacter.findMotion(id); - ensure("started motion is findable", motion != nullptr); + UNSCOPED_INFO("started motion is findable"); + REQUIRE(motion != nullptr); return static_cast(motion); } @@ -143,7 +148,8 @@ namespace void animateJoint(ALTestMotion* motion, const char* joint_name) { LLJoint* joint = mCharacter.getJoint(joint_name); - ensure("joint resolves", joint != nullptr); + UNSCOPED_INFO("joint resolves"); + REQUIRE(joint != nullptr); motion->addJoint(joint, LLJointState::ROT); } @@ -161,73 +167,89 @@ namespace return mCharacter.getMotionController().getActiveMotions(blend_type); } }; - typedef test_group llcharacter_test; - typedef llcharacter_test::object llcharacter_object; - tut::llcharacter_test llcharacter_testcase("LLCharacter"); -} - template<> template<> - void llcharacter_object::test<1>() + TEST_CASE_METHOD(llcharacter_data, "LLCharacter: joints resolve by name and index with their numbers and parents", "[LLCharacter]") { LLJoint* pelvis = mCharacter.getJoint("mPelvis"); - ensure("pelvis resolves", pelvis != nullptr); - ensure_equals("pelvis joint number", pelvis->getJointNum(), 0); - ensure("pelvis hangs off the root", pelvis->getParent() == mCharacter.getRootJoint()); + UNSCOPED_INFO("pelvis resolves"); + REQUIRE(pelvis != nullptr); + UNSCOPED_INFO("pelvis joint number"); + CHECK(pelvis->getJointNum() == 0); + UNSCOPED_INFO("pelvis hangs off the root"); + CHECK(pelvis->getParent() == mCharacter.getRootJoint()); LLJoint* head = mCharacter.getJoint("mHead"); - ensure("head resolves", head != nullptr); - ensure_equals("head joint number", head->getJointNum(), 4); - ensure_equals("head parent", head->getParent()->getName(), std::string("mNeck")); - - ensure("character joint by index", mCharacter.getCharacterJoint(2) == mCharacter.getJoint("mChest")); - ensure("unknown joint is null", mCharacter.getJoint("mNope") == nullptr); + UNSCOPED_INFO("head resolves"); + REQUIRE(head != nullptr); + UNSCOPED_INFO("head joint number"); + CHECK(head->getJointNum() == 4); + UNSCOPED_INFO("head parent"); + CHECK(head->getParent()->getName() == std::string("mNeck")); + + UNSCOPED_INFO("character joint by index"); + CHECK(mCharacter.getCharacterJoint(2) == mCharacter.getJoint("mChest")); + UNSCOPED_INFO("unknown joint is null"); + REQUIRE(mCharacter.getJoint("mNope") == nullptr); } - template<> template<> - void llcharacter_object::test<2>() + TEST_CASE_METHOD(llcharacter_data, "LLCharacter: registerMotion refuses duplicates and createMotion builds one instance", "[LLCharacter]") { LLUUID id = LLUUID::generateNewID(); - ensure("registerMotion accepts a new id", mCharacter.registerMotion(id, ALTestMotion::create)); - ensure("registerMotion refuses a duplicate", !mCharacter.registerMotion(id, ALTestMotion::create)); + UNSCOPED_INFO("registerMotion accepts a new id"); + CHECK(mCharacter.registerMotion(id, ALTestMotion::create)); + UNSCOPED_INFO("registerMotion refuses a duplicate"); + CHECK_FALSE(mCharacter.registerMotion(id, ALTestMotion::create)); LLMotion* motion = mCharacter.createMotion(id); - ensure("createMotion builds an instance", motion != nullptr); - ensure("findMotion returns the same instance", mCharacter.findMotion(id) == motion); - ensure("createMotion again returns the same instance", mCharacter.createMotion(id) == motion); - ensure_equals("onInitialize ran once", static_cast(motion)->mInitializeCount, 1); - ensure("created motion is not active", !mCharacter.isMotionActive(id)); + UNSCOPED_INFO("createMotion builds an instance"); + REQUIRE(motion != nullptr); + UNSCOPED_INFO("findMotion returns the same instance"); + CHECK(mCharacter.findMotion(id) == motion); + UNSCOPED_INFO("createMotion again returns the same instance"); + CHECK(mCharacter.createMotion(id) == motion); + UNSCOPED_INFO("onInitialize ran once"); + CHECK(static_cast(motion)->mInitializeCount == 1); + UNSCOPED_INFO("created motion is not active"); + CHECK_FALSE(mCharacter.isMotionActive(id)); } - template<> template<> - void llcharacter_object::test<3>() + TEST_CASE_METHOD(llcharacter_data, "LLCharacter: a started motion updates on normal updates until stopped", "[LLCharacter]") { LLUUID id; ALTestMotion* motion = startFreshMotion(id); - ensure("motion is active", mCharacter.isMotionActive(id)); - ensure_equals("onActivate ran once", motion->mActivateCount, 1); - ensure_equals("activation primes with one onUpdate", motion->mUpdateCount, 1); + UNSCOPED_INFO("motion is active"); + CHECK(mCharacter.isMotionActive(id)); + UNSCOPED_INFO("onActivate ran once"); + CHECK(motion->mActivateCount == 1); + UNSCOPED_INFO("activation primes with one onUpdate"); + CHECK(motion->mUpdateCount == 1); const LLQuaternion rot(0.7f, LLVector3::y_axis); LLJoint* pelvis = mCharacter.getJoint("mPelvis"); motion->addJoint(pelvis, LLJointState::ROT)->setRotation(rot); mCharacter.updateMotions(LLCharacter::HIDDEN_UPDATE); - ensure_equals("hidden update does not run onUpdate", motion->mUpdateCount, 1); + UNSCOPED_INFO("hidden update does not run onUpdate"); + CHECK(motion->mUpdateCount == 1); mCharacter.updateMotions(LLCharacter::NORMAL_UPDATE); - ensure_equals("normal update runs onUpdate", motion->mUpdateCount, 2); + UNSCOPED_INFO("normal update runs onUpdate"); + CHECK(motion->mUpdateCount == 2); ensure_quat_equals("normal update blends the pose into the skeleton", pelvis->getRotation(), rot); - ensure("stopMotion", mCharacter.stopMotion(id, true)); - ensure("stopped motion is inactive", !mCharacter.isMotionActive(id)); - ensure_equals("onDeactivate ran once", motion->mDeactivateCount, 1); + UNSCOPED_INFO("stopMotion"); + CHECK(mCharacter.stopMotion(id, true)); + UNSCOPED_INFO("stopped motion is inactive"); + CHECK_FALSE(mCharacter.isMotionActive(id)); + UNSCOPED_INFO("onDeactivate ran once"); + CHECK(motion->mDeactivateCount == 1); mCharacter.updateMotions(LLCharacter::NORMAL_UPDATE); - ensure_equals("inactive motion is not updated", motion->mUpdateCount, 2); + UNSCOPED_INFO("inactive motion is not updated"); + CHECK(motion->mUpdateCount == 2); } - template<> template<> - void llcharacter_object::test<4>() + TEST_CASE_METHOD(llcharacter_data, "LLCharacter: a motion whose onUpdate returns false is stopped", "[LLCharacter]") { // A motion that reports completion from onUpdate is stopped by the controller. LLUUID id; @@ -235,11 +257,11 @@ namespace motion->mUpdateResult = false; mCharacter.updateMotions(LLCharacter::NORMAL_UPDATE); - ensure("self-completing motion is marked stopped", motion->isStopped()); + UNSCOPED_INFO("self-completing motion is marked stopped"); + CHECK(motion->isStopped()); } - template<> template<> - void llcharacter_object::test<5>() + TEST_CASE_METHOD(llcharacter_data, "LLCharacter: motions are listed newest first and additive ones update first", "[LLCharacter]") { // The playing motions are kept newest first in a list per blend type, // and an update walks the whole additive list before the normal one: @@ -257,24 +279,40 @@ namespace const LLMotionController::motion_list_t& normals = activeMotions(LLMotion::NORMAL_BLEND); const LLMotionController::motion_list_t& additives = activeMotions(LLMotion::ADDITIVE_BLEND); - ensure_equals("two normal motions", normals.size(), 2u); - ensure("normal list is newest first", normals[0] == normal_second && normals[1] == normal_first); - ensure_equals("two additive motions", additives.size(), 2u); - ensure("additive list is newest first", additives[0] == additive_second && additives[1] == additive_first); + UNSCOPED_INFO("two normal motions"); + REQUIRE(normals.size() == 2u); + { + INFO("normal list is newest first"); + CHECK(normals[0] == normal_second); + CHECK(normals[1] == normal_first); + } + UNSCOPED_INFO("two additive motions"); + REQUIRE(additives.size() == 2u); + { + INFO("additive list is newest first"); + CHECK(additives[0] == additive_second); + CHECK(additives[1] == additive_first); + } mCharacter.updateMotions(LLCharacter::NORMAL_UPDATE); - ensure("newest additive motion is updated first", additive_second->mUpdateSerial < additive_first->mUpdateSerial); - ensure("the additive pass finishes before the normal pass begins", additive_first->mUpdateSerial < normal_second->mUpdateSerial); - ensure("newest normal motion is updated first", normal_second->mUpdateSerial < normal_first->mUpdateSerial); - - ensure("stopMotion", mCharacter.stopMotion(additive_first_id, true)); - ensure_equals("stopping an additive motion shortens the additive list", additives.size(), 1u); - ensure("the other additive motion stays", additives[0] == additive_second); - ensure_equals("and leaves the normal list alone", normals.size(), 2u); + UNSCOPED_INFO("newest additive motion is updated first"); + CHECK(additive_second->mUpdateSerial < additive_first->mUpdateSerial); + UNSCOPED_INFO("the additive pass finishes before the normal pass begins"); + CHECK(additive_first->mUpdateSerial < normal_second->mUpdateSerial); + UNSCOPED_INFO("newest normal motion is updated first"); + CHECK(normal_second->mUpdateSerial < normal_first->mUpdateSerial); + + UNSCOPED_INFO("stopMotion"); + CHECK(mCharacter.stopMotion(additive_first_id, true)); + UNSCOPED_INFO("stopping an additive motion shortens the additive list"); + REQUIRE(additives.size() == 1u); + UNSCOPED_INFO("the other additive motion stays"); + CHECK(additives[0] == additive_second); + UNSCOPED_INFO("and leaves the normal list alone"); + REQUIRE(normals.size() == 2u); } - template<> template<> - void llcharacter_object::test<6>() + TEST_CASE_METHOD(llcharacter_data, "LLCharacter: a motion leaving the list mid-update skips no other motion", "[LLCharacter]") { // A motion that finishes easing out takes itself off the list while // the list is being walked. The older motion behind it moves into its @@ -286,7 +324,8 @@ namespace animateJoint(oldest, "mPelvis"); animateJoint(ending, "mTorso"); animateJoint(newest, "mChest"); - ensure("stopMotion", mCharacter.stopMotion(ending_id, false)); + UNSCOPED_INFO("stopMotion"); + CHECK(mCharacter.stopMotion(ending_id, false)); S32 frames = 0; while (mCharacter.isMotionActive(ending_id) && frames < 20) @@ -294,18 +333,26 @@ namespace runFrame(); ++frames; } - ensure("the ending motion left the list", !mCharacter.isMotionActive(ending_id)); - ensure_equals("the ending motion was deactivated once", ending->mDeactivateCount, 1); - ensure_equals("the motion behind it was updated every frame", oldest->mUpdateCount, 1 + frames); - ensure_equals("the motion ahead of it was updated every frame", newest->mUpdateCount, 1 + frames); + UNSCOPED_INFO("the ending motion left the list"); + CHECK_FALSE(mCharacter.isMotionActive(ending_id)); + UNSCOPED_INFO("the ending motion was deactivated once"); + CHECK(ending->mDeactivateCount == 1); + UNSCOPED_INFO("the motion behind it was updated every frame"); + CHECK(oldest->mUpdateCount == 1 + frames); + UNSCOPED_INFO("the motion ahead of it was updated every frame"); + CHECK(newest->mUpdateCount == 1 + frames); const LLMotionController::motion_list_t& normals = activeMotions(LLMotion::NORMAL_BLEND); - ensure_equals("two motions remain", normals.size(), 2u); - ensure("remaining motions are still newest first", normals[0] == newest && normals[1] == oldest); + UNSCOPED_INFO("two motions remain"); + REQUIRE(normals.size() == 2u); + { + INFO("remaining motions are still newest first"); + CHECK(normals[0] == newest); + CHECK(normals[1] == oldest); + } } - template<> template<> - void llcharacter_object::test<7>() + TEST_CASE_METHOD(llcharacter_data, "LLCharacter: the pixel area is asked once per pass and not per motion", "[LLCharacter]") { // The LOD test compares every motion against the character's pixel // area, which is the same for all of them and a virtual call. It is @@ -323,11 +370,11 @@ namespace animateJoint(startFreshMotion(id, ALTestMotion::createAdditive), "mNeck"); mCharacter.mPixelAreaQueries = 0; mCharacter.updateMotions(LLCharacter::NORMAL_UPDATE); - ensure_equals("pixel area is not asked once per motion", mCharacter.mPixelAreaQueries, queries_with_one_motion); + UNSCOPED_INFO("pixel area is not asked once per motion"); + CHECK(mCharacter.mPixelAreaQueries == queries_with_one_motion); } - template<> template<> - void llcharacter_object::test<8>() + TEST_CASE_METHOD(llcharacter_data, "LLCharacter: flushAllMotions restarts normal and additive motions", "[LLCharacter]") { // A flush restarts every playing motion, whichever list it was on. LLUUID normal_id, additive_id; @@ -335,14 +382,17 @@ namespace startFreshMotion(additive_id, ALTestMotion::createAdditive); mCharacter.flushAllMotions(); - ensure("normal motion restarted", mCharacter.isMotionActive(normal_id)); - ensure("additive motion restarted", mCharacter.isMotionActive(additive_id)); - ensure_equals("one normal motion", activeMotions(LLMotion::NORMAL_BLEND).size(), 1u); - ensure_equals("one additive motion", activeMotions(LLMotion::ADDITIVE_BLEND).size(), 1u); + UNSCOPED_INFO("normal motion restarted"); + CHECK(mCharacter.isMotionActive(normal_id)); + UNSCOPED_INFO("additive motion restarted"); + CHECK(mCharacter.isMotionActive(additive_id)); + UNSCOPED_INFO("one normal motion"); + REQUIRE(activeMotions(LLMotion::NORMAL_BLEND).size() == 1u); + UNSCOPED_INFO("one additive motion"); + REQUIRE(activeMotions(LLMotion::ADDITIVE_BLEND).size() == 1u); } - template<> template<> - void llcharacter_object::test<9>() + TEST_CASE_METHOD(llcharacter_data, "LLCharacter: a hidden update drops every stopped motion in one pass", "[LLCharacter]") { // Hidden, the controller drops every stopped motion in one pass, and // a motion leaving the list must not hide the stopped one behind it. @@ -350,18 +400,23 @@ namespace startFreshMotion(oldest_id); startFreshMotion(middle_id); startFreshMotion(newest_id); - ensure("stop middle", mCharacter.stopMotion(middle_id, false)); - ensure("stop oldest", mCharacter.stopMotion(oldest_id, false)); + UNSCOPED_INFO("stop middle"); + CHECK(mCharacter.stopMotion(middle_id, false)); + UNSCOPED_INFO("stop oldest"); + CHECK(mCharacter.stopMotion(oldest_id, false)); mCharacter.updateMotions(LLCharacter::HIDDEN_UPDATE); - ensure("the stopped middle motion left the list", !mCharacter.isMotionActive(middle_id)); - ensure("the stopped motion behind it left the list too", !mCharacter.isMotionActive(oldest_id)); - ensure("the running motion stayed", mCharacter.isMotionActive(newest_id)); - ensure_equals("one motion remains", activeMotions(LLMotion::NORMAL_BLEND).size(), 1u); + UNSCOPED_INFO("the stopped middle motion left the list"); + CHECK_FALSE(mCharacter.isMotionActive(middle_id)); + UNSCOPED_INFO("the stopped motion behind it left the list too"); + CHECK_FALSE(mCharacter.isMotionActive(oldest_id)); + UNSCOPED_INFO("the running motion stayed"); + CHECK(mCharacter.isMotionActive(newest_id)); + UNSCOPED_INFO("one motion remains"); + REQUIRE(activeMotions(LLMotion::NORMAL_BLEND).size() == 1u); } - template<> template<> - void llcharacter_object::test<10>() + TEST_CASE_METHOD(llcharacter_data, "LLCharacter: setTimeStep snaps only a stopped motion's stop time", "[LLCharacter]") { // Moving onto the coarse clock snaps every playing motion's timestamps // to the quantum. A running motion has no stop time to snap, and @@ -372,21 +427,27 @@ namespace ALTestMotion* running = startFreshMotion(running_id); ALTestMotion* stopped = startFreshMotion(stopped_id); runFrame(); - ensure("stopMotion", mCharacter.stopMotion(stopped_id, false)); + UNSCOPED_INFO("stopMotion"); + CHECK(mCharacter.stopMotion(stopped_id, false)); stopped->mStopTimeCalls = 0; running->mStopTimeCalls = 0; mCharacter.getMotionController().setTimeStep(0.25f); - ensure("the running motion is still running", !running->isStopped()); - ensure_equals("its stop time was never set", running->mStopTimeCalls, 0); - ensure_equals("and it still reads as never stopped", running->getStopTime(), 0.0); - ensure("a motion that never ends keeps its open send-stop time", running->sendStopTimestamp() == F32_MAX); - ensure("the stopped motion is still stopped", stopped->isStopped()); - ensure_equals("its stop time sits on the quantum", fmod(stopped->getStopTime(), 0.25), 0.0); + UNSCOPED_INFO("the running motion is still running"); + CHECK_FALSE(running->isStopped()); + UNSCOPED_INFO("its stop time was never set"); + CHECK(running->mStopTimeCalls == 0); + UNSCOPED_INFO("and it still reads as never stopped"); + CHECK(running->getStopTime() == 0.0); + UNSCOPED_INFO("a motion that never ends keeps its open send-stop time"); + CHECK(running->sendStopTimestamp() == F32_MAX); + UNSCOPED_INFO("the stopped motion is still stopped"); + CHECK(stopped->isStopped()); + UNSCOPED_INFO("its stop time sits on the quantum"); + CHECK(fmod(stopped->getStopTime(), 0.25) == 0.0); } - template<> template<> - void llcharacter_object::test<11>() + TEST_CASE_METHOD(llcharacter_data, "LLCharacter: a motion started during an update waits for the next frame", "[LLCharacter]") { // A motion's update can start another motion -- an emote's does when // it finds the sit animation is for the wrong sex -- and the new one @@ -407,42 +468,59 @@ namespace newest->mEaseInDuration = 10.f; const LLUUID started_id = LLUUID::generateNewID(); - ensure("registerMotion", mCharacter.registerMotion(started_id, ALTestMotion::create)); + UNSCOPED_INFO("registerMotion"); + CHECK(mCharacter.registerMotion(started_id, ALTestMotion::create)); middle->mStartOnUpdate = started_id; mCharacter.updateMotions(LLCharacter::NORMAL_UPDATE); - ensure("the started motion is playing", mCharacter.isMotionActive(started_id)); - ensure_equals("the motion ahead of the starter was updated once", newest->mUpdateCount, 2); - ensure_equals("the starter was updated once", middle->mUpdateCount, 2); - ensure_equals("the motion behind the starter was updated once", oldest->mUpdateCount, 2); + UNSCOPED_INFO("the started motion is playing"); + CHECK(mCharacter.isMotionActive(started_id)); + UNSCOPED_INFO("the motion ahead of the starter was updated once"); + CHECK(newest->mUpdateCount == 2); + UNSCOPED_INFO("the starter was updated once"); + CHECK(middle->mUpdateCount == 2); + UNSCOPED_INFO("the motion behind the starter was updated once"); + CHECK(oldest->mUpdateCount == 2); ALTestMotion* started = static_cast(mCharacter.findMotion(started_id)); - ensure_equals("the new motion had only its activation update", started->mUpdateCount, 1); + UNSCOPED_INFO("the new motion had only its activation update"); + CHECK(started->mUpdateCount == 1); const LLMotionController::motion_list_t& normals = activeMotions(LLMotion::NORMAL_BLEND); - ensure_equals("four motions listed", normals.size(), 4u); - ensure("newest first, the started one ahead of them all", - normals[0] == started && normals[1] == newest && normals[2] == middle && normals[3] == oldest); + UNSCOPED_INFO("four motions listed"); + REQUIRE(normals.size() == 4u); + { + INFO("newest first, the started one ahead of them all"); + CHECK(normals[0] == started); + CHECK(normals[1] == newest); + CHECK(normals[2] == middle); + CHECK(normals[3] == oldest); + } } - template<> template<> - void llcharacter_object::test<12>() + TEST_CASE_METHOD(llcharacter_data, "LLCharacter: animation data is a slot and the hand pose request a copy", "[LLCharacter]") { // The values motions hand one another are slots on the character: set // by whoever owns the value, empty when nobody does. F32 speed = 3.f; - ensure("an unset channel reads empty", mCharacter.getAnimationData(LLCharacter::ANIM_CHANNEL_WALK_SPEED) == nullptr); + UNSCOPED_INFO("an unset channel reads empty"); + REQUIRE(mCharacter.getAnimationData(LLCharacter::ANIM_CHANNEL_WALK_SPEED) == nullptr); mCharacter.setAnimationData(LLCharacter::ANIM_CHANNEL_WALK_SPEED, &speed); - ensure("a set channel reads back what was set", mCharacter.getAnimationData(LLCharacter::ANIM_CHANNEL_WALK_SPEED) == &speed); - ensure("the other channels are untouched", - mCharacter.getAnimationData(LLCharacter::ANIM_CHANNEL_POINT_AT_POINT) == nullptr - && mCharacter.getAnimationData(LLCharacter::ANIM_CHANNEL_LOOK_AT_POINT) == nullptr); + UNSCOPED_INFO("a set channel reads back what was set"); + CHECK(mCharacter.getAnimationData(LLCharacter::ANIM_CHANNEL_WALK_SPEED) == &speed); + { + INFO("the other channels are untouched"); + REQUIRE(mCharacter.getAnimationData(LLCharacter::ANIM_CHANNEL_POINT_AT_POINT) == nullptr); + REQUIRE(mCharacter.getAnimationData(LLCharacter::ANIM_CHANNEL_LOOK_AT_POINT) == nullptr); + } mCharacter.removeAnimationData(LLCharacter::ANIM_CHANNEL_WALK_SPEED); - ensure("a removed channel reads empty", mCharacter.getAnimationData(LLCharacter::ANIM_CHANNEL_WALK_SPEED) == nullptr); + UNSCOPED_INFO("a removed channel reads empty"); + REQUIRE(mCharacter.getAnimationData(LLCharacter::ANIM_CHANNEL_WALK_SPEED) == nullptr); // The hand pose is not a slot but a copy, so the request stands after // whatever it was read from has changed or gone, and it is dropped // with the motions on a flush. - ensure("no hand pose is asked for to begin with", !mCharacter.hasHandPoseRequest()); + UNSCOPED_INFO("no hand pose is asked for to begin with"); + CHECK_FALSE(mCharacter.hasHandPoseRequest()); { S32 pose = 1; S32 priority = 2; @@ -450,19 +528,23 @@ namespace pose = 5; priority = 4; } - ensure("a request stands", mCharacter.hasHandPoseRequest()); - ensure_equals("with the pose asked for", mCharacter.getHandPoseRequest(), 1); - ensure_equals("at the priority asked at", mCharacter.getHandPoseRequestPriority(), 2); + UNSCOPED_INFO("a request stands"); + CHECK(mCharacter.hasHandPoseRequest()); + UNSCOPED_INFO("with the pose asked for"); + CHECK(mCharacter.getHandPoseRequest() == 1); + UNSCOPED_INFO("at the priority asked at"); + CHECK(mCharacter.getHandPoseRequestPriority() == 2); mCharacter.clearHandPoseRequest(); - ensure("a cleared request is gone", !mCharacter.hasHandPoseRequest()); + UNSCOPED_INFO("a cleared request is gone"); + CHECK_FALSE(mCharacter.hasHandPoseRequest()); mCharacter.requestHandPose(1, 2); mCharacter.flushAllMotions(); - ensure("a flush drops the hand pose", !mCharacter.hasHandPoseRequest()); + UNSCOPED_INFO("a flush drops the hand pose"); + CHECK_FALSE(mCharacter.hasHandPoseRequest()); } - template<> template<> - void llcharacter_object::test<13>() + TEST_CASE_METHOD(llcharacter_data, "LLCharacter: a motion stopped while easing in never rises in weight", "[LLCharacter]") { // A motion stopped before it finished easing in gets one more update, // to hand it the stop time it never saw, and then deactivates. It @@ -478,28 +560,30 @@ namespace runFrame(); runFrame(); const F32 easing_in = motion->getPose()->getWeight(); - ensure("the motion is still easing in", easing_in < 0.5f); + UNSCOPED_INFO("the motion is still easing in"); + CHECK(easing_in < 0.5f); mCharacter.stopMotion(id); runFrame(); - ensure("the last update does not raise the weight", - motion->getPose()->getWeight() <= easing_in); - ensure("the motion is still around for its last update", - activeMotions(LLMotion::NORMAL_BLEND).size() == 1u); + UNSCOPED_INFO("the last update does not raise the weight"); + CHECK(motion->getPose()->getWeight() <= easing_in); + UNSCOPED_INFO("the motion is still around for its last update"); + REQUIRE(activeMotions(LLMotion::NORMAL_BLEND).size() == 1u); runFrame(); - ensure("and is gone the frame after", activeMotions(LLMotion::NORMAL_BLEND).empty()); + UNSCOPED_INFO("and is gone the frame after"); + REQUIRE(activeMotions(LLMotion::NORMAL_BLEND).empty()); } - template<> template<> - void llcharacter_object::test<14>() + TEST_CASE_METHOD(llcharacter_data, "LLCharacter: a motion still easing in does not mask the one under it", "[LLCharacter]") { // The mask that lets a motion's joints go unread is built from motions // at full weight and no others. One still easing in has claimed // nothing, so what is under it is still blended and still shows. LLJoint* joint = mCharacter.getJoint("mPelvis"); - ensure("the joint resolves", joint != nullptr); + UNSCOPED_INFO("the joint resolves"); + REQUIRE(joint != nullptr); const LLQuaternion under_rot(0.9f, LLVector3::x_axis); const LLQuaternion over_rot(-1.4f, LLVector3::y_axis); @@ -518,22 +602,22 @@ namespace runFrame(); runFrame(); - ensure("the one easing in has not reached full weight", - over->getPose()->getWeight() < 0.5f); - ensure("so the one underneath is what the joint shows", - fabsf(dot(joint->getRotation(), under_rot)) > 0.99f); + UNSCOPED_INFO("the one easing in has not reached full weight"); + CHECK(over->getPose()->getWeight() < 0.5f); + UNSCOPED_INFO("so the one underneath is what the joint shows"); + CHECK(fabsf(dot(joint->getRotation(), under_rot)) > 0.99f); // and once it is all the way in, it owns the joint over->mEaseInDuration = 0.f; runFrame(); - ensure_equals("it is at full weight now", over->getPose()->getWeight(), 1.f); - ensure("and the joint is what it asked for", - fabsf(dot(joint->getRotation(), over_rot)) > 0.99f); + UNSCOPED_INFO("it is at full weight now"); + CHECK(over->getPose()->getWeight() == 1.f); + UNSCOPED_INFO("and the joint is what it asked for"); + CHECK(fabsf(dot(joint->getRotation(), over_rot)) > 0.99f); } - template<> template<> - void llcharacter_object::test<15>() + TEST_CASE_METHOD(llcharacter_data, "LLCharacter: updateVisualParams applies only parameters whose weight moved", "[LLCharacter]") { // The sweep applies a parameter whose weight has moved and leaves the // rest alone, and says whether it did anything. @@ -542,29 +626,38 @@ namespace // A parameter arrives at its default weight, which is the weight it // was last applied at, so there is nothing to do for either of them. - ensure("a sweep with nothing to do says so", !mCharacter.updateVisualParams()); - ensure_equals("and applies nothing", first->mApplied, 0); - ensure_equals("to either of them", second->mApplied, 0); + UNSCOPED_INFO("a sweep with nothing to do says so"); + CHECK_FALSE(mCharacter.updateVisualParams()); + UNSCOPED_INFO("and applies nothing"); + CHECK(first->mApplied == 0); + UNSCOPED_INFO("to either of them"); + CHECK(second->mApplied == 0); first->setWeight(0.5f); - ensure("a sweep after a weight moves says so", mCharacter.updateVisualParams()); - ensure_equals("the parameter that moved is applied", first->mApplied, 1); - ensure_equals("and the one that did not is not", second->mApplied, 0); - - ensure("and once it has settled again there is nothing to do", - !mCharacter.updateVisualParams()); - ensure_equals("with nothing applied a second time", first->mApplied, 1); + UNSCOPED_INFO("a sweep after a weight moves says so"); + CHECK(mCharacter.updateVisualParams()); + UNSCOPED_INFO("the parameter that moved is applied"); + CHECK(first->mApplied == 1); + UNSCOPED_INFO("and the one that did not is not"); + CHECK(second->mApplied == 0); + + UNSCOPED_INFO("and once it has settled again there is nothing to do"); + CHECK_FALSE(mCharacter.updateVisualParams()); + UNSCOPED_INFO("with nothing applied a second time"); + CHECK(first->mApplied == 1); // Both of them at once. first->setWeight(0.25f); second->setWeight(0.75f); - ensure("a sweep with two to do says so", mCharacter.updateVisualParams()); - ensure_equals("and applies the first", first->mApplied, 2); - ensure_equals("and the second", second->mApplied, 1); + UNSCOPED_INFO("a sweep with two to do says so"); + CHECK(mCharacter.updateVisualParams()); + UNSCOPED_INFO("and applies the first"); + CHECK(first->mApplied == 2); + UNSCOPED_INFO("and the second"); + CHECK(second->mApplied == 1); } - template<> template<> - void llcharacter_object::test<16>() + TEST_CASE_METHOD(llcharacter_data, "LLCharacter: updateVisualParams skips animating and other-sex parameters", "[LLCharacter]") { // A parameter that is animating is left to the animation, and one for // the other sex is applied at its default rather than its weight. @@ -577,23 +670,26 @@ namespace animating->setAnimating(true); animating->setWeight(0.5f); - ensure("an animating parameter is not swept", !mCharacter.updateVisualParams()); - ensure_equals("and not applied", animating->mApplied, settled); + UNSCOPED_INFO("an animating parameter is not swept"); + CHECK_FALSE(mCharacter.updateVisualParams()); + UNSCOPED_INFO("and not applied"); + CHECK(animating->mApplied == settled); animating->setAnimating(false); - ensure("once it stops animating the sweep takes it", mCharacter.updateVisualParams()); + UNSCOPED_INFO("once it stops animating the sweep takes it"); + CHECK(mCharacter.updateVisualParams()); // The female parameter settled at its default under a male sex, so // moving its weight changes nothing the sweep can see. const S32 female_settled = female->mApplied; female->setWeight(0.75f); - ensure("a parameter for the other sex has nothing to apply", - !mCharacter.updateVisualParams()); - ensure_equals("and is not applied", female->mApplied, female_settled); + UNSCOPED_INFO("a parameter for the other sex has nothing to apply"); + CHECK_FALSE(mCharacter.updateVisualParams()); + UNSCOPED_INFO("and is not applied"); + CHECK(female->mApplied == female_settled); } - template<> template<> - void llcharacter_object::test<17>() + TEST_CASE_METHOD(llcharacter_data, "LLCharacter: visual parameters are found by name in any case and walked by id", "[LLCharacter]") { // Parameters are found by name whatever case the name is asked in, // and the sweep walks them in the order of their ids however they @@ -602,7 +698,8 @@ namespace addParam(10, "First"); addParam(20, "Second"); - ensure_equals("every parameter is counted", mCharacter.getVisualParamCount(), 3); + UNSCOPED_INFO("every parameter is counted"); + CHECK(mCharacter.getVisualParamCount() == 3); std::vector ids; for (LLVisualParam* param = mCharacter.getFirstVisualParam(); @@ -611,65 +708,70 @@ namespace { ids.push_back(param->getID()); } - ensure_equals("all three are walked", ids.size(), 3u); - ensure_equals("in id order, not the order they arrived", ids[0], 10); - ensure_equals("second", ids[1], 20); - ensure_equals("third", ids[2], 30); + UNSCOPED_INFO("all three are walked"); + REQUIRE(ids.size() == 3u); + UNSCOPED_INFO("in id order, not the order they arrived"); + CHECK(ids[0] == 10); + UNSCOPED_INFO("second"); + CHECK(ids[1] == 20); + UNSCOPED_INFO("third"); + CHECK(ids[2] == 30); LLVisualParam* by_name = mCharacter.getVisualParam("Second"); - ensure("a parameter is found by its name", by_name != nullptr); - ensure_equals("and it is the right one", by_name->getID(), 20); - - ensure_equals("the name is matched without regard to case", - mCharacter.getVisualParam("sEcOnD"), by_name); - ensure_equals("in either direction", - mCharacter.getVisualParam("SECOND"), by_name); - ensure("a name nobody has is not found", - mCharacter.getVisualParam("NotAParameter") == nullptr); + UNSCOPED_INFO("a parameter is found by its name"); + REQUIRE(by_name != nullptr); + UNSCOPED_INFO("and it is the right one"); + CHECK(by_name->getID() == 20); + + UNSCOPED_INFO("the name is matched without regard to case"); + REQUIRE(mCharacter.getVisualParam("sEcOnD") == by_name); + UNSCOPED_INFO("in either direction"); + REQUIRE(mCharacter.getVisualParam("SECOND") == by_name); + UNSCOPED_INFO("a name nobody has is not found"); + REQUIRE(mCharacter.getVisualParam("NotAParameter") == nullptr); // And by id, which is the other way in. - ensure_equals("a parameter is found by its id", - mCharacter.getVisualParam(20), by_name); - - ensure("setting a weight by name finds it", - mCharacter.setVisualParamWeight("SeCoNd", 0.25f)); - ensure_approximately_equals("and sets it", - mCharacter.getVisualParamWeight("second"), 0.25f, 16); - ensure("setting one nobody has does not", - !mCharacter.setVisualParamWeight("NotAParameter", 0.25f)); + UNSCOPED_INFO("a parameter is found by its id"); + REQUIRE(mCharacter.getVisualParam(20) == by_name); + + UNSCOPED_INFO("setting a weight by name finds it"); + CHECK(mCharacter.setVisualParamWeight("SeCoNd", 0.25f)); + UNSCOPED_INFO("and sets it"); + CHECK_THAT(mCharacter.getVisualParamWeight("second"), ll_test::WithinBits(0.25f, 16)); + UNSCOPED_INFO("setting one nobody has does not"); + CHECK_FALSE(mCharacter.setVisualParamWeight("NotAParameter", 0.25f)); } - template<> template<> - void llcharacter_object::test<18>() + TEST_CASE_METHOD(llcharacter_data, "LLCharacter: a parameter added under a taken id replaces the old one", "[LLCharacter]") { // A parameter added under an id that is already taken replaces the one // there, in the list the sweep walks as well as in the map. ALTestVisualParam* first = addParam(7, "First"); ALTestVisualParam* second = addParam(7, "Second"); - ensure_equals("the second one took the first one's place", - mCharacter.getVisualParamCount(), 1); - ensure_equals("and it is the one that is found", - mCharacter.getVisualParam(7), (LLVisualParam*)second); + UNSCOPED_INFO("the second one took the first one's place"); + CHECK(mCharacter.getVisualParamCount() == 1); + UNSCOPED_INFO("and it is the one that is found"); + REQUIRE(mCharacter.getVisualParam(7) == (LLVisualParam*)second); S32 walked = 0; for (LLVisualParam* param = mCharacter.getFirstVisualParam(); param; param = mCharacter.getNextVisualParam()) { - ensure_equals("the list holds the one that is found", - param, (LLVisualParam*)second); + UNSCOPED_INFO("the list holds the one that is found"); + REQUIRE(param == (LLVisualParam*)second); walked++; } - ensure_equals("once", walked, 1); + UNSCOPED_INFO("once"); + CHECK(walked == 1); // The one it replaced is nobody's now, so the character will not free // it and this test has to. delete first; } - template<> template<> - void llcharacter_object::test<19>() + TEST_CASE_METHOD(llcharacter_data, "LLCharacter: setVisualParamWeight writes this character's own copy by id", "[LLCharacter]") { // Setting a weight through a parameter looks the parameter up by its // id and writes whatever this character has under that id, which is @@ -681,29 +783,30 @@ namespace ALTestVisualParamInfo foreign_info(5, "Mine"); ALTestVisualParam foreign; - ensure("the foreign parameter takes its info", foreign.setInfo(&foreign_info)); + UNSCOPED_INFO("the foreign parameter takes its info"); + REQUIRE(foreign.setInfo(&foreign_info)); - ensure("a foreign parameter with a known id is taken", - mCharacter.setVisualParamWeight(&foreign, 0.5f)); - ensure_approximately_equals("and this character's own is what moved", - mine->getWeight(), 0.5f, 16); - ensure_approximately_equals("not the one that was handed over", - foreign.getWeight(), 0.f, 16); + UNSCOPED_INFO("a foreign parameter with a known id is taken"); + CHECK(mCharacter.setVisualParamWeight(&foreign, 0.5f)); + UNSCOPED_INFO("and this character's own is what moved"); + CHECK_THAT(mine->getWeight(), ll_test::WithinBits(0.5f, 16)); + UNSCOPED_INFO("not the one that was handed over"); + CHECK_THAT(foreign.getWeight(), ll_test::WithinBits(0.f, 16)); // Handed this character's own, the lookup finds that same one, which // is what lets a caller who already has it write to it directly. - ensure_equals("the id finds the one that was added", - mCharacter.getVisualParam(5), (LLVisualParam*)mine); + UNSCOPED_INFO("the id finds the one that was added"); + REQUIRE(mCharacter.getVisualParam(5) == (LLVisualParam*)mine); ALTestVisualParamInfo unknown_info(6, "Unknown"); ALTestVisualParam unknown; - ensure("the unknown parameter takes its info", unknown.setInfo(&unknown_info)); - ensure("a parameter with an id this character has not got is refused", - !mCharacter.setVisualParamWeight(&unknown, 0.5f)); + UNSCOPED_INFO("the unknown parameter takes its info"); + REQUIRE(unknown.setInfo(&unknown_info)); + UNSCOPED_INFO("a parameter with an id this character has not got is refused"); + CHECK_FALSE(mCharacter.setVisualParamWeight(&unknown, 0.5f)); } - template<> template<> - void llcharacter_object::test<20>() + TEST_CASE_METHOD(llcharacter_data, "LLCharacter: speedMultiplier pins at the maximum instead of changing sign", "[LLCharacter]") { // The walk servo asks how much faster the animation has to play for // the planted foot to keep up with the ground, and the answer changes @@ -719,35 +822,35 @@ namespace // A planted foot is going nowhere, so the foot speed is the avatar's // and the animation plays as authored. - ensure_approximately_equals("a planted foot plays the animation as it is", - multiplier(1.5f, 1.5f), 1.f, 16); + UNSCOPED_INFO("a planted foot plays the animation as it is"); + CHECK_THAT(multiplier(1.5f, 1.5f), ll_test::WithinBits(1.f, 16)); // A foot that is not keeping up asks for more. - ensure_approximately_equals("a foot falling behind asks for more", - multiplier(1.5f, 1.0f), 1.5f, 16); - ensure_approximately_equals("but no more than it is allowed", - multiplier(1.5f, 0.1f), max_multiplier, 16); + UNSCOPED_INFO("a foot falling behind asks for more"); + CHECK_THAT(multiplier(1.5f, 1.0f), ll_test::WithinBits(1.5f, 16)); + UNSCOPED_INFO("but no more than it is allowed"); + CHECK_THAT(multiplier(1.5f, 0.1f), ll_test::WithinBits(max_multiplier, 16)); // A foot going forward exactly as fast as the avatar cannot say how // much faster to play, and the answer is as fast as allowed. - ensure_approximately_equals("a foot going with the avatar asks for all of it", - multiplier(1.5f, 0.f), max_multiplier, 16); + UNSCOPED_INFO("a foot going with the avatar asks for all of it"); + CHECK_THAT(multiplier(1.5f, 0.f), ll_test::WithinBits(max_multiplier, 16)); // And one going forward faster than the avatar is the case that used // to come out as the slowest multiplier there is: the animation slowed, // which made the foot slip further, which kept the answer negative. - ensure_approximately_equals("and one going faster than the avatar does too", - multiplier(1.5f, -0.2f), max_multiplier, 16); - ensure_approximately_equals("however much faster", - multiplier(1.5f, -50.f), max_multiplier, 16); + UNSCOPED_INFO("and one going faster than the avatar does too"); + CHECK_THAT(multiplier(1.5f, -0.2f), ll_test::WithinBits(max_multiplier, 16)); + UNSCOPED_INFO("however much faster"); + CHECK_THAT(multiplier(1.5f, -50.f), ll_test::WithinBits(max_multiplier, 16)); - ensure("a foot going forward faster is never read as standing still", - multiplier(1.5f, -0.2f) > min_multiplier); + UNSCOPED_INFO("a foot going forward faster is never read as standing still"); + CHECK(multiplier(1.5f, -0.2f) > min_multiplier); // Running asks for far more than it is allowed, so it sits at the top // of the range and never goes near the sign change at all. That is why // it was the walk that stuck. - ensure_approximately_equals("running is pinned at the ceiling", - multiplier(5.f, 0.5f), max_multiplier, 16); + UNSCOPED_INFO("running is pinned at the ceiling"); + CHECK_THAT(multiplier(5.f, 0.5f), ll_test::WithinBits(max_multiplier, 16)); } } diff --git a/indra/llcharacter/tests/lljoint_test.cpp b/indra/llcharacter/tests/lljoint_test.cpp index 3c92936e6e..4933afeee2 100644 --- a/indra/llcharacter/tests/lljoint_test.cpp +++ b/indra/llcharacter/tests/lljoint_test.cpp @@ -32,148 +32,141 @@ #include "../lljoint.h" -#include "../test/lltut.h" +#include +#include "altestmatchers.h" -namespace tut -{ namespace { - struct lljoint_data - { - }; - typedef test_group lljoint_test; - typedef lljoint_test::object lljoint_object; - tut::lljoint_test lljoint_testcase("LLJoint"); -} - - template<> template<> - void lljoint_object::test<1>() + TEST_CASE("LLJoint: a lone joint has no parent and is its own root", "[LLJoint]") { LLJoint lljoint; LLJoint* jnt = lljoint.getParent(); - ensure("getParent() failed ", (NULL == jnt)); - ensure("getRoot() failed ", (&lljoint == lljoint.getRoot())); + UNSCOPED_INFO("getParent() failed "); + REQUIRE((NULL == jnt)); + UNSCOPED_INFO("getRoot() failed "); + CHECK(&lljoint == lljoint.getRoot()); } - template<> template<> - void lljoint_object::test<2>() + TEST_CASE("LLJoint: setup attaches a joint to its parent", "[LLJoint]") { std::string str = "LLJoint"; LLJoint parent(str), child; child.setup(str, &parent); LLJoint* jnt = child.getParent(); - ensure("setup() failed ", (&parent == jnt)); + UNSCOPED_INFO("setup() failed "); + CHECK(&parent == jnt); } - template<> template<> - void lljoint_object::test<3>() + TEST_CASE("LLJoint: findJoint finds a child by name", "[LLJoint]") { LLJoint parent, child; std::string str = "LLJoint"; child.setup(str, &parent); LLJoint* jnt = parent.findJoint(str); - ensure("findJoint() failed ", (&child == jnt)); + UNSCOPED_INFO("findJoint() failed "); + CHECK(&child == jnt); } - template<> template<> - void lljoint_object::test<4>() + TEST_CASE("LLJoint: setName and getName round-trip", "[LLJoint]") { LLJoint parent; std::string str1 = "LLJoint", str2; parent.setName(str1); str2 = parent.getName(); - ensure("setName() failed ", (str1 == str2)); + UNSCOPED_INFO("setName() failed "); + CHECK(str1 == str2); } - template<> template<> - void lljoint_object::test<5>() + TEST_CASE("LLJoint: setPosition and getPosition round-trip", "[LLJoint]") { LLJoint lljoint; LLVector3 vec3(2.3f,30.f,10.f); // SL-315 lljoint.setPosition(vec3); LLVector3 pos = lljoint.getPosition(); - ensure("setPosition()/getPosition() failed ", (vec3 == pos)); + UNSCOPED_INFO("setPosition()/getPosition() failed "); + CHECK(vec3 == pos); } - template<> template<> - void lljoint_object::test<6>() + TEST_CASE("LLJoint: setWorldPosition sets the world and last world positions", "[LLJoint]") { LLJoint lljoint; LLVector3 vec3(2.3f,30.f,10.f); // SL-315 lljoint.setWorldPosition(vec3); LLVector3 pos = lljoint.getWorldPosition(); - ensure("1:setWorldPosition()/getWorldPosition() failed ", (vec3 == pos)); + UNSCOPED_INFO("1:setWorldPosition()/getWorldPosition() failed "); + CHECK(vec3 == pos); LLVector3 lastPos = lljoint.getLastWorldPosition(); - ensure("2:getLastWorldPosition failed ", (vec3 == lastPos)); + UNSCOPED_INFO("2:getLastWorldPosition failed "); + CHECK(vec3 == lastPos); } - template<> template<> - void lljoint_object::test<7>() + TEST_CASE("LLJoint: setRotation and getRotation round-trip", "[LLJoint]") { LLJoint lljoint("LLJoint"); LLQuaternion q(2.3f,30.f,10.f,1.f); lljoint.setRotation(q); LLQuaternion rot = lljoint.getRotation(); - ensure("setRotation()/getRotation() failed ", (q == rot)); + UNSCOPED_INFO("setRotation()/getRotation() failed "); + CHECK(q == rot); } - template<> template<> - void lljoint_object::test<8>() + TEST_CASE("LLJoint: setWorldRotation sets the world and last world rotations", "[LLJoint]") { LLJoint lljoint("LLJoint"); LLQuaternion q(2.3f,30.f,10.f,1.f); lljoint.setWorldRotation(q); LLQuaternion rot = lljoint.getWorldRotation(); - ensure("1:setWorldRotation()/getWorldRotation() failed ", (q == rot)); + UNSCOPED_INFO("1:setWorldRotation()/getWorldRotation() failed "); + CHECK(q == rot); LLQuaternion lastRot = lljoint.getLastWorldRotation(); - ensure("2:getLastWorldRotation failed ", (q == lastRot)); + UNSCOPED_INFO("2:getLastWorldRotation failed "); + CHECK(q == lastRot); } - template<> template<> - void lljoint_object::test<9>() + TEST_CASE("LLJoint: setScale and getScale round-trip", "[LLJoint]") { LLJoint lljoint; LLVector3 vec3(2.3f,30.f,10.f); lljoint.setScale(vec3); LLVector3 scale = lljoint.getScale(); - ensure("setScale()/getScale failed ", (vec3 == scale)); + UNSCOPED_INFO("setScale()/getScale failed "); + CHECK(vec3 == scale); } - template<> template<> - void lljoint_object::test<10>() + TEST_CASE("LLJoint: setWorldMatrix and getWorldMatrix round-trip the identity", "[LLJoint]") { LLJoint lljoint("LLJoint"); LLMatrix4 mat; mat.setIdentity(); lljoint.setWorldMatrix(mat);//giving warning setWorldMatrix not correctly implemented; LLMatrix4 mat4 = lljoint.getWorldMatrix().toMatrix4(); - ensure("setWorldMatrix()/getWorldMatrix failed ", (mat4 == mat)); + UNSCOPED_INFO("setWorldMatrix()/getWorldMatrix failed "); + CHECK(mat4 == mat); } - template<> template<> - void lljoint_object::test<11>() + TEST_CASE("LLJoint: the constructor stores the joint number", "[LLJoint]") { S32 joint_num = 12; LLJoint lljoint(joint_num); lljoint.setName("parent"); S32 jointNum = lljoint.getJointNum(); - ensure("getJointNum failed ", (jointNum == joint_num)); + UNSCOPED_INFO("getJointNum failed "); + CHECK(jointNum == joint_num); } - template<> template<> - void lljoint_object::test<12>() + TEST_CASE("LLJoint: setSkinOffset and getSkinOffset round-trip", "[LLJoint]") { LLJoint lljoint; LLVector3 vec3(2.3f,30.f,10.f); lljoint.setSkinOffset(vec3); LLVector3 offset = lljoint.getSkinOffset(); - ensure("1:setSkinOffset()/getSkinOffset() failed ", (vec3 == offset)); + UNSCOPED_INFO("1:setSkinOffset()/getSkinOffset() failed "); + CHECK(vec3 == offset); } - template<> template<> - void lljoint_object::test<13>() + TEST_CASE("LLJoint: addChild and removeChild keep the tree's links and lookups", "[LLJoint]") { LLJoint lljointgp("gparent"); LLJoint lljoint("parent"); @@ -186,28 +179,35 @@ namespace LLJoint* jnt = NULL; jnt = lljoint2.getParent(); - ensure("addChild() failed ", (&lljoint == jnt)); + UNSCOPED_INFO("addChild() failed "); + CHECK(&lljoint == jnt); LLJoint* jnt1 = lljoint.findJoint("child3"); - ensure("findJoint() failed ", (&lljoint3 == jnt1)); + UNSCOPED_INFO("findJoint() failed "); + CHECK(&lljoint3 == jnt1); lljoint.removeChild(&lljoint3); LLJoint* jnt2 = lljoint.findJoint("child3"); - ensure("removeChild() failed ", (NULL == jnt2)); + UNSCOPED_INFO("removeChild() failed "); + REQUIRE((NULL == jnt2)); lljointgp.addChild(&lljoint); - ensure("GetParent() failed ", (&lljoint== lljoint2.getParent())); - ensure("getRoot() failed ", (&lljointgp == lljoint2.getRoot())); + UNSCOPED_INFO("GetParent() failed "); + CHECK(&lljoint== lljoint2.getParent()); + UNSCOPED_INFO("getRoot() failed "); + CHECK(&lljointgp == lljoint2.getRoot()); - ensure("getRoot() failed ", &lljoint1 == lljoint.findJoint("child1")); + UNSCOPED_INFO("getRoot() failed "); + CHECK(&lljoint1 == lljoint.findJoint("child1")); lljointgp.removeAllChildren(); // parent removed from grandparent - so should not be able to locate child - ensure("removeAllChildren() failed ", (NULL == lljointgp.findJoint("child1"))); + UNSCOPED_INFO("removeAllChildren() failed "); + REQUIRE((NULL == lljointgp.findJoint("child1"))); // it should still exist in parent though - ensure("removeAllChildren() failed ", (&lljoint1 == lljoint.findJoint("child1"))); + UNSCOPED_INFO("removeAllChildren() failed "); + CHECK(&lljoint1 == lljoint.findJoint("child1")); } - template<> template<> - void lljoint_object::test<14>() + TEST_CASE("LLJoint: addChild takes a child away from its previous parent", "[LLJoint]") { LLJoint lljointgp("gparent"); @@ -220,12 +220,13 @@ namespace llparent1.addChild(&lladoptedchild); llparent2.addChild(&lladoptedchild); - ensure("1. addChild failed to remove prior parent", lladoptedchild.getParent() == &llparent2); - ensure("2. addChild failed to remove prior parent", llparent1.findJoint("child2") == NULL); + UNSCOPED_INFO("1. addChild failed to remove prior parent"); + CHECK(lladoptedchild.getParent() == &llparent2); + UNSCOPED_INFO("2. addChild failed to remove prior parent"); + REQUIRE(llparent1.findJoint("child2") == NULL); } - template<> template<> - void lljoint_object::test<15>() + TEST_CASE("LLJoint: updateWorldMatrixChildren recomputes only dirty updating joints", "[LLJoint]") { // updateWorldMatrixChildren() reports how many world matrices it // recomputed. A joint is recomputed only while MATRIX_DIRTY is set, @@ -237,33 +238,40 @@ namespace c.setup("c", &a); d.setup("d", &root); - ensure_equals("fresh tree recomputes every joint", root.updateWorldMatrixChildren(), 5); - ensure_equals("clean tree recomputes nothing", root.updateWorldMatrixChildren(), 0); + UNSCOPED_INFO("fresh tree recomputes every joint"); + CHECK(root.updateWorldMatrixChildren() == 5); + UNSCOPED_INFO("clean tree recomputes nothing"); + CHECK(root.updateWorldMatrixChildren() == 0); root.setRotation(LLQuaternion(0.5f, LLVector3::x_axis)); - ensure_equals("root rotation dirties the whole tree", root.updateWorldMatrixChildren(), 5); + UNSCOPED_INFO("root rotation dirties the whole tree"); + CHECK(root.updateWorldMatrixChildren() == 5); a.setRotation(LLQuaternion(0.25f, LLVector3::y_axis)); - ensure_equals("mid-level rotation dirties its subtree", root.updateWorldMatrixChildren(), 3); + UNSCOPED_INFO("mid-level rotation dirties its subtree"); + CHECK(root.updateWorldMatrixChildren() == 3); root.setPosition(root.getPosition()); - ensure_equals("unchanged position dirties nothing", root.updateWorldMatrixChildren(), 0); + UNSCOPED_INFO("unchanged position dirties nothing"); + CHECK(root.updateWorldMatrixChildren() == 0); root.setPosition(LLVector3(1.f, 2.f, 3.f)); - ensure_equals("root position dirties the whole tree", root.updateWorldMatrixChildren(), 5); + UNSCOPED_INFO("root position dirties the whole tree"); + CHECK(root.updateWorldMatrixChildren() == 5); a.setUpdateXform(false); root.setRotation(LLQuaternion(0.75f, LLVector3::z_axis)); - ensure_equals("subtree without mUpdateXform is skipped", root.updateWorldMatrixChildren(), 2); + UNSCOPED_INFO("subtree without mUpdateXform is skipped"); + CHECK(root.updateWorldMatrixChildren() == 2); // Coming back into the sweep rebuilds what was missed while out of it. a.setUpdateXform(true); - ensure_equals("rejoining the sweep recomputes the subtree", root.updateWorldMatrixChildren(), 3); + UNSCOPED_INFO("rejoining the sweep recomputes the subtree"); + CHECK(root.updateWorldMatrixChildren() == 3); } - template<> template<> - void lljoint_object::test<16>() + TEST_CASE("LLJoint: rewriting the same rotation does not dirty the joint", "[LLJoint]") { // Writing an unchanged rotation must not dirty the joint, since // touch() would carry that down the entire subtree. @@ -272,22 +280,25 @@ namespace child.setup("child", &root); root.updateWorldMatrixChildren(); - ensure_equals("tree starts clean", root.updateWorldMatrixChildren(), 0); + UNSCOPED_INFO("tree starts clean"); + CHECK(root.updateWorldMatrixChildren() == 0); const LLQuaternion rot(0.4f, LLVector3::y_axis); root.setRotation(rot); - ensure_equals("a new rotation dirties the subtree", root.updateWorldMatrixChildren(), 2); + UNSCOPED_INFO("a new rotation dirties the subtree"); + CHECK(root.updateWorldMatrixChildren() == 2); root.setRotation(rot); - ensure_equals("rewriting the same rotation dirties nothing", root.updateWorldMatrixChildren(), 0); + UNSCOPED_INFO("rewriting the same rotation dirties nothing"); + CHECK(root.updateWorldMatrixChildren() == 0); root.setRotation(LLQuaternion(0.9f, LLVector3::z_axis)); - ensure_equals("a different rotation dirties the subtree again", root.updateWorldMatrixChildren(), 2); + UNSCOPED_INFO("a different rotation dirties the subtree again"); + CHECK(root.updateWorldMatrixChildren() == 2); } - template<> template<> - void lljoint_object::test<17>() + TEST_CASE("LLJoint: touchIfXformParentMoved dirties the tree when the seat moves", "[LLJoint]") { // A sitting avatar hangs its root joint off the seat's transform, // which is no joint, and then writes seat relative values into the @@ -304,27 +315,32 @@ namespace root.getXform()->setParent(&seat); root.updateWorldMatrixChildren(); - ensure_equals("tree starts clean", root.updateWorldMatrixChildren(), 0); + UNSCOPED_INFO("tree starts clean"); + CHECK(root.updateWorldMatrixChildren() == 0); seat.setPosition(LLVector3(5.f, 0.f, 0.f)); seat.updateMatrix(); - ensure_equals("a moved seat dirties nothing on its own", root.updateWorldMatrixChildren(), 0); + UNSCOPED_INFO("a moved seat dirties nothing on its own"); + CHECK(root.updateWorldMatrixChildren() == 0); root.touchIfXformParentMoved(); - ensure_equals("the seat's move dirties the whole tree", root.updateWorldMatrixChildren(), 2); - ensure("the root followed the seat", root.getWorldPosition() == LLVector3(5.f, 0.f, 0.f)); + UNSCOPED_INFO("the seat's move dirties the whole tree"); + CHECK(root.updateWorldMatrixChildren() == 2); + UNSCOPED_INFO("the root followed the seat"); + CHECK(root.getWorldPosition() == LLVector3(5.f, 0.f, 0.f)); root.touchIfXformParentMoved(); - ensure_equals("a still seat dirties nothing", root.updateWorldMatrixChildren(), 0); + UNSCOPED_INFO("a still seat dirties nothing"); + CHECK(root.updateWorldMatrixChildren() == 0); seat.setRotation(LLQuaternion(0.5f, LLVector3::z_axis)); seat.updateMatrix(); root.touchIfXformParentMoved(); - ensure_equals("a rotated seat dirties the whole tree", root.updateWorldMatrixChildren(), 2); + UNSCOPED_INFO("a rotated seat dirties the whole tree"); + CHECK(root.updateWorldMatrixChildren() == 2); } - template<> template<> - void lljoint_object::test<18>() + TEST_CASE("LLJoint: a root slerping onto a still target stops dirtying the tree", "[LLJoint]") { // The avatar root is written every frame from a slerp toward a target // that has stopped moving, with an interpolant taken from the frame @@ -353,20 +369,20 @@ namespace const F32 u = (0.010f + 0.006f * (frame % 5)) / 0.4f; root.setWorldRotationIfMoved(slerp(u, root.getWorldRotation(), target)); } - ensure_equals("a root that has caught up with its target stops dirtying the tree", - root.updateWorldMatrixChildren(), 0); + UNSCOPED_INFO("a root that has caught up with its target stops dirtying the tree"); + CHECK(root.updateWorldMatrixChildren() == 0); // What it stopped short by is the whole cost of the tolerance. - ensure("the root stopped within a hundredth of a degree of its target", - root.getWorldRotation().isEqualEps(target, 1.e-4f)); + UNSCOPED_INFO("the root stopped within a hundredth of a degree of its target"); + CHECK(root.getWorldRotation().isEqualEps(target, 1.e-4f)); // A real turn still has to reach the skeleton. root.setWorldRotationIfMoved(LLQuaternion(0.4f + 1.f * DEG_TO_RAD, LLVector3::y_axis)); - ensure_equals("a real turn dirties the whole tree", root.updateWorldMatrixChildren(), 2); + UNSCOPED_INFO("a real turn dirties the whole tree"); + CHECK(root.updateWorldMatrixChildren() == 2); } - template<> template<> - void lljoint_object::test<19>() + TEST_CASE("LLJoint: the sweep descends only into marked subtrees", "[LLJoint]") { // The sweep walks only where something told it to. A joint carrying // neither a dirty matrix of its own nor the mark left by a write below @@ -381,27 +397,29 @@ namespace e.setup("e", &d); root.updateWorldMatrixChildren(); - ensure_equals("tree starts clean", root.updateWorldMatrixChildren(), 0); + UNSCOPED_INFO("tree starts clean"); + CHECK(root.updateWorldMatrixChildren() == 0); c.setRotation(LLQuaternion(0.3f, LLVector3::x_axis)); - ensure("the write is announced all the way up", - (root.mDirtyFlags & LLJoint::SUBTREE_DIRTY) != 0); - ensure("the sibling branch is left alone", d.mDirtyFlags == 0); + UNSCOPED_INFO("the write is announced all the way up"); + CHECK((root.mDirtyFlags & LLJoint::SUBTREE_DIRTY) != 0); + UNSCOPED_INFO("the sibling branch is left alone"); + CHECK(d.mDirtyFlags == 0); // Dirty a joint behind the sweep's back, below a branch nothing // marked. The sweep must stop at that branch's root and never look at // it -- the old walk visited every joint and would have rebuilt it. e.mDirtyFlags |= LLJoint::MATRIX_DIRTY; - ensure_equals("only the marked path is walked", root.updateWorldMatrixChildren(), 1); - ensure("the unmarked branch was never descended into", - (e.mDirtyFlags & LLJoint::MATRIX_DIRTY) != 0); + UNSCOPED_INFO("only the marked path is walked"); + CHECK(root.updateWorldMatrixChildren() == 1); + UNSCOPED_INFO("the unmarked branch was never descended into"); + CHECK((e.mDirtyFlags & LLJoint::MATRIX_DIRTY) != 0); - ensure("the mark is cleared once the subtree is clean", - (root.mDirtyFlags & LLJoint::SUBTREE_DIRTY) == 0); + UNSCOPED_INFO("the mark is cleared once the subtree is clean"); + CHECK((root.mDirtyFlags & LLJoint::SUBTREE_DIRTY) == 0); } - template<> template<> - void lljoint_object::test<20>() + TEST_CASE("LLJoint: getWorldMatrix leaves the subtree mark for the sweep", "[LLJoint]") { // getWorldMatrix rebuilds this joint and the ones above it on the // spot, without ever walking down. It must not carry the subtree mark @@ -413,21 +431,23 @@ namespace b.setup("b", &a); root.updateWorldMatrixChildren(); - ensure_equals("tree starts clean", root.updateWorldMatrixChildren(), 0); + UNSCOPED_INFO("tree starts clean"); + CHECK(root.updateWorldMatrixChildren() == 0); root.setRotation(LLQuaternion(0.3f, LLVector3::z_axis)); b.setRotation(LLQuaternion(0.2f, LLVector3::x_axis)); - ensure("the root carries both its own dirt and the mark", - (root.mDirtyFlags & LLJoint::MATRIX_DIRTY) != 0 && - (root.mDirtyFlags & LLJoint::SUBTREE_DIRTY) != 0); + { + INFO("the root carries both its own dirt and the mark"); + CHECK((root.mDirtyFlags & LLJoint::MATRIX_DIRTY) != 0); + CHECK((root.mDirtyFlags & LLJoint::SUBTREE_DIRTY) != 0); + } root.getWorldMatrix(); - ensure_equals("the joints below still get their turn", - root.updateWorldMatrixChildren(), 2); + UNSCOPED_INFO("the joints below still get their turn"); + CHECK(root.updateWorldMatrixChildren() == 2); } - template<> template<> - void lljoint_object::test<21>() + TEST_CASE("LLJoint: world transforms are current without a sweep in between", "[LLJoint]") { // A culled avatar does not get swept at all, which is only safe // because skipping the sweep costs nothing but time: the dirty flags @@ -440,21 +460,20 @@ namespace root.updateWorldMatrixChildren(); root.setPosition(LLVector3(1.f, 0.f, 0.f)); - ensure("a world position is current with no sweep in between", - b.getWorldPosition() == LLVector3(1.f, 0.f, 0.f)); - ensure_equals("and the sweep still owes the matrices", - root.updateWorldMatrixChildren(), 3); + UNSCOPED_INFO("a world position is current with no sweep in between"); + CHECK(b.getWorldPosition() == LLVector3(1.f, 0.f, 0.f)); + UNSCOPED_INFO("and the sweep still owes the matrices"); + CHECK(root.updateWorldMatrixChildren() == 3); root.setPosition(LLVector3(2.f, 0.f, 0.f)); const LLVector3 world_translation(b.getWorldMatrix().getTranslation().getF32ptr()); - ensure("a world matrix is current with no sweep in between", - world_translation == LLVector3(2.f, 0.f, 0.f)); - ensure_equals("and asking for it was the sweep's work", - root.updateWorldMatrixChildren(), 0); + UNSCOPED_INFO("a world matrix is current with no sweep in between"); + CHECK(world_translation == LLVector3(2.f, 0.f, 0.f)); + UNSCOPED_INFO("and asking for it was the sweep's work"); + CHECK(root.updateWorldMatrixChildren() == 0); } - template<> template<> - void lljoint_object::test<22>() + TEST_CASE("LLJoint: a joint healed before the sweep still sends it to its children", "[LLJoint]") { // The joint that was written is healed before the sweep -- the avatar // asks its root for a world matrix right after moving it -- and the @@ -470,18 +489,18 @@ namespace root.touch(); root.setWorldPosition(LLVector3(1.f, 0.f, 0.f)); root.getWorldMatrix(); - ensure_equals("a healed root still sends the sweep down to its children", - root.updateWorldMatrixChildren(), 2); + UNSCOPED_INFO("a healed root still sends the sweep down to its children"); + CHECK(root.updateWorldMatrixChildren() == 2); a.setRotation(LLQuaternion(0.3f, LLVector3::x_axis)); a.getWorldMatrix(); - ensure_equals("and so does a healed joint in the middle of the tree", - root.updateWorldMatrixChildren(), 1); - ensure_equals("after which the tree is clean", root.updateWorldMatrixChildren(), 0); + UNSCOPED_INFO("and so does a healed joint in the middle of the tree"); + CHECK(root.updateWorldMatrixChildren() == 1); + UNSCOPED_INFO("after which the tree is clean"); + CHECK(root.updateWorldMatrixChildren() == 0); } - template<> template<> - void lljoint_object::test<23>() + TEST_CASE("LLJoint: an unchanged scale does not dirty the joint", "[LLJoint]") { // A scale written back unchanged is not a change. The pose blender // writes every channel of a joint each interpolated frame, so a @@ -491,18 +510,27 @@ namespace a.setup("a", &root); root.setScale(LLVector3(2.f, 2.f, 2.f)); root.updateWorldMatrixChildren(); - ensure("tree starts clean", root.mDirtyFlags == 0 && a.mDirtyFlags == 0); + { + INFO("tree starts clean"); + CHECK(root.mDirtyFlags == 0); + CHECK(a.mDirtyFlags == 0); + } root.setScale(LLVector3(2.f, 2.f, 2.f)); - ensure("the same scale again dirties nothing", root.mDirtyFlags == 0 && a.mDirtyFlags == 0); + { + INFO("the same scale again dirties nothing"); + CHECK(root.mDirtyFlags == 0); + CHECK(a.mDirtyFlags == 0); + } root.setScale(LLVector3(3.f, 2.f, 2.f)); - ensure("a different scale dirties the joint", (root.mDirtyFlags & LLJoint::MATRIX_DIRTY) != 0); - ensure("and its children", (a.mDirtyFlags & LLJoint::MATRIX_DIRTY) != 0); + UNSCOPED_INFO("a different scale dirties the joint"); + CHECK((root.mDirtyFlags & LLJoint::MATRIX_DIRTY) != 0); + UNSCOPED_INFO("and its children"); + CHECK((a.mDirtyFlags & LLJoint::MATRIX_DIRTY) != 0); } - template<> template<> - void lljoint_object::test<24>() + TEST_CASE("LLJoint: setWorldRotation round-trips whatever the parent's scale", "[LLJoint]") { // A world rotation asked for is the world rotation that comes back. // This is the route the foot solver takes six times a frame for a @@ -532,13 +560,12 @@ namespace child.setWorldRotation(wanted); const LLQuaternion got = child.getWorldRotation(); - ensure_approximately_equals("the world rotation asked for is the one that comes back", - fabsf(dot(got, wanted)), 1.f, 16); + UNSCOPED_INFO("the world rotation asked for is the one that comes back"); + REQUIRE_THAT(fabsf(dot(got, wanted)), ll_test::WithinBits(1.f, 16)); } } - template<> template<> - void lljoint_object::test<25>() + TEST_CASE("LLJoint: setWorldRotation matches the matrix route under an unscaled parent", "[LLJoint]") { // With an unscaled parent it is the same local rotation the matrix // route arrived at, so the joints that were already getting the right @@ -563,12 +590,11 @@ namespace child.setWorldRotation(wanted); - ensure_approximately_equals("the two routes agree on the local rotation", - fabsf(dot(child.getRotation(), through_the_matrix)), 1.f, 16); + UNSCOPED_INFO("the two routes agree on the local rotation"); + CHECK_THAT(fabsf(dot(child.getRotation(), through_the_matrix)), ll_test::WithinBits(1.f, 16)); } - template<> template<> - void lljoint_object::test<26>() + TEST_CASE("LLJoint: a scaled parent defeats the matrix route but not setWorldRotation", "[LLJoint]") { // The route this replaced could not do that. It built the answer out // of matrices, and LLMatrix4::invert transposes -- which inverts a @@ -598,14 +624,13 @@ namespace child.setWorldRotation(wanted); const LLQuaternion now = child.getWorldRotation(); - ensure_approximately_equals("a scaled parent gives back what was asked for", - fabsf(dot(now, wanted)), 1.f, 16); - ensure("which the matrix route it replaced did not", - fabsf(dot(through_the_matrix, wanted)) < 0.999f); + UNSCOPED_INFO("a scaled parent gives back what was asked for"); + CHECK_THAT(fabsf(dot(now, wanted)), ll_test::WithinBits(1.f, 16)); + UNSCOPED_INFO("which the matrix route it replaced did not"); + CHECK(fabsf(dot(through_the_matrix, wanted)) < 0.999f); } - template<> template<> - void lljoint_object::test<27>() + TEST_CASE("LLJoint: setWorldRotation undoes a parent rotation that is not unit length", "[LLJoint]") { // Nothing normalizes a rotation on its way into a joint, so a parent // can be carrying one that has drifted off unit length. The world @@ -616,8 +641,8 @@ namespace LLJoint child("child", &parent); LLQuaternion drifted(0.3f, 0.4f, 0.5f, 0.6f); - ensure("the parent's rotation is not unit length", - fabsf(drifted.mQ[0]*drifted.mQ[0] + drifted.mQ[1]*drifted.mQ[1] + UNSCOPED_INFO("the parent's rotation is not unit length"); + CHECK(fabsf(drifted.mQ[0]*drifted.mQ[0] + drifted.mQ[1]*drifted.mQ[1] + drifted.mQ[2]*drifted.mQ[2] + drifted.mQ[3]*drifted.mQ[3] - 1.f) > 0.05f); parent.setRotation(drifted); @@ -627,8 +652,8 @@ namespace const LLQuaternion got = child.getWorldRotation(); for (S32 i = 0; i < 4; ++i) { - ensure_approximately_equals("the world rotation comes back as it was asked for", - got.mQ[i], wanted.mQ[i], 14); + UNSCOPED_INFO("the world rotation comes back as it was asked for"); + REQUIRE_THAT(got.mQ[i], ll_test::WithinBits(wanted.mQ[i], 14)); } } diff --git a/indra/llcharacter/tests/llkeyframemotion_test.cpp b/indra/llcharacter/tests/llkeyframemotion_test.cpp index 2406d4595a..209eae3e08 100644 --- a/indra/llcharacter/tests/llkeyframemotion_test.cpp +++ b/indra/llcharacter/tests/llkeyframemotion_test.cpp @@ -41,7 +41,9 @@ #include #include -#include "../test/lltut.h" +#include +#include +#include "altestmatchers.h" namespace { @@ -248,7 +250,8 @@ namespace template void ensure_same_value(const char* what, F32 time, const T& got, const T& want) { - tut::ensure(std::string(what) + " at t=" + std::to_string(time), got == want); + UNSCOPED_INFO(std::string(what) + " at t=" + std::to_string(time)); + REQUIRE(got == want); } // Rotations are normalized on the way out of the blend, and the curve does @@ -260,9 +263,8 @@ namespace { for (S32 i = 0; i < 4; ++i) { - tut::ensure_approximately_equals_range( - (std::string(what) + " at t=" + std::to_string(time)).c_str(), - got.mQ[i], want.mQ[i], 1e-6f); + UNSCOPED_INFO((std::string(what) + " at t=" + std::to_string(time)).c_str()); + REQUIRE_THAT(got.mQ[i], Catch::Matchers::WithinAbs(want.mQ[i], 1e-6f)); } } @@ -307,7 +309,8 @@ namespace ++wraps; } ensure_same_value(what, time, sample(curve, time, cursor), reference.getValue(time)); - tut::ensure_equals(std::string(what) + " cursor at t=" + std::to_string(time), cursor, reference.lowerBound(time)); + UNSCOPED_INFO(std::string(what) + " cursor at t=" + std::to_string(time)); + REQUIRE_THAT(cursor, ll_test::LLSDEquals(reference.lowerBound(time))); } return wraps; } @@ -486,8 +489,6 @@ namespace } } -namespace tut -{ namespace { struct llkeyframemotion_data @@ -520,13 +521,8 @@ namespace return motion.getPose()->findJointState(std::string(joint_name)); } }; - typedef test_group llkeyframemotion_test; - typedef llkeyframemotion_test::object llkeyframemotion_object; - tut::llkeyframemotion_test llkeyframemotion_testcase("LLKeyframeMotion"); -} - template<> template<> - void llkeyframemotion_object::test<1>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: setKey keeps keys sorted and unique and the last value wins", "[LLKeyframeMotion]") { // Keys arrive in whatever order the asset holds them, and a time can // repeat. The curve keeps them sorted and unique, the last value at a @@ -536,28 +532,35 @@ namespace set_key(curve, 1.f, LLVector3(1.f, 0.f, 0.f)); set_key(curve, 3.f, LLVector3(3.f, 0.f, 0.f)); set_key(curve, 2.f, LLVector3(0.f, 2.f, 0.f)); - ensure_equals("a repeated time does not add a key", curve.getNumKeys(), 3u); - ensure_equals("keys are sorted: first", curve.getKeyTime(0), 1.f); - ensure_equals("keys are sorted: second", curve.getKeyTime(1), 2.f); - ensure_equals("keys are sorted: third", curve.getKeyTime(2), 3.f); - ensure("the last value at a time wins", key_value(curve, 1) == LLVector3(0.f, 2.f, 0.f)); + UNSCOPED_INFO("a repeated time does not add a key"); + CHECK(curve.getNumKeys() == 3u); + UNSCOPED_INFO("keys are sorted: first"); + CHECK(curve.getKeyTime(0) == 1.f); + UNSCOPED_INFO("keys are sorted: second"); + CHECK(curve.getKeyTime(1) == 2.f); + UNSCOPED_INFO("keys are sorted: third"); + CHECK(curve.getKeyTime(2) == 3.f); + UNSCOPED_INFO("the last value at a time wins"); + CHECK(key_value(curve, 1) == LLVector3(0.f, 2.f, 0.f)); Rng rng; RotationCurve rotations; MapCurve reference; build(rng, rotations, reference, LLKeyframeMotion::IT_LINEAR); - ensure_equals("random keys: count matches the map", rotations.getNumKeys(), static_cast(reference.mKeys.size())); + UNSCOPED_INFO("random keys: count matches the map"); + REQUIRE(rotations.getNumKeys() == static_cast(reference.mKeys.size())); U32 k = 0; for (const auto& key : reference.mKeys) { - ensure_equals("random keys: time matches the map", rotations.getKeyTime(k), key.first); - ensure("random keys: value matches the map", key_value(rotations, k) == key.second); + UNSCOPED_INFO("random keys: time matches the map"); + REQUIRE(rotations.getKeyTime(k) == key.first); + UNSCOPED_INFO("random keys: value matches the map"); + REQUIRE(key_value(rotations, k) == key.second); ++k; } } - template<> template<> - void llkeyframemotion_object::test<2>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: sampling without a cursor matches the map curve", "[LLKeyframeMotion]") { // Sampled anywhere, without a cursor, the array curve returns what // the map curve returned: clamped outside the keys, the key itself on @@ -590,8 +593,7 @@ namespace } } - template<> template<> - void llkeyframemotion_object::test<3>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: playback with a cursor matches the map curve and its search", "[LLKeyframeMotion]") { // Played forward with a cursor, fine steps land on the last answer or // the one after it, coarse steps skip past both and fall back to a @@ -604,10 +606,12 @@ namespace build(rng, curve, reference, LLKeyframeMotion::IT_LINEAR); const U32 fine_wraps = ensure_playback_parity("fine playback", rng, curve, reference, 0.f, 0.03f, 1200); - ensure("fine playback wrapped", fine_wraps >= 3); + UNSCOPED_INFO("fine playback wrapped"); + CHECK(fine_wraps >= 3); const U32 coarse_wraps = ensure_playback_parity("coarse playback", rng, curve, reference, 0.3f, 1.f, 200); - ensure("coarse playback wrapped", coarse_wraps >= 20); + UNSCOPED_INFO("coarse playback wrapped"); + CHECK(coarse_wraps >= 20); PositionCurve stepped; MapCurve stepped_reference; @@ -615,8 +619,7 @@ namespace ensure_playback_parity("stepped playback", rng, stepped, stepped_reference, 0.f, 0.05f, 800); } - template<> template<> - void llkeyframemotion_object::test<4>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: a sample on a key returns that key whatever the cursor", "[LLKeyframeMotion]") { // A sample exactly on a key returns that key, whatever the cursor // claims: behind it, on it, past it, at the end, or past the end of @@ -636,18 +639,19 @@ namespace { U32 cursor = hint; ensure_same_value("exact key under any cursor", time, sample(curve, time, cursor), reference.getValue(time)); - ensure_equals("exact key leaves the cursor on the key", cursor, k); + UNSCOPED_INFO("exact key leaves the cursor on the key"); + REQUIRE(cursor == k); cursor = hint; const F32 above = std::nextafter(time, DURATION * 2.f); ensure_same_value("just past a key under any cursor", above, sample(curve, above, cursor), reference.getValue(above)); - ensure_equals("just past a key leaves the cursor after it", cursor, k + 1); + UNSCOPED_INFO("just past a key leaves the cursor after it"); + REQUIRE(cursor == k + 1); } } } - template<> template<> - void llkeyframemotion_object::test<5>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: an empty curve gives the identity and a one-key curve its key", "[LLKeyframeMotion]") { // No keys: the identity of the channel. One key: that key, wherever // the sample falls, with the cursor before or after it. @@ -655,26 +659,35 @@ namespace // has to say what it answers with rather than handing back whatever // was in the memory. PositionCurve no_positions; - ensure("an empty position curve is the zero vector", sample(no_positions, 1.f) == LLVector3()); + UNSCOPED_INFO("an empty position curve is the zero vector"); + CHECK(sample(no_positions, 1.f) == LLVector3()); RotationCurve no_rotations; - ensure("an empty rotation curve is the identity", sample(no_rotations, 1.f) == LLQuaternion()); + UNSCOPED_INFO("an empty rotation curve is the identity"); + CHECK(sample(no_rotations, 1.f) == LLQuaternion()); RotationCurve one; const LLQuaternion key(0.5f, LLVector3(0.f, 1.f, 0.f)); set_key(one, 1.f, key); U32 cursor = 0; - ensure("before the only key", sample(one, 0.f, cursor) == key); - ensure_equals("before the only key: cursor", cursor, 0u); - ensure("on the only key", sample(one, 1.f, cursor) == key); - ensure_equals("on the only key: cursor", cursor, 0u); - ensure("after the only key", sample(one, 2.f, cursor) == key); - ensure_equals("after the only key: cursor", cursor, 1u); - ensure("back before the only key", sample(one, 0.5f, cursor) == key); - ensure_equals("back before the only key: cursor", cursor, 0u); + UNSCOPED_INFO("before the only key"); + CHECK(sample(one, 0.f, cursor) == key); + UNSCOPED_INFO("before the only key: cursor"); + CHECK(cursor == 0u); + UNSCOPED_INFO("on the only key"); + CHECK(sample(one, 1.f, cursor) == key); + UNSCOPED_INFO("on the only key: cursor"); + CHECK(cursor == 0u); + UNSCOPED_INFO("after the only key"); + CHECK(sample(one, 2.f, cursor) == key); + UNSCOPED_INFO("after the only key: cursor"); + CHECK(cursor == 1u); + UNSCOPED_INFO("back before the only key"); + CHECK(sample(one, 0.5f, cursor) == key); + UNSCOPED_INFO("back before the only key: cursor"); + CHECK(cursor == 0u); } - template<> template<> - void llkeyframemotion_object::test<6>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: deserialize gives a joint motion per joint holding its keys", "[LLKeyframeMotion]") { // An asset read in gives one joint motion per joint in the file, in // file order, each holding the keys that followed its name. The @@ -683,26 +696,37 @@ namespace LLKeyframeMotion motion(LLUUID::generateNewID()); std::vector buffer(4096); const AssetAnimation anim = two_joint_animation(); - ensure("the animation is accepted", load(motion, anim, buffer.data(), (S32)buffer.size())); - - ensure_equals("both joints came through", motion.getNumJointMotions(), 2); - ensure_approximately_equals("duration", motion.getDuration(), anim.mDuration, 16); - ensure("the animation loops", motion.getLoop()); - ensure_approximately_equals("ease in", motion.getEaseInDuration(), anim.mEaseIn, 16); - ensure_approximately_equals("ease out", motion.getEaseOutDuration(), anim.mEaseOut, 16); - ensure_equals("base priority", (S32)motion.getPriority(), anim.mBasePriority); + UNSCOPED_INFO("the animation is accepted"); + REQUIRE(load(motion, anim, buffer.data(), (S32)buffer.size())); + + UNSCOPED_INFO("both joints came through"); + CHECK(motion.getNumJointMotions() == 2); + UNSCOPED_INFO("duration"); + CHECK_THAT(motion.getDuration(), ll_test::WithinBits(anim.mDuration, 16)); + UNSCOPED_INFO("the animation loops"); + CHECK(motion.getLoop()); + UNSCOPED_INFO("ease in"); + CHECK_THAT(motion.getEaseInDuration(), ll_test::WithinBits(anim.mEaseIn, 16)); + UNSCOPED_INFO("ease out"); + CHECK_THAT(motion.getEaseOutDuration(), ll_test::WithinBits(anim.mEaseOut, 16)); + UNSCOPED_INFO("base priority"); + CHECK((S32)motion.getPriority() == anim.mBasePriority); LLPose* pose = motion.getPose(); - ensure_equals("a joint state per joint motion", pose->getNumJointStates(), 2); + UNSCOPED_INFO("a joint state per joint motion"); + CHECK(pose->getNumJointStates() == 2); LLJointState* pelvis_state = pose->findJointState(std::string("mPelvis")); - ensure("the pelvis is in the pose", pelvis_state != nullptr); - ensure_equals("the pelvis carries both channels", - pelvis_state->getUsage(), (U32)(LLJointState::ROT | LLJointState::POS)); + UNSCOPED_INFO("the pelvis is in the pose"); + REQUIRE(pelvis_state != nullptr); + UNSCOPED_INFO("the pelvis carries both channels"); + CHECK(pelvis_state->getUsage() == (U32)(LLJointState::ROT | LLJointState::POS)); LLJointState* torso_state = pose->findJointState(std::string("mTorso")); - ensure("the torso is in the pose", torso_state != nullptr); - ensure_equals("the torso carries rotation only", torso_state->getUsage(), (U32)LLJointState::ROT); - ensure_equals("the torso keeps its own priority", - (S32)torso_state->getPriority(), anim.mJoints[1].mPriority); + UNSCOPED_INFO("the torso is in the pose"); + REQUIRE(torso_state != nullptr); + UNSCOPED_INFO("the torso carries rotation only"); + CHECK(torso_state->getUsage() == (U32)LLJointState::ROT); + UNSCOPED_INFO("the torso keeps its own priority"); + CHECK((S32)torso_state->getPriority() == anim.mJoints[1].mPriority); // and the keys hold what the file held. The format quantizes both // channels to sixteen bits, positions over five metres either way and @@ -710,32 +734,116 @@ namespace for (const auto& key : anim.mJoints[0].mPositions) { LLJointState* sampled = sampleAt(motion, key.first, "mPelvis"); - ensure("the pelvis is sampled", sampled != nullptr); + UNSCOPED_INFO("the pelvis is sampled"); + REQUIRE(sampled != nullptr); const LLVector3 got = sampled->getPosition(); for (S32 i = 0; i < 3; ++i) { - ensure_approximately_equals_range("position key survived the parse", - got.mV[i], key.second.mV[i], 1e-3f); + UNSCOPED_INFO("position key survived the parse"); + REQUIRE_THAT(got.mV[i], Catch::Matchers::WithinAbs(key.second.mV[i], 1e-3f)); } } for (const auto& key : anim.mJoints[0].mRotations) { LLJointState* sampled = sampleAt(motion, key.first, "mPelvis"); - ensure("the pelvis is sampled", sampled != nullptr); - ensure_approximately_equals_range("rotation key survived the parse", - fabsf(dot(sampled->getRotation(), key.second)), 1.f, 1e-3f); + UNSCOPED_INFO("the pelvis is sampled"); + REQUIRE(sampled != nullptr); + UNSCOPED_INFO("rotation key survived the parse"); + REQUIRE_THAT(fabsf(dot(sampled->getRotation(), key.second)), Catch::Matchers::WithinAbs(1.f, 1e-3f)); } LLJointState* torso_sample = sampleAt(motion, anim.mJoints[1].mRotations[0].first, "mTorso"); - ensure("the torso is sampled", torso_sample != nullptr); - ensure_approximately_equals_range("the torso's one key survived the parse", - fabsf(dot(torso_sample->getRotation(), anim.mJoints[1].mRotations[0].second)), - 1.f, 1e-3f); + UNSCOPED_INFO("the torso is sampled"); + REQUIRE(torso_sample != nullptr); + UNSCOPED_INFO("the torso's one key survived the parse"); + CHECK_THAT(fabsf(dot(torso_sample->getRotation(), anim.mJoints[1].mRotations[0].second)), + Catch::Matchers::WithinAbs(1.f, 1e-3f)); } - template<> template<> - void llkeyframemotion_object::test<8>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: serialize then deserialize gives back the same joints and keys", "[LLKeyframeMotion]") + { + // What was read can be written back and read again, and the second + // reading agrees with the first joint for joint and key for key. The + // format quantizes on the way out, so this holds only because the + // first reading was already quantized. + LLKeyframeMotion first(LLUUID::generateNewID()); + std::vector buffer(4096); + UNSCOPED_INFO("the animation is accepted"); + REQUIRE(load(first, two_joint_animation(), buffer.data(), (S32)buffer.size())); + + std::vector written(first.getFileSize()); + LLDataPackerBinaryBuffer writer(written.data(), (S32)written.size()); + UNSCOPED_INFO("it serializes"); + CHECK(first.serialize(writer)); + + LLKeyframeMotion second(LLUUID::generateNewID()); + second.setCharacter(&mCharacter); + LLDataPackerBinaryBuffer reader(written.data(), writer.getCurrentSize()); + UNSCOPED_INFO("what was written reads back"); + CHECK(second.deserialize(reader, second.getID())); + + UNSCOPED_INFO("same joint count"); + CHECK(second.getNumJointMotions() == first.getNumJointMotions()); + UNSCOPED_INFO("same duration"); + CHECK_THAT(second.getDuration(), ll_test::WithinBits(first.getDuration(), 16)); + UNSCOPED_INFO("same base priority"); + CHECK((S32)second.getPriority() == (S32)first.getPriority()); + + LLPose* first_pose = first.getPose(); + LLPose* second_pose = second.getPose(); + UNSCOPED_INFO("same joint state count"); + CHECK(second_pose->getNumJointStates() == first_pose->getNumJointStates()); + for (const char* name : { "mPelvis", "mTorso" }) + { + LLJointState* a = first_pose->findJointState(std::string(name)); + LLJointState* b = second_pose->findJointState(std::string(name)); + { + INFO("joint survived the round trip"); + REQUIRE(a != nullptr); + REQUIRE(b != nullptr); + } + UNSCOPED_INFO("same usage"); + REQUIRE(b->getUsage() == a->getUsage()); + UNSCOPED_INFO("same priority"); + REQUIRE((S32)b->getPriority() == (S32)a->getPriority()); + } + + // The keys themselves, sampled the way playback samples them. Without + // this the round trip could write a joint's name and another joint's + // curve and nothing here would notice. + for (F32 time = 0.f; time < 2.f; time += 0.25f) + { + for (const char* name : { "mPelvis", "mTorso" }) + { + LLJointState* a = sampleAt(first, time, name); + LLJointState* b = sampleAt(second, time, name); + { + INFO("joint sampled on both sides"); + REQUIRE(a != nullptr); + REQUIRE(b != nullptr); + } + + const LLQuaternion rot_a = a->getRotation(); + const LLQuaternion rot_b = b->getRotation(); + UNSCOPED_INFO("same rotation after the round trip"); + REQUIRE_THAT(fabsf(dot(rot_a, rot_b)), Catch::Matchers::WithinAbs(1.f, 1e-4f)); + + if (a->getUsage() & LLJointState::POS) + { + const LLVector3 pos_a = a->getPosition(); + const LLVector3 pos_b = b->getPosition(); + for (S32 i = 0; i < 3; ++i) + { + UNSCOPED_INFO("same position after the round trip"); + REQUIRE_THAT(pos_b.mV[i], Catch::Matchers::WithinAbs(pos_a.mV[i], 1e-4f)); + } + } + } + } + } + + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: only chains shorter than the solver arrays are accepted", "[LLKeyframeMotion]") { // The solver holds a constraint chain in arrays of MAX_CHAIN_LENGTH // and indexes them from zero to the chain length inclusive, so a chain @@ -747,29 +855,28 @@ namespace for (S32 chain_length = 0; chain_length < MAX_CHAIN_LENGTH; ++chain_length) { LLKeyframeMotion motion(LLUUID::generateNewID()); - ensure("a chain the arrays can hold is accepted", - load(motion, constrained_animation(chain_length), buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO("a chain the arrays can hold is accepted"); + REQUIRE(load(motion, constrained_animation(chain_length), buffer.data(), (S32)buffer.size())); } { // One longer, which every joint above the volume can still supply, // so the length is the only thing left to refuse it. LLKeyframeMotion motion(LLUUID::generateNewID()); - ensure("a chain as long as the arrays is refused", - !load(motion, constrained_animation(MAX_CHAIN_LENGTH), buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO("a chain as long as the arrays is refused"); + REQUIRE_FALSE(load(motion, constrained_animation(MAX_CHAIN_LENGTH), buffer.data(), (S32)buffer.size())); } { // and one past anything the skeleton could supply, which the // joint count check has always refused LLKeyframeMotion motion(LLUUID::generateNewID()); - ensure("a chain longer than the skeleton is refused", - !load(motion, constrained_animation(200), buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO("a chain longer than the skeleton is refused"); + REQUIRE_FALSE(load(motion, constrained_animation(200), buffer.data(), (S32)buffer.size())); } } - template<> template<> - void llkeyframemotion_object::test<9>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: the solver puts back joints it wrote when a higher priority stops it", "[LLKeyframeMotion]") { // The solver writes the chain's joints with the motion's own rotations // to measure against, and has to put them back. It gives up part way @@ -778,14 +885,18 @@ namespace // joint it had already written holding a pose nothing asked for. LLKeyframeMotion motion(LLUUID::generateNewID()); std::vector buffer(4096); - ensure("the constrained animation loads", - load(motion, constrained_animation(1), buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO("the constrained animation loads"); + REQUIRE(load(motion, constrained_animation(1), buffer.data(), (S32)buffer.size())); // The chain runs up from the collision volume: its parent and then its // grandparent, which for the test character is mHead and mNeck. LLJoint* first_in_chain = mCharacter.getJoint("mHead"); LLJoint* second_in_chain = mCharacter.getJoint("mNeck"); - ensure("the chain joints resolve", first_in_chain != nullptr && second_in_chain != nullptr); + { + INFO("the chain joints resolve"); + REQUIRE(first_in_chain != nullptr); + REQUIRE(second_in_chain != nullptr); + } const LLQuaternion marker(1.1f, LLVector3::x_axis); first_in_chain->setRotation(marker); @@ -798,79 +909,11 @@ namespace motion.onUpdate(1.f, mask); const LLQuaternion after = first_in_chain->getRotation(); - ensure_approximately_equals_range("the joint the solver wrote is put back", - fabsf(dot(after, marker)), 1.f, 1e-5f); - } - - template<> template<> - void llkeyframemotion_object::test<7>() - { - // What was read can be written back and read again, and the second - // reading agrees with the first joint for joint and key for key. The - // format quantizes on the way out, so this holds only because the - // first reading was already quantized. - LLKeyframeMotion first(LLUUID::generateNewID()); - std::vector buffer(4096); - ensure("the animation is accepted", load(first, two_joint_animation(), buffer.data(), (S32)buffer.size())); - - std::vector written(first.getFileSize()); - LLDataPackerBinaryBuffer writer(written.data(), (S32)written.size()); - ensure("it serializes", first.serialize(writer)); - - LLKeyframeMotion second(LLUUID::generateNewID()); - second.setCharacter(&mCharacter); - LLDataPackerBinaryBuffer reader(written.data(), writer.getCurrentSize()); - ensure("what was written reads back", second.deserialize(reader, second.getID())); - - ensure_equals("same joint count", second.getNumJointMotions(), first.getNumJointMotions()); - ensure_approximately_equals("same duration", second.getDuration(), first.getDuration(), 16); - ensure_equals("same base priority", (S32)second.getPriority(), (S32)first.getPriority()); - - LLPose* first_pose = first.getPose(); - LLPose* second_pose = second.getPose(); - ensure_equals("same joint state count", - second_pose->getNumJointStates(), first_pose->getNumJointStates()); - for (const char* name : { "mPelvis", "mTorso" }) - { - LLJointState* a = first_pose->findJointState(std::string(name)); - LLJointState* b = second_pose->findJointState(std::string(name)); - ensure("joint survived the round trip", a != nullptr && b != nullptr); - ensure_equals("same usage", b->getUsage(), a->getUsage()); - ensure_equals("same priority", (S32)b->getPriority(), (S32)a->getPriority()); - } - - // The keys themselves, sampled the way playback samples them. Without - // this the round trip could write a joint's name and another joint's - // curve and nothing here would notice. - for (F32 time = 0.f; time < 2.f; time += 0.25f) - { - for (const char* name : { "mPelvis", "mTorso" }) - { - LLJointState* a = sampleAt(first, time, name); - LLJointState* b = sampleAt(second, time, name); - ensure("joint sampled on both sides", a != nullptr && b != nullptr); - - const LLQuaternion rot_a = a->getRotation(); - const LLQuaternion rot_b = b->getRotation(); - ensure_approximately_equals_range("same rotation after the round trip", - fabsf(dot(rot_a, rot_b)), 1.f, 1e-4f); - - if (a->getUsage() & LLJointState::POS) - { - const LLVector3 pos_a = a->getPosition(); - const LLVector3 pos_b = b->getPosition(); - for (S32 i = 0; i < 3; ++i) - { - ensure_approximately_equals_range("same position after the round trip", - pos_b.mV[i], pos_a.mV[i], 1e-4f); - } - } - } - } + UNSCOPED_INFO("the joint the solver wrote is put back"); + CHECK_THAT(fabsf(dot(after, marker)), Catch::Matchers::WithinAbs(1.f, 1e-5f)); } - template<> template<> - void llkeyframemotion_object::test<10>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: a joint claimed at full weight is not sampled unless it also moves", "[LLKeyframeMotion]") { // A joint already turned by a motion at full weight, at a priority // this one cannot reach, is not sampled: the blend would interpolate @@ -879,7 +922,8 @@ namespace LLKeyframeMotion motion(LLUUID::generateNewID()); std::vector buffer(4096); const AssetAnimation anim = two_joint_animation(); - ensure("the animation loads", load(motion, anim, buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO("the animation loads"); + REQUIRE(load(motion, anim, buffer.data(), (S32)buffer.size())); U8 nothing_claimed[LL_CHARACTER_MAX_ANIMATED_JOINTS] = {}; U8 all_claimed[LL_CHARACTER_MAX_ANIMATED_JOINTS]; @@ -888,33 +932,37 @@ namespace LLPose* pose = motion.getPose(); LLJointState* torso = pose->findJointState(std::string("mTorso")); LLJointState* pelvis = pose->findJointState(std::string("mPelvis")); - ensure("both joints are in the pose", torso != nullptr && pelvis != nullptr); - ensure_equals("the torso only rotates", torso->getUsage(), (U32)LLJointState::ROT); + { + INFO("both joints are in the pose"); + REQUIRE(torso != nullptr); + REQUIRE(pelvis != nullptr); + } + UNSCOPED_INFO("the torso only rotates"); + CHECK(torso->getUsage() == (U32)LLJointState::ROT); const LLQuaternion marker(2.4f, LLVector3(0.f, 0.f, 1.f)); // claimed: the curves are not read and the state keeps what it held torso->setRotation(marker); motion.onUpdate(1.5f, all_claimed); - ensure_approximately_equals_range("a claimed rotation is left alone", - fabsf(dot(torso->getRotation(), marker)), 1.f, 1e-5f); + UNSCOPED_INFO("a claimed rotation is left alone"); + CHECK_THAT(fabsf(dot(torso->getRotation(), marker)), Catch::Matchers::WithinAbs(1.f, 1e-5f)); // and unclaimed, the same sample writes it motion.onUpdate(1.5f, nothing_claimed); - ensure("an unclaimed rotation is sampled", - fabsf(dot(torso->getRotation(), marker)) < 0.999f); + UNSCOPED_INFO("an unclaimed rotation is sampled"); + CHECK(fabsf(dot(torso->getRotation(), marker)) < 0.999f); // The pelvis carries a position as well, which is summed on its own // account however much of the rotation is spoken for, so it is sampled // whatever the mask says. pelvis->setRotation(marker); motion.onUpdate(0.5f, all_claimed); - ensure("a joint that also moves is sampled whatever is claimed", - fabsf(dot(pelvis->getRotation(), marker)) < 0.999f); + UNSCOPED_INFO("a joint that also moves is sampled whatever is claimed"); + CHECK(fabsf(dot(pelvis->getRotation(), marker)) < 0.999f); } - template<> template<> - void llkeyframemotion_object::test<11>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: deserialize refuses a malformed header", "[LLKeyframeMotion]") { // The header, field by field, with one thing wrong at a time. Every // one of these arrives in an animation another avatar can play at you. @@ -925,12 +973,14 @@ namespace auto refuses = [&](const char* what, const AssetAnimation& anim) { LLKeyframeMotion motion(LLUUID::generateNewID()); - ensure(what, !load(motion, anim, buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO(what); + REQUIRE_FALSE(load(motion, anim, buffer.data(), (S32)buffer.size())); }; auto accepts = [&](const char* what, const AssetAnimation& anim) { LLKeyframeMotion motion(LLUUID::generateNewID()); - ensure(what, load(motion, anim, buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO(what); + REQUIRE(load(motion, anim, buffer.data(), (S32)buffer.size())); }; { @@ -1002,8 +1052,7 @@ namespace } } - template<> template<> - void llkeyframemotion_object::test<12>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: deserialize refuses malformed joints but accepts an unknown name", "[LLKeyframeMotion]") { // The joints, the same way. std::vector buffer(8192); @@ -1011,7 +1060,8 @@ namespace auto refuses = [&](const char* what, const AssetAnimation& anim) { LLKeyframeMotion motion(LLUUID::generateNewID()); - ensure(what, !load(motion, anim, buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO(what); + REQUIRE_FALSE(load(motion, anim, buffer.data(), (S32)buffer.size())); }; for (const char* special : { "mRoot", "mScreen" }) @@ -1063,14 +1113,14 @@ namespace AssetAnimation anim = two_joint_animation(); anim.mJoints[1].mName = "mNotAJointAtAll"; LLKeyframeMotion motion(LLUUID::generateNewID()); - ensure("an unknown joint does not sink the animation", - load(motion, anim, buffer.data(), (S32)buffer.size())); - ensure_equals("and its joint motion is still there", motion.getNumJointMotions(), 2); + UNSCOPED_INFO("an unknown joint does not sink the animation"); + REQUIRE(load(motion, anim, buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO("and its joint motion is still there"); + CHECK(motion.getNumJointMotions() == 2); } } - template<> template<> - void llkeyframemotion_object::test<13>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: deserialize refuses malformed constraints", "[LLKeyframeMotion]") { // Constraints. The chain length is the one that used to run off the // end of five fixed arrays. @@ -1079,12 +1129,14 @@ namespace auto refuses = [&](const char* what, const AssetAnimation& anim) { LLKeyframeMotion motion(LLUUID::generateNewID()); - ensure(what, !load(motion, anim, buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO(what); + REQUIRE_FALSE(load(motion, anim, buffer.data(), (S32)buffer.size())); }; auto accepts = [&](const char* what, const AssetAnimation& anim) { LLKeyframeMotion motion(LLUUID::generateNewID()); - ensure(what, load(motion, anim, buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO(what); + REQUIRE(load(motion, anim, buffer.data(), (S32)buffer.size())); }; { @@ -1119,8 +1171,7 @@ namespace } } - template<> template<> - void llkeyframemotion_object::test<14>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: deserialize refuses every truncation of an animation", "[LLKeyframeMotion]") { // Cut anywhere and it is refused rather than half read. Every prefix // of a well formed animation, so the cut lands in each field in turn, @@ -1131,24 +1182,26 @@ namespace LLDataPackerBinaryBuffer writer(buffer.data(), (S32)buffer.size()); pack_animation(writer, anim); const S32 whole = writer.getCurrentSize(); - ensure("the animation is worth cutting up", whole > 40); + UNSCOPED_INFO("the animation is worth cutting up"); + CHECK(whole > 40); for (S32 length = 0; length < whole; ++length) { LLKeyframeMotion motion(LLUUID::generateNewID()); motion.setCharacter(&mCharacter); LLDataPackerBinaryBuffer reader(buffer.data(), length); - ensure("an animation cut short is refused", !motion.deserialize(reader, motion.getID())); + UNSCOPED_INFO("an animation cut short is refused"); + REQUIRE_FALSE(motion.deserialize(reader, motion.getID())); } LLKeyframeMotion motion(LLUUID::generateNewID()); motion.setCharacter(&mCharacter); LLDataPackerBinaryBuffer reader(buffer.data(), whole); - ensure("and the whole of it is not", motion.deserialize(reader, motion.getID())); + UNSCOPED_INFO("and the whole of it is not"); + CHECK(motion.deserialize(reader, motion.getID())); } - template<> template<> - void llkeyframemotion_object::test<15>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: deserialize refuses every truncation of a constrained animation", "[LLKeyframeMotion]") { // The same for a constrained animation, whose tail is the part with // the volume names and the four ease times in it. @@ -1164,18 +1217,18 @@ namespace LLKeyframeMotion motion(LLUUID::generateNewID()); motion.setCharacter(&mCharacter); LLDataPackerBinaryBuffer reader(buffer.data(), length); - ensure("a constrained animation cut short is refused", - !motion.deserialize(reader, motion.getID())); + UNSCOPED_INFO("a constrained animation cut short is refused"); + REQUIRE_FALSE(motion.deserialize(reader, motion.getID())); } LLKeyframeMotion motion(LLUUID::generateNewID()); motion.setCharacter(&mCharacter); LLDataPackerBinaryBuffer reader(buffer.data(), whole); - ensure("and the whole of it is not", motion.deserialize(reader, motion.getID())); + UNSCOPED_INFO("and the whole of it is not"); + CHECK(motion.deserialize(reader, motion.getID())); } - template<> template<> - void llkeyframemotion_object::test<16>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: the data cache shares what was read by reference count", "[LLKeyframeMotion]") { // What an animation was read into is kept so that playing it again // costs nothing, and it belongs to the cache and to every motion @@ -1183,31 +1236,38 @@ namespace std::vector buffer(8192); const AssetAnimation anim = two_joint_animation(); - ensure_equals("the cache starts empty", LLKeyframeDataCache::size(), 0u); + UNSCOPED_INFO("the cache starts empty"); + CHECK(LLKeyframeDataCache::size() == 0u); LLKeyframeMotion motion(LLUUID::generateNewID()); - ensure("the animation is read", load(motion, anim, buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO("the animation is read"); + REQUIRE(load(motion, anim, buffer.data(), (S32)buffer.size())); - ensure_equals("and kept", LLKeyframeDataCache::size(), 1u); + UNSCOPED_INFO("and kept"); + CHECK(LLKeyframeDataCache::size() == 1u); LLKeyframeMotion::JointMotionList* held = LLKeyframeDataCache::getKeyframeData(motion.getID()); - ensure("under the id it was read for", held != nullptr); - ensure_equals("with the joints it was read with", held->getNumJointMotions(), 2u); + UNSCOPED_INFO("under the id it was read for"); + REQUIRE(held != nullptr); + UNSCOPED_INFO("with the joints it was read with"); + CHECK(held->getNumJointMotions() == 2u); // Counted rather than owned: the cache has one hold on it and the // motion reading it has the other. - ensure_equals("two things are holding it", held->getNumRefs(), 2); + UNSCOPED_INFO("two things are holding it"); + CHECK(held->getNumRefs() == 2); { LLKeyframeMotion second(motion.getID()); - ensure_equals("a second motion reading it makes three", - second.onInitialize(&mCharacter), LLMotion::STATUS_SUCCESS); - ensure_equals("holds", held->getNumRefs(), 3); + UNSCOPED_INFO("a second motion reading it makes three"); + CHECK(second.onInitialize(&mCharacter) == LLMotion::STATUS_SUCCESS); + UNSCOPED_INFO("holds"); + CHECK(held->getNumRefs() == 3); } - ensure_equals("and letting go puts it back", held->getNumRefs(), 2); + UNSCOPED_INFO("and letting go puts it back"); + CHECK(held->getNumRefs() == 2); } - template<> template<> - void llkeyframemotion_object::test<17>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: clearing the cache leaves a playing motion its animation", "[LLKeyframeMotion]") { // Emptying the cache does not stop an animation somebody is playing. // This is what the cache could not do before: it held the animation @@ -1216,25 +1276,30 @@ namespace const AssetAnimation anim = two_joint_animation(); LLKeyframeMotion motion(LLUUID::generateNewID()); - ensure("the animation is read", load(motion, anim, buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO("the animation is read"); + REQUIRE(load(motion, anim, buffer.data(), (S32)buffer.size())); const F32 duration = motion.getDuration(); - ensure("it has a duration to lose", duration > 0.f); + UNSCOPED_INFO("it has a duration to lose"); + CHECK(duration > 0.f); LLKeyframeDataCache::clear(); - ensure_equals("the cache lets go of it", LLKeyframeDataCache::size(), 0u); - ensure("and no longer hands it out", - LLKeyframeDataCache::getKeyframeData(motion.getID()) == nullptr); + UNSCOPED_INFO("the cache lets go of it"); + CHECK(LLKeyframeDataCache::size() == 0u); + UNSCOPED_INFO("and no longer hands it out"); + REQUIRE(LLKeyframeDataCache::getKeyframeData(motion.getID()) == nullptr); - ensure_approximately_equals("the motion still has it", motion.getDuration(), duration, 16); - ensure_equals("whole", motion.getNumJointMotions(), 2); + UNSCOPED_INFO("the motion still has it"); + CHECK_THAT(motion.getDuration(), ll_test::WithinBits(duration, 16)); + UNSCOPED_INFO("whole"); + CHECK(motion.getNumJointMotions() == 2); // And it still plays, which reads the curves the cache used to own. LLJointState* state = sampleAt(motion, 0.5f, "mPelvis"); - ensure("a motion whose cache entry is gone still animates", state != nullptr); + UNSCOPED_INFO("a motion whose cache entry is gone still animates"); + REQUIRE(state != nullptr); } - template<> template<> - void llkeyframemotion_object::test<18>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: removeKeyframeData leaves a playing motion its animation", "[LLKeyframeMotion]") { // Removing one by name is the same: it goes from the cache and stays // with whoever is playing it. @@ -1242,19 +1307,22 @@ namespace const AssetAnimation anim = two_joint_animation(); LLKeyframeMotion motion(LLUUID::generateNewID()); - ensure("the animation is read", load(motion, anim, buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO("the animation is read"); + REQUIRE(load(motion, anim, buffer.data(), (S32)buffer.size())); LLKeyframeDataCache::removeKeyframeData(motion.getID()); - ensure_equals("the cache lets go of it", LLKeyframeDataCache::size(), 0u); - ensure_equals("the motion does not", motion.getNumJointMotions(), 2); + UNSCOPED_INFO("the cache lets go of it"); + CHECK(LLKeyframeDataCache::size() == 0u); + UNSCOPED_INFO("the motion does not"); + CHECK(motion.getNumJointMotions() == 2); // Removing one nobody has is not an error. LLKeyframeDataCache::removeKeyframeData(LLUUID::generateNewID()); - ensure_equals("and the cache is no worse for it", LLKeyframeDataCache::size(), 0u); + UNSCOPED_INFO("and the cache is no worse for it"); + CHECK(LLKeyframeDataCache::size() == 0u); } - template<> template<> - void llkeyframemotion_object::test<19>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: purge drops only what no motion is holding", "[LLKeyframeMotion]") { // The purge drops what no motion is holding and keeps what one is, // which is the whole of how the cache is allowed to end a session @@ -1263,31 +1331,36 @@ namespace const AssetAnimation anim = two_joint_animation(); LLKeyframeMotion kept(LLUUID::generateNewID()); - ensure("the first animation is read", load(kept, anim, buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO("the first animation is read"); + REQUIRE(load(kept, anim, buffer.data(), (S32)buffer.size())); LLUUID dropped_id; { LLKeyframeMotion dropped(LLUUID::generateNewID()); dropped_id = dropped.getID(); - ensure("the second is read", load(dropped, anim, buffer.data(), (S32)buffer.size())); - ensure_equals("both are in the cache", LLKeyframeDataCache::size(), 2u); + UNSCOPED_INFO("the second is read"); + REQUIRE(load(dropped, anim, buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO("both are in the cache"); + CHECK(LLKeyframeDataCache::size() == 2u); LLKeyframeDataCache::purge(); - ensure_equals("a purge keeps what is being played", LLKeyframeDataCache::size(), 2u); + UNSCOPED_INFO("a purge keeps what is being played"); + CHECK(LLKeyframeDataCache::size() == 2u); } // The second motion is gone, so nothing can play what it was reading. LLKeyframeDataCache::purge(); - ensure_equals("a purge drops what nobody is playing", LLKeyframeDataCache::size(), 1u); - ensure("the one that went is the one nobody had", - LLKeyframeDataCache::getKeyframeData(dropped_id) == nullptr); - ensure("and the one that stayed is the one somebody had", - LLKeyframeDataCache::getKeyframeData(kept.getID()) != nullptr); - ensure_equals("still whole", kept.getNumJointMotions(), 2); + UNSCOPED_INFO("a purge drops what nobody is playing"); + CHECK(LLKeyframeDataCache::size() == 1u); + UNSCOPED_INFO("the one that went is the one nobody had"); + REQUIRE(LLKeyframeDataCache::getKeyframeData(dropped_id) == nullptr); + UNSCOPED_INFO("and the one that stayed is the one somebody had"); + REQUIRE(LLKeyframeDataCache::getKeyframeData(kept.getID()) != nullptr); + UNSCOPED_INFO("still whole"); + CHECK(kept.getNumJointMotions() == 2); } - template<> template<> - void llkeyframemotion_object::test<20>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: two motions on one id share one cached animation", "[LLKeyframeMotion]") { // Two motions reading the same animation share the one copy of it, // and it outlives either of them going. @@ -1300,26 +1373,28 @@ namespace LLKeyframeMotion second(id); { LLKeyframeMotion first(id); - ensure("the animation is read", load(first, anim, buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO("the animation is read"); + REQUIRE(load(first, anim, buffer.data(), (S32)buffer.size())); first_list = LLKeyframeDataCache::getKeyframeData(id); - ensure("and kept", first_list != nullptr); + UNSCOPED_INFO("and kept"); + REQUIRE(first_list != nullptr); // The second finds it in the cache rather than reading it again, // which is the path every repeat play of an animation takes. - ensure_equals("the second motion is initialized from the cache", - second.onInitialize(&mCharacter), LLMotion::STATUS_SUCCESS); - ensure_equals("with the joints the first one read", - second.getNumJointMotions(), 2); + UNSCOPED_INFO("the second motion is initialized from the cache"); + CHECK(second.onInitialize(&mCharacter) == LLMotion::STATUS_SUCCESS); + UNSCOPED_INFO("with the joints the first one read"); + CHECK(second.getNumJointMotions() == 2); } LLKeyframeDataCache::purge(); - ensure_equals("one motion going does not take the animation with it", - LLKeyframeDataCache::size(), 1u); - ensure_equals("and it is the same one", LLKeyframeDataCache::getKeyframeData(id), first_list); + UNSCOPED_INFO("one motion going does not take the animation with it"); + CHECK(LLKeyframeDataCache::size() == 1u); + UNSCOPED_INFO("and it is the same one"); + REQUIRE(LLKeyframeDataCache::getKeyframeData(id) == first_list); } - template<> template<> - void llkeyframemotion_object::test<21>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: a loop seam leads the tail after the last key back to the loop start", "[LLKeyframeMotion]") { // A looping animation whose keys stop before its loop does. Between // the last key and the loop out point the curve used to hold the last @@ -1331,34 +1406,33 @@ namespace // Without a seam the tail holds the last key, which is what a motion // that does not loop wants. - ensure_approximately_equals_range("no seam: the tail holds the last key", - sample(curve, 1.5f).mV[VX], 10.f, 1e-4f); + UNSCOPED_INFO("no seam: the tail holds the last key"); + CHECK_THAT(sample(curve, 1.5f).mV[VX], Catch::Matchers::WithinAbs(10.f, 1e-4f)); // The loop runs to two, a second past the last key, and starts from // the pose at zero. curve.setLoopSeam(true, 0.f, 2.f); - ensure_approximately_equals_range("the last key is still the last key", - sample(curve, 1.f).mV[VX], 10.f, 1e-4f); - ensure_approximately_equals_range("half way along the tail is half way back", - sample(curve, 1.5f).mV[VX], 5.f, 1e-4f); - ensure_approximately_equals_range("and the end of the tail is the loop's first pose", - sample(curve, 2.f).mV[VX], 0.f, 1e-4f); - ensure_approximately_equals_range("past the loop out point it stays there", - sample(curve, 3.f).mV[VX], 0.f, 1e-4f); + UNSCOPED_INFO("the last key is still the last key"); + CHECK_THAT(sample(curve, 1.f).mV[VX], Catch::Matchers::WithinAbs(10.f, 1e-4f)); + UNSCOPED_INFO("half way along the tail is half way back"); + CHECK_THAT(sample(curve, 1.5f).mV[VX], Catch::Matchers::WithinAbs(5.f, 1e-4f)); + UNSCOPED_INFO("and the end of the tail is the loop's first pose"); + CHECK_THAT(sample(curve, 2.f).mV[VX], Catch::Matchers::WithinAbs(0.f, 1e-4f)); + UNSCOPED_INFO("past the loop out point it stays there"); + CHECK_THAT(sample(curve, 3.f).mV[VX], Catch::Matchers::WithinAbs(0.f, 1e-4f)); // Inside the keys nothing changed. - ensure_approximately_equals_range("the keyed range is untouched", - sample(curve, 0.5f).mV[VX], 5.f, 1e-4f); + UNSCOPED_INFO("the keyed range is untouched"); + CHECK_THAT(sample(curve, 0.5f).mV[VX], Catch::Matchers::WithinAbs(5.f, 1e-4f)); // Told it does not loop, the tail goes away again. curve.setLoopSeam(false, 0.f, 2.f); - ensure_approximately_equals_range("without a loop the tail holds again", - sample(curve, 1.5f).mV[VX], 10.f, 1e-4f); + UNSCOPED_INFO("without a loop the tail holds again"); + CHECK_THAT(sample(curve, 1.5f).mV[VX], Catch::Matchers::WithinAbs(10.f, 1e-4f)); } - template<> template<> - void llkeyframemotion_object::test<22>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: a loop seam with no tail to lead holds the last key", "[LLKeyframeMotion]") { // A loop that ends on its last key has no tail to lead anywhere, and // one that ends before its last key never reaches it. @@ -1367,28 +1441,28 @@ namespace set_key(curve, 1.f, LLVector3(10.f, 0.f, 0.f)); curve.setLoopSeam(true, 0.f, 1.f); - ensure_approximately_equals_range("a loop ending on the last key holds past it", - sample(curve, 1.5f).mV[VX], 10.f, 1e-4f); + UNSCOPED_INFO("a loop ending on the last key holds past it"); + CHECK_THAT(sample(curve, 1.5f).mV[VX], Catch::Matchers::WithinAbs(10.f, 1e-4f)); curve.setLoopSeam(true, 0.f, 0.5f); - ensure_approximately_equals_range("a loop ending before the last key holds too", - sample(curve, 1.5f).mV[VX], 10.f, 1e-4f); + UNSCOPED_INFO("a loop ending before the last key holds too"); + CHECK_THAT(sample(curve, 1.5f).mV[VX], Catch::Matchers::WithinAbs(10.f, 1e-4f)); // A curve with one key has nowhere to lead from. PositionCurve single; set_key(single, 0.f, LLVector3(4.f, 0.f, 0.f)); single.setLoopSeam(true, 0.f, 2.f); - ensure_approximately_equals_range("one key is the whole animation", - sample(single, 1.f).mV[VX], 4.f, 1e-4f); + UNSCOPED_INFO("one key is the whole animation"); + CHECK_THAT(sample(single, 1.f).mV[VX], Catch::Matchers::WithinAbs(4.f, 1e-4f)); // An empty curve is asked nothing and says nothing. PositionCurve empty; empty.setLoopSeam(true, 0.f, 2.f); - ensure_equals("an empty curve has no keys", empty.getNumKeys(), 0u); + UNSCOPED_INFO("an empty curve has no keys"); + CHECK(empty.getNumKeys() == 0u); } - template<> template<> - void llkeyframemotion_object::test<23>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: a loop seam reads the loop start pose from the keys every time", "[LLKeyframeMotion]") { // The loop's first pose is read from the keys, not from a tail left // over from the last time the seam was set up. @@ -1397,18 +1471,18 @@ namespace set_key(curve, 1.f, LLVector3(10.f, 0.f, 0.f)); curve.setLoopSeam(true, 0.f, 2.f); - ensure_approximately_equals_range("the tail leads back to zero", - sample(curve, 1.5f).mV[VX], 5.f, 1e-4f); + UNSCOPED_INFO("the tail leads back to zero"); + CHECK_THAT(sample(curve, 1.5f).mV[VX], Catch::Matchers::WithinAbs(5.f, 1e-4f)); // Setting it up again against a later loop in point reads that pose, // and reads it from the keys rather than through the tail it already // has. curve.setLoopSeam(true, 0.5f, 2.f); - ensure_approximately_equals_range("a later loop in point is read from the keys", - sample(curve, 1.5f).mV[VX], 7.5f, 1e-4f); + UNSCOPED_INFO("a later loop in point is read from the keys"); + CHECK_THAT(sample(curve, 1.5f).mV[VX], Catch::Matchers::WithinAbs(7.5f, 1e-4f)); curve.setLoopSeam(true, 0.5f, 2.f); - ensure_approximately_equals_range("and reads the same the second time", - sample(curve, 1.5f).mV[VX], 7.5f, 1e-4f); + UNSCOPED_INFO("and reads the same the second time"); + CHECK_THAT(sample(curve, 1.5f).mV[VX], Catch::Matchers::WithinAbs(7.5f, 1e-4f)); // A rotation curve leads back the same way. RotationCurve rotations; @@ -1418,13 +1492,12 @@ namespace const LLQuaternion at_end = sample(rotations, 2.f); // q and -q are the same rotation, so compare through the dot product. - ensure_approximately_equals_range("the rotation tail ends on the loop's first pose", - fabsf(dot(at_end, LLQuaternion(0.f, LLVector3::z_axis))), - 1.f, 1e-4f); + UNSCOPED_INFO("the rotation tail ends on the loop's first pose"); + CHECK_THAT(fabsf(dot(at_end, LLQuaternion(0.f, LLVector3::z_axis))), + Catch::Matchers::WithinAbs(1.f, 1e-4f)); } - template<> template<> - void llkeyframemotion_object::test<24>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: a hand pose request outlives the animation and respects priority", "[LLKeyframeMotion]") { // Sampling asks the character for the hand pose the animation carries. // The request has to outlive the animation: the motion can go, and the @@ -1436,42 +1509,48 @@ namespace { LLKeyframeMotion motion(LLUUID::generateNewID()); - ensure("the animation is read", load(motion, anim, buffer.data(), (S32)buffer.size())); - ensure("nothing is asked for before it is sampled", !mCharacter.hasHandPoseRequest()); + UNSCOPED_INFO("the animation is read"); + REQUIRE(load(motion, anim, buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO("nothing is asked for before it is sampled"); + CHECK_FALSE(mCharacter.hasHandPoseRequest()); sampleAt(motion, 0.f, "mPelvis"); - ensure("sampling asks for a hand pose", mCharacter.hasHandPoseRequest()); - ensure_equals("the one the animation carries", - mCharacter.getHandPoseRequest(), (S32)LLHandMotion::HAND_POSE_FIST); - ensure_equals("at the animation's highest joint priority", - mCharacter.getHandPoseRequestPriority(), (S32)LLJoint::HIGH_PRIORITY); + UNSCOPED_INFO("sampling asks for a hand pose"); + CHECK(mCharacter.hasHandPoseRequest()); + UNSCOPED_INFO("the one the animation carries"); + CHECK(mCharacter.getHandPoseRequest() == (S32)LLHandMotion::HAND_POSE_FIST); + UNSCOPED_INFO("at the animation's highest joint priority"); + CHECK(mCharacter.getHandPoseRequestPriority() == (S32)LLJoint::HIGH_PRIORITY); } LLKeyframeDataCache::purge(); - ensure_equals("the animation is gone", LLKeyframeDataCache::size(), 0u); - ensure("the request is not", mCharacter.hasHandPoseRequest()); - ensure_equals("and still reads the pose", - mCharacter.getHandPoseRequest(), (S32)LLHandMotion::HAND_POSE_FIST); + UNSCOPED_INFO("the animation is gone"); + CHECK(LLKeyframeDataCache::size() == 0u); + UNSCOPED_INFO("the request is not"); + CHECK(mCharacter.hasHandPoseRequest()); + UNSCOPED_INFO("and still reads the pose"); + CHECK(mCharacter.getHandPoseRequest() == (S32)LLHandMotion::HAND_POSE_FIST); // A motion asking at a lower priority does not take the request away. anim.mHandPose = LLHandMotion::HAND_POSE_POINT; anim.mJoints[0].mPriority = LLJoint::MEDIUM_PRIORITY; LLKeyframeMotion lower(LLUUID::generateNewID()); - ensure("the lower priority animation is read", load(lower, anim, buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO("the lower priority animation is read"); + REQUIRE(load(lower, anim, buffer.data(), (S32)buffer.size())); sampleAt(lower, 0.f, "mPelvis"); - ensure_equals("a lower priority motion leaves the request standing", - mCharacter.getHandPoseRequest(), (S32)LLHandMotion::HAND_POSE_FIST); + UNSCOPED_INFO("a lower priority motion leaves the request standing"); + CHECK(mCharacter.getHandPoseRequest() == (S32)LLHandMotion::HAND_POSE_FIST); // One asking at the same priority, later, does. anim.mHandPose = LLHandMotion::HAND_POSE_PEACE_R; anim.mJoints[0].mPriority = LLJoint::HIGH_PRIORITY; LLKeyframeMotion equal(LLUUID::generateNewID()); - ensure("the equal priority animation is read", load(equal, anim, buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO("the equal priority animation is read"); + REQUIRE(load(equal, anim, buffer.data(), (S32)buffer.size())); sampleAt(equal, 0.f, "mPelvis"); - ensure_equals("an equal priority motion asking later takes the request", - mCharacter.getHandPoseRequest(), (S32)LLHandMotion::HAND_POSE_PEACE_R); + UNSCOPED_INFO("an equal priority motion asking later takes the request"); + CHECK(mCharacter.getHandPoseRequest() == (S32)LLHandMotion::HAND_POSE_PEACE_R); } - template<> template<> - void llkeyframemotion_object::test<25>() + TEST_CASE_METHOD(llkeyframemotion_data, "LLKeyframeMotion: a loop a day old still advances by the frame time", "[LLKeyframeMotion]") { // A loop that has been playing for a day still moves by the time a frame // took. Its phase comes from the time since activation: a day of that in @@ -1479,7 +1558,8 @@ namespace // or by nothing, never by the 4.2 ms that passed. std::vector buffer(8192); LLKeyframeMotion motion(LLUUID::generateNewID()); - ensure("the animation is read", load(motion, two_joint_animation(), buffer.data(), (S32)buffer.size())); + UNSCOPED_INFO("the animation is read"); + REQUIRE(load(motion, two_joint_animation(), buffer.data(), (S32)buffer.size())); motion.setStopped(false); const F64 a_day = 86400.0; @@ -1489,6 +1569,7 @@ namespace const F32 before = motion.getLastUpdateTime(); motion.onUpdate(a_day + frame, mask); const F32 after = motion.getLastUpdateTime(); - ensure_approximately_equals_range("the loop moves by the frame", after - before, (F32)frame, 1e-5f); + UNSCOPED_INFO("the loop moves by the frame"); + CHECK_THAT(after - before, Catch::Matchers::WithinAbs((F32)frame, 1e-5f)); } } diff --git a/indra/llcharacter/tests/llmotioncontroller_test.cpp b/indra/llcharacter/tests/llmotioncontroller_test.cpp index ceece16b05..434cbf2782 100644 --- a/indra/llcharacter/tests/llmotioncontroller_test.cpp +++ b/indra/llcharacter/tests/llmotioncontroller_test.cpp @@ -26,7 +26,8 @@ #include "llmotioncontroller.h" -#include "../test/lltut.h" +#include +#include "altestmatchers.h" namespace { @@ -38,22 +39,11 @@ namespace constexpr S32 FRAMES_PER_QUANTUM = 16; } -namespace tut -{ namespace { - struct llmotioncontroller_data - { - }; - typedef test_group llmotioncontroller_test; - typedef llmotioncontroller_test::object llmotioncontroller_object; - tut::llmotioncontroller_test llmotioncontroller_testcase("LLMotionController"); -} - typedef LLMotionController::QuantumStep QuantumStep; - template<> template<> - void llmotioncontroller_object::test<1>() + TEST_CASE("LLMotionController: computeQuantumStep crosses one boundary per quantum of wall time", "[LLMotionController]") { // Animation time has to track wall time. Run a clock for ten seconds // at the largest quantum the viewer hands out, a quarter second, and @@ -74,11 +64,11 @@ namespace last_count = q.count; } } - ensure_equals("ten seconds at a quarter-second quantum crosses forty boundaries", crossings, 40); + UNSCOPED_INFO("ten seconds at a quarter-second quantum crosses forty boundaries"); + CHECK(crossings == 40); } - template<> template<> - void llmotioncontroller_object::test<2>() + TEST_CASE("LLMotionController: the quantized target stays at most one quantum ahead of real time", "[LLMotionController]") { // The quantized clock runs one quantum ahead of real time, never more // and never behind: the pose it computes is the target the frames in @@ -91,13 +81,14 @@ namespace continuous += dt; const QuantumStep q = LLMotionController::computeQuantumStep(continuous, step, 0); const F32 target = q.count * step; - ensure("target is ahead of real time", target > continuous); - ensure("target is no more than one quantum ahead", target <= continuous + step + 1e-5f); + UNSCOPED_INFO("target is ahead of real time"); + REQUIRE(target > continuous); + UNSCOPED_INFO("target is no more than one quantum ahead"); + REQUIRE(target <= continuous + step + 1e-5f); } } - template<> template<> - void llmotioncontroller_object::test<3>() + TEST_CASE("LLMotionController: interp never falls within a quantum and quanta have equal frames", "[LLMotionController]") { // Within a quantum interp climbs from zero toward one and never comes // back; crossing a boundary is reported exactly once, and every @@ -111,41 +102,49 @@ namespace { continuous += FRAME; const QuantumStep q = LLMotionController::computeQuantumStep(continuous, QUANTUM, last_count); - ensure("interp is at least zero", q.interp >= 0.f); - ensure("interp is below one", q.interp < 1.f); + UNSCOPED_INFO("interp is at least zero"); + REQUIRE(q.interp >= 0.f); + UNSCOPED_INFO("interp is below one"); + REQUIRE(q.interp < 1.f); if (q.advanced) { - ensure_equals("advanced means the count moved by one", q.count, last_count + 1); - ensure_equals("a quantum is the same number of frames every time", frames_in_quantum, FRAMES_PER_QUANTUM); + UNSCOPED_INFO("advanced means the count moved by one"); + REQUIRE(q.count == last_count + 1); + UNSCOPED_INFO("a quantum is the same number of frames every time"); + REQUIRE(frames_in_quantum == FRAMES_PER_QUANTUM); frames_in_quantum = 0; last_count = q.count; } else { - ensure("interp does not go backwards inside a quantum", q.interp >= last_interp); + UNSCOPED_INFO("interp does not go backwards inside a quantum"); + REQUIRE(q.interp >= last_interp); } last_interp = q.interp; ++frames_in_quantum; } } - template<> template<> - void llmotioncontroller_object::test<4>() + TEST_CASE("LLMotionController: time at or before zero heads for the first boundary", "[LLMotionController]") { // Time before the first quantum boundary heads for the first boundary, // and time before zero is treated as zero. const QuantumStep at_start = LLMotionController::computeQuantumStep(0.f, QUANTUM, 0); - ensure_equals("the first target is the first boundary", at_start.count, 1); - ensure_equals("nothing of the quantum has elapsed", at_start.interp, 0.f); - ensure("a fresh clock has advanced from nowhere", at_start.advanced); + UNSCOPED_INFO("the first target is the first boundary"); + REQUIRE(at_start.count == 1); + UNSCOPED_INFO("nothing of the quantum has elapsed"); + CHECK(at_start.interp == 0.f); + UNSCOPED_INFO("a fresh clock has advanced from nowhere"); + CHECK(at_start.advanced); const QuantumStep before = LLMotionController::computeQuantumStep(-1.f, QUANTUM, 1); - ensure_equals("negative time does not run the count backwards", before.count, 1); - ensure_equals("nor does it produce a negative interp", before.interp, 0.f); + UNSCOPED_INFO("negative time does not run the count backwards"); + REQUIRE(before.count == 1); + UNSCOPED_INFO("nor does it produce a negative interp"); + CHECK(before.interp == 0.f); } - template<> template<> - void llmotioncontroller_object::test<5>() + TEST_CASE("LLMotionController: quantumInterpolant keeps the blended pose on the linear track", "[LLMotionController]") { // The blender lerps from wherever the pose is now toward the target. To // track real time it needs the fraction of the distance still to go, @@ -159,41 +158,53 @@ namespace { const F32 u = LLMotionController::quantumInterpolant(interp, last_interp); pose = lerp(pose, target, u); - ensure_approximately_equals("the pose sits on the linear track", pose, target * interp, 16); + UNSCOPED_INFO("the pose sits on the linear track"); + REQUIRE_THAT(pose, ll_test::WithinBits(target * interp, 16)); last_interp = interp; } - ensure_approximately_equals("the pose has arrived at the boundary", pose, target, 16); + UNSCOPED_INFO("the pose has arrived at the boundary"); + CHECK_THAT(pose, ll_test::WithinBits(target, 16)); + } + + TEST_CASE("LLMotionController: quantumInterpolant is zero with no progress and one at the end", "[LLMotionController]") + { + // The edges: no progress is no movement, reaching the end is all of + // the way, and a clock already at the end has nothing left to move. + UNSCOPED_INFO("no progress moves nothing"); + CHECK(LLMotionController::quantumInterpolant(0.3f, 0.3f) == 0.f); + UNSCOPED_INFO("reaching the end moves all the way"); + CHECK(LLMotionController::quantumInterpolant(1.f, 0.3f) == 1.f); + UNSCOPED_INFO("at the end already is all the way"); + CHECK(LLMotionController::quantumInterpolant(0.5f, 1.f) == 1.f); + UNSCOPED_INFO("from nothing to something is that fraction"); + CHECK(LLMotionController::quantumInterpolant(0.25f, 0.f) == 0.25f); } - template<> template<> - void llmotioncontroller_object::test<7>() + TEST_CASE("LLMotionController: quantizeTimeStep lands on sixteenths up to a quarter", "[LLMotionController]") { // The requested quantum is a smooth function of size and crowd and // would move a little every frame. It lands on sixteenths, never // finer than one and never coarser than a quarter, and anything under // a sixteenth is not worth quantizing at all. - ensure_equals("a quarter stays a quarter", LLMotionController::quantizeTimeStep(0.25f), 0.25f); - ensure_equals("more than a quarter is capped", LLMotionController::quantizeTimeStep(0.4f), 0.25f); - ensure_equals("a fifth rounds down to three sixteenths", LLMotionController::quantizeTimeStep(0.2f), 0.1875f); - ensure_equals("just under an eighth is a sixteenth", LLMotionController::quantizeTimeStep(0.124f), 0.0625f); - ensure_equals("a twentieth is too fine to bother with", LLMotionController::quantizeTimeStep(0.05f), 0.f); - ensure_equals("nothing asked for is nothing", LLMotionController::quantizeTimeStep(0.f), 0.f); - ensure_equals("a negative request is nothing", LLMotionController::quantizeTimeStep(-1.f), 0.f); + UNSCOPED_INFO("a quarter stays a quarter"); + CHECK(LLMotionController::quantizeTimeStep(0.25f) == 0.25f); + UNSCOPED_INFO("more than a quarter is capped"); + CHECK(LLMotionController::quantizeTimeStep(0.4f) == 0.25f); + UNSCOPED_INFO("a fifth rounds down to three sixteenths"); + CHECK(LLMotionController::quantizeTimeStep(0.2f) == 0.1875f); + UNSCOPED_INFO("just under an eighth is a sixteenth"); + CHECK(LLMotionController::quantizeTimeStep(0.124f) == 0.0625f); + UNSCOPED_INFO("a twentieth is too fine to bother with"); + CHECK(LLMotionController::quantizeTimeStep(0.05f) == 0.f); + UNSCOPED_INFO("nothing asked for is nothing"); + CHECK(LLMotionController::quantizeTimeStep(0.f) == 0.f); + UNSCOPED_INFO("a negative request is nothing"); + CHECK(LLMotionController::quantizeTimeStep(-1.f) == 0.f); // Every rung is exact in binary, so a clock stepping by one is exact. F32 t = 0.f; for (S32 i = 0; i < 64; ++i) { t += LLMotionController::quantizeTimeStep(0.0625f); } - ensure_equals("sixty-four sixteenths is exactly four", t, 4.f); - } - - template<> template<> - void llmotioncontroller_object::test<6>() - { - // The edges: no progress is no movement, reaching the end is all of - // the way, and a clock already at the end has nothing left to move. - ensure_equals("no progress moves nothing", LLMotionController::quantumInterpolant(0.3f, 0.3f), 0.f); - ensure_equals("reaching the end moves all the way", LLMotionController::quantumInterpolant(1.f, 0.3f), 1.f); - ensure_equals("at the end already is all the way", LLMotionController::quantumInterpolant(0.5f, 1.f), 1.f); - ensure_equals("from nothing to something is that fraction", LLMotionController::quantumInterpolant(0.25f, 0.f), 0.25f); + UNSCOPED_INFO("sixty-four sixteenths is exactly four"); + CHECK(t == 4.f); } } diff --git a/indra/llcharacter/tests/llmultigesture_test.cpp b/indra/llcharacter/tests/llmultigesture_test.cpp index 20a20db4e7..479b6761d9 100644 --- a/indra/llcharacter/tests/llmultigesture_test.cpp +++ b/indra/llcharacter/tests/llmultigesture_test.cpp @@ -28,7 +28,7 @@ #include "lldatapacker.h" -#include "../test/lltut.h" +#include #include #include @@ -75,7 +75,8 @@ namespace { std::vector buffer(gesture.getMaxSerialSize() + 1, '\0'); LLDataPackerAsciiBuffer dp(buffer.data(), (S32)buffer.size()); - tut::ensure("the gesture serializes", gesture.serialize(dp)); + UNSCOPED_INFO("the gesture serializes"); + REQUIRE(gesture.serialize(dp)); buffer.resize(dp.getCurrentSize()); buffer.back() = '\0'; return buffer; @@ -86,7 +87,8 @@ namespace { std::vector buffer(step.getMaxSerialSize() + 1, '\0'); LLDataPackerAsciiBuffer dp(buffer.data(), (S32)buffer.size()); - tut::ensure("the step serializes", step.serialize(dp)); + UNSCOPED_INFO("the step serializes"); + REQUIRE(step.serialize(dp)); buffer.resize(dp.getCurrentSize()); buffer.back() = '\0'; return buffer; @@ -98,9 +100,14 @@ namespace { const std::string text(bytes.data()); const size_t last = text.find_last_of('\n'); - tut::ensure("the step has a last field", last != std::string::npos && last > 0); + { + INFO("the step has a last field"); + REQUIRE(last != std::string::npos); + REQUIRE(last > 0); + } const size_t previous = text.find_last_of('\n', last - 1); - tut::ensure("and one before it", previous != std::string::npos); + UNSCOPED_INFO("and one before it"); + REQUIRE(previous != std::string::npos); std::vector shortened(bytes.begin(), bytes.begin() + previous + 1); shortened.push_back('\0'); @@ -116,20 +123,9 @@ namespace } } -namespace tut -{ namespace { - struct llmultigesture_data - { - }; - typedef test_group llmultigesture_test; - typedef llmultigesture_test::object llmultigesture_object; - tut::llmultigesture_test llmultigesture_testcase("LLMultiGesture"); -} - - template<> template<> - void llmultigesture_object::test<1>() + TEST_CASE("LLMultiGesture: a gesture it wrote reads back", "[LLMultiGesture]") { // What it writes it reads back. std::unique_ptr written = sample_gesture(); @@ -137,24 +133,29 @@ namespace LLMultiGesture read; LLDataPackerAsciiBuffer dp(bytes.data(), (S32)bytes.size()); - ensure("a gesture it wrote is accepted", read.deserialize(dp)); - - ensure_equals("key", read.mKey, written->mKey); - ensure_equals("mask", read.mMask, written->mMask); - ensure_equals("trigger", read.mTrigger, written->mTrigger); - ensure_equals("replace text", read.mReplaceText, written->mReplaceText); - ensure_equals("step count", read.mSteps.size(), written->mSteps.size()); - - ensure_equals("the animation step came back", - static_cast(read.mSteps[0])->mAnimName, + UNSCOPED_INFO("a gesture it wrote is accepted"); + REQUIRE(read.deserialize(dp)); + + UNSCOPED_INFO("key"); + CHECK(read.mKey == written->mKey); + UNSCOPED_INFO("mask"); + CHECK(read.mMask == written->mMask); + UNSCOPED_INFO("trigger"); + CHECK(read.mTrigger == written->mTrigger); + UNSCOPED_INFO("replace text"); + CHECK(read.mReplaceText == written->mReplaceText); + UNSCOPED_INFO("step count"); + REQUIRE(read.mSteps.size() == written->mSteps.size()); + + UNSCOPED_INFO("the animation step came back"); + CHECK(static_cast(read.mSteps[0])->mAnimName == std::string("express_wave")); - ensure_equals("the chat step came back", - static_cast(read.mSteps[2])->mChatText, + UNSCOPED_INFO("the chat step came back"); + CHECK(static_cast(read.mSteps[2])->mChatText == std::string("hello")); } - template<> template<> - void llmultigesture_object::test<2>() + TEST_CASE("LLMultiGesture: a gesture cut short anywhere is refused", "[LLMultiGesture]") { // Cut anywhere, and it is refused rather than half read. Every prefix // of a real gesture, so the cut lands in each field in turn. @@ -173,12 +174,12 @@ namespace LLMultiGesture read; LLDataPackerAsciiBuffer dp(truncated.data(), (S32)truncated.size()); - ensure("a gesture cut short is refused", !read.deserialize(dp)); + UNSCOPED_INFO("a gesture cut short is refused"); + REQUIRE_FALSE(read.deserialize(dp)); } } - template<> template<> - void llmultigesture_object::test<3>() + TEST_CASE("LLMultiGesture: a step count with no steps behind it is refused", "[LLMultiGesture]") { // The step count says how many to read and arrives in the asset. Set // against a body that has none of them, it used to be the number of @@ -198,12 +199,13 @@ namespace LLMultiGesture read; LLDataPackerAsciiBuffer dp(bytes.data(), (S32)bytes.size()); - ensure("a step count with no steps behind it is refused", !read.deserialize(dp)); - ensure("and nothing was built from it", read.mSteps.empty()); + UNSCOPED_INFO("a step count with no steps behind it is refused"); + REQUIRE_FALSE(read.deserialize(dp)); + UNSCOPED_INFO("and nothing was built from it"); + REQUIRE(read.mSteps.empty()); } - template<> template<> - void llmultigesture_object::test<4>() + TEST_CASE("LLMultiGesture: a step count past one real step is refused", "[LLMultiGesture]") { // The same, with one real step in front of the lie: what was read is // kept, the gesture is still refused, and the count does not turn into @@ -226,13 +228,13 @@ namespace LLMultiGesture read; LLDataPackerAsciiBuffer dp(bytes.data(), (S32)bytes.size()); - ensure("still refused", !read.deserialize(dp)); - ensure("and stopped at the one step the body actually held", - read.mSteps.size() <= 1u); + UNSCOPED_INFO("still refused"); + REQUIRE_FALSE(read.deserialize(dp)); + UNSCOPED_INFO("and stopped at the one step the body actually held"); + REQUIRE(read.mSteps.size() <= 1u); } - template<> template<> - void llmultigesture_object::test<5>() + TEST_CASE("LLMultiGesture: each step reads back and refuses a missing last field", "[LLMultiGesture]") { // Each step reads back what it wrote, and refuses what is missing its // last field. Through a whole gesture the difference does not show -- @@ -246,13 +248,16 @@ namespace const std::vector bytes = serialize_step(full); LLGestureStepAnimation whole; - ensure("the animation step reads back", step_accepts(whole, bytes)); - ensure_equals("with its name", whole.mAnimName, full.mAnimName); - ensure_equals("and its flags", whole.mFlags, full.mFlags); + UNSCOPED_INFO("the animation step reads back"); + CHECK(step_accepts(whole, bytes)); + UNSCOPED_INFO("with its name"); + CHECK(whole.mAnimName == full.mAnimName); + UNSCOPED_INFO("and its flags"); + CHECK(whole.mFlags == full.mFlags); LLGestureStepAnimation cut; - ensure("an animation step missing its flags is refused", - !step_accepts(cut, without_last_field(bytes))); + UNSCOPED_INFO("an animation step missing its flags is refused"); + CHECK_FALSE(step_accepts(cut, without_last_field(bytes))); } { LLGestureStepSound full; @@ -262,12 +267,14 @@ namespace const std::vector bytes = serialize_step(full); LLGestureStepSound whole; - ensure("the sound step reads back", step_accepts(whole, bytes)); - ensure_equals("with its name", whole.mSoundName, full.mSoundName); + UNSCOPED_INFO("the sound step reads back"); + CHECK(step_accepts(whole, bytes)); + UNSCOPED_INFO("with its name"); + CHECK(whole.mSoundName == full.mSoundName); LLGestureStepSound cut; - ensure("a sound step missing its flags is refused", - !step_accepts(cut, without_last_field(bytes))); + UNSCOPED_INFO("a sound step missing its flags is refused"); + CHECK_FALSE(step_accepts(cut, without_last_field(bytes))); } { LLGestureStepChat full; @@ -276,13 +283,16 @@ namespace const std::vector bytes = serialize_step(full); LLGestureStepChat whole; - ensure("the chat step reads back", step_accepts(whole, bytes)); - ensure_equals("with its text", whole.mChatText, full.mChatText); - ensure_equals("and its flags", whole.mFlags, full.mFlags); + UNSCOPED_INFO("the chat step reads back"); + CHECK(step_accepts(whole, bytes)); + UNSCOPED_INFO("with its text"); + CHECK(whole.mChatText == full.mChatText); + UNSCOPED_INFO("and its flags"); + CHECK(whole.mFlags == full.mFlags); LLGestureStepChat cut; - ensure("a chat step missing its flags is refused", - !step_accepts(cut, without_last_field(bytes))); + UNSCOPED_INFO("a chat step missing its flags is refused"); + CHECK_FALSE(step_accepts(cut, without_last_field(bytes))); } { LLGestureStepWait full; @@ -291,17 +301,18 @@ namespace const std::vector bytes = serialize_step(full); LLGestureStepWait whole; - ensure("the wait step reads back", step_accepts(whole, bytes)); - ensure_equals("with its flags", whole.mFlags, full.mFlags); + UNSCOPED_INFO("the wait step reads back"); + CHECK(step_accepts(whole, bytes)); + UNSCOPED_INFO("with its flags"); + CHECK(whole.mFlags == full.mFlags); LLGestureStepWait cut; - ensure("a wait step missing its flags is refused", - !step_accepts(cut, without_last_field(bytes))); + UNSCOPED_INFO("a wait step missing its flags is refused"); + CHECK_FALSE(step_accepts(cut, without_last_field(bytes))); } } - template<> template<> - void llmultigesture_object::test<6>() + TEST_CASE("LLMultiGesture: a bad header is refused and an empty step list loads", "[LLMultiGesture]") { // The header, one thing wrong at a time. Each of these is a line in // the file, and the file comes from whoever sent the gesture. @@ -314,26 +325,27 @@ namespace return gesture.deserialize(dp); }; - ensure("a version from another format is refused", - !read("1\n71\n0\n/wave\n\n0\n")); - ensure("and so is one from no format at all", - !read("999\n71\n0\n/wave\n\n0\n")); - ensure("a negative step count is refused", - !read("2\n71\n0\n/wave\n\n-5\n")); - ensure("a step type off the end of the list is refused", - !read("2\n71\n0\n/wave\n\n1\n99\n")); - ensure("a negative step type is refused", - !read("2\n71\n0\n/wave\n\n1\n-1\n")); - ensure("nothing at all is refused", !read("")); - ensure("and so is a lone newline", !read("\n")); + UNSCOPED_INFO("a version from another format is refused"); + CHECK_FALSE(read("1\n71\n0\n/wave\n\n0\n")); + UNSCOPED_INFO("and so is one from no format at all"); + CHECK_FALSE(read("999\n71\n0\n/wave\n\n0\n")); + UNSCOPED_INFO("a negative step count is refused"); + CHECK_FALSE(read("2\n71\n0\n/wave\n\n-5\n")); + UNSCOPED_INFO("a step type off the end of the list is refused"); + CHECK_FALSE(read("2\n71\n0\n/wave\n\n1\n99\n")); + UNSCOPED_INFO("a negative step type is refused"); + CHECK_FALSE(read("2\n71\n0\n/wave\n\n1\n-1\n")); + UNSCOPED_INFO("nothing at all is refused"); + CHECK_FALSE(read("")); + UNSCOPED_INFO("and so is a lone newline"); + CHECK_FALSE(read("\n")); // A gesture with no steps in it is a gesture, and loads. - ensure("no steps is not the same as broken", - read("2\n71\n0\n/wave\n\n0\n")); + UNSCOPED_INFO("no steps is not the same as broken"); + CHECK(read("2\n71\n0\n/wave\n\n0\n")); } - template<> template<> - void llmultigesture_object::test<7>() + TEST_CASE("LLMultiGesture: a step count smaller than the body reads only that many", "[LLMultiGesture]") { // A count smaller than the body leaves the rest unread, which is not // an error: the reader takes what it was told to take. @@ -349,12 +361,15 @@ namespace { at = text.find('\n', at) + 1; } - ensure("the step count line was found", text[at] == '4'); + UNSCOPED_INFO("the step count line was found"); + CHECK(text[at] == '4'); fewer[at] = '2'; LLMultiGesture read; LLDataPackerAsciiBuffer dp(fewer.data(), (S32)fewer.size()); - ensure("a gesture that claims fewer steps than it holds is taken", read.deserialize(dp)); - ensure_equals("with the number it claimed", read.mSteps.size(), 2u); + UNSCOPED_INFO("a gesture that claims fewer steps than it holds is taken"); + REQUIRE(read.deserialize(dp)); + UNSCOPED_INFO("with the number it claimed"); + REQUIRE(read.mSteps.size() == 2u); } } diff --git a/indra/llcharacter/tests/llpose_test.cpp b/indra/llcharacter/tests/llpose_test.cpp index a722691dd1..d6ad015c97 100644 --- a/indra/llcharacter/tests/llpose_test.cpp +++ b/indra/llcharacter/tests/llpose_test.cpp @@ -36,7 +36,8 @@ #include "altestmotion.h" -#include "../test/lltut.h" +#include +#include #include #include @@ -49,15 +50,19 @@ namespace void ensure_vec3_equals(const char* msg, const LLVector3& actual, const LLVector3& expected) { - tut::ensure_approximately_equals_range(msg, actual.mV[VX], expected.mV[VX], TOLERANCE); - tut::ensure_approximately_equals_range(msg, actual.mV[VY], expected.mV[VY], TOLERANCE); - tut::ensure_approximately_equals_range(msg, actual.mV[VZ], expected.mV[VZ], TOLERANCE); + UNSCOPED_INFO(msg); + REQUIRE_THAT(actual.mV[VX], Catch::Matchers::WithinAbs(expected.mV[VX], TOLERANCE)); + UNSCOPED_INFO(msg); + REQUIRE_THAT(actual.mV[VY], Catch::Matchers::WithinAbs(expected.mV[VY], TOLERANCE)); + UNSCOPED_INFO(msg); + REQUIRE_THAT(actual.mV[VZ], Catch::Matchers::WithinAbs(expected.mV[VZ], TOLERANCE)); } // q and -q are the same rotation, so compare through the absolute dot product. void ensure_quat_equals(const char* msg, const LLQuaternion& actual, const LLQuaternion& expected) { - tut::ensure_approximately_equals_range(msg, fabsf(dot(actual, expected)), 1.f, TOLERANCE); + UNSCOPED_INFO(msg); + REQUIRE_THAT(fabsf(dot(actual, expected)), Catch::Matchers::WithinAbs(1.f, TOLERANCE)); } LLPointer make_state(LLJoint* joint, U32 usage, F32 weight = 1.f, @@ -75,8 +80,6 @@ namespace const LLQuaternion ROT_C(-0.8f, LLVector3::z_axis); } -namespace tut -{ namespace { struct joints @@ -95,38 +98,31 @@ namespace 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>() + TEST_CASE_METHOD(lljointstate_data, "LLJointState: a fresh state has no usage or weight and default channels", "[LLJointState]") { // What a fresh state holds. The blender seeds a channel from the joint // when nothing contributes to it, so these only ever reach a joint // through a state that carries the matching usage, but a state that // came back with something else in it would be a silent wrong pose. LLPointer state = new LLJointState(&mA); - ensure_equals("no usage", state->getUsage(), 0u); - ensure_equals("no weight", state->getWeight(), 0.f); + UNSCOPED_INFO("no usage"); + CHECK(state->getUsage() == 0u); + UNSCOPED_INFO("no weight"); + CHECK(state->getWeight() == 0.f); ensure_vec3_equals("position starts at the origin", state->getPosition(), LLVector3::zero); ensure_quat_equals("rotation starts at identity", state->getRotation(), LLQuaternion::DEFAULT); ensure_vec3_equals("scale starts at zero", state->getScale(), LLVector3::zero); } - template<> template<> - void lljointstate_object::test<2>() + TEST_CASE_METHOD(lljointstate_data, "LLJointState: values set as scalars read back through both views", "[LLJointState]") { // The two views of a channel are the same channel: written either // way, read either way. @@ -153,8 +149,7 @@ namespace ensure_quat_equals("rotation through the vector view", from_vector, ROT_A); } - template<> template<> - void lljointstate_object::test<3>() + TEST_CASE_METHOD(lljointstate_data, "LLJointState: values set through the vector view read back as scalars", "[LLJointState]") { // and written through the vector view, read through the scalar one LLPointer state = new LLJointState(&mA); @@ -177,177 +172,31 @@ namespace //------------------------------------------------------------------------- // LLPose //------------------------------------------------------------------------- -namespace -{ struct llpose_data : public joints { LLPose mPose; }; - typedef test_group llpose_test; - typedef llpose_test::object llpose_object; - tut::llpose_test llpose_testcase("LLPose"); -} - - template<> template<> - void llpose_object::test<1>() - { - ensure_equals("empty pose has no states", mPose.getNumJointStates(), 0); - ensure("empty pose has nothing to iterate", mPose.getJointStates().empty()); - ensure("empty pose finds nothing by joint", mPose.findJointState(&mA) == nullptr); - ensure("empty pose finds nothing by name", mPose.findJointState(std::string("a")) == nullptr); - ensure_equals("empty pose weight", mPose.getWeight(), 0.f); - } - - template<> template<> - void llpose_object::test<2>() - { - LLPointer sa = make_state(&mA, LLJointState::ROT); - ensure("addJointState returns true", mPose.addJointState(sa)); - ensure_equals("one state", mPose.getNumJointStates(), 1); - ensure("find by joint", mPose.findJointState(&mA) == sa.get()); - ensure("find by name", mPose.findJointState(std::string("a")) == sa.get()); - ensure("other joint absent", mPose.findJointState(&mB) == nullptr); - ensure_equals("one state to iterate", mPose.getJointStates().size(), (size_t)1); - ensure("the state to iterate is the one added", mPose.getJointStates()[0].get() == sa.get()); - } - - template<> template<> - void llpose_object::test<3>() - { - // Two states on the same joint collapse to one; the first added wins. - LLPointer first = make_state(&mA, LLJointState::ROT); - LLPointer second = make_state(&mA, LLJointState::POS); - mPose.addJointState(first); - mPose.addJointState(second); - ensure_equals("same joint dedups", mPose.getNumJointStates(), 1); - ensure("first state kept", mPose.findJointState(&mA) == first.get()); - } - - template<> template<> - void llpose_object::test<4>() - { - LLPointer sa = make_state(&mA, LLJointState::ROT); - LLPointer sb = make_state(&mB, LLJointState::ROT); - mPose.addJointState(sa); - mPose.addJointState(sb); - ensure_equals("two states", mPose.getNumJointStates(), 2); - - mPose.removeJointState(sa); - ensure_equals("one left after remove", mPose.getNumJointStates(), 1); - ensure("removed state gone", mPose.findJointState(&mA) == nullptr); - ensure("other state stays", mPose.findJointState(&mB) == sb.get()); - - mPose.addJointState(sa); - ensure_equals("re-add after remove", mPose.getNumJointStates(), 2); - - mPose.removeAllJointStates(); - ensure_equals("removeAll empties", mPose.getNumJointStates(), 0); - ensure("nothing to iterate", mPose.getJointStates().empty()); - } - - template<> template<> - void llpose_object::test<5>() - { - LLPointer sa = make_state(&mA, LLJointState::ROT); - LLPointer sb = make_state(&mB, LLJointState::ROT); - LLPointer sc = make_state(&mC, LLJointState::ROT); - mPose.addJointState(sa); - mPose.addJointState(sb); - mPose.addJointState(sc); - - std::vector seen; - for (const LLPointer& state : mPose.getJointStates()) - { - seen.push_back(state.get()); - } - ensure_equals("iteration visits every state", seen.size(), (size_t)3); - ensure("iteration visits a", std::count(seen.begin(), seen.end(), sa.get()) == 1); - ensure("iteration visits b", std::count(seen.begin(), seen.end(), sb.get()) == 1); - ensure("iteration visits c", std::count(seen.begin(), seen.end(), sc.get()) == 1); - } - - template<> template<> - void llpose_object::test<6>() - { - LLPointer sa = make_state(&mA, LLJointState::ROT, 0.f); - LLPointer sb = make_state(&mB, LLJointState::ROT, 0.f); - mPose.addJointState(sa); - mPose.addJointState(sb); - - mPose.setWeight(0.5f); - ensure_equals("pose weight stored", mPose.getWeight(), 0.5f); - ensure_equals("weight reaches a", sa->getWeight(), 0.5f); - ensure_equals("weight reaches b", sb->getWeight(), 0.5f); - - // A state added after setWeight is brought up to the pose's weight. - // Every state carrying the pose weight is what lets setWeight skip - // the walk when the weight has not changed; left at its own weight, - // a late state would sit there until the next change. - LLPointer sc = make_state(&mC, LLJointState::ROT, 0.f); - mPose.addJointState(sc); - ensure_equals("late state receives the pose weight", sc->getWeight(), 0.5f); - - mPose.setWeight(1.f); - ensure_equals("second setWeight reaches a", sa->getWeight(), 1.f); - ensure_equals("second setWeight reaches b", sb->getWeight(), 1.f); - ensure_equals("second setWeight reaches c", sc->getWeight(), 1.f); - } - - template<> template<> - void llpose_object::test<7>() - { - // The poser's runtime path: find the state on a joint, remove it, add - // another for the same joint. Dedup is by joint, so two states on - // one joint are still one entry; find by name still reaches it; and - // removing a state that is not there is harmless. - LLPointer first = make_state(&mA, LLJointState::ROT); - LLPointer second = make_state(&mA, LLJointState::POS); - LLPointer other = make_state(&mB, LLJointState::ROT); - mPose.addJointState(first); - mPose.addJointState(second); - mPose.addJointState(other); - ensure_equals("two states on one joint are one entry", mPose.getNumJointStates(), 2); - ensure("found by joint", mPose.findJointState(&mA) == first.get()); - ensure("found by name", mPose.findJointState("a") == first.get()); - ensure("the other joint is found by name too", mPose.findJointState("b") == other.get()); - ensure("an unknown name finds nothing", mPose.findJointState("nobody") == nullptr); - - mPose.removeJointState(mPose.findJointState(&mA)); - ensure_equals("removed by the state found", mPose.getNumJointStates(), 1); - ensure("the joint is gone", mPose.findJointState(&mA) == nullptr); - mPose.removeJointState(second); - ensure_equals("removing an absent state changes nothing", mPose.getNumJointStates(), 1); - - mPose.addJointState(second); - ensure("the joint can be re-added with a different state", mPose.findJointState(&mA) == second.get()); - } //------------------------------------------------------------------------- // LLJointStateBlender //------------------------------------------------------------------------- -namespace -{ struct lljointstateblender_data : public joints { LLJointStateBlender mBlender; }; - typedef test_group lljointstateblender_test; - typedef lljointstateblender_test::object lljointstateblender_object; - tut::lljointstateblender_test lljointstateblender_testcase("LLJointStateBlender"); -} - template<> template<> - void lljointstateblender_object::test<1>() + TEST_CASE_METHOD(lljointstateblender_data, "LLJointStateBlender: addJointState refuses a state with no joint", "[LLJointStateBlender]") { LLPointer orphan = make_state(nullptr, LLJointState::ROT); - ensure("state without a joint is refused", !mBlender.addJointState(orphan, LLJoint::MEDIUM_PRIORITY, false)); + UNSCOPED_INFO("state without a joint is refused"); + CHECK_FALSE(mBlender.addJointState(orphan, LLJoint::MEDIUM_PRIORITY, false)); LLPointer sa = make_state(&mA, LLJointState::ROT); - ensure("state with a joint is accepted", mBlender.addJointState(sa, LLJoint::MEDIUM_PRIORITY, false)); + UNSCOPED_INFO("state with a joint is accepted"); + CHECK(mBlender.addJointState(sa, LLJoint::MEDIUM_PRIORITY, false)); } - template<> template<> - void lljointstateblender_object::test<2>() + TEST_CASE_METHOD(lljointstateblender_data, "LLJointStateBlender: a lone full-weight state is applied to its joint", "[LLJointStateBlender]") { LLPointer sa = make_state(&mA, LLJointState::POS | LLJointState::ROT | LLJointState::SCALE); sa->setPosition(LLVector3(1.f, 2.f, 3.f)); @@ -362,8 +211,7 @@ namespace ensure_vec3_equals("single state scale", mA.getScale(), LLVector3(2.f, 2.f, 2.f)); } - template<> template<> - void lljointstateblender_object::test<3>() + TEST_CASE_METHOD(lljointstateblender_data, "LLJointStateBlender: a zero-weight state leaves the joint alone", "[LLJointStateBlender]") { LLPointer sa = make_state(&mA, LLJointState::POS | LLJointState::ROT, 0.f); sa->setPosition(LLVector3(1.f, 2.f, 3.f)); @@ -376,8 +224,7 @@ namespace ensure_quat_equals("zero-weight state leaves rotation", mA.getRotation(), LLQuaternion::DEFAULT); } - template<> template<> - void lljointstateblender_object::test<4>() + TEST_CASE_METHOD(lljointstateblender_data, "LLJointStateBlender: higher priority wins and equal priority keeps the first", "[LLJointStateBlender]") { // Higher priority wins at full weight regardless of insertion order. LLPointer low = make_state(&mA, LLJointState::ROT); @@ -404,8 +251,7 @@ namespace ensure_quat_equals("equal priority keeps the first", mA.getRotation(), ROT_A); } - template<> template<> - void lljointstateblender_object::test<5>() + TEST_CASE_METHOD(lljointstateblender_data, "LLJointStateBlender: two half-weight positions meet in the middle", "[LLJointStateBlender]") { // Two half-weight positions at the same priority meet in the middle. LLPointer first = make_state(&mA, LLJointState::POS, 0.5f); @@ -420,8 +266,7 @@ namespace ensure_vec3_equals("half and half", mA.getPosition(), LLVector3(1.f, 2.f, 0.f)); } - template<> template<> - void lljointstateblender_object::test<6>() + TEST_CASE_METHOD(lljointstateblender_data, "LLJointStateBlender: an additive state adds position and scale and composes rotation", "[LLJointStateBlender]") { LLPointer base = make_state(&mA, LLJointState::POS | LLJointState::ROT | LLJointState::SCALE); base->setPosition(LLVector3(0.f, 1.f, 0.f)); @@ -442,8 +287,7 @@ namespace ensure_vec3_equals("additive scale sums", mA.getScale(), LLVector3(1.5f, 1.f, 1.f)); } - template<> template<> - void lljointstateblender_object::test<7>() + TEST_CASE_METHOD(lljointstateblender_data, "LLJointStateBlender: blending or clearing empties the queued states", "[LLJointStateBlender]") { // blendJointStates() consumes its slots: a second call with nothing added is a no-op. LLPointer sa = make_state(&mA, LLJointState::ROT); @@ -463,29 +307,30 @@ namespace ensure_quat_equals("cleared blend leaves the joint alone", mA.getRotation(), ROT_C); } - template<> template<> - void lljointstateblender_object::test<8>() + TEST_CASE_METHOD(lljointstateblender_data, "LLJointStateBlender: a full blender refuses lower priority and takes higher", "[LLJointStateBlender]") { std::vector> states; for (S32 i = 0; i < JSB_NUM_JOINT_STATES; ++i) { states.push_back(make_state(&mA, LLJointState::ROT)); - ensure("slot accepted", mBlender.addJointState(states.back(), LLJoint::MEDIUM_PRIORITY, false)); + UNSCOPED_INFO("slot accepted"); + REQUIRE(mBlender.addJointState(states.back(), LLJoint::MEDIUM_PRIORITY, false)); } LLPointer lower = make_state(&mA, LLJointState::ROT); - ensure("full blender refuses a lower priority", !mBlender.addJointState(lower, LLJoint::LOW_PRIORITY, false)); + UNSCOPED_INFO("full blender refuses a lower priority"); + CHECK_FALSE(mBlender.addJointState(lower, LLJoint::LOW_PRIORITY, false)); LLPointer higher = make_state(&mA, LLJointState::ROT); higher->setRotation(ROT_B); - ensure("full blender takes a higher priority", mBlender.addJointState(higher, LLJoint::HIGH_PRIORITY, false)); + UNSCOPED_INFO("full blender takes a higher priority"); + CHECK(mBlender.addJointState(higher, LLJoint::HIGH_PRIORITY, false)); mBlender.blendJointStates(); ensure_quat_equals("the higher priority lands in front", mA.getRotation(), ROT_B); } - template<> template<> - void lljointstateblender_object::test<9>() + TEST_CASE_METHOD(lljointstateblender_data, "LLJointStateBlender: a rotation-only blend does not dirty the position", "[LLJointStateBlender]") { // A channel no joint state contributes to holds the value read from // the joint, so writing it back is a no-op that still dirties the @@ -496,7 +341,8 @@ namespace const LLVector3 scale(2.f, 3.f, 4.f); mA.setScale(scale); mA.updateWorldMatrix(); - ensure("joint starts clean", mA.mDirtyFlags == 0); + UNSCOPED_INFO("joint starts clean"); + CHECK(mA.mDirtyFlags == 0); LLPointer rot_only = make_state(&mA, LLJointState::ROT); rot_only->setRotation(ROT_A); @@ -505,14 +351,13 @@ namespace ensure_vec3_equals("rotation-only blend leaves scale alone", mA.getScale(), scale); ensure_quat_equals("rotation-only blend still applies rotation", mA.getRotation(), ROT_A); - ensure("rotation-only blend does not dirty position", - (mA.mDirtyFlags & LLJoint::POSITION_DIRTY) == 0); - ensure("rotation-only blend does dirty rotation", - (mA.mDirtyFlags & LLJoint::ROTATION_DIRTY) != 0); + UNSCOPED_INFO("rotation-only blend does not dirty position"); + CHECK((mA.mDirtyFlags & LLJoint::POSITION_DIRTY) == 0); + UNSCOPED_INFO("rotation-only blend does dirty rotation"); + CHECK((mA.mDirtyFlags & LLJoint::ROTATION_DIRTY) != 0); } - template<> template<> - void lljointstateblender_object::test<10>() + TEST_CASE_METHOD(lljointstateblender_data, "LLJointStateBlender: a scale-only state still writes the scale", "[LLJointStateBlender]") { // A state that does carry SCALE still writes, including when it is the // only channel in use. @@ -525,8 +370,7 @@ namespace ensure_vec3_equals("scale-only blend applies scale", mA.getScale(), scale); } - template<> template<> - void lljointstateblender_object::test<11>() + TEST_CASE_METHOD(lljointstateblender_data, "LLJointStateBlender: a non-finite scale never reaches the joint", "[LLJointStateBlender]") { // A non-finite scale contribution must never reach the joint. Two // layers enforce this, the reset in blendJointStates and the one in @@ -538,12 +382,12 @@ namespace mBlender.addJointState(bad_scale, LLJoint::MEDIUM_PRIORITY, false); mBlender.blendJointStates(); - ensure("joint scale stays finite", mA.getScale().isFinite()); + UNSCOPED_INFO("joint scale stays finite"); + CHECK(mA.getScale().isFinite()); ensure_vec3_equals("non-finite scale is reset", mA.getScale(), LLVector3(1.f, 1.f, 1.f)); } - template<> template<> - void lljointstateblender_object::test<12>() + TEST_CASE_METHOD(lljointstateblender_data, "LLJointStateBlender: holding a pose stops dirtying the joint", "[LLJointStateBlender]") { // The payoff of the scale skip and the rotation compare together: a // motion holding a pose blends the same values every frame, and must @@ -557,15 +401,16 @@ namespace ensure_quat_equals("first blend applies the pose", mA.getRotation(), ROT_A); mA.updateWorldMatrix(); - ensure("joint is clean once the pose has been applied", mA.mDirtyFlags == 0); + UNSCOPED_INFO("joint is clean once the pose has been applied"); + CHECK(mA.mDirtyFlags == 0); mBlender.addJointState(held, LLJoint::MEDIUM_PRIORITY, false); mBlender.blendJointStates(); - ensure("holding the pose does not dirty the joint again", mA.mDirtyFlags == 0); + UNSCOPED_INFO("holding the pose does not dirty the joint again"); + CHECK(mA.mDirtyFlags == 0); } - template<> template<> - void lljointstateblender_object::test<13>() + TEST_CASE_METHOD(lljointstateblender_data, "LLJointStateBlender: a full blender refuses equal priority and blending frees the slots", "[LLJointStateBlender]") { // There are six slots. Filling them refuses anything of lower or // equal priority, and applying the blend hands them all back. @@ -574,18 +419,18 @@ namespace { states.push_back(make_state(&mA, LLJointState::ROT)); states.back()->setRotation(ROT_B); - ensure("a free slot takes the state", - mBlender.addJointState(states.back(), LLJoint::HIGH_PRIORITY, false)); + UNSCOPED_INFO("a free slot takes the state"); + REQUIRE(mBlender.addJointState(states.back(), LLJoint::HIGH_PRIORITY, false)); } LLPointer equal = make_state(&mA, LLJointState::ROT); - ensure("a full blender refuses an equal priority", - !mBlender.addJointState(equal, LLJoint::HIGH_PRIORITY, false)); + UNSCOPED_INFO("a full blender refuses an equal priority"); + CHECK_FALSE(mBlender.addJointState(equal, LLJoint::HIGH_PRIORITY, false)); LLPointer higher = make_state(&mA, LLJointState::ROT); higher->setRotation(ROT_A); - ensure("a full blender takes a higher priority", - mBlender.addJointState(higher, LLJoint::HIGHEST_PRIORITY, false)); + UNSCOPED_INFO("a full blender takes a higher priority"); + CHECK(mBlender.addJointState(higher, LLJoint::HIGHEST_PRIORITY, false)); mBlender.blendJointStates(); ensure_quat_equals("the highest priority won", mA.getRotation(), ROT_A); @@ -594,16 +439,183 @@ namespace // than inheriting six states that are already spoken for. LLPointer next = make_state(&mA, LLJointState::ROT); next->setRotation(ROT_C); - ensure("the slots came back", mBlender.addJointState(next, LLJoint::LOW_PRIORITY, false)); + UNSCOPED_INFO("the slots came back"); + CHECK(mBlender.addJointState(next, LLJoint::LOW_PRIORITY, false)); mBlender.blendJointStates(); ensure_quat_equals("the next frame blends only its own state", mA.getRotation(), ROT_C); } + TEST_CASE_METHOD(llpose_data, "LLPose: an empty pose has no states and no weight", "[LLPose]") + { + UNSCOPED_INFO("empty pose has no states"); + CHECK(mPose.getNumJointStates() == 0); + UNSCOPED_INFO("empty pose has nothing to iterate"); + REQUIRE(mPose.getJointStates().empty()); + UNSCOPED_INFO("empty pose finds nothing by joint"); + REQUIRE(mPose.findJointState(&mA) == nullptr); + UNSCOPED_INFO("empty pose finds nothing by name"); + REQUIRE(mPose.findJointState(std::string("a")) == nullptr); + UNSCOPED_INFO("empty pose weight"); + CHECK(mPose.getWeight() == 0.f); + } + + TEST_CASE_METHOD(llpose_data, "LLPose: an added state is counted and found by joint and by name", "[LLPose]") + { + LLPointer sa = make_state(&mA, LLJointState::ROT); + UNSCOPED_INFO("addJointState returns true"); + CHECK(mPose.addJointState(sa)); + UNSCOPED_INFO("one state"); + CHECK(mPose.getNumJointStates() == 1); + UNSCOPED_INFO("find by joint"); + CHECK(mPose.findJointState(&mA) == sa.get()); + UNSCOPED_INFO("find by name"); + CHECK(mPose.findJointState(std::string("a")) == sa.get()); + UNSCOPED_INFO("other joint absent"); + REQUIRE(mPose.findJointState(&mB) == nullptr); + UNSCOPED_INFO("one state to iterate"); + REQUIRE(mPose.getJointStates().size() == (size_t)1); + UNSCOPED_INFO("the state to iterate is the one added"); + CHECK(mPose.getJointStates()[0].get() == sa.get()); + } + + TEST_CASE_METHOD(llpose_data, "LLPose: states on one joint collapse to the first added", "[LLPose]") + { + // Two states on the same joint collapse to one; the first added wins. + LLPointer first = make_state(&mA, LLJointState::ROT); + LLPointer second = make_state(&mA, LLJointState::POS); + mPose.addJointState(first); + mPose.addJointState(second); + UNSCOPED_INFO("same joint dedups"); + CHECK(mPose.getNumJointStates() == 1); + UNSCOPED_INFO("first state kept"); + CHECK(mPose.findJointState(&mA) == first.get()); + } + + TEST_CASE_METHOD(llpose_data, "LLPose: removeJointState and removeAllJointStates drop states", "[LLPose]") + { + LLPointer sa = make_state(&mA, LLJointState::ROT); + LLPointer sb = make_state(&mB, LLJointState::ROT); + mPose.addJointState(sa); + mPose.addJointState(sb); + UNSCOPED_INFO("two states"); + CHECK(mPose.getNumJointStates() == 2); + + mPose.removeJointState(sa); + UNSCOPED_INFO("one left after remove"); + CHECK(mPose.getNumJointStates() == 1); + UNSCOPED_INFO("removed state gone"); + REQUIRE(mPose.findJointState(&mA) == nullptr); + UNSCOPED_INFO("other state stays"); + CHECK(mPose.findJointState(&mB) == sb.get()); + + mPose.addJointState(sa); + UNSCOPED_INFO("re-add after remove"); + CHECK(mPose.getNumJointStates() == 2); + + mPose.removeAllJointStates(); + UNSCOPED_INFO("removeAll empties"); + CHECK(mPose.getNumJointStates() == 0); + UNSCOPED_INFO("nothing to iterate"); + REQUIRE(mPose.getJointStates().empty()); + } + + TEST_CASE_METHOD(llpose_data, "LLPose: iterating the states visits each once", "[LLPose]") + { + LLPointer sa = make_state(&mA, LLJointState::ROT); + LLPointer sb = make_state(&mB, LLJointState::ROT); + LLPointer sc = make_state(&mC, LLJointState::ROT); + mPose.addJointState(sa); + mPose.addJointState(sb); + mPose.addJointState(sc); + + std::vector seen; + for (const LLPointer& state : mPose.getJointStates()) + { + seen.push_back(state.get()); + } + UNSCOPED_INFO("iteration visits every state"); + REQUIRE(seen.size() == (size_t)3); + UNSCOPED_INFO("iteration visits a"); + CHECK(std::count(seen.begin(), seen.end(), sa.get()) == 1); + UNSCOPED_INFO("iteration visits b"); + CHECK(std::count(seen.begin(), seen.end(), sb.get()) == 1); + UNSCOPED_INFO("iteration visits c"); + CHECK(std::count(seen.begin(), seen.end(), sc.get()) == 1); + } + + TEST_CASE_METHOD(llpose_data, "LLPose: setWeight reaches every state including one added later", "[LLPose]") + { + LLPointer sa = make_state(&mA, LLJointState::ROT, 0.f); + LLPointer sb = make_state(&mB, LLJointState::ROT, 0.f); + mPose.addJointState(sa); + mPose.addJointState(sb); + + mPose.setWeight(0.5f); + UNSCOPED_INFO("pose weight stored"); + CHECK(mPose.getWeight() == 0.5f); + UNSCOPED_INFO("weight reaches a"); + CHECK(sa->getWeight() == 0.5f); + UNSCOPED_INFO("weight reaches b"); + CHECK(sb->getWeight() == 0.5f); + + // A state added after setWeight is brought up to the pose's weight. + // Every state carrying the pose weight is what lets setWeight skip + // the walk when the weight has not changed; left at its own weight, + // a late state would sit there until the next change. + LLPointer sc = make_state(&mC, LLJointState::ROT, 0.f); + mPose.addJointState(sc); + UNSCOPED_INFO("late state receives the pose weight"); + CHECK(sc->getWeight() == 0.5f); + + mPose.setWeight(1.f); + UNSCOPED_INFO("second setWeight reaches a"); + CHECK(sa->getWeight() == 1.f); + UNSCOPED_INFO("second setWeight reaches b"); + CHECK(sb->getWeight() == 1.f); + UNSCOPED_INFO("second setWeight reaches c"); + CHECK(sc->getWeight() == 1.f); + } + + TEST_CASE_METHOD(llpose_data, "LLPose: a state found on a joint can be removed and replaced", "[LLPose]") + { + // The poser's runtime path: find the state on a joint, remove it, add + // another for the same joint. Dedup is by joint, so two states on + // one joint are still one entry; find by name still reaches it; and + // removing a state that is not there is harmless. + LLPointer first = make_state(&mA, LLJointState::ROT); + LLPointer second = make_state(&mA, LLJointState::POS); + LLPointer other = make_state(&mB, LLJointState::ROT); + mPose.addJointState(first); + mPose.addJointState(second); + mPose.addJointState(other); + UNSCOPED_INFO("two states on one joint are one entry"); + CHECK(mPose.getNumJointStates() == 2); + UNSCOPED_INFO("found by joint"); + CHECK(mPose.findJointState(&mA) == first.get()); + UNSCOPED_INFO("found by name"); + CHECK(mPose.findJointState("a") == first.get()); + UNSCOPED_INFO("the other joint is found by name too"); + CHECK(mPose.findJointState("b") == other.get()); + UNSCOPED_INFO("an unknown name finds nothing"); + REQUIRE(mPose.findJointState("nobody") == nullptr); + + mPose.removeJointState(mPose.findJointState(&mA)); + UNSCOPED_INFO("removed by the state found"); + CHECK(mPose.getNumJointStates() == 1); + UNSCOPED_INFO("the joint is gone"); + REQUIRE(mPose.findJointState(&mA) == nullptr); + mPose.removeJointState(second); + UNSCOPED_INFO("removing an absent state changes nothing"); + CHECK(mPose.getNumJointStates() == 1); + + mPose.addJointState(second); + UNSCOPED_INFO("the joint can be re-added with a different state"); + CHECK(mPose.findJointState(&mA) == second.get()); + } + //------------------------------------------------------------------------- // LLPoseBlender //------------------------------------------------------------------------- -namespace -{ struct llposeblender_data : public joints { LLPoseBlender mBlender; @@ -616,13 +628,8 @@ namespace static void arm(ALTestMotion& motion) { motion.getPose()->setWeight(1.f); } }; - typedef test_group llposeblender_test; - typedef llposeblender_test::object llposeblender_object; - tut::llposeblender_test llposeblender_testcase("LLPoseBlender"); -} - template<> template<> - void llposeblender_object::test<1>() + TEST_CASE_METHOD(llposeblender_data, "LLPoseBlender: a motion's joint states are applied to their joints", "[LLPoseBlender]") { ALTestMotion motion; LLPointer sa = motion.addJoint(&mA, LLJointState::ROT); @@ -631,15 +638,15 @@ namespace sb->setPosition(LLVector3(3.f, 2.f, 1.f)); arm(motion); - ensure("addMotion returns true", addMotion(&motion)); + UNSCOPED_INFO("addMotion returns true"); + CHECK(addMotion(&motion)); mBlender.blendAndApply(); ensure_quat_equals("motion rotation applied", mA.getRotation(), ROT_A); ensure_vec3_equals("motion position applied", mB.getPosition(), LLVector3(3.f, 2.f, 1.f)); } - template<> template<> - void llposeblender_object::test<2>() + TEST_CASE_METHOD(llposeblender_data, "LLPoseBlender: the higher priority motion wins in either order", "[LLPoseBlender]") { ALTestMotion low(LLUUID::generateNewID(), LLJoint::LOW_PRIORITY); low.addJoint(&mA, LLJointState::ROT)->setRotation(ROT_A); @@ -661,8 +668,7 @@ namespace ensure_quat_equals("higher motion priority wins, high first", mA.getRotation(), ROT_B); } - template<> template<> - void llposeblender_object::test<3>() + TEST_CASE_METHOD(llposeblender_data, "LLPoseBlender: a joint state's own priority overrides its motion's", "[LLPoseBlender]") { // A joint state's own priority overrides its motion's. ALTestMotion low(LLUUID::generateNewID(), LLJoint::LOW_PRIORITY); @@ -679,8 +685,70 @@ namespace ensure_quat_equals("joint state priority beats motion priority", mA.getRotation(), ROT_A); } - template<> template<> - void llposeblender_object::test<7>() + TEST_CASE_METHOD(llposeblender_data, "LLPoseBlender: an additive motion composes ahead of the base and is masked behind", "[LLPoseBlender]") + { + // The controller feeds additive motions to the blender before normal + // ones. At equal priority the earlier state stays in front, and an + // additive state only contributes while the states ahead of it have + // not already saturated the weight. + ALTestMotion base(LLUUID::generateNewID(), LLJoint::MEDIUM_PRIORITY, LLMotion::NORMAL_BLEND); + base.addJoint(&mA, LLJointState::ROT)->setRotation(ROT_A); + arm(base); + + ALTestMotion additive(LLUUID::generateNewID(), LLJoint::MEDIUM_PRIORITY, LLMotion::ADDITIVE_BLEND); + additive.addJoint(&mA, LLJointState::ROT)->setRotation(ROT_B); + arm(additive); + + addMotion(&additive); + addMotion(&base); + mBlender.blendAndApply(); + ensure_quat_equals("additive ahead of the base composes onto it", mA.getRotation(), ROT_B * ROT_A); + + mA.setRotation(LLQuaternion::DEFAULT); + addMotion(&base); + addMotion(&additive); + mBlender.blendAndApply(); + ensure_quat_equals("additive behind a saturated base is masked", mA.getRotation(), ROT_A); + } + + TEST_CASE_METHOD(llposeblender_data, "LLPoseBlender: blendAndApply and clearBlenders empty the queued motions", "[LLPoseBlender]") + { + ALTestMotion motion; + motion.addJoint(&mA, LLJointState::ROT)->setRotation(ROT_A); + arm(motion); + + addMotion(&motion); + mBlender.blendAndApply(); + ensure_quat_equals("first apply", mA.getRotation(), ROT_A); + + // The active list is consumed by blendAndApply(). + mA.setRotation(ROT_C); + mBlender.blendAndApply(); + ensure_quat_equals("second apply without addMotion is a no-op", mA.getRotation(), ROT_C); + + // clearBlenders() discards queued states. + addMotion(&motion); + mBlender.clearBlenders(); + mBlender.blendAndApply(); + ensure_quat_equals("cleared queue is a no-op", mA.getRotation(), ROT_C); + } + + TEST_CASE_METHOD(llposeblender_data, "LLPoseBlender: a joint with no number is kept in the motion's pose", "[LLPoseBlender]") + { + // A joint that never received a joint number is kept in the pose but + // left out of the motion's joint signature. + LLJoint unnumbered; + unnumbered.setup("unnumbered"); + UNSCOPED_INFO("default joint number"); + CHECK(unnumbered.getJointNum() == -1); + + ALTestMotion motion; + motion.addJoint(&unnumbered, LLJointState::ROT); + UNSCOPED_INFO("unnumbered joint stays in the pose"); + CHECK(motion.getPose()->getNumJointStates() == 1); + } + + TEST_CASE_METHOD(llposeblender_data, "LLPoseBlender: a joint with no number is skipped and the rest still blend", "[LLPoseBlender]") { // The pool is indexed by joint number. A joint that never received one // -- LLJoint::init leaves it at -1 -- is in the motion's pose, since @@ -689,7 +757,8 @@ namespace // is not. LLJoint unnumbered; unnumbered.setup("unnumbered"); - ensure_equals("a fresh joint has no number", unnumbered.getJointNum(), -1); + UNSCOPED_INFO("a fresh joint has no number"); + CHECK(unnumbered.getJointNum() == -1); const LLQuaternion before = unnumbered.getRotation(); ALTestMotion motion; @@ -703,8 +772,7 @@ namespace ensure_quat_equals("the numbered joint in the same motion is", mA.getRotation(), ROT_A); } - template<> template<> - void llposeblender_object::test<8>() + TEST_CASE_METHOD(llposeblender_data, "LLPoseBlender: the first and the last pool slots both blend", "[LLPoseBlender]") { // Both ends of the pool: the first slot and the highest number a real // joint can carry. setJointNum refuses the top two, which belong to @@ -724,8 +792,7 @@ namespace ensure_quat_equals("the last slot blends", last.getRotation(), ROT_C); } - template<> template<> - void llposeblender_object::test<9>() + TEST_CASE_METHOD(llposeblender_data, "LLPoseBlender: blendAndCache holds the blend and interpolate moves toward it", "[LLPoseBlender]") { // The coarse clock's path: blend into the cache without touching the // joint, then interpolate the joint toward it. The cache used to be a @@ -756,8 +823,7 @@ namespace ensure_vec3_equals("a cleared blender no longer drives the joint", mA.getPosition(), LLVector3::zero); } - template<> template<> - void llposeblender_object::test<10>() + TEST_CASE_METHOD(llposeblender_data, "LLPoseBlender: interpolating a rotation-only cache does not dirty the position", "[LLPoseBlender]") { // Interpolating toward the cache moves only the channels the blend // wrote. A joint on the coarse clock is interpolated every frame, so @@ -770,80 +836,17 @@ namespace addMotion(&motion); mBlender.blendAndCache(true); mA.updateWorldMatrixChildren(); - ensure("the joint starts clean", mA.mDirtyFlags == 0); + UNSCOPED_INFO("the joint starts clean"); + CHECK(mA.mDirtyFlags == 0); mBlender.interpolate(0.5f); - ensure("the rotation moved, so the matrix is dirty", (mA.mDirtyFlags & LLJoint::MATRIX_DIRTY) != 0); - ensure("an unchanged position and scale dirty nothing of their own", - (mA.mDirtyFlags & LLJoint::POSITION_DIRTY) == 0); - } - - template<> template<> - void llposeblender_object::test<4>() - { - // The controller feeds additive motions to the blender before normal - // ones. At equal priority the earlier state stays in front, and an - // additive state only contributes while the states ahead of it have - // not already saturated the weight. - ALTestMotion base(LLUUID::generateNewID(), LLJoint::MEDIUM_PRIORITY, LLMotion::NORMAL_BLEND); - base.addJoint(&mA, LLJointState::ROT)->setRotation(ROT_A); - arm(base); - - ALTestMotion additive(LLUUID::generateNewID(), LLJoint::MEDIUM_PRIORITY, LLMotion::ADDITIVE_BLEND); - additive.addJoint(&mA, LLJointState::ROT)->setRotation(ROT_B); - arm(additive); - - addMotion(&additive); - addMotion(&base); - mBlender.blendAndApply(); - ensure_quat_equals("additive ahead of the base composes onto it", mA.getRotation(), ROT_B * ROT_A); - - mA.setRotation(LLQuaternion::DEFAULT); - addMotion(&base); - addMotion(&additive); - mBlender.blendAndApply(); - ensure_quat_equals("additive behind a saturated base is masked", mA.getRotation(), ROT_A); + UNSCOPED_INFO("the rotation moved, so the matrix is dirty"); + CHECK((mA.mDirtyFlags & LLJoint::MATRIX_DIRTY) != 0); + UNSCOPED_INFO("an unchanged position and scale dirty nothing of their own"); + CHECK((mA.mDirtyFlags & LLJoint::POSITION_DIRTY) == 0); } - template<> template<> - void llposeblender_object::test<5>() - { - ALTestMotion motion; - motion.addJoint(&mA, LLJointState::ROT)->setRotation(ROT_A); - arm(motion); - - addMotion(&motion); - mBlender.blendAndApply(); - ensure_quat_equals("first apply", mA.getRotation(), ROT_A); - - // The active list is consumed by blendAndApply(). - mA.setRotation(ROT_C); - mBlender.blendAndApply(); - ensure_quat_equals("second apply without addMotion is a no-op", mA.getRotation(), ROT_C); - - // clearBlenders() discards queued states. - addMotion(&motion); - mBlender.clearBlenders(); - mBlender.blendAndApply(); - ensure_quat_equals("cleared queue is a no-op", mA.getRotation(), ROT_C); - } - - template<> template<> - void llposeblender_object::test<6>() - { - // A joint that never received a joint number is kept in the pose but - // left out of the motion's joint signature. - LLJoint unnumbered; - unnumbered.setup("unnumbered"); - ensure_equals("default joint number", unnumbered.getJointNum(), -1); - - ALTestMotion motion; - motion.addJoint(&unnumbered, LLJointState::ROT); - ensure_equals("unnumbered joint stays in the pose", motion.getPose()->getNumJointStates(), 1); - } - - template<> template<> - void llposeblender_object::test<11>() + TEST_CASE_METHOD(llposeblender_data, "LLPoseBlender: zero-weight motions do not take a joint's slots", "[LLPoseBlender]") { // A joint has six blend slots and they go by priority. A motion at // zero weight contributes nothing whatever slot it lands in, so @@ -856,7 +859,8 @@ namespace silent.push_back(std::make_unique(LLUUID::generateNewID(), LLJoint::HIGH_PRIORITY)); silent.back()->addJoint(&mA, LLJointState::ROT)->setRotation(ROT_B); // left at the weight a pose starts and ends its life on - ensure_equals("a silent motion weighs nothing", silent.back()->getPose()->getWeight(), 0.f); + UNSCOPED_INFO("a silent motion weighs nothing"); + REQUIRE(silent.back()->getPose()->getWeight() == 0.f); addMotion(silent.back().get()); } @@ -869,8 +873,7 @@ namespace ensure_quat_equals("the motion with a weight still reaches the joint", mA.getRotation(), ROT_A); } - template<> template<> - void llposeblender_object::test<12>() + TEST_CASE_METHOD(llposeblender_data, "LLPoseBlender: leaving out a saturated rotation gives the same pose", "[LLPoseBlender]") { // The differential the skip rests on. A motion at full weight claims // the whole of a joint's rotation, so a lower priority rotation @@ -906,33 +909,7 @@ namespace ensure_quat_equals("and so does leaving it out", left_out, blended_in); } - template<> template<> - void llposeblender_object::test<14>() - { - // Which is why the mask is built from motions at full weight and no - // others. An owner only part way in has not claimed the joint, the one - // underneath is still visible through it, and leaving it out would be - // a pose nobody asked for. - ALTestMotion owner(LLUUID::generateNewID(), LLJoint::HIGH_PRIORITY); - owner.addJoint(&mA, LLJointState::ROT)->setRotation(ROT_A); - owner.getPose()->setWeight(0.5f); - - ALTestMotion underneath(LLUUID::generateNewID(), LLJoint::MEDIUM_PRIORITY); - underneath.addJoint(&mA, LLJointState::ROT)->setRotation(ROT_B); - arm(underneath); - - mA.setRotation(LLQuaternion::DEFAULT); - addMotion(&owner); - addMotion(&underneath); - mBlender.blendAndApply(); - - const LLQuaternion got = mA.getRotation(); - ensure("a half faded owner leaves the one underneath showing through", - fabsf(dot(got, ROT_A)) < 0.999f && fabsf(dot(got, ROT_B)) < 0.999f); - } - - template<> template<> - void llposeblender_object::test<13>() + TEST_CASE_METHOD(llposeblender_data, "LLPoseBlender: only rotation is left out under a saturated joint", "[LLPoseBlender]") { // Only a rotation is left out. A state that also moves or scales the // joint is summed on its own account, whatever is claimed above it. @@ -954,8 +931,34 @@ namespace ensure_quat_equals("and so does the rotation that came with it", mA.getRotation(), ROT_B); } - template<> template<> - void llposeblender_object::test<15>() + TEST_CASE_METHOD(llposeblender_data, "LLPoseBlender: a half-faded owner does not mask what is underneath", "[LLPoseBlender]") + { + // Which is why the mask is built from motions at full weight and no + // others. An owner only part way in has not claimed the joint, the one + // underneath is still visible through it, and leaving it out would be + // a pose nobody asked for. + ALTestMotion owner(LLUUID::generateNewID(), LLJoint::HIGH_PRIORITY); + owner.addJoint(&mA, LLJointState::ROT)->setRotation(ROT_A); + owner.getPose()->setWeight(0.5f); + + ALTestMotion underneath(LLUUID::generateNewID(), LLJoint::MEDIUM_PRIORITY); + underneath.addJoint(&mA, LLJointState::ROT)->setRotation(ROT_B); + arm(underneath); + + mA.setRotation(LLQuaternion::DEFAULT); + addMotion(&owner); + addMotion(&underneath); + mBlender.blendAndApply(); + + const LLQuaternion got = mA.getRotation(); + { + INFO("a half faded owner leaves the one underneath showing through"); + CHECK(fabsf(dot(got, ROT_A)) < 0.999f); + CHECK(fabsf(dot(got, ROT_B)) < 0.999f); + } + } + + TEST_CASE_METHOD(llposeblender_data, "LLPoseBlender: left-out rotations leave a slot for a motion that needs one", "[LLPoseBlender]") { // What leaving them out buys, beyond the work: a joint has six slots // and a state that would be interpolated away to nothing was taking @@ -987,8 +990,7 @@ namespace mA.getPosition(), offset); } - template<> template<> - void llposeblender_object::test<16>() + TEST_CASE_METHOD(llposeblender_data, "LLPoseBlender: interpolate leaves channels nothing blended where they are", "[LLPoseBlender]") { // A rotation-only blend caches a rotation and nothing else. What sits // in the other two is the joint's own value, put there when the cache @@ -1012,11 +1014,11 @@ namespace mBlender.interpolate(0.5f); ensure_vec3_equals("a position nothing blended is left where it is", mA.getPosition(), moved); ensure_vec3_equals("and so is a scale", mA.getScale(), rescaled); - ensure("the channel that was blended still moves", mA.getRotation() != LLQuaternion::DEFAULT); + UNSCOPED_INFO("the channel that was blended still moves"); + CHECK(mA.getRotation() != LLQuaternion::DEFAULT); } - template<> template<> - void llposeblender_object::test<17>() + TEST_CASE_METHOD(llposeblender_data, "LLPoseBlender: interpolate follows the arc between rotations", "[LLPoseBlender]") { // The frames between quanta follow the arc, like the blend they are // standing in for. A quantum is up to a quarter of a second, which is @@ -1038,8 +1040,7 @@ namespace } - template<> template<> - void llposeblender_object::test<18>() + TEST_CASE_METHOD(llposeblender_data, "LLPoseBlender: the pelvis fix still gets a slot under a landing's seven states", "[LLPoseBlender]") { // Landing from a run puts seven states on the pelvis at once: the two // adjust servos, both live for the frame the fly one hands over to the @@ -1100,8 +1101,8 @@ namespace // The animations between them claim 0.15 of the joint, so the crouch // arrives about a seventh as deep. Refuse the fix a slot and it // arrives at seven eighths of it. - ensure("the pelvis fix took up the weight the animations did not claim", - fabsf(mA.getPosition().mV[VZ]) < 0.5f * fabsf(crouch.mV[VZ])); + UNSCOPED_INFO("the pelvis fix took up the weight the animations did not claim"); + CHECK(fabsf(mA.getPosition().mV[VZ]) < 0.5f * fabsf(crouch.mV[VZ])); } } diff --git a/indra/llfilesystem/CMakeLists.txt b/indra/llfilesystem/CMakeLists.txt index dce31cb115..e47b2e9daf 100644 --- a/indra/llfilesystem/CMakeLists.txt +++ b/indra/llfilesystem/CMakeLists.txt @@ -32,6 +32,10 @@ target_precompile_headers(llfilesystem REUSE_FROM llprecompiled) # Add tests if(AL_BUILD_TESTS) - al_add_test(lldiriterator PROJECT llfilesystem LIBRARIES llfilesystem) - al_add_test(lldir PROJECT llfilesystem LIBRARIES llmath llcommon llfilesystem) + al_add_test_binary( + llfilesystem_tests + PROJECT llfilesystem + SOURCES tests/lldir_test.cpp tests/lldiriterator_test.cpp + LIBRARIES llfilesystem llmath llcommon + ) endif() diff --git a/indra/llfilesystem/tests/lldir_test.cpp b/indra/llfilesystem/tests/lldir_test.cpp index 3f243d1e3b..47b398b789 100644 --- a/indra/llfilesystem/tests/lldir_test.cpp +++ b/indra/llfilesystem/tests/lldir_test.cpp @@ -28,12 +28,13 @@ #include "linden_common.h" #include +#include #include "llstring.h" #include "tests/StringVec.h" #include "../lldir.h" #include "../lldiriterator.h" -#include "../test/lltut.h" +#include #include "../test/namedtempfile.h" #include "stringize.h" @@ -170,13 +171,13 @@ struct LLDir_Dummy: public LLDir void clear_checked() { mChecked.clear(); } void ensure_checked(const std::string& pathname) const { - tut::ensure(STRINGIZE(pathname << " was not checked but should have been"), - mChecked.find(pathname) != mChecked.end()); + UNSCOPED_INFO((STRINGIZE(pathname << " was not checked but should have been"))); + REQUIRE(mChecked.find(pathname) != mChecked.end()); } void ensure_not_checked(const std::string& pathname) const { - tut::ensure(STRINGIZE(pathname << " was checked but should not have been"), - mChecked.find(pathname) == mChecked.end()); + UNSCOPED_INFO((STRINGIZE(pathname << " was checked but should not have been"))); + REQUIRE(mChecked.find(pathname) == mChecked.end()); } std::set mFilesystem; @@ -279,20 +280,16 @@ namespace struct LLDirTest { }; - 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>() + TEST_CASE_METHOD(LLDirTest, "LLDir: getDirDelimiter is not empty", "[LLDir]") // getDirDelimiter { - ensure("getDirDelimiter", !gDirUtilp->getDirDelimiter().empty()); + UNSCOPED_INFO("getDirDelimiter"); + REQUIRE_FALSE(gDirUtilp->getDirDelimiter().empty()); } - template<> template<> - void LLDirTest_object_t::test<2>() + TEST_CASE_METHOD(LLDirTest, "LLDir: getBaseFileName with and without stripping the extension", "[LLDir]") // getBaseFileName { std::string delim = gDirUtilp->getDirDelimiter(); @@ -308,92 +305,91 @@ namespace // foo[.bAr] - ensure_equals("getBaseFileName/r-no-ext/no-strip-exten", - gDirUtilp->getBaseFileName(rawFile, false), + UNSCOPED_INFO("getBaseFileName/r-no-ext/no-strip-exten"); + CHECK(gDirUtilp->getBaseFileName(rawFile, false) == "foo"); - ensure_equals("getBaseFileName/r-no-ext/strip-exten", - gDirUtilp->getBaseFileName(rawFile, true), + UNSCOPED_INFO("getBaseFileName/r-no-ext/strip-exten"); + CHECK(gDirUtilp->getBaseFileName(rawFile, true) == "foo"); - ensure_equals("getBaseFileName/r-ext/no-strip-exten", - gDirUtilp->getBaseFileName(rawFileExt, false), + UNSCOPED_INFO("getBaseFileName/r-ext/no-strip-exten"); + CHECK(gDirUtilp->getBaseFileName(rawFileExt, false) == "foo.bAr"); - ensure_equals("getBaseFileName/r-ext/strip-exten", - gDirUtilp->getBaseFileName(rawFileExt, true), + UNSCOPED_INFO("getBaseFileName/r-ext/strip-exten"); + CHECK(gDirUtilp->getBaseFileName(rawFileExt, true) == "foo"); // foo. - ensure_equals("getBaseFileName/rn-no-ext/no-strip-exten", - gDirUtilp->getBaseFileName(rawFileNullExt, false), + UNSCOPED_INFO("getBaseFileName/rn-no-ext/no-strip-exten"); + CHECK(gDirUtilp->getBaseFileName(rawFileNullExt, false) == "foo."); - ensure_equals("getBaseFileName/rn-no-ext/strip-exten", - gDirUtilp->getBaseFileName(rawFileNullExt, true), + UNSCOPED_INFO("getBaseFileName/rn-no-ext/strip-exten"); + CHECK(gDirUtilp->getBaseFileName(rawFileNullExt, true) == "foo"); // .bAr // interesting case - with no basename, this IS the basename, not the extension. - ensure_equals("getBaseFileName/e-ext/no-strip-exten", - gDirUtilp->getBaseFileName(rawExt, false), + UNSCOPED_INFO("getBaseFileName/e-ext/no-strip-exten"); + CHECK(gDirUtilp->getBaseFileName(rawExt, false) == ".bAr"); - ensure_equals("getBaseFileName/e-ext/strip-exten", - gDirUtilp->getBaseFileName(rawExt, true), + UNSCOPED_INFO("getBaseFileName/e-ext/strip-exten"); + CHECK(gDirUtilp->getBaseFileName(rawExt, true) == ".bAr"); // . - ensure_equals("getBaseFileName/d/no-strip-exten", - gDirUtilp->getBaseFileName(rawDot, false), + UNSCOPED_INFO("getBaseFileName/d/no-strip-exten"); + CHECK(gDirUtilp->getBaseFileName(rawDot, false) == "."); - ensure_equals("getBaseFileName/d/strip-exten", - gDirUtilp->getBaseFileName(rawDot, true), + UNSCOPED_INFO("getBaseFileName/d/strip-exten"); + CHECK(gDirUtilp->getBaseFileName(rawDot, true) == "."); // aa/bb/cc/dd/ee[.eXt] - ensure_equals("getBaseFileName/no-ext/no-strip-exten", - gDirUtilp->getBaseFileName(pathNoExt, false), + UNSCOPED_INFO("getBaseFileName/no-ext/no-strip-exten"); + CHECK(gDirUtilp->getBaseFileName(pathNoExt, false) == "ee"); - ensure_equals("getBaseFileName/no-ext/strip-exten", - gDirUtilp->getBaseFileName(pathNoExt, true), + UNSCOPED_INFO("getBaseFileName/no-ext/strip-exten"); + CHECK(gDirUtilp->getBaseFileName(pathNoExt, true) == "ee"); - ensure_equals("getBaseFileName/ext/no-strip-exten", - gDirUtilp->getBaseFileName(pathExt, false), + UNSCOPED_INFO("getBaseFileName/ext/no-strip-exten"); + CHECK(gDirUtilp->getBaseFileName(pathExt, false) == "ee.eXt"); - ensure_equals("getBaseFileName/ext/strip-exten", - gDirUtilp->getBaseFileName(pathExt, true), + UNSCOPED_INFO("getBaseFileName/ext/strip-exten"); + CHECK(gDirUtilp->getBaseFileName(pathExt, true) == "ee"); // aa/bb/cc.dd/ee[.eXt] - ensure_equals("getBaseFileName/d-no-ext/no-strip-exten", - gDirUtilp->getBaseFileName(dottedPathNoExt, false), + UNSCOPED_INFO("getBaseFileName/d-no-ext/no-strip-exten"); + CHECK(gDirUtilp->getBaseFileName(dottedPathNoExt, false) == "ee"); - ensure_equals("getBaseFileName/d-no-ext/strip-exten", - gDirUtilp->getBaseFileName(dottedPathNoExt, true), + UNSCOPED_INFO("getBaseFileName/d-no-ext/strip-exten"); + CHECK(gDirUtilp->getBaseFileName(dottedPathNoExt, true) == "ee"); - ensure_equals("getBaseFileName/d-ext/no-strip-exten", - gDirUtilp->getBaseFileName(dottedPathExt, false), + UNSCOPED_INFO("getBaseFileName/d-ext/no-strip-exten"); + CHECK(gDirUtilp->getBaseFileName(dottedPathExt, false) == "ee.eXt"); - ensure_equals("getBaseFileName/d-ext/strip-exten", - gDirUtilp->getBaseFileName(dottedPathExt, true), + UNSCOPED_INFO("getBaseFileName/d-ext/strip-exten"); + CHECK(gDirUtilp->getBaseFileName(dottedPathExt, true) == "ee"); } - template<> template<> - void LLDirTest_object_t::test<3>() + TEST_CASE_METHOD(LLDirTest, "LLDir: getDirName drops the last path component", "[LLDir]") // getDirName { std::string delim = gDirUtilp->getDirDelimiter(); @@ -406,37 +402,36 @@ namespace // foo[.bAr] - ensure_equals("getDirName/r-no-ext", - gDirUtilp->getDirName(rawFile), + UNSCOPED_INFO("getDirName/r-no-ext"); + CHECK(gDirUtilp->getDirName(rawFile) == ""); - ensure_equals("getDirName/r-ext", - gDirUtilp->getDirName(rawFileExt), + UNSCOPED_INFO("getDirName/r-ext"); + CHECK(gDirUtilp->getDirName(rawFileExt) == ""); // aa/bb/cc/dd/ee[.eXt] - ensure_equals("getDirName/no-ext", - gDirUtilp->getDirName(pathNoExt), + UNSCOPED_INFO("getDirName/no-ext"); + CHECK(gDirUtilp->getDirName(pathNoExt) == "aa" + delim + "bb" + delim + "cc" + delim + "dd"); - ensure_equals("getDirName/ext", - gDirUtilp->getDirName(pathExt), + UNSCOPED_INFO("getDirName/ext"); + CHECK(gDirUtilp->getDirName(pathExt) == "aa" + delim + "bb" + delim + "cc" + delim + "dd"); // aa/bb/cc.dd/ee[.eXt] - ensure_equals("getDirName/d-no-ext", - gDirUtilp->getDirName(dottedPathNoExt), + UNSCOPED_INFO("getDirName/d-no-ext"); + CHECK(gDirUtilp->getDirName(dottedPathNoExt) == "aa" + delim + "bb" + delim + "cc.dd"); - ensure_equals("getDirName/d-ext", - gDirUtilp->getDirName(dottedPathExt), + UNSCOPED_INFO("getDirName/d-ext"); + CHECK(gDirUtilp->getDirName(dottedPathExt) == "aa" + delim + "bb" + delim + "cc.dd"); } - template<> template<> - void LLDirTest_object_t::test<4>() + TEST_CASE_METHOD(LLDirTest, "LLDir: getExtension is the lower-cased extension of the basename", "[LLDir]") // getExtension { std::string delim = gDirUtilp->getDirDelimiter(); @@ -452,51 +447,51 @@ namespace // foo[.bAr] - ensure_equals("getExtension/r-no-ext", - gDirUtilp->getExtension(rawFile), + UNSCOPED_INFO("getExtension/r-no-ext"); + CHECK(gDirUtilp->getExtension(rawFile) == ""); - ensure_equals("getExtension/r-ext", - gDirUtilp->getExtension(rawFileExt), + UNSCOPED_INFO("getExtension/r-ext"); + CHECK(gDirUtilp->getExtension(rawFileExt) == "bar"); // foo. - ensure_equals("getExtension/rn-no-ext", - gDirUtilp->getExtension(rawFileNullExt), + UNSCOPED_INFO("getExtension/rn-no-ext"); + CHECK(gDirUtilp->getExtension(rawFileNullExt) == ""); // .bAr // interesting case - with no basename, this IS the basename, not the extension. - ensure_equals("getExtension/e-ext", - gDirUtilp->getExtension(rawExt), + UNSCOPED_INFO("getExtension/e-ext"); + CHECK(gDirUtilp->getExtension(rawExt) == ""); // . - ensure_equals("getExtension/d", - gDirUtilp->getExtension(rawDot), + UNSCOPED_INFO("getExtension/d"); + CHECK(gDirUtilp->getExtension(rawDot) == ""); // aa/bb/cc/dd/ee[.eXt] - ensure_equals("getExtension/no-ext", - gDirUtilp->getExtension(pathNoExt), + UNSCOPED_INFO("getExtension/no-ext"); + CHECK(gDirUtilp->getExtension(pathNoExt) == ""); - ensure_equals("getExtension/ext", - gDirUtilp->getExtension(pathExt), + UNSCOPED_INFO("getExtension/ext"); + CHECK(gDirUtilp->getExtension(pathExt) == "ext"); // aa/bb/cc.dd/ee[.eXt] - ensure_equals("getExtension/d-no-ext", - gDirUtilp->getExtension(dottedPathNoExt), + UNSCOPED_INFO("getExtension/d-no-ext"); + CHECK(gDirUtilp->getExtension(dottedPathNoExt) == ""); - ensure_equals("getExtension/d-ext", - gDirUtilp->getExtension(dottedPathExt), + UNSCOPED_INFO("getExtension/d-ext"); + CHECK(gDirUtilp->getExtension(dottedPathExt) == "ext"); } @@ -506,10 +501,12 @@ namespace { std::string path = dir + file; LLFILE* handle = LLFile::fopen(path, LLFILE_MODE("w")); - ensure("failed to open test file '"+path+"'", handle != NULL ); + UNSCOPED_INFO("failed to open test file '"+path+"'"); + REQUIRE(handle != NULL); // Harbison & Steele, 4th ed., p. 366: "If an error occurs, fputs // returns EOF; otherwise, it returns some other, nonnegative value." - ensure("failed to write to test file '"+path+"'", EOF != fputs("test file", handle) ); + UNSCOPED_INFO("failed to write to test file '"+path+"'"); + REQUIRE(EOF != fputs("test file", handle)); fclose(handle); return path; } @@ -552,30 +549,32 @@ namespace // check is now either 5 (not found) or the index of the matching name if (check < 5) { - ensure( "found file '"+(std::string)DirScanFilename[check]+"' twice", ! filesFound[check] ); + UNSCOPED_INFO("found file '"+(std::string)DirScanFilename[check]+"' twice"); + REQUIRE_FALSE(filesFound[check]); filesFound[check] = true; } else // check is 5 - should not happen { - fail( "found unknown file '"+scanResult+"'"); + FAIL("found unknown file '"+scanResult+"'"); } } for (int i=0; i<5; i++) { if (correctResult[i]) { - ensure("scan of '"+directory+"' using '"+pattern+"' did not return '"+DirScanFilename[i]+"'", filesFound[i]); + UNSCOPED_INFO("scan of '"+directory+"' using '"+pattern+"' did not return '"+DirScanFilename[i]+"'"); + REQUIRE(filesFound[i]); } else { - ensure("scan of '"+directory+"' using '"+pattern+"' incorrectly returned '"+DirScanFilename[i]+"'", !filesFound[i]); + UNSCOPED_INFO("scan of '"+directory+"' using '"+pattern+"' incorrectly returned '"+DirScanFilename[i]+"'"); + REQUIRE_FALSE(filesFound[i]); } } } } - template<> template<> - void LLDirTest_object_t::test<5>() + TEST_CASE_METHOD(LLDirTest, "LLDir: LLDirIterator::next matches glob patterns", "[LLDir]") // LLDirIterator::next { std::string delim = gDirUtilp->getDirDelimiter(); @@ -662,58 +661,56 @@ namespace LLFile::remove(dir2); } - template<> template<> - void LLDirTest_object_t::test<6>() + TEST_CASE_METHOD(LLDirTest, "LLDir: findSkinnedFilenames searches skins and languages and caches probes", "[LLDir]") { - set_test_name("findSkinnedFilenames()"); LLDir_Dummy lldir; /*------------------------ "default", "en" -------------------------*/ // Setting "default" means we shouldn't consider any "*/skins/steam" // directories; setting "en" means we shouldn't consider any "xui/fr" // directories. lldir.setSkinFolder("default", "en"); - ensure_equals(lldir.getSkinFolder(), "default"); - ensure_equals(lldir.getLanguage(), "en"); + CHECK(lldir.getSkinFolder() == "default"); + CHECK(lldir.getLanguage() == "en"); // top-level directory of a skin isn't localized - ensure_equals(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS), + CHECK(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS) == StringVec{ "install/skins/default/colors.xml", "user/skins/default/colors.xml" }); // We should not have needed to check for skins/default/en. We should // just "know" that SKINBASE is not localized. lldir.ensure_not_checked("install/skins/default/en"); - ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_default.jpeg"), + CHECK(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_default.jpeg") == StringVec{ "install/skins/default/textures/only_default.jpeg" }); // Nor should we have needed to check skins/default/textures/en // because textures is known not to be localized. lldir.ensure_not_checked("install/skins/default/textures/en"); StringVec expected(StringVec{ "install/skins/default/xui/en/strings.xml", "user/skins/default/xui/en/strings.xml" }); - ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS), + CHECK(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS) == expected); // The first time, we had to probe to find out whether xui was localized. lldir.ensure_checked("install/skins/default/xui/en"); lldir.clear_checked(); // Now make the same call again -- should return same result -- - ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS), + CHECK(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS) == expected); // but this time it should remember that xui is localized. lldir.ensure_not_checked("install/skins/default/xui/en"); // localized subdir with "en-us" instead of "en" - ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"), StringVec{ "install/skins/default/html/en-us/welcome.html" }); + CHECK(lldir.findSkinnedFilenames("html", "welcome.html") == StringVec{ "install/skins/default/html/en-us/welcome.html" }); lldir.ensure_checked("install/skins/default/html/en"); lldir.ensure_checked("install/skins/default/html/en-us"); lldir.clear_checked(); - ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"), StringVec{ "install/skins/default/html/en-us/welcome.html" }); + CHECK(lldir.findSkinnedFilenames("html", "welcome.html") == StringVec{ "install/skins/default/html/en-us/welcome.html" }); lldir.ensure_not_checked("install/skins/default/html/en"); lldir.ensure_not_checked("install/skins/default/html/en-us"); - ensure_equals(lldir.findSkinnedFilenames("future", "somefile.txt"), StringVec{ "install/skins/default/future/somefile.txt" }); + CHECK(lldir.findSkinnedFilenames("future", "somefile.txt") == StringVec{ "install/skins/default/future/somefile.txt" }); // Test probing for an unrecognized unlocalized future subdir. lldir.ensure_checked("install/skins/default/future/en"); lldir.clear_checked(); - ensure_equals(lldir.findSkinnedFilenames("future", "somefile.txt"), StringVec{ "install/skins/default/future/somefile.txt" }); + CHECK(lldir.findSkinnedFilenames("future", "somefile.txt") == StringVec{ "install/skins/default/future/somefile.txt" }); // Second time it should remember that future is unlocalized. lldir.ensure_not_checked("install/skins/default/future/en"); @@ -724,68 +721,68 @@ namespace // make the default localization be "en" and allow "en-gb" (or // whatever) localizations, which would work much more the way you'd // expect. - ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"), StringVec{ "install/skins/default/html/en-us/welcome.html" }); + CHECK(lldir.findSkinnedFilenames("html", "welcome.html") == StringVec{ "install/skins/default/html/en-us/welcome.html" }); /*------------------------ "default", "fr" -------------------------*/ // We start being able to distinguish localized subdirs from // unlocalized when we ask for a non-English language. lldir.setSkinFolder("default", "fr"); - ensure_equals(lldir.getLanguage(), "fr"); + CHECK(lldir.getLanguage() == "fr"); // pass merge=true to request this filename in all relevant skins - ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS), + CHECK(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS) == StringVec{ "install/skins/default/xui/en/strings.xml", "install/skins/default/xui/fr/strings.xml", "user/skins/default/xui/en/strings.xml", "user/skins/default/xui/fr/strings.xml" }); // pass (or default) merge=false to request only most specific skin - ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"), + CHECK(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml") == StringVec{ "user/skins/default/xui/en/strings.xml", "user/skins/default/xui/fr/strings.xml" }); // Our dummy floater.xml has a user localization (for "fr") but no // English override. This is a case in which CURRENT_SKIN nonetheless // returns paths from two different skins. - ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "floater.xml"), + CHECK(lldir.findSkinnedFilenames(LLDir::XUI, "floater.xml") == StringVec{ "install/skins/default/xui/en/floater.xml", "user/skins/default/xui/fr/floater.xml" }); // Our dummy newfile.xml has an English override but no user // localization. This is another case in which CURRENT_SKIN // nonetheless returns paths from two different skins. - ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "newfile.xml"), + CHECK(lldir.findSkinnedFilenames(LLDir::XUI, "newfile.xml") == StringVec{ "user/skins/default/xui/en/newfile.xml", "install/skins/default/xui/fr/newfile.xml" }); - ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"), + CHECK(lldir.findSkinnedFilenames("html", "welcome.html") == StringVec{ "install/skins/default/html/en-us/welcome.html", "install/skins/default/html/fr/welcome.html" }); /*------------------------ "default", "zh" -------------------------*/ lldir.setSkinFolder("default", "zh"); // Because strings.xml has only a "fr" override but no "zh" override // in any skin, the most localized version we can find is "en". - ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"), StringVec{ "user/skins/default/xui/en/strings.xml" }); + CHECK(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml") == StringVec{ "user/skins/default/xui/en/strings.xml" }); /*------------------------- "steam", "en" --------------------------*/ lldir.setSkinFolder("steam", "en"); - ensure_equals(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS), + CHECK(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS) == StringVec{ "install/skins/default/colors.xml", "install/skins/steam/colors.xml", "user/skins/default/colors.xml", "user/skins/steam/colors.xml" }); - ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_default.jpeg"), + CHECK(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_default.jpeg") == StringVec{ "install/skins/default/textures/only_default.jpeg" }); - ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_steam.jpeg"), + CHECK(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_steam.jpeg") == StringVec{ "install/skins/steam/textures/only_steam.jpeg" }); - ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_user_default.jpeg"), + CHECK(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_user_default.jpeg") == StringVec{ "user/skins/default/textures/only_user_default.jpeg" }); - ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_user_steam.jpeg"), + CHECK(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_user_steam.jpeg") == StringVec{ "user/skins/steam/textures/only_user_steam.jpeg" }); // CURRENT_SKIN - ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"), StringVec{ "user/skins/steam/xui/en/strings.xml" }); + CHECK(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml") == StringVec{ "user/skins/steam/xui/en/strings.xml" }); // pass constraint=ALL_SKINS to request this filename in all relevant skins - ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS), + CHECK(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS) == StringVec{ "install/skins/default/xui/en/strings.xml", "install/skins/steam/xui/en/strings.xml", "user/skins/default/xui/en/strings.xml", "user/skins/steam/xui/en/strings.xml" }); @@ -793,127 +790,165 @@ namespace lldir.setSkinFolder("steam", "fr"); // pass CURRENT_SKIN to request only the most specialized files - ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"), + CHECK(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml") == StringVec{ "user/skins/steam/xui/en/strings.xml", "user/skins/steam/xui/fr/strings.xml" }); // pass ALL_SKINS to request this filename in all relevant skins - ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS), + CHECK(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS) == StringVec{ "install/skins/default/xui/en/strings.xml", "install/skins/default/xui/fr/strings.xml", "install/skins/steam/xui/en/strings.xml", "install/skins/steam/xui/fr/strings.xml", "user/skins/default/xui/en/strings.xml", "user/skins/default/xui/fr/strings.xml", "user/skins/steam/xui/en/strings.xml", "user/skins/steam/xui/fr/strings.xml" }); } - template<> template<> - void LLDirTest_object_t::test<7>() + TEST_CASE_METHOD(LLDirTest, "LLDir: add joins with a single slash and skips an empty part", "[LLDir]") { - set_test_name("add()"); LLDir_Dummy lldir; - ensure_equals("both empty", lldir.add("", ""), ""); - ensure_equals("path empty", lldir.add("", "b"), "b"); - ensure_equals("name empty", lldir.add("a", ""), "a"); - ensure_equals("both simple", lldir.add("a", "b"), "a/b"); - ensure_equals("name leading slash", lldir.add("a", "/b"), "a/b"); - ensure_equals("path trailing slash", lldir.add("a/", "b"), "a/b"); - ensure_equals("both bring slashes", lldir.add("a/", "/b"), "a/b"); + UNSCOPED_INFO("both empty"); + CHECK(lldir.add("", "") == ""); + UNSCOPED_INFO("path empty"); + CHECK(lldir.add("", "b") == "b"); + UNSCOPED_INFO("name empty"); + CHECK(lldir.add("a", "") == "a"); + UNSCOPED_INFO("both simple"); + CHECK(lldir.add("a", "b") == "a/b"); + UNSCOPED_INFO("name leading slash"); + CHECK(lldir.add("a", "/b") == "a/b"); + UNSCOPED_INFO("path trailing slash"); + CHECK(lldir.add("a/", "b") == "a/b"); + UNSCOPED_INFO("both bring slashes"); + CHECK(lldir.add("a/", "/b") == "a/b"); } - template<> template<> - void LLDirTest_object_t::test<8>() + TEST_CASE_METHOD(LLDirTest, "LLDir: relocateLegacyPath maps a moved legacy profile into its new dirs", "[LLDir]") { - set_test_name("relocateLegacyPath()"); LLDir_Relocated lldir; - ensure_equals("root", lldir.relocateLegacyPath("old"), "user"); - ensure_equals("root slash", lldir.relocateLegacyPath("old/"), "user"); - ensure_equals("account", lldir.relocateLegacyPath("old/bob_resident"), "user/bob_resident"); - ensure_equals("settings", lldir.relocateLegacyPath("old/user_settings/settings.xml"), "cfg/settings.xml"); - ensure_equals("logs", lldir.relocateLegacyPath("old/logs"), "state"); - ensure_equals("cache", lldir.relocateLegacyPath("old/cache/texturecache"), "cachedir/texturecache"); - ensure_equals("sibling", lldir.relocateLegacyPath("oldish/x"), "oldish/x"); - ensure_equals("elsewhere", lldir.relocateLegacyPath("chat/logs"), "chat/logs"); + UNSCOPED_INFO("root"); + CHECK(lldir.relocateLegacyPath("old") == "user"); + UNSCOPED_INFO("root slash"); + CHECK(lldir.relocateLegacyPath("old/") == "user"); + UNSCOPED_INFO("account"); + CHECK(lldir.relocateLegacyPath("old/bob_resident") == "user/bob_resident"); + UNSCOPED_INFO("settings"); + CHECK(lldir.relocateLegacyPath("old/user_settings/settings.xml") == "cfg/settings.xml"); + UNSCOPED_INFO("logs"); + CHECK(lldir.relocateLegacyPath("old/logs") == "state"); + UNSCOPED_INFO("cache"); + CHECK(lldir.relocateLegacyPath("old/cache/texturecache") == "cachedir/texturecache"); + UNSCOPED_INFO("sibling"); + CHECK(lldir.relocateLegacyPath("oldish/x") == "oldish/x"); + UNSCOPED_INFO("elsewhere"); + CHECK(lldir.relocateLegacyPath("chat/logs") == "chat/logs"); lldir.buildFilesystem("old/bob_resident"); - ensure_equals("still there", lldir.relocateLegacyPath("old/bob_resident"), "old/bob_resident"); + UNSCOPED_INFO("still there"); + CHECK(lldir.relocateLegacyPath("old/bob_resident") == "old/bob_resident"); LLDir_Dummy unmoved; - ensure_equals("no legacy root", unmoved.relocateLegacyPath("old/bob_resident"), "old/bob_resident"); + UNSCOPED_INFO("no legacy root"); + CHECK(unmoved.relocateLegacyPath("old/bob_resident") == "old/bob_resident"); } #if LL_LINUX - template<> template<> - void LLDirTest_object_t::test<9>() + TEST_CASE_METHOD(LLDirTest, "LLDir: LLDirXDG::layout uses only absolute XDG variables and legacyLayout the old dirs", "[LLDir]") { - set_test_name("LLDirXDG::layout()"); ScratchHome scratch; LLDirXDG::Layout dirs = LLDirXDG::layout("/h", "app"); - ensure_equals("data default", dirs.data, "/h/.local/share/app"); - ensure_equals("config default", dirs.config, "/h/.config/app"); - ensure_equals("cache default", dirs.cache, "/h/.cache/app"); - ensure_equals("state default", dirs.state, "/h/.local/state/app"); + UNSCOPED_INFO("data default"); + CHECK(dirs.data == "/h/.local/share/app"); + UNSCOPED_INFO("config default"); + CHECK(dirs.config == "/h/.config/app"); + UNSCOPED_INFO("cache default"); + CHECK(dirs.cache == "/h/.cache/app"); + UNSCOPED_INFO("state default"); + CHECK(dirs.state == "/h/.local/state/app"); ::setenv("XDG_DATA_HOME", "/d", 1); ::setenv("XDG_CONFIG_HOME", "relative/c", 1); ::setenv("XDG_CACHE_HOME", "", 1); ::setenv("XDG_STATE_HOME", "/s", 1); dirs = LLDirXDG::layout("/h", "app"); - ensure_equals("data set", dirs.data, "/d/app"); - ensure_equals("config relative ignored", dirs.config, "/h/.config/app"); - ensure_equals("cache empty ignored", dirs.cache, "/h/.cache/app"); - ensure_equals("state set", dirs.state, "/s/app"); + UNSCOPED_INFO("data set"); + CHECK(dirs.data == "/d/app"); + UNSCOPED_INFO("config relative ignored"); + CHECK(dirs.config == "/h/.config/app"); + UNSCOPED_INFO("cache empty ignored"); + CHECK(dirs.cache == "/h/.cache/app"); + UNSCOPED_INFO("state set"); + CHECK(dirs.state == "/s/app"); dirs = LLDirXDG::legacyLayout("/h/.app"); - ensure_equals("legacy data", dirs.data, "/h/.app"); - ensure_equals("legacy config", dirs.config, "/h/.app/user_settings"); - ensure_equals("legacy cache", dirs.cache, "/h/.app/cache"); - ensure_equals("legacy state", dirs.state, "/h/.app/logs"); + UNSCOPED_INFO("legacy data"); + CHECK(dirs.data == "/h/.app"); + UNSCOPED_INFO("legacy config"); + CHECK(dirs.config == "/h/.app/user_settings"); + UNSCOPED_INFO("legacy cache"); + CHECK(dirs.cache == "/h/.app/cache"); + UNSCOPED_INFO("legacy state"); + CHECK(dirs.state == "/h/.app/logs"); } - template<> template<> - void LLDirTest_object_t::test<10>() + TEST_CASE_METHOD(LLDirTest, "LLDir: LLDirXDG::migrate moves a legacy profile into the XDG dirs", "[LLDir]") { - set_test_name("LLDirXDG::migrate() moves a legacy profile"); ScratchHome scratch; std::vector notes; const LLDirXDG::Layout dirs = LLDirXDG::layout(scratch.home, "app"); - ensure("no legacy profile", LLDirXDG::migrate(scratch.path(".app"), dirs, notes) == LLDirXDG::Migration::NONE); - ensure("nothing made for no profile", !scratch.exists(".local")); + UNSCOPED_INFO("no legacy profile"); + REQUIRE((LLDirXDG::migrate(scratch.path(".app"), dirs, notes) == LLDirXDG::Migration::NONE)); + UNSCOPED_INFO("nothing made for no profile"); + REQUIRE(!scratch.exists(".local")); scratch.writeLegacyProfile(); - ensure("moved", LLDirXDG::migrate(scratch.path(".app"), dirs, notes) == LLDirXDG::Migration::MOVED); - ensure("legacy root gone", !scratch.exists(".app")); - ensure("settings", scratch.exists(".config/app/settings.xml")); - ensure("logs", scratch.exists(".local/state/app/Alchemy.log")); - ensure("cache", scratch.exists(".cache/app/texturecache/t")); - ensure("account", scratch.exists(".local/share/app/bob_resident/settings_per_account.xml")); - ensure("skins", scratch.exists(".local/share/app/skins/default/colors.xml")); - ensure("no settings left in data", !scratch.exists(".local/share/app/user_settings")); - ensure("no logs left in data", !scratch.exists(".local/share/app/logs")); - ensure("no cache left in data", !scratch.exists(".local/share/app/cache")); - ensure("base dirs private", - (fs::status(scratch.path(".local/state")).permissions() & fs::perms::all) == fs::perms::owner_all); + UNSCOPED_INFO("moved"); + REQUIRE((LLDirXDG::migrate(scratch.path(".app"), dirs, notes) == LLDirXDG::Migration::MOVED)); + UNSCOPED_INFO("legacy root gone"); + REQUIRE(!scratch.exists(".app")); + UNSCOPED_INFO("settings"); + REQUIRE(scratch.exists(".config/app/settings.xml")); + UNSCOPED_INFO("logs"); + REQUIRE(scratch.exists(".local/state/app/Alchemy.log")); + UNSCOPED_INFO("cache"); + REQUIRE(scratch.exists(".cache/app/texturecache/t")); + UNSCOPED_INFO("account"); + REQUIRE(scratch.exists(".local/share/app/bob_resident/settings_per_account.xml")); + UNSCOPED_INFO("skins"); + REQUIRE(scratch.exists(".local/share/app/skins/default/colors.xml")); + UNSCOPED_INFO("no settings left in data"); + REQUIRE(!scratch.exists(".local/share/app/user_settings")); + UNSCOPED_INFO("no logs left in data"); + REQUIRE(!scratch.exists(".local/share/app/logs")); + UNSCOPED_INFO("no cache left in data"); + REQUIRE(!scratch.exists(".local/share/app/cache")); + UNSCOPED_INFO("base dirs private"); + REQUIRE((fs::status(scratch.path(".local/state")).permissions() & fs::perms::all) == fs::perms::owner_all); // an older viewer run afterwards makes a new legacy root; it stays put scratch.write(".app/user_settings/settings.xml", "new"); - ensure("second legacy root left", LLDirXDG::migrate(scratch.path(".app"), dirs, notes) == LLDirXDG::Migration::NONE); - ensure("second legacy root intact", scratch.exists(".app/user_settings/settings.xml")); + UNSCOPED_INFO("second legacy root left"); + REQUIRE((LLDirXDG::migrate(scratch.path(".app"), dirs, notes) == LLDirXDG::Migration::NONE)); + UNSCOPED_INFO("second legacy root intact"); + REQUIRE(scratch.exists(".app/user_settings/settings.xml")); } - template<> template<> - void LLDirTest_object_t::test<11>() + TEST_CASE_METHOD(LLDirTest, "LLDir: LLDirXDG::migrate leaves a profile it cannot move whole", "[LLDir]") { - set_test_name("LLDirXDG::migrate() leaves a profile it can't move whole"); std::vector notes; { ScratchHome scratch; const LLDirXDG::Layout dirs = LLDirXDG::layout(scratch.home, "app"); scratch.writeLegacyProfile(); scratch.write(".config/app/other.xml"); - ensure("config dir in the way", LLDirXDG::migrate(scratch.path(".app"), dirs, notes) == LLDirXDG::Migration::FAILED); - ensure("put back", scratch.legacyProfileIntact()); - ensure("no data dir", !scratch.exists(".local/share/app")); - ensure("no state dir", !scratch.exists(".local/state/app")); - ensure("config dir untouched", scratch.exists(".config/app/other.xml")); + UNSCOPED_INFO("config dir in the way"); + REQUIRE((LLDirXDG::migrate(scratch.path(".app"), dirs, notes) == LLDirXDG::Migration::FAILED)); + UNSCOPED_INFO("put back"); + REQUIRE(scratch.legacyProfileIntact()); + UNSCOPED_INFO("no data dir"); + REQUIRE(!scratch.exists(".local/share/app")); + UNSCOPED_INFO("no state dir"); + REQUIRE(!scratch.exists(".local/state/app")); + UNSCOPED_INFO("config dir untouched"); + REQUIRE(scratch.exists(".config/app/other.xml")); } { ScratchHome scratch; @@ -921,11 +956,15 @@ namespace scratch.writeLegacyProfile(); scratch.write(".app/logs/Alchemy.exec_marker"); const int fd = ::open(scratch.path(".app/logs/Alchemy.exec_marker").c_str(), O_RDWR); - ensure("marker locked", fd != -1 && ::flock(fd, LOCK_EX | LOCK_NB) == 0); - ensure("viewer running", LLDirXDG::migrate(scratch.path(".app"), dirs, notes) == LLDirXDG::Migration::FAILED); - ensure("left while running", scratch.legacyProfileIntact()); + UNSCOPED_INFO("marker locked"); + REQUIRE((fd != -1 && ::flock(fd, LOCK_EX | LOCK_NB) == 0)); + UNSCOPED_INFO("viewer running"); + REQUIRE((LLDirXDG::migrate(scratch.path(".app"), dirs, notes) == LLDirXDG::Migration::FAILED)); + UNSCOPED_INFO("left while running"); + REQUIRE(scratch.legacyProfileIntact()); ::close(fd); - ensure("moved once it exits", LLDirXDG::migrate(scratch.path(".app"), dirs, notes) == LLDirXDG::Migration::MOVED); + UNSCOPED_INFO("moved once it exits"); + REQUIRE((LLDirXDG::migrate(scratch.path(".app"), dirs, notes) == LLDirXDG::Migration::MOVED)); } { ScratchHome scratch; @@ -933,34 +972,41 @@ namespace scratch.writeLegacyProfile(); fs::rename(scratch.path(".app"), scratch.path("elsewhere")); fs::create_directory_symlink(scratch.path("elsewhere"), scratch.path(".app")); - ensure("symlink kept", LLDirXDG::migrate(scratch.path(".app"), dirs, notes) == LLDirXDG::Migration::FAILED); - ensure("symlinked profile intact", scratch.legacyProfileIntact()); - ensure("symlink still a symlink", fs::is_symlink(scratch.path(".app"))); + UNSCOPED_INFO("symlink kept"); + REQUIRE((LLDirXDG::migrate(scratch.path(".app"), dirs, notes) == LLDirXDG::Migration::FAILED)); + UNSCOPED_INFO("symlinked profile intact"); + REQUIRE(scratch.legacyProfileIntact()); + UNSCOPED_INFO("symlink still a symlink"); + REQUIRE(fs::is_symlink(scratch.path(".app"))); } } - template<> template<> - void LLDirTest_object_t::test<12>() + TEST_CASE_METHOD(LLDirTest, "LLDir: LLDirXDG::adoptCache renames an old cache only where none is in the way", "[LLDir]") { - set_test_name("LLDirXDG::adoptCache()"); ScratchHome scratch; std::vector notes; const std::string old_cache = scratch.path(".cache/App"); const std::string cache = scratch.path(".cache/app"); LLDirXDG::adoptCache(old_cache, cache, notes); - ensure("nothing to adopt", !scratch.exists(".cache")); + UNSCOPED_INFO("nothing to adopt"); + REQUIRE(!scratch.exists(".cache")); scratch.write(".cache/App/texturecache/t"); LLDirXDG::adoptCache(old_cache, cache, notes); - ensure("adopted", scratch.exists(".cache/app/texturecache/t")); - ensure("old name gone", !scratch.exists(".cache/App")); + UNSCOPED_INFO("adopted"); + REQUIRE(scratch.exists(".cache/app/texturecache/t")); + UNSCOPED_INFO("old name gone"); + REQUIRE(!scratch.exists(".cache/App")); scratch.write(".cache/App/texturecache/stale"); LLDirXDG::adoptCache(old_cache, cache, notes); - ensure("current cache kept", scratch.exists(".cache/app/texturecache/t")); - ensure("old cache left beside it", scratch.exists(".cache/App/texturecache/stale")); - ensure("old cache not merged in", !scratch.exists(".cache/app/texturecache/stale")); + UNSCOPED_INFO("current cache kept"); + REQUIRE(scratch.exists(".cache/app/texturecache/t")); + UNSCOPED_INFO("old cache left beside it"); + REQUIRE(scratch.exists(".cache/App/texturecache/stale")); + UNSCOPED_INFO("old cache not merged in"); + REQUIRE(!scratch.exists(".cache/app/texturecache/stale")); } #endif // LL_LINUX } diff --git a/indra/llfilesystem/tests/lldiriterator_test.cpp b/indra/llfilesystem/tests/lldiriterator_test.cpp index 15470bd966..6877e266b3 100644 --- a/indra/llfilesystem/tests/lldiriterator_test.cpp +++ b/indra/llfilesystem/tests/lldiriterator_test.cpp @@ -26,24 +26,19 @@ */ #include "linden_common.h" -#include "lltut.h" +#include #include "../lldiriterator.h" -namespace tut -{ - namespace { + struct LLDirIteratorFixture { LLDirIteratorFixture() { } }; - typedef test_group LLDirIteratorTest_factory; - typedef LLDirIteratorTest_factory::object LLDirIteratorTest_t; - LLDirIteratorTest_factory tf("LLDirIterator"); /* CHOP-662 was originally introduced to deal with crashes deleting files from @@ -57,10 +52,8 @@ namespace LLDirIterator iter1(".","))--@---bad-group-name2((?\?-??.*\\.txt"); LLDirIterator iter2(".","__^v--x)Cuide d sua vida(x--v^__?\?-??.*"); } -} - template<> template<> - void LLDirIteratorTest_t::test<1>() + TEST_CASE_METHOD(LLDirIteratorFixture, "LLDirIterator: glob patterns full of regex characters do not crash", "[LLDirIterator]") { test_chop_662(); } diff --git a/indra/llimage/CMakeLists.txt b/indra/llimage/CMakeLists.txt index 5d9d9642ab..9cd09b4b9d 100644 --- a/indra/llimage/CMakeLists.txt +++ b/indra/llimage/CMakeLists.txt @@ -59,5 +59,11 @@ target_precompile_headers(llimage REUSE_FROM llprecompiled) 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) + al_add_test_binary( + llimageworker_test + PROJECT llimage + SOURCES tests/llimageworker_test.cpp + UNIT llimageworker.cpp + LIBRARIES llmath llcommon + ) endif() diff --git a/indra/llimage/tests/llimageworker_test.cpp b/indra/llimage/tests/llimageworker_test.cpp index 663142d79d..90cbbedb23 100644 --- a/indra/llimage/tests/llimageworker_test.cpp +++ b/indra/llimage/tests/llimageworker_test.cpp @@ -34,7 +34,7 @@ // for lltrace class #include "../llcommon/lltrace.h" // Tut header -#include "../test/lltut.h" +#include // ------------------------------------------------------------------------------------------- // Stubbing: Declarations required to link and run the class being tested @@ -98,8 +98,6 @@ const std::string& LLImage::getLastThreadError() { static std::string msg; retur // TUT // ------------------------------------------------------------------------------------------- -namespace tut -{ namespace { // Test wrapper declarations @@ -149,11 +147,6 @@ namespace } }; - // Tut templating thingamagic: test group, object and test instance - typedef test_group imagedecodethread_t; - typedef imagedecodethread_t::object imagedecodethread_object_t; - tut::imagedecodethread_t tut_imagedecodethread("LLImageDecodeThread"); -} // --------------------------------------------------------------------------------------- // Test functions @@ -167,17 +160,18 @@ namespace // Test the LLImageDecodeThread interface // --------------------------------------------------------------------------------------- - template<> template<> - void imagedecodethread_object_t::test<1>() + TEST_CASE_METHOD(imagedecodethread_test, "LLImageDecodeThread: a threaded decodeImage calls the responder", "[LLImageDecodeThread]") { // Test a *threaded* instance of the class mThread = new LLImageDecodeThread(true); - ensure("LLImageDecodeThread: threaded constructor failed", mThread != NULL); + UNSCOPED_INFO("LLImageDecodeThread: threaded constructor failed"); + REQUIRE(mThread != NULL); // Insert something in the queue bool done = false; LLImageDecodeThread::handle_t decodeHandle = mThread->decodeImage(NULL, 0, false, new responder_test(&done)); // Verifies we get back a valid handle - ensure("LLImageDecodeThread: threaded decodeImage(), returned handle is null", decodeHandle != 0); + UNSCOPED_INFO("LLImageDecodeThread: threaded decodeImage(), returned handle is null"); + CHECK(decodeHandle != 0); // Wait till the thread has time to handle the work order (though it doesn't do much per work order...) const U32 INCREMENT_TIME = 500; // 500 milliseconds const U32 MAX_TIME = 20 * INCREMENT_TIME; // Do the loop 20 times max, i.e. wait 10 seconds but no more @@ -188,6 +182,7 @@ namespace total_time += INCREMENT_TIME; } // Verifies that the responder has now been called - ensure("LLImageDecodeThread: threaded work unit not processed", done == true); + UNSCOPED_INFO("LLImageDecodeThread: threaded work unit not processed"); + CHECK(done); } } diff --git a/indra/llinventory/CMakeLists.txt b/indra/llinventory/CMakeLists.txt index 7bdd325ccb..fc80455bcf 100644 --- a/indra/llinventory/CMakeLists.txt +++ b/indra/llinventory/CMakeLists.txt @@ -57,15 +57,16 @@ target_precompile_headers(llinventory REUSE_FROM llprecompiled) # add unit tests if(AL_BUILD_TESTS) - foreach( - test - inventorymisc - llparcel - llpermissions - llsaleinfo - llsettingssky - lluserrelations + al_add_test_binary( + llinventory_tests + PROJECT llinventory + SOURCES + tests/inventorymisc_test.cpp + tests/llparcel_test.cpp + tests/llpermissions_test.cpp + tests/llsaleinfo_test.cpp + tests/llsettingssky_test.cpp + tests/lluserrelations_test.cpp + LIBRARIES llinventory llmath llcorehttp llfilesystem ) - al_add_test(${test} PROJECT llinventory LIBRARIES llinventory llmath llcorehttp llfilesystem) - endforeach() endif() diff --git a/indra/llinventory/tests/inventorymisc_test.cpp b/indra/llinventory/tests/inventorymisc_test.cpp index 1a7eeeb26b..3e50deb6a2 100644 --- a/indra/llinventory/tests/inventorymisc_test.cpp +++ b/indra/llinventory/tests/inventorymisc_test.cpp @@ -31,7 +31,7 @@ #include "llsdserialize.h" #include "../llinventory.h" -#include "../test/lltut.h" +#include #include "../test/namedtempfile.h" @@ -127,86 +127,92 @@ LLPointer create_random_inventory_cat() } } -namespace tut -{ namespace { - struct inventory_data - { - }; - typedef test_group inventory_test; - typedef inventory_test::object inventory_object; - tut::inventory_test inv("LLInventory"); -} - //***class LLInventoryType***// - template<> template<> - void inventory_object::test<1>() + TEST_CASE("LLInventory: LLInventoryType::lookup maps a name to its type", "[LLInventory]") { LLInventoryType::EType retType = LLInventoryType::lookup(std::string("sound")); - ensure("1.LLInventoryType::lookup(char*) failed", retType == LLInventoryType::IT_SOUND); + UNSCOPED_INFO("1.LLInventoryType::lookup(char*) failed"); + CHECK(retType == LLInventoryType::IT_SOUND); retType = LLInventoryType::lookup(std::string("snapshot")); - ensure("2.LLInventoryType::lookup(char*) failed", retType == LLInventoryType::IT_SNAPSHOT); + UNSCOPED_INFO("2.LLInventoryType::lookup(char*) failed"); + CHECK(retType == LLInventoryType::IT_SNAPSHOT); } - template<> template<> - void inventory_object::test<2>() + TEST_CASE("LLInventory: LLInventoryType::lookup maps a type to its name", "[LLInventory]") { static std::string retType = LLInventoryType::lookup(LLInventoryType::IT_CALLINGCARD); - ensure("1.LLInventoryType::lookup(EType) failed", (retType == "callcard")); + UNSCOPED_INFO("1.LLInventoryType::lookup(EType) failed"); + CHECK(retType == "callcard"); retType = LLInventoryType::lookup(LLInventoryType::IT_LANDMARK); - ensure("2.LLInventoryType::lookup(EType) failed", (retType == "landmark")); + UNSCOPED_INFO("2.LLInventoryType::lookup(EType) failed"); + CHECK(retType == "landmark"); } - template<> template<> - void inventory_object::test<3>() + TEST_CASE("LLInventory: lookupHumanReadable gives a type's display name", "[LLInventory]") { static std::string retType = LLInventoryType::lookupHumanReadable(LLInventoryType::IT_CALLINGCARD); - ensure("1.LLInventoryType::lookupHumanReadable(EType) failed", (retType == "calling card")); + UNSCOPED_INFO("1.LLInventoryType::lookupHumanReadable(EType) failed"); + CHECK(retType == "calling card"); retType = LLInventoryType::lookupHumanReadable(LLInventoryType::IT_LANDMARK); - ensure("2.LLInventoryType::lookupHumanReadable(EType) failed", (retType == "landmark")); + UNSCOPED_INFO("2.LLInventoryType::lookupHumanReadable(EType) failed"); + CHECK(retType == "landmark"); } - template<> template<> - void inventory_object::test<4>() + TEST_CASE("LLInventory: defaultForAssetType gives an asset type's inventory type", "[LLInventory]") { static LLInventoryType::EType retType = LLInventoryType::defaultForAssetType(LLAssetType::AT_TEXTURE); - ensure("1.LLInventoryType::defaultForAssetType(LLAssetType EType) failed", retType == LLInventoryType::IT_TEXTURE); + UNSCOPED_INFO("1.LLInventoryType::defaultForAssetType(LLAssetType EType) failed"); + CHECK(retType == LLInventoryType::IT_TEXTURE); retType = LLInventoryType::defaultForAssetType(LLAssetType::AT_LANDMARK); - ensure("2.LLInventoryType::defaultForAssetType(LLAssetType EType) failed", retType == LLInventoryType::IT_LANDMARK); + UNSCOPED_INFO("2.LLInventoryType::defaultForAssetType(LLAssetType EType) failed"); + CHECK(retType == LLInventoryType::IT_LANDMARK); } //*****class LLInventoryItem*****// - template<> template<> - void inventory_object::test<5>() + TEST_CASE("LLInventory: an item survives an LLSD round trip before and after changes", "[LLInventory]") { LLPointer src = create_random_inventory_item(); LLSD sd = ll_create_sd_from_inventory_item(src); //LL_INFOS() << "sd: " << *sd << LL_ENDL; LLPointer dst = new LLInventoryItem; bool successful_parse = dst->fromLLSD(sd); - ensure_equals("0.LLInventoryItem::fromLLSD()", successful_parse, true); - ensure_equals("1.item id::getUUID() failed", dst->getUUID(), src->getUUID()); - ensure_equals("2.parent::getParentUUID() failed", dst->getParentUUID(), src->getParentUUID()); - ensure_equals("3.name::getName() failed", dst->getName(), src->getName()); - ensure_equals("4.type::getType() failed", dst->getType(), src->getType()); - - ensure_equals("5.permissions::getPermissions() failed", dst->getPermissions(), src->getPermissions()); - ensure_equals("6.description::getDescription() failed", dst->getDescription(), src->getDescription()); - ensure_equals("7.sale type::getSaleType() failed", dst->getSaleInfo().getSaleType(), src->getSaleInfo().getSaleType()); - ensure_equals("8.sale price::getSalePrice() failed", dst->getSaleInfo().getSalePrice(), src->getSaleInfo().getSalePrice()); - ensure_equals("9.asset id::getAssetUUID() failed", dst->getAssetUUID(), src->getAssetUUID()); - ensure_equals("10.inventory type::getInventoryType() failed", dst->getInventoryType(), src->getInventoryType()); - ensure_equals("11.flags::getFlags() failed", dst->getFlags(), src->getFlags()); - ensure_equals("12.creation::getCreationDate() failed", dst->getCreationDate(), src->getCreationDate()); + UNSCOPED_INFO("0.LLInventoryItem::fromLLSD()"); + CHECK(successful_parse == true); + UNSCOPED_INFO("1.item id::getUUID() failed"); + CHECK(dst->getUUID() == src->getUUID()); + UNSCOPED_INFO("2.parent::getParentUUID() failed"); + CHECK(dst->getParentUUID() == src->getParentUUID()); + UNSCOPED_INFO("3.name::getName() failed"); + CHECK(dst->getName() == src->getName()); + UNSCOPED_INFO("4.type::getType() failed"); + CHECK(dst->getType() == src->getType()); + + UNSCOPED_INFO("5.permissions::getPermissions() failed"); + CHECK(dst->getPermissions() == src->getPermissions()); + UNSCOPED_INFO("6.description::getDescription() failed"); + CHECK(dst->getDescription() == src->getDescription()); + UNSCOPED_INFO("7.sale type::getSaleType() failed"); + CHECK(dst->getSaleInfo().getSaleType() == src->getSaleInfo().getSaleType()); + UNSCOPED_INFO("8.sale price::getSalePrice() failed"); + CHECK(dst->getSaleInfo().getSalePrice() == src->getSaleInfo().getSalePrice()); + UNSCOPED_INFO("9.asset id::getAssetUUID() failed"); + CHECK(dst->getAssetUUID() == src->getAssetUUID()); + UNSCOPED_INFO("10.inventory type::getInventoryType() failed"); + CHECK(dst->getInventoryType() == src->getInventoryType()); + UNSCOPED_INFO("11.flags::getFlags() failed"); + CHECK(dst->getFlags() == src->getFlags()); + UNSCOPED_INFO("12.creation::getCreationDate() failed"); + CHECK(dst->getCreationDate() == src->getCreationDate()); LLUUID new_item_id, new_parent_id; new_item_id.generate(); @@ -258,24 +264,35 @@ namespace sd = ll_create_sd_from_inventory_item(src); //LL_INFOS() << "sd: " << *sd << LL_ENDL; successful_parse = dst->fromLLSD(sd); - ensure_equals("13.item id::getUUID() failed", dst->getUUID(), src->getUUID()); - ensure_equals("14.parent::getParentUUID() failed", dst->getParentUUID(), src->getParentUUID()); - ensure_equals("15.name::getName() failed", dst->getName(), src->getName()); - ensure_equals("16.type::getType() failed", dst->getType(), src->getType()); - - ensure_equals("17.permissions::getPermissions() failed", dst->getPermissions(), src->getPermissions()); - ensure_equals("18.description::getDescription() failed", dst->getDescription(), src->getDescription()); - ensure_equals("19.sale type::getSaleType() failed type", dst->getSaleInfo().getSaleType(), src->getSaleInfo().getSaleType()); - ensure_equals("20.sale price::getSalePrice() failed price", dst->getSaleInfo().getSalePrice(), src->getSaleInfo().getSalePrice()); - ensure_equals("21.asset id::getAssetUUID() failed id", dst->getAssetUUID(), src->getAssetUUID()); - ensure_equals("22.inventory type::getInventoryType() failed type", dst->getInventoryType(), src->getInventoryType()); - ensure_equals("23.flags::getFlags() failed", dst->getFlags(), src->getFlags()); - ensure_equals("24.creation::getCreationDate() failed", dst->getCreationDate(), src->getCreationDate()); + UNSCOPED_INFO("13.item id::getUUID() failed"); + CHECK(dst->getUUID() == src->getUUID()); + UNSCOPED_INFO("14.parent::getParentUUID() failed"); + CHECK(dst->getParentUUID() == src->getParentUUID()); + UNSCOPED_INFO("15.name::getName() failed"); + CHECK(dst->getName() == src->getName()); + UNSCOPED_INFO("16.type::getType() failed"); + CHECK(dst->getType() == src->getType()); + + UNSCOPED_INFO("17.permissions::getPermissions() failed"); + CHECK(dst->getPermissions() == src->getPermissions()); + UNSCOPED_INFO("18.description::getDescription() failed"); + CHECK(dst->getDescription() == src->getDescription()); + UNSCOPED_INFO("19.sale type::getSaleType() failed type"); + CHECK(dst->getSaleInfo().getSaleType() == src->getSaleInfo().getSaleType()); + UNSCOPED_INFO("20.sale price::getSalePrice() failed price"); + CHECK(dst->getSaleInfo().getSalePrice() == src->getSaleInfo().getSalePrice()); + UNSCOPED_INFO("21.asset id::getAssetUUID() failed id"); + CHECK(dst->getAssetUUID() == src->getAssetUUID()); + UNSCOPED_INFO("22.inventory type::getInventoryType() failed type"); + CHECK(dst->getInventoryType() == src->getInventoryType()); + UNSCOPED_INFO("23.flags::getFlags() failed"); + CHECK(dst->getFlags() == src->getFlags()); + UNSCOPED_INFO("24.creation::getCreationDate() failed"); + CHECK(dst->getCreationDate() == src->getCreationDate()); } - template<> template<> - void inventory_object::test<6>() + TEST_CASE("LLInventory: copyItem copies an item and generateUUID gives it a new id", "[LLInventory]") { LLPointer src = create_random_inventory_item(); @@ -331,32 +348,45 @@ namespace LLSD sd = ll_create_sd_from_inventory_item(src); LLPointer dst = new LLInventoryItem; bool successful_parse = dst->fromLLSD(sd); - ensure_equals("0.LLInventoryItem::fromLLSD()", successful_parse, true); + UNSCOPED_INFO("0.LLInventoryItem::fromLLSD()"); + CHECK(successful_parse == true); LLPointer src1 = create_random_inventory_item(); src1->copyItem(src); - ensure_equals("1.item id::getUUID() failed", dst->getUUID(), src1->getUUID()); - ensure_equals("2.parent::getParentUUID() failed", dst->getParentUUID(), src1->getParentUUID()); - ensure_equals("3.name::getName() failed", dst->getName(), src1->getName()); - ensure_equals("4.type::getType() failed", dst->getType(), src1->getType()); - - ensure_equals("5.permissions::getPermissions() failed", dst->getPermissions(), src1->getPermissions()); - ensure_equals("6.description::getDescription() failed", dst->getDescription(), src1->getDescription()); - ensure_equals("7.sale type::getSaleType() failed type", dst->getSaleInfo().getSaleType(), src1->getSaleInfo().getSaleType()); - ensure_equals("8.sale price::getSalePrice() failed price", dst->getSaleInfo().getSalePrice(), src1->getSaleInfo().getSalePrice()); - ensure_equals("9.asset id::getAssetUUID() failed id", dst->getAssetUUID(), src1->getAssetUUID()); - ensure_equals("10.inventory type::getInventoryType() failed type", dst->getInventoryType(), src1->getInventoryType()); - ensure_equals("11.flags::getFlags() failed", dst->getFlags(), src1->getFlags()); - ensure_equals("12.creation::getCreationDate() failed", dst->getCreationDate(), src1->getCreationDate()); + UNSCOPED_INFO("1.item id::getUUID() failed"); + CHECK(dst->getUUID() == src1->getUUID()); + UNSCOPED_INFO("2.parent::getParentUUID() failed"); + CHECK(dst->getParentUUID() == src1->getParentUUID()); + UNSCOPED_INFO("3.name::getName() failed"); + CHECK(dst->getName() == src1->getName()); + UNSCOPED_INFO("4.type::getType() failed"); + CHECK(dst->getType() == src1->getType()); + + UNSCOPED_INFO("5.permissions::getPermissions() failed"); + CHECK(dst->getPermissions() == src1->getPermissions()); + UNSCOPED_INFO("6.description::getDescription() failed"); + CHECK(dst->getDescription() == src1->getDescription()); + UNSCOPED_INFO("7.sale type::getSaleType() failed type"); + CHECK(dst->getSaleInfo().getSaleType() == src1->getSaleInfo().getSaleType()); + UNSCOPED_INFO("8.sale price::getSalePrice() failed price"); + CHECK(dst->getSaleInfo().getSalePrice() == src1->getSaleInfo().getSalePrice()); + UNSCOPED_INFO("9.asset id::getAssetUUID() failed id"); + CHECK(dst->getAssetUUID() == src1->getAssetUUID()); + UNSCOPED_INFO("10.inventory type::getInventoryType() failed type"); + CHECK(dst->getInventoryType() == src1->getInventoryType()); + UNSCOPED_INFO("11.flags::getFlags() failed"); + CHECK(dst->getFlags() == src1->getFlags()); + UNSCOPED_INFO("12.creation::getCreationDate() failed"); + CHECK(dst->getCreationDate() == src1->getCreationDate()); // quick test to make sure generateUUID() really works src1->generateUUID(); - ensure_not_equals("13.item id::generateUUID() failed", src->getUUID(), src1->getUUID()); + UNSCOPED_INFO("13.item id::generateUUID() failed"); + CHECK(src->getUUID() != src1->getUUID()); } - template<> template<> - void inventory_object::test<7>() + TEST_CASE("LLInventory: an item survives asLLSD through a notation file", "[LLInventory]") { NamedTempFile temp_file("linden_file", "", ".dat"); std::string filename(temp_file.getPath().string()); @@ -395,22 +425,33 @@ namespace file.close(); - ensure_equals("1.item id::getUUID() failed", src1->getUUID(), src2->getUUID()); - ensure_equals("2.parent::getParentUUID() failed", src1->getParentUUID(), src2->getParentUUID()); - ensure_equals("3.permissions::getPermissions() failed", src1->getPermissions(), src2->getPermissions()); - ensure_equals("4.sale price::getSalePrice() failed price", src1->getSaleInfo().getSalePrice(), src2->getSaleInfo().getSalePrice()); - ensure_equals("5.asset id::getAssetUUID() failed id", src1->getAssetUUID(), src2->getAssetUUID()); - ensure_equals("6.type::getType() failed", src1->getType(), src2->getType()); - ensure_equals("7.inventory type::getInventoryType() failed type", src1->getInventoryType(), src2->getInventoryType()); - ensure_equals("8.name::getName() failed", src1->getName(), src2->getName()); - ensure_equals("9.description::getDescription() failed", src1->getDescription(), src2->getDescription()); - ensure_equals("10.creation::getCreationDate() failed", src1->getCreationDate(), src2->getCreationDate()); - ensure_equals("13.thumbnails::getThumbnailUUID() failed", src1->getThumbnailUUID(), src2->getThumbnailUUID()); - ensure_equals("14.favorites::getIsFavorite() failed", src1->getIsFavorite(), src2->getIsFavorite()); + UNSCOPED_INFO("1.item id::getUUID() failed"); + CHECK(src1->getUUID() == src2->getUUID()); + UNSCOPED_INFO("2.parent::getParentUUID() failed"); + CHECK(src1->getParentUUID() == src2->getParentUUID()); + UNSCOPED_INFO("3.permissions::getPermissions() failed"); + CHECK(src1->getPermissions() == src2->getPermissions()); + UNSCOPED_INFO("4.sale price::getSalePrice() failed price"); + CHECK(src1->getSaleInfo().getSalePrice() == src2->getSaleInfo().getSalePrice()); + UNSCOPED_INFO("5.asset id::getAssetUUID() failed id"); + CHECK(src1->getAssetUUID() == src2->getAssetUUID()); + UNSCOPED_INFO("6.type::getType() failed"); + CHECK(src1->getType() == src2->getType()); + UNSCOPED_INFO("7.inventory type::getInventoryType() failed type"); + CHECK(src1->getInventoryType() == src2->getInventoryType()); + UNSCOPED_INFO("8.name::getName() failed"); + CHECK(src1->getName() == src2->getName()); + UNSCOPED_INFO("9.description::getDescription() failed"); + CHECK(src1->getDescription() == src2->getDescription()); + UNSCOPED_INFO("10.creation::getCreationDate() failed"); + CHECK(src1->getCreationDate() == src2->getCreationDate()); + UNSCOPED_INFO("13.thumbnails::getThumbnailUUID() failed"); + CHECK(src1->getThumbnailUUID() == src2->getThumbnailUUID()); + UNSCOPED_INFO("14.favorites::getIsFavorite() failed"); + CHECK(src1->getIsFavorite() == src2->getIsFavorite()); } - template<> template<> - void inventory_object::test<8>() + TEST_CASE("LLInventory: an item survives the legacy stream but its favorite does not", "[LLInventory]") { LLPointer src1 = create_random_inventory_item(); @@ -421,76 +462,104 @@ namespace LLPointer src2 = new LLInventoryItem(); src2->importLegacyStream(istream); - ensure_equals("1.item id::getUUID() failed", src1->getUUID(), src2->getUUID()); - ensure_equals("2.parent::getParentUUID() failed", src1->getParentUUID(), src2->getParentUUID()); - ensure_equals("3.permissions::getPermissions() failed", src1->getPermissions(), src2->getPermissions()); - ensure_equals("4.sale price::getSalePrice() failed price", src1->getSaleInfo().getSalePrice(), src2->getSaleInfo().getSalePrice()); - ensure_equals("5.asset id::getAssetUUID() failed id", src1->getAssetUUID(), src2->getAssetUUID()); - ensure_equals("6.type::getType() failed", src1->getType(), src2->getType()); - ensure_equals("7.inventory type::getInventoryType() failed type", src1->getInventoryType(), src2->getInventoryType()); - ensure_equals("8.name::getName() failed", src1->getName(), src2->getName()); - ensure_equals("9.description::getDescription() failed", src1->getDescription(), src2->getDescription()); - ensure_equals("10.creation::getCreationDate() failed", src1->getCreationDate(), src2->getCreationDate()); - ensure_equals("11.thumbnails::getThumbnailUUID() failed", src1->getThumbnailUUID(), src2->getThumbnailUUID()); - ensure_equals("12.favorites::getIsFavorite() failed", false, src2->getIsFavorite()); // not supposed to carry over + UNSCOPED_INFO("1.item id::getUUID() failed"); + CHECK(src1->getUUID() == src2->getUUID()); + UNSCOPED_INFO("2.parent::getParentUUID() failed"); + CHECK(src1->getParentUUID() == src2->getParentUUID()); + UNSCOPED_INFO("3.permissions::getPermissions() failed"); + CHECK(src1->getPermissions() == src2->getPermissions()); + UNSCOPED_INFO("4.sale price::getSalePrice() failed price"); + CHECK(src1->getSaleInfo().getSalePrice() == src2->getSaleInfo().getSalePrice()); + UNSCOPED_INFO("5.asset id::getAssetUUID() failed id"); + CHECK(src1->getAssetUUID() == src2->getAssetUUID()); + UNSCOPED_INFO("6.type::getType() failed"); + CHECK(src1->getType() == src2->getType()); + UNSCOPED_INFO("7.inventory type::getInventoryType() failed type"); + CHECK(src1->getInventoryType() == src2->getInventoryType()); + UNSCOPED_INFO("8.name::getName() failed"); + CHECK(src1->getName() == src2->getName()); + UNSCOPED_INFO("9.description::getDescription() failed"); + CHECK(src1->getDescription() == src2->getDescription()); + UNSCOPED_INFO("10.creation::getCreationDate() failed"); + CHECK(src1->getCreationDate() == src2->getCreationDate()); + UNSCOPED_INFO("11.thumbnails::getThumbnailUUID() failed"); + CHECK(src1->getThumbnailUUID() == src2->getThumbnailUUID()); + UNSCOPED_INFO("12.favorites::getIsFavorite() failed"); + CHECK(false == src2->getIsFavorite()); // not supposed to carry over } - template<> template<> - void inventory_object::test<9>() + TEST_CASE("LLInventory: placeholder for the removed XML export and import", "[LLInventory]") { // Deleted LLInventoryItem::exportFileXML() and LLInventoryItem::importXML() // because I can't find any non-test code references to it. 2009-05-04 JC } - template<> template<> - void inventory_object::test<11>() + TEST_CASE("LLInventory: an item survives asLLSD and fromLLSD", "[LLInventory]") { LLPointer src1 = create_random_inventory_item(); LLSD retSd = src1->asLLSD(); LLPointer src2 = new LLInventoryItem(); src2->fromLLSD(retSd); - ensure_equals("1.item id::getUUID() failed", src1->getUUID(), src2->getUUID()); - ensure_equals("2.parent::getParentUUID() failed", src1->getParentUUID(), src2->getParentUUID()); - ensure_equals("3.permissions::getPermissions() failed", src1->getPermissions(), src2->getPermissions()); - ensure_equals("4.asset id::getAssetUUID() failed id", src1->getAssetUUID(), src2->getAssetUUID()); - ensure_equals("5.type::getType() failed", src1->getType(), src2->getType()); - ensure_equals("6.inventory type::getInventoryType() failed type", src1->getInventoryType(), src2->getInventoryType()); - ensure_equals("7.flags::getFlags() failed", src1->getFlags(), src2->getFlags()); - ensure_equals("8.sale type::getSaleType() failed type", src1->getSaleInfo().getSaleType(), src2->getSaleInfo().getSaleType()); - ensure_equals("9.sale price::getSalePrice() failed price", src1->getSaleInfo().getSalePrice(), src2->getSaleInfo().getSalePrice()); - ensure_equals("10.name::getName() failed", src1->getName(), src2->getName()); - ensure_equals("11.description::getDescription() failed", src1->getDescription(), src2->getDescription()); - ensure_equals("12.creation::getCreationDate() failed", src1->getCreationDate(), src2->getCreationDate()); - ensure_equals("13.thumbnails::getThumbnailUUID() failed", src1->getThumbnailUUID(), src2->getThumbnailUUID()); - ensure_equals("14.favorites::getIsFavorite() failed", src1->getIsFavorite(), src2->getIsFavorite()); + UNSCOPED_INFO("1.item id::getUUID() failed"); + CHECK(src1->getUUID() == src2->getUUID()); + UNSCOPED_INFO("2.parent::getParentUUID() failed"); + CHECK(src1->getParentUUID() == src2->getParentUUID()); + UNSCOPED_INFO("3.permissions::getPermissions() failed"); + CHECK(src1->getPermissions() == src2->getPermissions()); + UNSCOPED_INFO("4.asset id::getAssetUUID() failed id"); + CHECK(src1->getAssetUUID() == src2->getAssetUUID()); + UNSCOPED_INFO("5.type::getType() failed"); + CHECK(src1->getType() == src2->getType()); + UNSCOPED_INFO("6.inventory type::getInventoryType() failed type"); + CHECK(src1->getInventoryType() == src2->getInventoryType()); + UNSCOPED_INFO("7.flags::getFlags() failed"); + CHECK(src1->getFlags() == src2->getFlags()); + UNSCOPED_INFO("8.sale type::getSaleType() failed type"); + CHECK(src1->getSaleInfo().getSaleType() == src2->getSaleInfo().getSaleType()); + UNSCOPED_INFO("9.sale price::getSalePrice() failed price"); + CHECK(src1->getSaleInfo().getSalePrice() == src2->getSaleInfo().getSalePrice()); + UNSCOPED_INFO("10.name::getName() failed"); + CHECK(src1->getName() == src2->getName()); + UNSCOPED_INFO("11.description::getDescription() failed"); + CHECK(src1->getDescription() == src2->getDescription()); + UNSCOPED_INFO("12.creation::getCreationDate() failed"); + CHECK(src1->getCreationDate() == src2->getCreationDate()); + UNSCOPED_INFO("13.thumbnails::getThumbnailUUID() failed"); + CHECK(src1->getThumbnailUUID() == src2->getThumbnailUUID()); + UNSCOPED_INFO("14.favorites::getIsFavorite() failed"); + CHECK(src1->getIsFavorite() == src2->getIsFavorite()); } //******class LLInventoryCategory*******// - template<> template<> - void inventory_object::test<12>() + TEST_CASE("LLInventory: a category survives an LLSD round trip with its preferred type", "[LLInventory]") { LLPointer src = create_random_inventory_cat(); LLSD sd = ll_create_sd_from_inventory_category(src); LLPointer dst = ll_create_category_from_sd(sd); - ensure_equals("1.item id::getUUID() failed", dst->getUUID(), src->getUUID()); - ensure_equals("2.parent::getParentUUID() failed", dst->getParentUUID(), src->getParentUUID()); - ensure_equals("3.name::getName() failed", dst->getName(), src->getName()); - ensure_equals("4.type::getType() failed", dst->getType(), src->getType()); - ensure_equals("5.preferred type::getPreferredType() failed", dst->getPreferredType(), src->getPreferredType()); + UNSCOPED_INFO("1.item id::getUUID() failed"); + CHECK(dst->getUUID() == src->getUUID()); + UNSCOPED_INFO("2.parent::getParentUUID() failed"); + CHECK(dst->getParentUUID() == src->getParentUUID()); + UNSCOPED_INFO("3.name::getName() failed"); + CHECK(dst->getName() == src->getName()); + UNSCOPED_INFO("4.type::getType() failed"); + CHECK(dst->getType() == src->getType()); + UNSCOPED_INFO("5.preferred type::getPreferredType() failed"); + CHECK(dst->getPreferredType() == src->getPreferredType()); src->setPreferredType( LLFolderType::FT_TEXTURE); sd = ll_create_sd_from_inventory_category(src); dst = ll_create_category_from_sd(sd); - ensure_equals("6.preferred type::getPreferredType() failed", dst->getPreferredType(), src->getPreferredType()); + UNSCOPED_INFO("6.preferred type::getPreferredType() failed"); + CHECK(dst->getPreferredType() == src->getPreferredType()); } - template<> template<> - void inventory_object::test<13>() + TEST_CASE("LLInventory: a category survives exportLLSD through a notation file", "[LLInventory]") { NamedTempFile temp_file("linden_file", "", ".dat"); std::string filename(temp_file.getPath().string()); @@ -529,17 +598,23 @@ namespace LLPointer src2 = new LLInventoryCategory(); src2->importLLSDMap(s_item); - ensure_equals("1.item id::getUUID() failed", src1->getUUID(), src2->getUUID()); - ensure_equals("2.parent::getParentUUID() failed", src1->getParentUUID(), src2->getParentUUID()); - ensure_equals("3.type::getType() failed", src1->getType(), src2->getType()); - ensure_equals("4.preferred type::getPreferredType() failed", src1->getPreferredType(), src2->getPreferredType()); - ensure_equals("5.name::getName() failed", src1->getName(), src2->getName()); - ensure_equals("6.thumbnails::getThumbnailUUID() failed", src1->getThumbnailUUID(), src2->getThumbnailUUID()); - ensure_equals("7.favorites::getIsFavorite() failed", src1->getIsFavorite(), src2->getIsFavorite()); + UNSCOPED_INFO("1.item id::getUUID() failed"); + CHECK(src1->getUUID() == src2->getUUID()); + UNSCOPED_INFO("2.parent::getParentUUID() failed"); + CHECK(src1->getParentUUID() == src2->getParentUUID()); + UNSCOPED_INFO("3.type::getType() failed"); + CHECK(src1->getType() == src2->getType()); + UNSCOPED_INFO("4.preferred type::getPreferredType() failed"); + CHECK(src1->getPreferredType() == src2->getPreferredType()); + UNSCOPED_INFO("5.name::getName() failed"); + CHECK(src1->getName() == src2->getName()); + UNSCOPED_INFO("6.thumbnails::getThumbnailUUID() failed"); + CHECK(src1->getThumbnailUUID() == src2->getThumbnailUUID()); + UNSCOPED_INFO("7.favorites::getIsFavorite() failed"); + CHECK(src1->getIsFavorite() == src2->getIsFavorite()); } - template<> template<> - void inventory_object::test<14>() + TEST_CASE("LLInventory: a category survives the legacy stream but its favorite does not", "[LLInventory]") { LLPointer src1 = create_random_inventory_cat(); @@ -550,12 +625,19 @@ namespace LLPointer src2 = new LLInventoryCategory(); src2->importLegacyStream(istream); - ensure_equals("1.item id::getUUID() failed", src1->getUUID(), src2->getUUID()); - ensure_equals("2.parent::getParentUUID() failed", src1->getParentUUID(), src2->getParentUUID()); - ensure_equals("3.type::getType() failed", src1->getType(), src2->getType()); - ensure_equals("4.preferred type::getPreferredType() failed", src1->getPreferredType(), src2->getPreferredType()); - ensure_equals("5.name::getName() failed", src1->getName(), src2->getName()); - ensure_equals("13.thumbnails::getThumbnailUUID() failed", src1->getThumbnailUUID(), src2->getThumbnailUUID()); - ensure_equals("14.favorites::getIsFavorite() failed", false, src2->getIsFavorite()); // currently not supposed to carry over + UNSCOPED_INFO("1.item id::getUUID() failed"); + CHECK(src1->getUUID() == src2->getUUID()); + UNSCOPED_INFO("2.parent::getParentUUID() failed"); + CHECK(src1->getParentUUID() == src2->getParentUUID()); + UNSCOPED_INFO("3.type::getType() failed"); + CHECK(src1->getType() == src2->getType()); + UNSCOPED_INFO("4.preferred type::getPreferredType() failed"); + CHECK(src1->getPreferredType() == src2->getPreferredType()); + UNSCOPED_INFO("5.name::getName() failed"); + CHECK(src1->getName() == src2->getName()); + UNSCOPED_INFO("13.thumbnails::getThumbnailUUID() failed"); + CHECK(src1->getThumbnailUUID() == src2->getThumbnailUUID()); + UNSCOPED_INFO("14.favorites::getIsFavorite() failed"); + CHECK(false == src2->getIsFavorite()); // currently not supposed to carry over } } diff --git a/indra/llinventory/tests/llparcel_test.cpp b/indra/llinventory/tests/llparcel_test.cpp index c32d17b449..27fc7eea2d 100644 --- a/indra/llinventory/tests/llparcel_test.cpp +++ b/indra/llinventory/tests/llparcel_test.cpp @@ -31,42 +31,34 @@ #include "../llparcel.h" -#include "../test/lltut.h" +#include -namespace tut -{ namespace { - struct llinventoryparcel_data - { - }; - typedef test_group llinventoryparcel_test; - typedef llinventoryparcel_test::object llinventoryparcel_object; - tut::llinventoryparcel_test llinventoryparcel("LLInventoryParcel"); -} - - template<> template<> - void llinventoryparcel_object::test<1>() + TEST_CASE("LLInventoryParcel: each category's string and UI string map back to it", "[LLInventoryParcel]") { for (S32 i=0; i +#include #include "linden_common.h" -#include "lltut.h" #include "message.h" #include "llpermissions.h" -namespace tut -{ namespace { - struct permission - { - }; - 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>() + TEST_CASE("permission: a default LLPermissions has null ids and full masks and no owner", "[permission]") { LLPermissions permissions; LLUUID uuid = permissions.getCreator(); @@ -56,15 +44,29 @@ namespace LLUUID uuid2 = permissions.getGroup(); LLUUID uuid3 = permissions.getLastOwner(); - ensure("LLPermission Get Functions failed", (uuid == LLUUID::null && uuid1 == LLUUID::null && - uuid2 == LLUUID::null && uuid3 == LLUUID::null)); - ensure("LLPermission Get Functions failed", (permissions.getMaskBase() == PERM_ALL && permissions.getMaskOwner() == PERM_ALL && - permissions.getMaskGroup() == PERM_ALL && permissions.getMaskEveryone() == PERM_ALL && permissions.getMaskNextOwner() == PERM_ALL)); - ensure("Ownership functions failed", ((! permissions.isGroupOwned()) && (! permissions.isOwned()))); + { + INFO("LLPermission Get Functions failed"); + CHECK(uuid == LLUUID::null); + CHECK(uuid1 == LLUUID::null); + CHECK(uuid2 == LLUUID::null); + CHECK(uuid3 == LLUUID::null); + } + { + INFO("LLPermission Get Functions failed"); + CHECK(permissions.getMaskBase() == PERM_ALL); + CHECK(permissions.getMaskOwner() == PERM_ALL); + CHECK(permissions.getMaskGroup() == PERM_ALL); + CHECK(permissions.getMaskEveryone() == PERM_ALL); + CHECK(permissions.getMaskNextOwner() == PERM_ALL); + } + { + INFO("Ownership functions failed"); + CHECK_FALSE(permissions.isGroupOwned()); + CHECK_FALSE(permissions.isOwned()); + } } - template<> template<> - void permission_object_t::test<2>() + TEST_CASE("permission: init sets the creator and owner and last owner and group", "[permission]") { LLPermissions permissions; LLUUID creator("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); @@ -73,14 +75,17 @@ namespace LLUUID group("9c8eca51-53d5-42a7-bb58-cef070395db8"); permissions.init(creator, owner, lastOwner, group); - ensure_equals("init/getCreator():failed to return the creator ", creator, permissions.getCreator()); - ensure_equals("init/getOwner():failed to return the owner ", owner, permissions.getOwner()); - ensure_equals("init/getLastOwner():failed to return the group ", lastOwner, permissions.getLastOwner()); - ensure_equals("init/getGroup():failed to return the group ", group, permissions.getGroup()); + UNSCOPED_INFO("init/getCreator():failed to return the creator "); + CHECK(creator == permissions.getCreator()); + UNSCOPED_INFO("init/getOwner():failed to return the owner "); + CHECK(owner == permissions.getOwner()); + UNSCOPED_INFO("init/getLastOwner():failed to return the group "); + CHECK(lastOwner == permissions.getLastOwner()); + UNSCOPED_INFO("init/getGroup():failed to return the group "); + CHECK(group == permissions.getGroup()); } - template<> template<> - void permission_object_t::test<3>() + TEST_CASE("permission: initMasks fixes the masks and the mask setters keep them as given", "[permission]") { LLPermissions permissions; U32 base = PERM_ALL; @@ -96,27 +101,37 @@ namespace U32 fixednext = PERM_NONE; permissions.initMasks(base, owner, everyone, group, next); // will fix perms if not allowed. - ensure_equals("initMasks/getMaskBase():failed to return the MaskBase ", fixedbase, permissions.getMaskBase()); - ensure_equals("initMasks/getMaskOwner():failed to return the MaskOwner ", fixedowner, permissions.getMaskOwner()); - ensure_equals("initMasks/getMaskEveryone():failed to return the MaskGroup ", fixedgroup, permissions.getMaskGroup()); - ensure_equals("initMasks/getMaskEveryone():failed to return the MaskEveryone ", fixedeveryone, permissions.getMaskEveryone()); - ensure_equals("initMasks/getMaskNextOwner():failed to return the MaskNext ", fixednext, permissions.getMaskNextOwner()); + UNSCOPED_INFO("initMasks/getMaskBase():failed to return the MaskBase "); + CHECK(fixedbase == permissions.getMaskBase()); + UNSCOPED_INFO("initMasks/getMaskOwner():failed to return the MaskOwner "); + CHECK(fixedowner == permissions.getMaskOwner()); + UNSCOPED_INFO("initMasks/getMaskEveryone():failed to return the MaskGroup "); + CHECK(fixedgroup == permissions.getMaskGroup()); + UNSCOPED_INFO("initMasks/getMaskEveryone():failed to return the MaskEveryone "); + CHECK(fixedeveryone == permissions.getMaskEveryone()); + UNSCOPED_INFO("initMasks/getMaskNextOwner():failed to return the MaskNext "); + CHECK(fixednext == permissions.getMaskNextOwner()); // explictly set should maintain the values permissions.setMaskBase(base); //no fixing - ensure_equals("setMaskBase/getMaskBase():failed to return the MaskBase ", base, permissions.getMaskBase()); + UNSCOPED_INFO("setMaskBase/getMaskBase():failed to return the MaskBase "); + CHECK(base == permissions.getMaskBase()); permissions.setMaskOwner(owner); - ensure_equals("setMaskOwner/getMaskOwner():failed to return the MaskOwner ", owner, permissions.getMaskOwner()); + UNSCOPED_INFO("setMaskOwner/getMaskOwner():failed to return the MaskOwner "); + CHECK(owner == permissions.getMaskOwner()); permissions.setMaskEveryone(everyone); - ensure_equals("setMaskEveryone/getMaskEveryone():failed to return the MaskEveryone ", everyone, permissions.getMaskEveryone()); + UNSCOPED_INFO("setMaskEveryone/getMaskEveryone():failed to return the MaskEveryone "); + CHECK(everyone == permissions.getMaskEveryone()); permissions.setMaskGroup(group); - ensure_equals("setMaskGroup/getMaskEveryone():failed to return the MaskGroup ", group, permissions.getMaskGroup()); + UNSCOPED_INFO("setMaskGroup/getMaskEveryone():failed to return the MaskGroup "); + CHECK(group == permissions.getMaskGroup()); permissions.setMaskNext(next); - ensure_equals("setMaskNext/getMaskNextOwner():failed to return the MaskNext ", next, permissions.getMaskNextOwner()); + UNSCOPED_INFO("setMaskNext/getMaskNextOwner():failed to return the MaskNext "); + CHECK(next == permissions.getMaskNextOwner()); // further tests can be added to ensure perms for owner/group/everyone etc. get properly fixed. // code however suggests that there is no explict check if the perms are correct and the user of this @@ -124,8 +139,7 @@ namespace // perm combinations. } - template<> template<> - void permission_object_t::test<4>() + TEST_CASE("permission: set copies the ids of another LLPermissions", "[permission]") { LLPermissions perm,perm1; LLUUID creator("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); @@ -134,12 +148,16 @@ namespace LLUUID group("9c8eca51-53d5-42a7-bb58-cef070395db8"); perm1.init(creator,owner,lastOwner,group); perm.set(perm1); - ensure("set():failed to set ", (creator == perm.getCreator()) && (owner == perm.getOwner())&& - (lastOwner == perm.getLastOwner())&& (group == perm.getGroup())); + { + INFO("set():failed to set "); + CHECK(creator == perm.getCreator()); + CHECK(owner == perm.getOwner()); + CHECK(lastOwner == perm.getLastOwner()); + CHECK(group == perm.getGroup()); + } } - template<> template<> - void permission_object_t::test<5>() + TEST_CASE("permission: accumulate restricts the masks to those both allow", "[permission]") { LLPermissions perm,perm1; LLUUID creator("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); @@ -174,15 +192,19 @@ namespace U32 fixedeveryone = PERM_TRANSFER; U32 fixednext = PERM_NONE; - ensure_equals("accumulate failed ", fixedbase, perm.getMaskBase()); - ensure_equals("accumulate failed ", fixedowner, perm.getMaskOwner()); - ensure_equals("accumulate failed ", fixedgroup, perm.getMaskGroup()); - ensure_equals("accumulate failed ", fixedeveryone, perm.getMaskEveryone()); - ensure_equals("accumulate failed ", fixednext, perm.getMaskNextOwner()); + UNSCOPED_INFO("accumulate failed "); + CHECK(fixedbase == perm.getMaskBase()); + UNSCOPED_INFO("accumulate failed "); + CHECK(fixedowner == perm.getMaskOwner()); + UNSCOPED_INFO("accumulate failed "); + CHECK(fixedgroup == perm.getMaskGroup()); + UNSCOPED_INFO("accumulate failed "); + CHECK(fixedeveryone == perm.getMaskEveryone()); + UNSCOPED_INFO("accumulate failed "); + CHECK(fixednext == perm.getMaskNextOwner()); } - template<> template<> - void permission_object_t::test<6>() + TEST_CASE("permission: getSafeOwner is the owner or the group where there is none", "[permission]") { LLPermissions perm; LLUUID creator("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); @@ -190,20 +212,22 @@ namespace LLUUID lastOwner("5e47a0dc-97bf-44e0-8b40-de06718cee9d"); LLUUID group("9c8eca51-53d5-42a7-bb58-cef070395db8"); perm.init(creator,owner,lastOwner,group); - ensure_equals("getSafeOwner:failed ", owner,perm.getSafeOwner()); + UNSCOPED_INFO("getSafeOwner:failed "); + CHECK(owner == perm.getSafeOwner()); ///NULL Owner perm.init(creator,LLUUID::null,lastOwner,group); - ensure_equals("getSafeOwner:failed ", group, perm.getSafeOwner()); + UNSCOPED_INFO("getSafeOwner:failed "); + CHECK(group == perm.getSafeOwner()); } - template<> template<> - void permission_object_t::test<7>() + TEST_CASE("permission: getOwnership gives the owner or else the owning group", "[permission]") { LLPermissions perm1; LLUUID uuid; bool is_group_owned = false; - ensure("1:getOwnership:failed ", ! perm1.getOwnership(uuid,is_group_owned)); + UNSCOPED_INFO("1:getOwnership:failed "); + REQUIRE_FALSE(perm1.getOwnership(uuid,is_group_owned)); LLPermissions perm; LLUUID creator("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); @@ -212,15 +236,22 @@ namespace LLUUID group("9c8eca51-53d5-42a7-bb58-cef070395db8"); perm.init(creator,owner,lastOwner,group); perm.getOwnership(uuid,is_group_owned); - ensure("2:getOwnership:failed ", ((uuid == owner) && (! is_group_owned))); + { + INFO("2:getOwnership:failed "); + CHECK(uuid == owner); + CHECK_FALSE(is_group_owned); + } perm.init(creator,LLUUID::null,lastOwner,group); perm.getOwnership(uuid,is_group_owned); - ensure("3:getOwnership:failed ", ((uuid == group) && is_group_owned)); + { + INFO("3:getOwnership:failed "); + CHECK(uuid == group); + CHECK(is_group_owned); + } } - template<> template<> - void permission_object_t::test<8>() + TEST_CASE("permission: equal LLPermissions have the same CRC32", "[permission]") { LLPermissions perm,perm1; LLUUID creator("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); @@ -229,89 +260,100 @@ namespace LLUUID group("9c8eca51-53d5-42a7-bb58-cef070395db8"); perm.init(creator,owner,lastOwner,group); perm1.init(creator,owner,lastOwner,group); - ensure_equals("getCRC32:failed ", perm.getCRC32(),perm1.getCRC32()); + UNSCOPED_INFO("getCRC32:failed "); + CHECK(perm.getCRC32() == perm1.getCRC32()); } - template<> template<> - void permission_object_t::test<9>() + TEST_CASE("permission: setOwnerAndGroup needs the agent to be the current owner", "[permission]") { LLPermissions perm; LLUUID agent("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); LLUUID owner("68edcf47-ccd7-45b8-9f90-1649d7f12806"); LLUUID group("9c8eca51-53d5-42a7-bb58-cef070395db8"); bool is_atomic = true; - ensure("setOwnerAndGroup():failed ", perm.setOwnerAndGroup(agent,owner,group,is_atomic)); + UNSCOPED_INFO("setOwnerAndGroup():failed "); + REQUIRE(perm.setOwnerAndGroup(agent,owner,group,is_atomic)); LLUUID owner2("68edcf47-ccd7-45b8-9f90-1649d7f12807"); LLUUID group2("9c8eca51-53d5-42a7-bb58-cef070395db9"); // cant change - agent need to be current owner - ensure("setOwnerAndGroup():failed ", ! perm.setOwnerAndGroup(agent,owner2,group2,is_atomic)); + UNSCOPED_INFO("setOwnerAndGroup():failed "); + CHECK_FALSE(perm.setOwnerAndGroup(agent,owner2,group2,is_atomic)); // should be able to change - agent and owner same as current owner - ensure("setOwnerAndGroup():failed ", perm.setOwnerAndGroup(owner,owner,group2,is_atomic)); + UNSCOPED_INFO("setOwnerAndGroup():failed "); + CHECK(perm.setOwnerAndGroup(owner,owner,group2,is_atomic)); } - template<> template<> - void permission_object_t::test<10>() + TEST_CASE("permission: the system can deed to a group", "[permission]") { LLPermissions perm; LLUUID agent; LLUUID group("9c8eca51-53d5-42a7-bb58-cef070395db8"); - ensure("deedToGroup():failed ", perm.deedToGroup(agent,group)); + UNSCOPED_INFO("deedToGroup():failed "); + CHECK(perm.deedToGroup(agent,group)); } - template<> template<> - void permission_object_t::test<11>() + TEST_CASE("permission: the system but not a stranger sets base and owner bits", "[permission]") { LLPermissions perm; LLUUID agent; bool set = true; U32 bits = PERM_TRANSFER | PERM_MODIFY; - ensure("setBaseBits():failed ", perm.setBaseBits(agent, set, bits)); - ensure("setOwnerBits():failed ", perm.setOwnerBits(agent, set, bits)); + UNSCOPED_INFO("setBaseBits():failed "); + REQUIRE(perm.setBaseBits(agent, set, bits)); + UNSCOPED_INFO("setOwnerBits():failed "); + CHECK(perm.setOwnerBits(agent, set, bits)); LLUUID agent1("9c8eca51-53d5-42a7-bb58-cef070395db8"); - ensure("setBaseBits():failed ", ! perm.setBaseBits(agent1, set, bits)); - ensure("setOwnerBits():failed ", ! perm.setOwnerBits(agent1, set, bits)); + UNSCOPED_INFO("setBaseBits():failed "); + CHECK_FALSE(perm.setBaseBits(agent1, set, bits)); + UNSCOPED_INFO("setOwnerBits():failed "); + CHECK_FALSE(perm.setOwnerBits(agent1, set, bits)); } - template<> template<> - void permission_object_t::test<12>() + TEST_CASE("permission: the system but not a stranger sets group and everyone and next bits", "[permission]") { LLPermissions perm; LLUUID agent; LLUUID group("9c8eca51-53d5-42a7-bb58-cef070395db8"); bool set = true; U32 bits = 10; - ensure("setGroupBits():failed ", perm.setGroupBits(agent,group, set, bits)); - ensure("setEveryoneBits():failed ", perm.setEveryoneBits(agent,group, set, bits)); - ensure("setNextOwnerBits():failed ", perm.setNextOwnerBits(agent,group, set, bits)); + UNSCOPED_INFO("setGroupBits():failed "); + REQUIRE(perm.setGroupBits(agent,group, set, bits)); + UNSCOPED_INFO("setEveryoneBits():failed "); + CHECK(perm.setEveryoneBits(agent,group, set, bits)); + UNSCOPED_INFO("setNextOwnerBits():failed "); + CHECK(perm.setNextOwnerBits(agent,group, set, bits)); LLUUID agent1("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); - ensure("setGroupBits():failed ", ! perm.setGroupBits(agent1,group, set, bits)); - ensure("setEveryoneBits():failed ", ! perm.setEveryoneBits(agent1,group, set, bits)); - ensure("setNextOwnerBits():failed ", ! perm.setNextOwnerBits(agent1,group, set, bits)); + UNSCOPED_INFO("setGroupBits():failed "); + CHECK_FALSE(perm.setGroupBits(agent1,group, set, bits)); + UNSCOPED_INFO("setEveryoneBits():failed "); + CHECK_FALSE(perm.setEveryoneBits(agent1,group, set, bits)); + UNSCOPED_INFO("setNextOwnerBits():failed "); + CHECK_FALSE(perm.setNextOwnerBits(agent1,group, set, bits)); } - template<> template<> - void permission_object_t::test<13>() + TEST_CASE("permission: allowOperationBy allows the system and a member of the group", "[permission]") { LLPermissions perm; LLUUID agent; LLUUID group("9c8eca51-53d5-42a7-bb58-cef070395db8"); U32 bits = 10; - ensure("allowOperationBy():failed ", perm.allowOperationBy(bits,agent,group)); + UNSCOPED_INFO("allowOperationBy():failed "); + CHECK(perm.allowOperationBy(bits,agent,group)); LLUUID agent1("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); LLUUID creator("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); LLUUID owner("68edcf47-ccd7-45b8-9f90-1649d7f12806"); LLUUID lastOwner("5e47a0dc-97bf-44e0-8b40-de06718cee9d"); perm.init(creator,owner,lastOwner,group); - ensure("allowOperationBy():failed ", perm.allowOperationBy(bits,agent1,group)); + UNSCOPED_INFO("allowOperationBy():failed "); + CHECK(perm.allowOperationBy(bits,agent1,group)); } - template<> template<> - void permission_object_t::test<14>() + TEST_CASE("permission: allowModifyBy allows any agent under the default masks", "[permission]") { LLPermissions perm; LLUUID creator("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); @@ -320,16 +362,19 @@ namespace LLUUID group("9c8eca51-53d5-42a7-bb58-cef070395db8"); perm.init(creator,owner,lastOwner,group); LLUUID agent; - ensure("1:allowModifyBy():failed ", perm.allowModifyBy(agent)); - ensure("2:allowModifyBy():failed ", perm.allowModifyBy(agent,group)); + UNSCOPED_INFO("1:allowModifyBy():failed "); + CHECK(perm.allowModifyBy(agent)); + UNSCOPED_INFO("2:allowModifyBy():failed "); + CHECK(perm.allowModifyBy(agent,group)); LLUUID agent1("9c8eca51-53d5-42a7-bb58-cef070395db8"); - ensure("3:allowModifyBy():failed ", perm.allowModifyBy(agent1)); - ensure("4:allowModifyBy():failed ", perm.allowModifyBy(agent1,group)); + UNSCOPED_INFO("3:allowModifyBy():failed "); + CHECK(perm.allowModifyBy(agent1)); + UNSCOPED_INFO("4:allowModifyBy():failed "); + CHECK(perm.allowModifyBy(agent1,group)); } - template<> template<> - void permission_object_t::test<15>() + TEST_CASE("permission: allowCopyBy allows any agent under the default masks", "[permission]") { LLPermissions perm; LLUUID creator("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); @@ -338,16 +383,19 @@ namespace LLUUID group("9c8eca51-53d5-42a7-bb58-cef070395db8"); perm.init(creator,owner,lastOwner,group); LLUUID agent; - ensure("1:allowCopyBy():failed ", perm.allowModifyBy(agent)); - ensure("2:allowCopyBy():failed ", perm.allowModifyBy(agent,group)); + UNSCOPED_INFO("1:allowCopyBy():failed "); + CHECK(perm.allowModifyBy(agent)); + UNSCOPED_INFO("2:allowCopyBy():failed "); + CHECK(perm.allowModifyBy(agent,group)); LLUUID agent1("9c8eca51-53d5-42a7-bb58-cef070395db8"); - ensure("3:allowCopyBy():failed ", perm.allowCopyBy(agent1)); - ensure("4:allowCopyBy():failed ", perm.allowCopyBy(agent1,group)); + UNSCOPED_INFO("3:allowCopyBy():failed "); + CHECK(perm.allowCopyBy(agent1)); + UNSCOPED_INFO("4:allowCopyBy():failed "); + CHECK(perm.allowCopyBy(agent1,group)); } - template<> template<> - void permission_object_t::test<16>() + TEST_CASE("permission: allowMoveBy allows any agent under the default masks", "[permission]") { LLPermissions perm; LLUUID creator("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); @@ -356,16 +404,19 @@ namespace LLUUID group("9c8eca51-53d5-42a7-bb58-cef070395db8"); perm.init(creator,owner,lastOwner,group); LLUUID agent; - ensure("1:allowMoveBy():failed ", perm.allowMoveBy(agent)); - ensure("2:allowMoveBy():failed ", perm.allowMoveBy(agent,group)); + UNSCOPED_INFO("1:allowMoveBy():failed "); + CHECK(perm.allowMoveBy(agent)); + UNSCOPED_INFO("2:allowMoveBy():failed "); + CHECK(perm.allowMoveBy(agent,group)); LLUUID agent1("9c8eca51-53d5-42a7-bb58-cef070395db8"); - ensure("3:allowMoveBy():failed ", perm.allowMoveBy(agent1)); - ensure("4:allowMoveBy():failed ", perm.allowMoveBy(agent1,group)); + UNSCOPED_INFO("3:allowMoveBy():failed "); + CHECK(perm.allowMoveBy(agent1)); + UNSCOPED_INFO("4:allowMoveBy():failed "); + CHECK(perm.allowMoveBy(agent1,group)); } - template<> template<> - void permission_object_t::test<17>() + TEST_CASE("permission: allowTransferTo allows the null agent while the owner is null", "[permission]") { LLPermissions perm; LLUUID creator("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); @@ -373,17 +424,19 @@ namespace LLUUID lastOwner("5e47a0dc-97bf-44e0-8b40-de06718cee9d"); LLUUID group("9c8eca51-53d5-42a7-bb58-cef070395db8"); LLUUID agent; - ensure("1:allowMoveBy():failed ", perm.allowTransferTo(agent)); + UNSCOPED_INFO("1:allowMoveBy():failed "); + CHECK(perm.allowTransferTo(agent)); perm.init(creator,owner,lastOwner,group); - ensure("2:allowMoveBy():failed ", perm.allowTransferTo(agent)); + UNSCOPED_INFO("2:allowMoveBy():failed "); + CHECK(perm.allowTransferTo(agent)); } - template<> template<> - void permission_object_t::test<18>() + TEST_CASE("permission: operator== holds for defaults and after assignment", "[permission]") { LLPermissions perm,perm1; - ensure_equals("1:Operator==:failed ", perm, perm1); + UNSCOPED_INFO("1:Operator==:failed "); + CHECK(perm == perm1); LLUUID creator("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); LLUUID owner("68edcf47-ccd7-45b8-9f90-1649d7f12806"); @@ -391,11 +444,11 @@ namespace LLUUID group("9c8eca51-53d5-42a7-bb58-cef070395db8"); perm.init(creator,owner,lastOwner,group); perm = perm1; - ensure_equals("2:Operator==:failed ", perm, perm1); + UNSCOPED_INFO("2:Operator==:failed "); + CHECK(perm == perm1); } - template<> template<> - void permission_object_t::test<19>() + TEST_CASE("permission: operator!= tells an initialised LLPermissions from a default one", "[permission]") { LLPermissions perm,perm1; LLUUID creator("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); @@ -403,11 +456,11 @@ namespace LLUUID lastOwner("5e47a0dc-97bf-44e0-8b40-de06718cee9d"); LLUUID group("9c8eca51-53d5-42a7-bb58-cef070395db8"); perm.init(creator,owner,lastOwner,group); - ensure_not_equals("2:Operator==:failed ", perm, perm1); + UNSCOPED_INFO("2:Operator==:failed "); + CHECK(perm != perm1); } - template<> template<> - void permission_object_t::test<20>() + TEST_CASE("permission: the legacy stream export and import round-trip", "[permission]") { LLPermissions perm,perm1; LLUUID creator("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); @@ -429,11 +482,11 @@ namespace std::istringstream istream(ostream.str()); perm1.importLegacyStream(istream); - ensure_equals("exportStream()/importStream():failed to export and import the data ", perm1, perm); + UNSCOPED_INFO("exportStream()/importStream():failed to export and import the data "); + CHECK(perm1 == perm); } - template<> template<> - void permission_object_t::test<21>() + TEST_CASE("permission: operator<< writes equal LLPermissions alike", "[permission]") { LLPermissions perm,perm1; LLUUID creator("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); @@ -445,11 +498,11 @@ namespace stream1 << perm; perm1.init(creator,owner,lastOwner,group); stream2 << perm1; - ensure_equals("1:operator << failed", stream1.str(), stream2.str()); + UNSCOPED_INFO("1:operator << failed"); + CHECK(stream1.str() == stream2.str()); } - template<> template<> - void permission_object_t::test<22>() + TEST_CASE("permission: LLPermissions survive an LLSD round trip", "[permission]") { LLPermissions perm,perm1; LLUUID creator("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); @@ -468,27 +521,33 @@ namespace LLSD sd = ll_create_sd_from_permissions(perm); perm1 = ll_permissions_from_sd(sd); - ensure_equals("ll_permissions_from_sd() and ll_create_sd_from_permissions()functions failed", perm, perm1); + UNSCOPED_INFO("ll_permissions_from_sd() and ll_create_sd_from_permissions()functions failed"); + CHECK(perm == perm1); } - template<> template<> - void permission_object_t::test<23>() + TEST_CASE("permission: LLAggregatePermissions aggregates masks and prints them", "[permission]") { LLAggregatePermissions AggrPermission; LLAggregatePermissions AggrPermission1; - ensure_equals("getU8() function failed", AggrPermission.getU8(), 0); - ensure("isEmpty() function failed", AggrPermission.isEmpty()); + UNSCOPED_INFO("getU8() function failed"); + CHECK(AggrPermission.getU8() == 0); + UNSCOPED_INFO("isEmpty() function failed"); + CHECK(AggrPermission.isEmpty()); AggrPermission.getValue(PERM_TRANSFER); - ensure_equals("getValue() function failed", AggrPermission.getValue(PERM_TRANSFER), 0x00); + UNSCOPED_INFO("getValue() function failed"); + CHECK(AggrPermission.getValue(PERM_TRANSFER) == 0x00); AggrPermission.aggregate(PERM_ITEM_UNRESTRICTED); - ensure("aggregate() function failed", ! AggrPermission.isEmpty()); + UNSCOPED_INFO("aggregate() function failed"); + CHECK_FALSE(AggrPermission.isEmpty()); AggrPermission1.aggregate(AggrPermission); - ensure("aggregate() function failed", ! AggrPermission1.isEmpty()); + UNSCOPED_INFO("aggregate() function failed"); + CHECK_FALSE(AggrPermission1.isEmpty()); std::ostringstream stream1; stream1 << AggrPermission; - ensure_equals("operator<< failed", stream1.str(), "{PI_COPY=All, PI_MODIFY=All, PI_TRANSFER=All}"); + UNSCOPED_INFO("operator<< failed"); + CHECK(stream1.str() == "{PI_COPY=All, PI_MODIFY=All, PI_TRANSFER=All}"); } } diff --git a/indra/llinventory/tests/llsaleinfo_test.cpp b/indra/llinventory/tests/llsaleinfo_test.cpp index 44c47c2662..87b63e1297 100644 --- a/indra/llinventory/tests/llsaleinfo_test.cpp +++ b/indra/llinventory/tests/llsaleinfo_test.cpp @@ -26,25 +26,14 @@ * $/LicenseInfo$ */ -#include +#include +#include #include "linden_common.h" -#include "lltut.h" #include "llsaleinfo.h" -namespace tut -{ namespace { - struct llsaleinfo_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>() + TEST_CASE("llsaleinfo: each sale type sets its price and CRC32 and survives LLSD", "[llsaleinfo]") { //test case for getSaleType(), getSalePrice(), getCRC32() fn. //test case for setSaleType(), setSalePrice() fn. @@ -56,13 +45,20 @@ namespace LLSD llsd_obj1 = ll_create_sd_from_sale_info(llsaleinfo); LLSaleInfo saleinfo1 = ll_sale_info_from_sd(llsd_obj1); - ensure("1. The getSaleType() fn failed", LLSaleInfo::FS_COPY == llsaleinfo.getSaleType()); - ensure("2. LLSaleInfo::isForSale() fn failed", true == llsaleinfo.isForSale()); - ensure("3. The getSalePrice() fn failed", sale_price == llsaleinfo.getSalePrice()); - ensure("4. The getCRC32() fn failed", 235833404 == llsaleinfo.getCRC32()); - ensure("5. LLSaleInfo::lookup(const char* name) fn failed", LLSaleInfo::FS_COPY == llsaleinfo.lookup(sale)); - ensure_equals("6. ll_create_sd_from_sale_info() fn failed", llsaleinfo.getSalePrice(), saleinfo1.getSalePrice()); - ensure_equals("7. ll_create_sd_from_sale_info() fn failed", llsaleinfo.getSaleType(), saleinfo1.getSaleType()); + UNSCOPED_INFO("1. The getSaleType() fn failed"); + CHECK(llsaleinfo.getSaleType() == LLSaleInfo::FS_COPY); + UNSCOPED_INFO("2. LLSaleInfo::isForSale() fn failed"); + CHECK(llsaleinfo.isForSale()); + UNSCOPED_INFO("3. The getSalePrice() fn failed"); + CHECK(sale_price == llsaleinfo.getSalePrice()); + UNSCOPED_INFO("4. The getCRC32() fn failed"); + CHECK(llsaleinfo.getCRC32() == 235833404); + UNSCOPED_INFO("5. LLSaleInfo::lookup(const char* name) fn failed"); + CHECK(llsaleinfo.lookup(sale) == LLSaleInfo::FS_COPY); + UNSCOPED_INFO("6. ll_create_sd_from_sale_info() fn failed"); + CHECK(llsaleinfo.getSalePrice() == saleinfo1.getSalePrice()); + UNSCOPED_INFO("7. ll_create_sd_from_sale_info() fn failed"); + CHECK(llsaleinfo.getSaleType() == saleinfo1.getSaleType()); llsaleinfo.setSalePrice(10000000); llsaleinfo.setSaleType(LLSaleInfo::FS_ORIGINAL); @@ -70,13 +66,20 @@ namespace llsd_obj1 = ll_create_sd_from_sale_info(llsaleinfo); saleinfo1 = ll_sale_info_from_sd(llsd_obj1); - ensure("8. The getSaleType() and setSaleType() fn failed", LLSaleInfo::FS_ORIGINAL == llsaleinfo.getSaleType()); - ensure("9. LLSaleInfo::isForSale() fn failed", true == llsaleinfo.isForSale()); - ensure("10. The getSalePrice() fn failed", 10000000 == llsaleinfo.getSalePrice()); - ensure("11. The getCRC32() fn failed", 127911702 == llsaleinfo.getCRC32()); - ensure("12. LLSaleInfo::lookup(const char* name) fn failed", LLSaleInfo::FS_CONTENTS == llsaleinfo.lookup(sale)); - ensure_equals("13. ll_create_sd_from_sale_info() fn failed", llsaleinfo.getSalePrice(), saleinfo1.getSalePrice()); - ensure_equals("14. ll_create_sd_from_sale_info() fn failed", llsaleinfo.getSaleType(), saleinfo1.getSaleType()); + UNSCOPED_INFO("8. The getSaleType() and setSaleType() fn failed"); + CHECK(llsaleinfo.getSaleType() == LLSaleInfo::FS_ORIGINAL); + UNSCOPED_INFO("9. LLSaleInfo::isForSale() fn failed"); + CHECK(llsaleinfo.isForSale()); + UNSCOPED_INFO("10. The getSalePrice() fn failed"); + CHECK(llsaleinfo.getSalePrice() == 10000000); + UNSCOPED_INFO("11. The getCRC32() fn failed"); + CHECK(llsaleinfo.getCRC32() == 127911702); + UNSCOPED_INFO("12. LLSaleInfo::lookup(const char* name) fn failed"); + CHECK(llsaleinfo.lookup(sale) == LLSaleInfo::FS_CONTENTS); + UNSCOPED_INFO("13. ll_create_sd_from_sale_info() fn failed"); + CHECK(llsaleinfo.getSalePrice() == saleinfo1.getSalePrice()); + UNSCOPED_INFO("14. ll_create_sd_from_sale_info() fn failed"); + CHECK(llsaleinfo.getSaleType() == saleinfo1.getSaleType()); llsaleinfo.setSalePrice(55000550); llsaleinfo.setSaleType(LLSaleInfo::FS_CONTENTS); @@ -84,13 +87,20 @@ namespace llsd_obj1 = ll_create_sd_from_sale_info(llsaleinfo); saleinfo1 = ll_sale_info_from_sd(llsd_obj1); - ensure("15. The getSaleType() and setSaleType() fn failed", LLSaleInfo::FS_CONTENTS == llsaleinfo.getSaleType()); - ensure("16. LLSaleInfo::isForSale() fn failed", true == llsaleinfo.isForSale()); - ensure("17. The getSalePrice() fn failed", 55000550 == llsaleinfo.getSalePrice()); - ensure("18. The getCRC32() fn failed", 408735656 == llsaleinfo.getCRC32()); - ensure("19. LLSaleInfo::lookup(const char* name) fn failed", LLSaleInfo::FS_ORIGINAL == llsaleinfo.lookup(sale)); - ensure_equals("20. ll_create_sd_from_sale_info() fn failed", llsaleinfo.getSalePrice(), saleinfo1.getSalePrice()); - ensure_equals("21. ll_create_sd_from_sale_info() fn failed", llsaleinfo.getSaleType(), saleinfo1.getSaleType()); + UNSCOPED_INFO("15. The getSaleType() and setSaleType() fn failed"); + CHECK(llsaleinfo.getSaleType() == LLSaleInfo::FS_CONTENTS); + UNSCOPED_INFO("16. LLSaleInfo::isForSale() fn failed"); + CHECK(llsaleinfo.isForSale()); + UNSCOPED_INFO("17. The getSalePrice() fn failed"); + CHECK(llsaleinfo.getSalePrice() == 55000550); + UNSCOPED_INFO("18. The getCRC32() fn failed"); + CHECK(llsaleinfo.getCRC32() == 408735656); + UNSCOPED_INFO("19. LLSaleInfo::lookup(const char* name) fn failed"); + CHECK(llsaleinfo.lookup(sale) == LLSaleInfo::FS_ORIGINAL); + UNSCOPED_INFO("20. ll_create_sd_from_sale_info() fn failed"); + CHECK(llsaleinfo.getSalePrice() == saleinfo1.getSalePrice()); + UNSCOPED_INFO("21. ll_create_sd_from_sale_info() fn failed"); + CHECK(llsaleinfo.getSaleType() == saleinfo1.getSaleType()); llsaleinfo.setSalePrice(-6432); llsaleinfo.setSaleType(LLSaleInfo::FS_NOT); @@ -98,17 +108,23 @@ namespace llsd_obj1 = ll_create_sd_from_sale_info(llsaleinfo); saleinfo1 = ll_sale_info_from_sd(llsd_obj1); - ensure("22. The getSaleType() and setSaleType() fn failed", LLSaleInfo::FS_NOT == llsaleinfo.getSaleType()); - ensure("23. LLSaleInfo::isForSale() fn failed", false == llsaleinfo.isForSale()); - ensure("24. The getSalePrice() fn failed", 0 == llsaleinfo.getSalePrice()); - ensure("25. The getCRC32() fn failed", 0 == llsaleinfo.getCRC32()); - ensure("26. LLSaleInfo::lookup(const char* name) fn failed", LLSaleInfo::FS_NOT == llsaleinfo.lookup(sale)); - ensure_equals("27. ll_create_sd_from_sale_info() fn failed", llsaleinfo.getSalePrice(), saleinfo1.getSalePrice()); - ensure_equals("28. ll_create_sd_from_sale_info() fn failed", llsaleinfo.getSaleType(), saleinfo1.getSaleType()); + UNSCOPED_INFO("22. The getSaleType() and setSaleType() fn failed"); + CHECK(llsaleinfo.getSaleType() == LLSaleInfo::FS_NOT); + UNSCOPED_INFO("23. LLSaleInfo::isForSale() fn failed"); + CHECK_FALSE(llsaleinfo.isForSale()); + UNSCOPED_INFO("24. The getSalePrice() fn failed"); + CHECK(llsaleinfo.getSalePrice() == 0); + UNSCOPED_INFO("25. The getCRC32() fn failed"); + CHECK(llsaleinfo.getCRC32() == 0); + UNSCOPED_INFO("26. LLSaleInfo::lookup(const char* name) fn failed"); + CHECK(llsaleinfo.lookup(sale) == LLSaleInfo::FS_NOT); + UNSCOPED_INFO("27. ll_create_sd_from_sale_info() fn failed"); + CHECK(llsaleinfo.getSalePrice() == saleinfo1.getSalePrice()); + UNSCOPED_INFO("28. ll_create_sd_from_sale_info() fn failed"); + CHECK(llsaleinfo.getSaleType() == saleinfo1.getSaleType()); } - template<> template<> - void llsaleinfo_test_t::test<2>() + TEST_CASE("llsaleinfo: the legacy stream export and import round-trip", "[llsaleinfo]") { S32 sale_price = 525452; LLSaleInfo llsaleinfo(LLSaleInfo::FS_ORIGINAL, sale_price); @@ -122,13 +138,14 @@ namespace bool has_perm_mask = false; llsaleinfo1.importLegacyStream(istream, has_perm_mask, perm_mask); - ensure("importStream() fn failed ", - llsaleinfo.getSalePrice() == llsaleinfo1.getSalePrice() && - llsaleinfo.getSaleType() == llsaleinfo1.getSaleType()); + { + INFO("importStream() fn failed "); + CHECK(llsaleinfo.getSalePrice() == llsaleinfo1.getSalePrice()); + CHECK(llsaleinfo.getSaleType() == llsaleinfo1.getSaleType()); + } } - template<> template<> - void llsaleinfo_test_t::test<3>() + TEST_CASE("llsaleinfo: asLLSD and fromLLSD round-trip", "[llsaleinfo]") { S32 sale_price = 99000; LLSaleInfo saleinfo(LLSaleInfo::FS_ORIGINAL, sale_price); @@ -141,52 +158,55 @@ namespace LLSaleInfo saleinfo1; saleinfo1.fromLLSD( sd_result, has_perm_mask, perm_mask); - ensure_equals("asLLSD and fromLLSD failed", saleinfo.getSalePrice(), saleinfo1.getSalePrice()); - ensure_equals("asLLSD and fromLLSD failed", saleinfo.getSaleType(), saleinfo1.getSaleType()); + UNSCOPED_INFO("asLLSD and fromLLSD failed"); + CHECK(saleinfo.getSalePrice() == saleinfo1.getSalePrice()); + UNSCOPED_INFO("asLLSD and fromLLSD failed"); + CHECK(saleinfo.getSaleType() == saleinfo1.getSaleType()); } //static EForSale lookup(const char* name) fn test - template<> template<> - void llsaleinfo_test_t::test<4>() + TEST_CASE("llsaleinfo: lookup maps names to sale types and sale types to names", "[llsaleinfo]") { S32 sale_price = 233223; LLSaleInfo::EForSale ret_type = LLSaleInfo::lookup("orig"); - ensure_equals("lookup(const char* name) fn failed", ret_type, LLSaleInfo::FS_ORIGINAL); + UNSCOPED_INFO("lookup(const char* name) fn failed"); + CHECK(ret_type == LLSaleInfo::FS_ORIGINAL); LLSaleInfo saleinfo(LLSaleInfo::FS_COPY, sale_price); const char* result = LLSaleInfo::lookup(LLSaleInfo::FS_COPY); - ensure("char* lookup(EForSale type) fn failed", 0 == strcmp("copy", result)); + UNSCOPED_INFO("char* lookup(EForSale type) fn failed"); + CHECK(std::string_view(result) == "copy"); } //void LLSaleInfo::accumulate(const LLSaleInfo& sale_info) fn test - template<> template<> - void llsaleinfo_test_t::test<5>() + TEST_CASE("llsaleinfo: accumulate adds the prices", "[llsaleinfo]") { S32 sale_price = 20; LLSaleInfo saleinfo(LLSaleInfo::FS_COPY, sale_price); LLSaleInfo saleinfo1(LLSaleInfo::FS_COPY, sale_price); saleinfo1.accumulate(saleinfo); - ensure_equals("LLSaleInfo::accumulate(const LLSaleInfo& sale_info) fn failed", saleinfo1.getSalePrice(), 40); + UNSCOPED_INFO("LLSaleInfo::accumulate(const LLSaleInfo& sale_info) fn failed"); + CHECK(saleinfo1.getSalePrice() == 40); } // test cases of bool operator==(const LLSaleInfo &rhs) fn // test case of bool operator!=(const LLSaleInfo &rhs) fn - template<> template<> - void llsaleinfo_test_t::test<6>() + TEST_CASE("llsaleinfo: operator== and operator!= tell sale infos apart by price", "[llsaleinfo]") { S32 sale_price = 55000; LLSaleInfo saleinfo(LLSaleInfo::FS_ORIGINAL, sale_price); LLSaleInfo saleinfoequal(LLSaleInfo::FS_ORIGINAL, sale_price); LLSaleInfo saleinfonotequal(LLSaleInfo::FS_ORIGINAL, sale_price*2); - ensure("operator == fn. failed", true == (saleinfo == saleinfoequal)); - ensure("operator != fn. failed", true == (saleinfo != saleinfonotequal)); + UNSCOPED_INFO("operator == fn. failed"); + CHECK(saleinfo == saleinfoequal); + UNSCOPED_INFO("operator != fn. failed"); + CHECK(saleinfo != saleinfonotequal); } - template<> template<> - void llsaleinfo_test_t::test<7>() + TEST_CASE("llsaleinfo: placeholder for the untested message packing", "[llsaleinfo]") { //TBD: void LLSaleInfo::packMessage(LLMessageSystem* msg) const diff --git a/indra/llinventory/tests/llsettingssky_test.cpp b/indra/llinventory/tests/llsettingssky_test.cpp index 9e64572853..1c48a6023b 100644 --- a/indra/llinventory/tests/llsettingssky_test.cpp +++ b/indra/llinventory/tests/llsettingssky_test.cpp @@ -28,7 +28,8 @@ #include "llsdutil.h" -#include "lltut.h" +#include +#include namespace { @@ -57,7 +58,8 @@ namespace void ensure_close(const std::string& msg, F32 actual, F32 expected) { - tut::ensure_approximately_equals_range(msg.c_str(), actual, expected, fabsf(expected) * 1.0e-5f + 1.0e-9f); + UNSCOPED_INFO(msg.c_str()); + REQUIRE_THAT(actual, Catch::Matchers::WithinAbs(expected, fabsf(expected) * 1.0e-5f + 1.0e-9f)); } LLSD layer(F32 width, F32 exp_term, F32 exp_scale, F32 linear, F32 constant, F32 aniso = 0.0f) @@ -66,21 +68,10 @@ namespace } } -namespace tut -{ namespace { - struct llsettingssky_data - { - }; - 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<> - void llsettingssky_object::test<1>() + TEST_CASE("llsettingssky: setMieAnisotropy changes the sky's g and the LLSD it saves", "[llsettingssky]") { LLSettingsSky::ptr_t sky = make_sky(default_settings()); ensure_close("default g", sky->getMieAnisotropy(), 0.8f); @@ -93,14 +84,15 @@ namespace // The default ozone profile is Bruneton's tent, 0 at 10 km, 1 at 25 km, 0 at 40 km, and it // survives validation, negative terms included. - template<> template<> - void llsettingssky_object::test<2>() + TEST_CASE("llsettingssky: the default ozone profile is Bruneton's tent and validates", "[llsettingssky]") { LLSettingsSky::ptr_t sky = make_sky(default_settings()); - ensure("the default sky validates", sky->validate()); + UNSCOPED_INFO("the default sky validates"); + CHECK(sky->validate()); LLSD ozone = sky->getAbsorptionConfigs(); - ensure_equals("ozone layers", ozone.size(), size_t(2)); + UNSCOPED_INFO("ozone layers"); + REQUIRE(ozone.size() == size_t(2)); ensure_close("lower width", term(ozone[0], LLSettingsSky::SETTING_DENSITY_PROFILE_WIDTH), 25000.0f); ensure_close("lower linear", term(ozone[0], LLSettingsSky::SETTING_DENSITY_PROFILE_LINEAR_TERM), 1.0f / 15000.0f); @@ -110,8 +102,7 @@ namespace } // Blending mixes the profiles term by term, anisotropy included. - template<> template<> - void llsettingssky_object::test<3>() + TEST_CASE("llsettingssky: blending mixes the profiles term by term with anisotropy", "[llsettingssky]") { LLSD from = default_settings(); from[LLSettingsSky::SETTING_RAYLEIGH_CONFIG] = llsd::array(layer(0.0f, 1.0f, -1.0f / 8000.0f, 0.0f, 0.0f)); @@ -135,8 +126,7 @@ namespace // A one-layer profile blended with a two-layer one is padded by repeating its layer, taking // the other profile's boundary, so at 0 it still describes the same atmosphere. - template<> template<> - void llsettingssky_object::test<4>() + TEST_CASE("llsettingssky: a one-layer profile blends with a two-layer one by repeating its layer", "[llsettingssky]") { LLSD from = default_settings(); from[LLSettingsSky::SETTING_ABSORPTION_CONFIG] = llsd::array(layer(0.0f, 0.0f, 0.0f, 0.0f, 0.2f)); @@ -150,7 +140,8 @@ namespace LLSettingsSky::ptr_t start = make_sky(from); start->blend(end, 0.0); LLSD at_start = start->getAbsorptionConfigs(); - ensure_equals("layers at 0", at_start.size(), size_t(2)); + UNSCOPED_INFO("layers at 0"); + REQUIRE(at_start.size() == size_t(2)); ensure_close("boundary at 0", term(at_start[0], LLSettingsSky::SETTING_DENSITY_PROFILE_WIDTH), 25000.0f); ensure_close("below the boundary at 0", term(at_start[0], LLSettingsSky::SETTING_DENSITY_PROFILE_CONSTANT_TERM), 0.2f); ensure_close("above the boundary at 0", term(at_start[1], LLSettingsSky::SETTING_DENSITY_PROFILE_CONSTANT_TERM), 0.2f); @@ -165,21 +156,21 @@ namespace // An unknown key inside a layer is stripped and the sky still validates; a sky that failed // would drop out of its day cycle. - template<> template<> - void llsettingssky_object::test<5>() + TEST_CASE("llsettingssky: an unknown key inside a layer is stripped and the sky still validates", "[llsettingssky]") { LLSD settings = default_settings(); settings[LLSettingsSky::SETTING_RAYLEIGH_CONFIG][0]["not_a_density_term"] = 1.0; LLSettingsSky::ptr_t sky = make_sky(settings); - ensure("the sky validates", sky->validate()); - ensure("the unknown key is stripped", !sky->getRayleighConfig().has("not_a_density_term")); + UNSCOPED_INFO("the sky validates"); + CHECK(sky->validate()); + UNSCOPED_INFO("the unknown key is stripped"); + REQUIRE_FALSE(sky->getRayleighConfig().has("not_a_density_term")); } // Skies saved by earlier Density tabs hold the absorption profile as [[layer], layer]. // Validation visits the layer after the nested array too, and the getters look through it. - template<> template<> - void llsettingssky_object::test<6>() + TEST_CASE("llsettingssky: an absorption profile nested by earlier Density tabs still reads", "[llsettingssky]") { LLSD settings = default_settings(); settings[LLSettingsSky::SETTING_ABSORPTION_CONFIG] = llsd::array(llsd::array(layer(25000.0f, 0.0f, 0.0f, 0.0f, 0.5f)), @@ -192,41 +183,47 @@ namespace ensure_close("the layer after the nested array is clamped", term(stored[1], LLSettingsSky::SETTING_DENSITY_PROFILE_CONSTANT_TERM), 10.0f); LLSD flat = LLSettingsSky::flattenDensityProfile(stored); - ensure_equals("flattened layers", flat.size(), size_t(2)); - ensure("flattened layers are maps", flat[0].isMap() && flat[1].isMap()); + UNSCOPED_INFO("flattened layers"); + REQUIRE(flat.size() == size_t(2)); + { + INFO("flattened layers are maps"); + REQUIRE(flat[0].isMap()); + REQUIRE(flat[1].isMap()); + } LLSD first = sky->getAbsorptionConfig(); - ensure("the first layer is a map", first.isMap()); + UNSCOPED_INFO("the first layer is a map"); + REQUIRE(first.isMap()); ensure_close("the first layer's constant", term(first, LLSettingsSky::SETTING_DENSITY_PROFILE_CONSTANT_TERM), 0.5f); } // Setting a profile marks the sky dirty, so its derived state is recomputed. - template<> template<> - void llsettingssky_object::test<7>() + TEST_CASE("llsettingssky: setting a profile marks the sky dirty", "[llsettingssky]") { LLSettingsSky::ptr_t sky = make_sky(default_settings()); sky->update(); - ensure("clean after update", !sky->isDirty()); + UNSCOPED_INFO("clean after update"); + CHECK_FALSE(sky->isDirty()); sky->setRayleighConfigs(llsd::array(layer(0.0f, 1.0f, -1.0f / 4000.0f, 0.0f, 0.0f))); - ensure("dirty after setRayleighConfigs", sky->isDirty()); + UNSCOPED_INFO("dirty after setRayleighConfigs"); + CHECK(sky->isDirty()); } // A profile with no layers has no first layer. - template<> template<> - void llsettingssky_object::test<8>() + TEST_CASE("llsettingssky: a profile with no layers has no first layer", "[llsettingssky]") { LLSD settings = default_settings(); settings[LLSettingsSky::SETTING_RAYLEIGH_CONFIG] = LLSD::emptyArray(); LLSettingsSky::ptr_t sky = make_sky(settings); - ensure("no first Rayleigh layer", sky->getRayleighConfig().isUndefined()); + UNSCOPED_INFO("no first Rayleigh layer"); + REQUIRE(sky->getRayleighConfig().isUndefined()); } // Cloud cover fades in from a sky without a cloud texture toward the next sky's cover, and out toward none, // so neither end of the blend jumps. - template<> template<> - void llsettingssky_object::test<9>() + TEST_CASE("llsettingssky: cloud cover fades in from and out to a sky without a cloud texture", "[llsettingssky]") { const LLUUID cloud_texture("2bfd3884-7e27-69b9-ba3a-3e673f680004"); @@ -244,7 +241,8 @@ namespace LLSettingsSky::ptr_t sky = make_sky(clear); sky->blend(to_cloudy, t); ensure_close("fading in at " + std::to_string(t), sky->getCloudShadow(), 0.6f * (F32)t); - ensure("fading in takes the next sky's texture", sky->getCloudNoiseTextureId() == cloud_texture); + UNSCOPED_INFO("fading in takes the next sky's texture"); + REQUIRE(sky->getCloudNoiseTextureId() == cloud_texture); } LLSettingsBase::ptr_t to_clear = make_sky(clear); @@ -253,7 +251,8 @@ namespace LLSettingsSky::ptr_t sky = make_sky(cloudy); sky->blend(to_clear, t); ensure_close("fading out at " + std::to_string(t), sky->getCloudShadow(), 0.6f * (1.f - (F32)t)); - ensure("fading out keeps its own texture", sky->getCloudNoiseTextureId() == cloud_texture); + UNSCOPED_INFO("fading out keeps its own texture"); + REQUIRE(sky->getCloudNoiseTextureId() == cloud_texture); } LLSD cloudier = cloudy; @@ -266,8 +265,7 @@ namespace // Flattening looks through nesting to any depth, as validation and the first-layer getters do, so the // anisotropy setter reaches a layer nested two arrays deep. - template<> template<> - void llsettingssky_object::test<10>() + TEST_CASE("llsettingssky: flattening and the anisotropy setter look through any depth of nesting", "[llsettingssky]") { LLSD settings = default_settings(); const LLSD deep = layer(0.0f, 1.0f, -1.0f / 1200.0f, 0.0f, 0.0f, 0.8f); @@ -275,13 +273,15 @@ namespace settings[LLSettingsSky::SETTING_MIE_CONFIG] = llsd::array(llsd::array(llsd::array(deep)), shallow); LLSD flat = LLSettingsSky::flattenDensityProfile(settings[LLSettingsSky::SETTING_MIE_CONFIG]); - ensure_equals("both layers flattened", flat.size(), size_t(2)); + UNSCOPED_INFO("both layers flattened"); + REQUIRE(flat.size() == size_t(2)); ensure_close("the deep layer first", term(flat[0], LLSettingsSky::SETTING_MIE_ANISOTROPY_FACTOR), 0.8f); LLSettingsSky::ptr_t sky = make_sky(settings); ensure_close("g from the deep layer", sky->getMieAnisotropy(), 0.8f); sky->setMieAnisotropy(0.6f); ensure_close("g after the setter", sky->getMieAnisotropy(), 0.6f); - ensure_equals("the setter keeps both layers", sky->getMieConfigs().size(), size_t(2)); + UNSCOPED_INFO("the setter keeps both layers"); + REQUIRE(sky->getMieConfigs().size() == size_t(2)); } } diff --git a/indra/llinventory/tests/lluserrelations_test.cpp b/indra/llinventory/tests/lluserrelations_test.cpp index 0b0706c9b5..a3042b207c 100644 --- a/indra/llinventory/tests/lluserrelations_test.cpp +++ b/indra/llinventory/tests/lluserrelations_test.cpp @@ -26,128 +26,108 @@ * $/LicenseInfo$ */ -#include +#include #include "linden_common.h" #include "lluserrelations.h" -namespace tut -{ namespace { struct user_relationship { LLRelationship mRelationship; }; - 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>() + TEST_CASE_METHOD(user_relationship, "relationships: a default relationship grants nothing and is offline", "[relationships]") { // Test the default construction - ensure( - "No granted rights to", - !mRelationship.isRightGrantedTo( + UNSCOPED_INFO("No granted rights to"); + CHECK_FALSE(mRelationship.isRightGrantedTo( LLRelationship::GRANT_ONLINE_STATUS)); - ensure( - "No granted rights from", - !mRelationship.isRightGrantedFrom( + UNSCOPED_INFO("No granted rights from"); + CHECK_FALSE(mRelationship.isRightGrantedFrom( LLRelationship::GRANT_ONLINE_STATUS)); - ensure("No online status",!mRelationship.isOnline()); + UNSCOPED_INFO("No online status"); + CHECK_FALSE(mRelationship.isOnline()); } - template<> template<> - void user_relationship_object_t::test<2>() + TEST_CASE_METHOD(user_relationship, "relationships: grantRights grants rights to and from separately", "[relationships]") { // Test some granting mRelationship.grantRights( LLRelationship::GRANT_ONLINE_STATUS, LLRelationship::GRANT_MODIFY_OBJECTS); - ensure( - "Granted rights to has online", - mRelationship.isRightGrantedTo( + UNSCOPED_INFO("Granted rights to has online"); + CHECK(mRelationship.isRightGrantedTo( LLRelationship::GRANT_ONLINE_STATUS)); - ensure( - "Granted rights from does not have online", - !mRelationship.isRightGrantedFrom( + UNSCOPED_INFO("Granted rights from does not have online"); + CHECK_FALSE(mRelationship.isRightGrantedFrom( LLRelationship::GRANT_ONLINE_STATUS)); - ensure( - "Granted rights to does not have modify", - !mRelationship.isRightGrantedTo( + UNSCOPED_INFO("Granted rights to does not have modify"); + CHECK_FALSE(mRelationship.isRightGrantedTo( LLRelationship::GRANT_MODIFY_OBJECTS)); - ensure( - "Granted rights from has modify", - mRelationship.isRightGrantedFrom( + UNSCOPED_INFO("Granted rights from has modify"); + CHECK(mRelationship.isRightGrantedFrom( LLRelationship::GRANT_MODIFY_OBJECTS)); } - template<> template<> - void user_relationship_object_t::test<3>() + TEST_CASE_METHOD(user_relationship, "relationships: revokeRights removes only the rights it names", "[relationships]") { // Test revoking mRelationship.grantRights( LLRelationship::GRANT_ONLINE_STATUS | LLRelationship::GRANT_MAP_LOCATION, LLRelationship::GRANT_ONLINE_STATUS); - ensure( - "Granted rights to has online and map", - mRelationship.isRightGrantedTo( + UNSCOPED_INFO("Granted rights to has online and map"); + CHECK(mRelationship.isRightGrantedTo( LLRelationship::GRANT_ONLINE_STATUS | LLRelationship::GRANT_MAP_LOCATION)); - ensure( - "Granted rights from has online", - mRelationship.isRightGrantedFrom( + UNSCOPED_INFO("Granted rights from has online"); + CHECK(mRelationship.isRightGrantedFrom( LLRelationship::GRANT_ONLINE_STATUS)); mRelationship.revokeRights( LLRelationship::GRANT_MAP_LOCATION, LLRelationship::GRANT_NONE); - ensure( - "Granted rights revoked map", - !mRelationship.isRightGrantedTo( + UNSCOPED_INFO("Granted rights revoked map"); + CHECK_FALSE(mRelationship.isRightGrantedTo( LLRelationship::GRANT_ONLINE_STATUS | LLRelationship::GRANT_MAP_LOCATION)); - ensure( - "Granted rights revoked still has online", - mRelationship.isRightGrantedTo( + UNSCOPED_INFO("Granted rights revoked still has online"); + CHECK(mRelationship.isRightGrantedTo( LLRelationship::GRANT_ONLINE_STATUS)); mRelationship.grantRights( LLRelationship::GRANT_NONE, LLRelationship::GRANT_MODIFY_OBJECTS); - ensure( - "Granted rights from still has online", - mRelationship.isRightGrantedFrom( + UNSCOPED_INFO("Granted rights from still has online"); + CHECK(mRelationship.isRightGrantedFrom( LLRelationship::GRANT_ONLINE_STATUS)); - ensure( - "Granted rights from has full grant", - mRelationship.isRightGrantedFrom( + UNSCOPED_INFO("Granted rights from has full grant"); + CHECK(mRelationship.isRightGrantedFrom( LLRelationship::GRANT_ONLINE_STATUS | LLRelationship::GRANT_MODIFY_OBJECTS)); mRelationship.revokeRights( LLRelationship::GRANT_NONE, LLRelationship::GRANT_MODIFY_OBJECTS); - ensure( - "Granted rights from still has online", - mRelationship.isRightGrantedFrom( + UNSCOPED_INFO("Granted rights from still has online"); + CHECK(mRelationship.isRightGrantedFrom( LLRelationship::GRANT_ONLINE_STATUS)); - ensure( - "Granted rights from no longer modify", - !mRelationship.isRightGrantedFrom( + UNSCOPED_INFO("Granted rights from no longer modify"); + CHECK_FALSE(mRelationship.isRightGrantedFrom( LLRelationship::GRANT_MODIFY_OBJECTS)); } - template<> template<> - void user_relationship_object_t::test<4>() + TEST_CASE_METHOD(user_relationship, "relationships: online sets the online status", "[relationships]") { - ensure("No online status", !mRelationship.isOnline()); + UNSCOPED_INFO("No online status"); + CHECK_FALSE(mRelationship.isOnline()); mRelationship.online(true); - ensure("Online status", mRelationship.isOnline()); + UNSCOPED_INFO("Online status"); + CHECK(mRelationship.isOnline()); mRelationship.online(false); - ensure("No online status", !mRelationship.isOnline()); + UNSCOPED_INFO("No online status"); + CHECK_FALSE(mRelationship.isOnline()); } /* diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt index 109b537ef2..a5236ea781 100644 --- a/indra/llprimitive/CMakeLists.txt +++ b/indra/llprimitive/CMakeLists.txt @@ -57,10 +57,25 @@ target_precompile_headers(llprimitive REUSE_FROM llprecompiled) # add unit tests if(AL_BUILD_TESTS) - foreach(test llmediaentry llgltfmaterial llmaterialid llmaterial) - al_add_test(${test} PROJECT llprimitive LIBRARIES llprimitive) - endforeach() + # llgltfmaterial_test.cpp is the one file that defines tinygltf's and stb's + # implementations, which the library leaves to the viewer. + al_add_test_binary( + llprimitive_tests + PROJECT llprimitive + SOURCES + tests/llgltfmaterial_test.cpp + tests/llmaterial_test.cpp + tests/llmaterialid_test.cpp + tests/llmediaentry_test.cpp + LIBRARIES llprimitive + ) # 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) + al_add_test_binary( + llprimitive_test + PROJECT llprimitive + SOURCES tests/llprimitive_test.cpp + UNIT llprimitive.cpp + LIBRARIES llmessage llmath llcommon + ) endif() diff --git a/indra/llprimitive/tests/llgltfmaterial_test.cpp b/indra/llprimitive/tests/llgltfmaterial_test.cpp index 479f3fc0cd..ad2f42a304 100644 --- a/indra/llprimitive/tests/llgltfmaterial_test.cpp +++ b/indra/llprimitive/tests/llgltfmaterial_test.cpp @@ -24,7 +24,7 @@ */ #include "linden_common.h" -#include "lltut.h" +#include #include @@ -51,17 +51,8 @@ #include -namespace tut -{ namespace { - struct llgltfmaterial - { - }; - typedef test_group llgltfmaterial_t; - typedef llgltfmaterial_t::object llgltfmaterial_object_t; - tut::llgltfmaterial_t tut_llgltfmaterial("llgltfmaterial"); - // A positive 32-bit float with a long string representation constexpr F32 test_fraction = 1.09045365e-32f; // A larger positive 32-bit float for values that get zeroed if below a threshold @@ -125,19 +116,24 @@ namespace std::string warn_msg; std::string error_msg; bool serialize_success = material_out.fromJSON(json_in, warn_msg, error_msg); - ensure_equals("LLGLTFMaterial serialization has no warnings: " + ensure_suffix, "", warn_msg); - ensure_equals("LLGLTFMaterial serialization has no errors: " + ensure_suffix, "", error_msg); - ensure("LLGLTFMaterial serializes successfully: " + ensure_suffix, serialize_success); - ensure("LLGLTFMaterial is preserved when deserialized: " + ensure_suffix, material_in == material_out); + UNSCOPED_INFO("LLGLTFMaterial serialization has no warnings: " + ensure_suffix); + REQUIRE("" == warn_msg); + UNSCOPED_INFO("LLGLTFMaterial serialization has no errors: " + ensure_suffix); + REQUIRE("" == error_msg); + UNSCOPED_INFO("LLGLTFMaterial serializes successfully: " + ensure_suffix); + REQUIRE(serialize_success); + UNSCOPED_INFO("LLGLTFMaterial is preserved when deserialized: " + ensure_suffix); + REQUIRE(material_in == material_out); const std::string json_out = material_out.asJSON(); - ensure_equals("LLGLTFMaterial is preserved when serialized: " + ensure_suffix, json_in, json_out); + UNSCOPED_INFO("LLGLTFMaterial is preserved when serialized: " + ensure_suffix); + REQUIRE(json_in == json_out); } void ensure_gltf_material_trimmed(const std::string& material_json, const std::string& must_not_contain) { - ensure("LLGLTFMaterial serialization trims property '" + must_not_contain + "'", material_json.find(must_not_contain) == std::string::npos); + UNSCOPED_INFO("LLGLTFMaterial serialization trims property '" + must_not_contain + "'"); + REQUIRE(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 @@ -145,22 +141,21 @@ namespace // the hash snapshot/cache members (which aren't part of the hashed // payload), so it stopped being a useful regression check. test<12> // exercises the actual hash invariant directly. - template<> template<> - void llgltfmaterial_object_t::test<1>() + TEST_CASE("llgltfmaterial: there are four texture infos", "[llgltfmaterial]") { - ensure_equals("LLGLTFMaterial texture info count", (U32)LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT, 4); + UNSCOPED_INFO("LLGLTFMaterial texture info count"); + CHECK((U32)LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT == 4); } // Test that occlusion and metallicRoughness are the same (They are different for asset validation. See lluploadmaterial.cpp) - template<> template<> - void llgltfmaterial_object_t::test<2>() + TEST_CASE("llgltfmaterial: occlusion and metallicRoughness share a texture info", "[llgltfmaterial]") { - ensure_equals("LLGLTFMaterial occlusion does not differ from metallic roughness", LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS, LLGLTFMaterial::GLTF_TEXTURE_INFO_OCCLUSION); + UNSCOPED_INFO("LLGLTFMaterial occlusion does not differ from metallic roughness"); + CHECK(LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS == LLGLTFMaterial::GLTF_TEXTURE_INFO_OCCLUSION); } // Ensure double sided and alpha mode overrides serialize as expected - template<> template<> - void llgltfmaterial_object_t::test<3>() + TEST_CASE("llgltfmaterial: double sided and alpha mode overrides set their flag and serialize", "[llgltfmaterial]") { const bool doubleSideds[] { false, true }; const LLGLTFMaterial::AlphaMode alphaModes[] { LLGLTFMaterial::ALPHA_MODE_OPAQUE, LLGLTFMaterial::ALPHA_MODE_BLEND, LLGLTFMaterial::ALPHA_MODE_MASK }; @@ -173,7 +168,8 @@ namespace LLGLTFMaterial material; material.setDoubleSided(doubleSided, forOverride); const bool overrideBit = (doubleSided == false) && forOverride; - ensure_equals("LLGLTFMaterial: double sided = " + std::to_string(doubleSided) + " override bit when forOverride = " + std::to_string(forOverride), material.mOverrideDoubleSided, overrideBit); + UNSCOPED_INFO("LLGLTFMaterial: double sided = " + std::to_string(doubleSided) + " override bit when forOverride = " + std::to_string(forOverride)); + REQUIRE(material.mOverrideDoubleSided == overrideBit); ensure_gltf_material_serialize("double sided = " + std::to_string(doubleSided), material); } } @@ -185,15 +181,15 @@ namespace LLGLTFMaterial material; material.setAlphaMode(alphaMode, forOverride); const bool overrideBit = (alphaMode == LLGLTFMaterial::ALPHA_MODE_OPAQUE) && forOverride; - ensure_equals("LLGLTFMaterial: alpha mode = " + std::to_string(alphaMode) + " override bit when forOverride = " + std::to_string(forOverride), material.mOverrideAlphaMode, overrideBit); + UNSCOPED_INFO("LLGLTFMaterial: alpha mode = " + std::to_string(alphaMode) + " override bit when forOverride = " + std::to_string(forOverride)); + REQUIRE(material.mOverrideAlphaMode == overrideBit); ensure_gltf_material_serialize("alpha mode = " + std::to_string(alphaMode), material); } } } // Test that a GLTF material's transform components serialize as expected - template<> template<> - void llgltfmaterial_object_t::test<4>() + TEST_CASE("llgltfmaterial: a texture transform survives a JSON round trip", "[llgltfmaterial]") { LLGLTFMaterial material; LLGLTFMaterial::TextureTransform& transform = material.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR]; @@ -206,8 +202,7 @@ namespace } // Test that a GLTF material avoids serializing a material unnecessarily - template<> template<> - void llgltfmaterial_object_t::test<5>() + TEST_CASE("llgltfmaterial: asJSON leaves out default properties and unset textures", "[llgltfmaterial]") { { const LLGLTFMaterial material; @@ -228,8 +223,7 @@ namespace } // Test that a GLTF material preserves values on serialization - template<> template<> - void llgltfmaterial_object_t::test<6>() + TEST_CASE("llgltfmaterial: full and partial materials survive a JSON round trip", "[llgltfmaterial]") { { const LLGLTFMaterial full_material = create_test_material(); @@ -256,29 +250,28 @@ namespace } // Test that sDefault is a no-op override - template<> template<> - void llgltfmaterial_object_t::test<7>() + TEST_CASE("llgltfmaterial: sDefault is a no-op override", "[llgltfmaterial]") { const LLGLTFMaterial material_asset = create_test_material(); LLGLTFMaterial render_material = material_asset; render_material.applyOverride(LLGLTFMaterial::sDefault); - ensure("LLGLTFMaterial: sDefault is a no-op override", material_asset == render_material); + UNSCOPED_INFO("LLGLTFMaterial: sDefault is a no-op override"); + CHECK(material_asset == render_material); } // Test application of transform overrides - template<> template<> - void llgltfmaterial_object_t::test<8>() + TEST_CASE("llgltfmaterial: applyOverride applies texture transform overrides", "[llgltfmaterial]") { LLGLTFMaterial override_material; apply_test_material_texture_transforms(override_material); LLGLTFMaterial render_material; render_material.applyOverride(override_material); - ensure("LLGLTFMaterial: transform overrides", render_material == override_material); + UNSCOPED_INFO("LLGLTFMaterial: transform overrides"); + CHECK(render_material == override_material); } // Test application of flag-based overrides - template<> template<> - void llgltfmaterial_object_t::test<9>() + TEST_CASE("llgltfmaterial: applyOverride applies alpha mode and double sided overrides", "[llgltfmaterial]") { { LLGLTFMaterial override_material; @@ -289,7 +282,8 @@ namespace render_material.applyOverride(override_material); - ensure("LLGLTFMaterial: extra overrides with non-default values applied over default", render_material == override_material); + UNSCOPED_INFO("LLGLTFMaterial: extra overrides with non-default values applied over default"); + CHECK(render_material == override_material); } { LLGLTFMaterial override_material; @@ -305,13 +299,13 @@ namespace override_material.mOverrideDoubleSided = false; override_material.mOverrideAlphaMode = false; - ensure("LLGLTFMaterial: extra overrides with default values applied over non-default", render_material == override_material); + UNSCOPED_INFO("LLGLTFMaterial: extra overrides with default values applied over non-default"); + CHECK(render_material == override_material); } } // Test application of texture overrides - template<> template<> - void llgltfmaterial_object_t::test<10>() + TEST_CASE("llgltfmaterial: applyOverride replaces every texture ID with the override", "[llgltfmaterial]") { const U32 texture_count = 2; const LLUUID override_textures[texture_count] = { LLUUID::null, LLUUID::generateNewID() }; @@ -337,14 +331,14 @@ namespace for (LLGLTFMaterial::TextureInfo j = LLGLTFMaterial::TextureInfo(0); j < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; j = LLGLTFMaterial::TextureInfo(U32(j) + 1)) { const LLUUID& render_texture = render_material.mTextureId[j]; - ensure_equals("LLGLTFMaterial: Override texture ID " + override_texture.asString() + " replaces underlying texture ID " + asset_texture.asString(), render_texture, override_texture); + UNSCOPED_INFO("LLGLTFMaterial: Override texture ID " + override_texture.asString() + " replaces underlying texture ID " + asset_texture.asString()); + REQUIRE(render_texture == override_texture); } } } // Test non-persistence of default value flags in overrides - template<> template<> - void llgltfmaterial_object_t::test<11>() + TEST_CASE("llgltfmaterial: setting a non-default value clears the override flag", "[llgltfmaterial]") { const S32 non_default_alpha_modes[] = { LLGLTFMaterial::ALPHA_MODE_BLEND, LLGLTFMaterial::ALPHA_MODE_MASK }; for (S32 non_default_alpha_mode : non_default_alpha_modes) @@ -352,33 +346,37 @@ namespace LLGLTFMaterial material; // Set default alpha mode material.setAlphaMode(LLGLTFMaterial::ALPHA_MODE_OPAQUE, true); - ensure_equals("LLGLTFMaterial: alpha mode override flag set", material.mOverrideAlphaMode, true); + UNSCOPED_INFO("LLGLTFMaterial: alpha mode override flag set"); + REQUIRE(material.mOverrideAlphaMode == true); // Set non-default alpha mode material.setAlphaMode(non_default_alpha_mode, true); - ensure_equals("LLGLTFMaterial: alpha mode override flag unset", material.mOverrideAlphaMode, false); + UNSCOPED_INFO("LLGLTFMaterial: alpha mode override flag unset"); + REQUIRE(material.mOverrideAlphaMode == false); } { // Set default double sided LLGLTFMaterial material; material.setDoubleSided(false, true); - ensure_equals("LLGLTFMaterial: double sided override flag set", material.mOverrideDoubleSided, true); + UNSCOPED_INFO("LLGLTFMaterial: double sided override flag set"); + CHECK(material.mOverrideDoubleSided == true); // Set non-default double sided material.setDoubleSided(true, true); - ensure_equals("LLGLTFMaterial: double sided override flag unset", material.mOverrideDoubleSided, false); + UNSCOPED_INFO("LLGLTFMaterial: double sided override flag unset"); + CHECK(material.mOverrideDoubleSided == false); } } -namespace -{ template void ensure_material_hash_pre(LLGLTFMaterial& material, T& material_field, const T new_value, const std::string& field_name) { - ensure("LLGLTFMaterial: Hash: Test field " + field_name + " is part of the test material object", ( - size_t(&material_field) >= size_t(&material) && - (size_t(&material_field) + sizeof(material_field)) <= (size_t(&material) + sizeof(material)) - )); - ensure("LLGLTFMaterial: Hash: " + field_name + " differs and will cause a perturbation worth hashing", material_field != new_value); + { + INFO("LLGLTFMaterial: Hash: Test field " + field_name + " is part of the test material object"); + REQUIRE(size_t(&material_field) >= size_t(&material)); + REQUIRE((size_t(&material_field) + sizeof(material_field)) <= (size_t(&material) + sizeof(material))); + } + UNSCOPED_INFO("LLGLTFMaterial: Hash: " + field_name + " differs and will cause a perturbation worth hashing"); + REQUIRE(material_field != new_value); } template @@ -389,7 +387,8 @@ namespace const LLGLTFMaterial old_material = material; material_field = new_value; // 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_equals(("LLGLTFMaterial: Hash: Perturbing " + field_name + " to new value does NOT change the hash").c_str(), material.getHash(), old_material.getHash()); + UNSCOPED_INFO(("LLGLTFMaterial: Hash: Perturbing " + field_name + " to new value does NOT change the hash").c_str()); + REQUIRE(material.getHash() == old_material.getHash()); } template @@ -400,9 +399,9 @@ namespace const LLGLTFMaterial old_material = material; material_field = new_value; // 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()); + UNSCOPED_INFO(("LLGLTFMaterial: Hash: Perturbing " + field_name + " to new value changes the hash").c_str()); + REQUIRE(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) @@ -410,8 +409,7 @@ namespace // Test LLGLTFMaterial::getHash. The hash is now built from a canonical // packing of the meaningful members, so the previous structure-layout // fragility is gone. - template<> template<> - void llgltfmaterial_object_t::test<12>() + TEST_CASE("llgltfmaterial: getHash covers every field but the local texture tracking map", "[llgltfmaterial]") { // *NOTE: Due to direct manipulation of the fields of materials // throughout this test, the resulting modified materials may not be diff --git a/indra/llprimitive/tests/llmaterial_test.cpp b/indra/llprimitive/tests/llmaterial_test.cpp index ce9168dea1..6c0742450f 100644 --- a/indra/llprimitive/tests/llmaterial_test.cpp +++ b/indra/llprimitive/tests/llmaterial_test.cpp @@ -19,20 +19,12 @@ */ #include "linden_common.h" -#include "lltut.h" +#include #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"); static const LLUUID kIdB("66666666-7777-8888-9999-aaaaaaaaaaaa"); @@ -52,26 +44,28 @@ namespace const LLUUID before = m.getHash(); (m.*setter)(after_value); - ensure((name + ": new value changes hash").c_str(), - m.getHash() != before); + UNSCOPED_INFO((name + ": new value changes hash").c_str()); + REQUIRE(m.getHash() != before); (m.*setter)(before_value); - ensure_equals((name + ": restoring value restores hash").c_str(), - m.getHash(), before); + UNSCOPED_INFO((name + ": restoring value restores hash").c_str()); + REQUIRE(m.getHash() == before); } // Hash is deterministic for repeated reads with no mutation. The first // call populates the cache; subsequent calls hit it. - template<> template<> - void llmaterial_object_t::test<1>() + TEST_CASE("llmaterial: getHash is deterministic for repeated reads with no mutation", "[llmaterial]") { LLMaterial m; const LLUUID a = m.getHash(); const LLUUID b = m.getHash(); const LLUUID c = m.getHash(); - ensure_equals("repeated reads", a, b); - ensure_equals("repeated reads", b, c); - ensure("hash is not the null UUID", a.notNull()); + UNSCOPED_INFO("repeated reads"); + CHECK(a == b); + UNSCOPED_INFO("repeated reads"); + CHECK(b == c); + UNSCOPED_INFO("hash is not the null UUID"); + CHECK(a.notNull()); } // Every individual setter invalidates the cache AND changes the hash to @@ -79,8 +73,7 @@ namespace // when-set-back) proves the cache invalidation is wired up *and* that the // hash function is a pure function of the member state, not of mutation // history. - template<> template<> - void llmaterial_object_t::test<2>() + TEST_CASE("llmaterial: every setter changes the hash and setting back restores it", "[llmaterial]") { // ID setters. ensure_setter_round_trip("setNormalID", @@ -137,8 +130,7 @@ namespace // The paired-axis setters share the same underlying members as the X/Y // individual setters and must produce the identical hash for the same // logical state. This pins that the combined and split write paths agree. - template<> template<> - void llmaterial_object_t::test<3>() + TEST_CASE("llmaterial: paired-axis setters hash the same as the split X and Y setters", "[llmaterial]") { LLMaterial split; split.setNormalOffsetX(0.5f); @@ -156,57 +148,56 @@ namespace paired.setSpecularOffset(4.5f, 5.5f); paired.setSpecularRepeat(6.5f, 7.5f); - ensure_equals("paired vs. split writes agree", - split.getHash(), paired.getHash()); + UNSCOPED_INFO("paired vs. split writes agree"); + CHECK(split.getHash() == paired.getHash()); } // Hash distinguishes which slot a value lives in. If two materials carry // the same scalar but in different members, the hashes must differ — // otherwise the packing order or hashing scheme has aliased two fields. - template<> template<> - void llmaterial_object_t::test<4>() + TEST_CASE("llmaterial: the hash distinguishes which slot a value lives in", "[llmaterial]") { // mNormalOffsetX vs mNormalOffsetY { LLMaterial x; x.setNormalOffsetX(1.0f); LLMaterial y; y.setNormalOffsetY(1.0f); - ensure("Normal offset X vs Y distinguishes", - x.getHash() != y.getHash()); + UNSCOPED_INFO("Normal offset X vs Y distinguishes"); + CHECK(x.getHash() != y.getHash()); } // mNormalRepeatX vs mNormalRepeatY { LLMaterial x; x.setNormalRepeatX(2.0f); LLMaterial y; y.setNormalRepeatY(2.0f); - ensure("Normal repeat X vs Y distinguishes", - x.getHash() != y.getHash()); + UNSCOPED_INFO("Normal repeat X vs Y distinguishes"); + CHECK(x.getHash() != y.getHash()); } // mSpecularOffsetX vs mSpecularOffsetY { LLMaterial x; x.setSpecularOffsetX(3.0f); LLMaterial y; y.setSpecularOffsetY(3.0f); - ensure("Specular offset X vs Y distinguishes", - x.getHash() != y.getHash()); + UNSCOPED_INFO("Specular offset X vs Y distinguishes"); + CHECK(x.getHash() != y.getHash()); } // mSpecularRepeatX vs mSpecularRepeatY { LLMaterial x; x.setSpecularRepeatX(4.0f); LLMaterial y; y.setSpecularRepeatY(4.0f); - ensure("Specular repeat X vs Y distinguishes", - x.getHash() != y.getHash()); + UNSCOPED_INFO("Specular repeat X vs Y distinguishes"); + CHECK(x.getHash() != y.getHash()); } // Same UUIDs swapped between mNormalID and mSpecularID. { LLMaterial ab; ab.setNormalID(kIdA); ab.setSpecularID(kIdB); LLMaterial ba; ba.setNormalID(kIdB); ba.setSpecularID(kIdA); - ensure("Normal/Specular ID swap distinguishes", - ab.getHash() != ba.getHash()); + UNSCOPED_INFO("Normal/Specular ID swap distinguishes"); + CHECK(ab.getHash() != ba.getHash()); } // Normal vs specular rotation { LLMaterial n; n.setNormalRotation(1.0f); LLMaterial s; s.setSpecularRotation(1.0f); - ensure("Normal vs Specular rotation distinguishes", - n.getHash() != s.getHash()); + UNSCOPED_INFO("Normal vs Specular rotation distinguishes"); + CHECK(n.getHash() != s.getHash()); } // Trailing U8 fields all share size/alignment — make sure the packer // doesn't accidentally read them as a single uniform word. @@ -215,19 +206,18 @@ namespace LLMaterial i; i.setEnvironmentIntensity(7); LLMaterial d; d.setDiffuseAlphaMode((U8)LLMaterial::DIFFUSE_ALPHA_MODE_MASK); LLMaterial c; c.setAlphaMaskCutoff(7); - ensure("exponent vs intensity distinguishes", - e.getHash() != i.getHash()); - ensure("intensity vs alpha mode distinguishes", - i.getHash() != d.getHash()); - ensure("alpha mode vs cutoff distinguishes", - d.getHash() != c.getHash()); + UNSCOPED_INFO("exponent vs intensity distinguishes"); + CHECK(e.getHash() != i.getHash()); + UNSCOPED_INFO("intensity vs alpha mode distinguishes"); + CHECK(i.getHash() != d.getHash()); + UNSCOPED_INFO("alpha mode vs cutoff distinguishes"); + CHECK(d.getHash() != c.getHash()); } } // fromLLSD touches every member at once. Verify it invalidates the cache // and that hashes computed before vs. after differ. - template<> template<> - void llmaterial_object_t::test<5>() + TEST_CASE("llmaterial: fromLLSD invalidates the cache and round-trips the hash", "[llmaterial]") { LLMaterial m; const LLUUID before = m.getHash(); @@ -237,23 +227,22 @@ namespace LLMaterial back; back.setAlphaMaskCutoff(99); // make cache dirty + state altered back.fromLLSD(sd); - ensure_equals("fromLLSD round-trip yields baseline hash", - back.getHash(), before); + UNSCOPED_INFO("fromLLSD round-trip yields baseline hash"); + CHECK(back.getHash() == before); // Now perturb a field through LLSD and check the hash moves. LLMaterial perturbed; perturbed.fromLLSD(sd); - ensure_equals("post-fromLLSD matches baseline", - perturbed.getHash(), before); + UNSCOPED_INFO("post-fromLLSD matches baseline"); + CHECK(perturbed.getHash() == before); perturbed.setEnvironmentIntensity(42); - ensure("post-fromLLSD setter still invalidates", - perturbed.getHash() != before); + UNSCOPED_INFO("post-fromLLSD setter still invalidates"); + CHECK(perturbed.getHash() != before); } // Copy semantics: a copy carries the cached state, but mutations on the // copy invalidate only the copy's cache. - template<> template<> - void llmaterial_object_t::test<6>() + TEST_CASE("llmaterial: a copy carries the cached hash and its changes stay its own", "[llmaterial]") { LLMaterial src; src.setAlphaMaskCutoff(42); @@ -261,29 +250,28 @@ namespace // Copy after the source's cache has been populated. LLMaterial copy_warm(src); - ensure_equals("warm copy preserves hash", - copy_warm.getHash(), src_hash); + UNSCOPED_INFO("warm copy preserves hash"); + CHECK(copy_warm.getHash() == src_hash); // Copy from a source whose cache is dirty (never read). LLMaterial dirty_src; dirty_src.setEnvironmentIntensity(7); // marks dirty without reading LLMaterial copy_cold(dirty_src); - ensure_equals("cold copy still produces the right hash", - copy_cold.getHash(), dirty_src.getHash()); + UNSCOPED_INFO("cold copy still produces the right hash"); + CHECK(copy_cold.getHash() == dirty_src.getHash()); // Mutating the copy must not bleed back into the source. copy_warm.setEnvironmentIntensity(99); - ensure("mutating copy invalidates copy's hash", - copy_warm.getHash() != src_hash); - ensure_equals("source hash unchanged", - src.getHash(), src_hash); + UNSCOPED_INFO("mutating copy invalidates copy's hash"); + CHECK(copy_warm.getHash() != src_hash); + UNSCOPED_INFO("source hash unchanged"); + CHECK(src.getHash() == src_hash); } // Redundantly writing the same value through a setter must still result // in the same hash on the next read (the cache may be marked dirty and // recomputed, but the recomputed value matches because state didn't move). - template<> template<> - void llmaterial_object_t::test<7>() + TEST_CASE("llmaterial: redundant setter writes keep the hash", "[llmaterial]") { LLMaterial m; m.setAlphaMaskCutoff(50); @@ -293,12 +281,12 @@ namespace m.setEnvironmentIntensity(0); // already the default m.setNormalRotation(0.0f); // already the default const LLUUID h2 = m.getHash(); - ensure_equals("redundant setter writes preserve hash", h1, h2); + UNSCOPED_INFO("redundant setter writes preserve hash"); + CHECK(h1 == h2); } // operator== iff hashes match for materials of equal state. - template<> template<> - void llmaterial_object_t::test<8>() + TEST_CASE("llmaterial: operator== holds exactly when the hashes match", "[llmaterial]") { LLMaterial a, b; a.setNormalID(kIdA); @@ -311,13 +299,16 @@ namespace b.setSpecularLightColor(LLColor4U(10, 20, 30, 40)); b.setEnvironmentIntensity(50); - ensure("operator== for equal state", a == b); - ensure("hash equality follows operator==", a.getHash() == b.getHash()); + UNSCOPED_INFO("operator== for equal state"); + CHECK(a == b); + UNSCOPED_INFO("hash equality follows operator=="); + CHECK(a.getHash() == b.getHash()); // One-bit perturbation must break both equalities together. b.setEnvironmentIntensity(51); - ensure("operator!= after single-field change", a != b); - ensure("hash inequality after single-field change", - a.getHash() != b.getHash()); + UNSCOPED_INFO("operator!= after single-field change"); + CHECK(a != b); + UNSCOPED_INFO("hash inequality after single-field change"); + CHECK(a.getHash() != b.getHash()); } } diff --git a/indra/llprimitive/tests/llmaterialid_test.cpp b/indra/llprimitive/tests/llmaterialid_test.cpp index 1060aaff41..ecb3dc75fd 100644 --- a/indra/llprimitive/tests/llmaterialid_test.cpp +++ b/indra/llprimitive/tests/llmaterialid_test.cpp @@ -18,22 +18,16 @@ */ #include "linden_common.h" -#include "lltut.h" +#include #include #include #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, @@ -41,68 +35,70 @@ namespace }; // Default construction yields a zeroed, null-equal ID. - template<> template<> - void llmaterialid_object_t::test<1>() + TEST_CASE_METHOD(llmaterialid_data, "llmaterialid: default construction yields a zeroed null ID", "[llmaterialid]") { LLMaterialID id; - ensure("default-constructed isNull", id.isNull()); - ensure("default-constructed == LLMaterialID::null", id == LLMaterialID::null); + UNSCOPED_INFO("default-constructed isNull"); + CHECK(id.isNull()); + UNSCOPED_INFO("default-constructed == LLMaterialID::null"); + CHECK(id == LLMaterialID::null); for (int i = 0; i < MATERIAL_ID_SIZE; ++i) { - ensure_equals("default byte zero", (int)id.get()[i], 0); + UNSCOPED_INFO("default byte zero"); + REQUIRE((int)id.get()[i] == 0); } } // Raw-memory constructor copies the full 16 bytes. - template<> template<> - void llmaterialid_object_t::test<2>() + TEST_CASE_METHOD(llmaterialid_data, "llmaterialid: the raw-memory constructor copies the full 16 bytes", "[llmaterialid]") { LLMaterialID id(kSampleBytes); - ensure("bytes preserved", std::memcmp(id.get(), kSampleBytes, MATERIAL_ID_SIZE) == 0); - ensure("non-null sample is not null", !id.isNull()); + UNSCOPED_INFO("bytes preserved"); + CHECK(std::memcmp(id.get(), kSampleBytes, MATERIAL_ID_SIZE) == 0); + UNSCOPED_INFO("non-null sample is not null"); + CHECK_FALSE(id.isNull()); } // LLUUID round-trip preserves the byte pattern in both directions. - template<> template<> - void llmaterialid_object_t::test<3>() + TEST_CASE_METHOD(llmaterialid_data, "llmaterialid: LLUUID round-trip preserves the byte pattern in both directions", "[llmaterialid]") { LLUUID uuid; std::memcpy(uuid.mData, kSampleBytes, UUID_BYTES); LLMaterialID id(uuid); - ensure("LLUUID -> LLMaterialID bytes preserved", - std::memcmp(id.get(), uuid.mData, MATERIAL_ID_SIZE) == 0); + UNSCOPED_INFO("LLUUID -> LLMaterialID bytes preserved"); + CHECK(std::memcmp(id.get(), uuid.mData, MATERIAL_ID_SIZE) == 0); LLUUID back = id.asUUID(); - ensure_equals("asUUID round-trips", back, uuid); + UNSCOPED_INFO("asUUID round-trips"); + CHECK(back == uuid); } // LLSD::Binary constructor copies a well-formed 16-byte payload. - template<> template<> - void llmaterialid_object_t::test<4>() + TEST_CASE_METHOD(llmaterialid_data, "llmaterialid: LLSD::Binary constructor copies a well-formed 16-byte payload", "[llmaterialid]") { LLSD::Binary bin(kSampleBytes, kSampleBytes + MATERIAL_ID_SIZE); LLMaterialID id(bin); - ensure("LLSD::Binary bytes preserved", - std::memcmp(id.get(), bin.data(), MATERIAL_ID_SIZE) == 0); + UNSCOPED_INFO("LLSD::Binary bytes preserved"); + CHECK(std::memcmp(id.get(), bin.data(), MATERIAL_ID_SIZE) == 0); } // LLSD round-trip: asLLSD() produces a Binary that reconstructs the same ID. - template<> template<> - void llmaterialid_object_t::test<5>() + TEST_CASE_METHOD(llmaterialid_data, "llmaterialid: asLLSD produces a Binary that reconstructs the same ID", "[llmaterialid]") { LLMaterialID original(kSampleBytes); LLSD sd = original.asLLSD(); - ensure("asLLSD is binary", sd.isBinary()); - ensure_equals("asLLSD size is MATERIAL_ID_SIZE", - (int)sd.asBinary().size(), MATERIAL_ID_SIZE); + UNSCOPED_INFO("asLLSD is binary"); + CHECK(sd.isBinary()); + UNSCOPED_INFO("asLLSD size is MATERIAL_ID_SIZE"); + REQUIRE((int)sd.asBinary().size() == MATERIAL_ID_SIZE); LLMaterialID round_trip(sd); - ensure("LLSD round-trip equals original", round_trip == original); + UNSCOPED_INFO("LLSD round-trip equals original"); + CHECK(round_trip == original); } // Equality and inequality across copies and one-byte differences. - template<> template<> - void llmaterialid_object_t::test<6>() + TEST_CASE_METHOD(llmaterialid_data, "llmaterialid: equality and inequality across copies and a one-byte difference", "[llmaterialid]") { U8 a[MATERIAL_ID_SIZE]; U8 b[MATERIAL_ID_SIZE]; @@ -111,10 +107,14 @@ namespace b[MATERIAL_ID_SIZE - 1] ^= 0x01; LLMaterialID id_a(a), id_a_copy(a), id_b(b); - ensure("a == a_copy", id_a == id_a_copy); - ensure("!(a != a_copy)", !(id_a != id_a_copy)); - ensure("a != b (one bit differs)", id_a != id_b); - ensure("!(a == b)", !(id_a == id_b)); + UNSCOPED_INFO("a == a_copy"); + CHECK(id_a == id_a_copy); + UNSCOPED_INFO("!(a != a_copy)"); + CHECK_FALSE(id_a != id_a_copy); + UNSCOPED_INFO("a != b (one bit differs)"); + CHECK(id_a != id_b); + UNSCOPED_INFO("!(a == b)"); + CHECK_FALSE(id_a == id_b); } // Ordering is byte-lexicographic (not word-endian). @@ -124,8 +124,7 @@ namespace // greater than {0x01, 0x00, 0,...} read as 0x00000001 (1) — reversing // the byte-lexicographic intuition. memcmp gets it right: the first // differing byte wins. - template<> template<> - void llmaterialid_object_t::test<7>() + TEST_CASE_METHOD(llmaterialid_data, "llmaterialid: ordering is byte-lexicographic (not word-endian)", "[llmaterialid]") { U8 low[MATERIAL_ID_SIZE] = {0}; U8 high[MATERIAL_ID_SIZE] = {0}; @@ -133,21 +132,29 @@ namespace high[0] = 0x01; LLMaterialID id_low(low), id_high(high); - ensure("byte-lex: id_low < id_high", id_low < id_high); - ensure("byte-lex: id_high > id_low", id_high > id_low); - ensure("byte-lex: id_low <= id_high", id_low <= id_high); - ensure("byte-lex: id_high >= id_low", id_high >= id_low); - ensure("byte-lex: !(id_low > id_high)", !(id_low > id_high)); - - ensure("reflexive: id <= id", id_low <= id_low); - ensure("reflexive: id >= id", id_low >= id_low); - ensure("irreflexive: !(id < id)", !(id_low < id_low)); - ensure("irreflexive: !(id > id)", !(id_low > id_low)); + UNSCOPED_INFO("byte-lex: id_low < id_high"); + CHECK(id_low < id_high); + UNSCOPED_INFO("byte-lex: id_high > id_low"); + CHECK(id_high > id_low); + UNSCOPED_INFO("byte-lex: id_low <= id_high"); + CHECK(id_low <= id_high); + UNSCOPED_INFO("byte-lex: id_high >= id_low"); + CHECK(id_high >= id_low); + UNSCOPED_INFO("byte-lex: !(id_low > id_high)"); + CHECK_FALSE(id_low > id_high); + + UNSCOPED_INFO("reflexive: id <= id"); + CHECK(id_low <= id_low); + UNSCOPED_INFO("reflexive: id >= id"); + CHECK(id_low >= id_low); + UNSCOPED_INFO("irreflexive: !(id < id)"); + CHECK_FALSE(id_low < id_low); + UNSCOPED_INFO("irreflexive: !(id > id)"); + CHECK_FALSE(id_low > id_low); } // asString uses little-endian word formatting (preserved for log greppability). - template<> template<> - void llmaterialid_object_t::test<8>() + TEST_CASE_METHOD(llmaterialid_data, "llmaterialid: asString formats in little-endian words", "[llmaterialid]") { const U8 bytes[MATERIAL_ID_SIZE] = { 0xaa, 0xbb, 0xcc, 0xdd, @@ -156,51 +163,52 @@ namespace 0xff, 0xff, 0xff, 0xff }; LLMaterialID id(bytes); - ensure_equals("asString little-endian word format", - id.asString(), + UNSCOPED_INFO("asString little-endian word format"); + CHECK(id.asString() == std::string("ddccbbaa-44332211-01000000-ffffffff")); - ensure_equals("null asString", - LLMaterialID::null.asString(), + UNSCOPED_INFO("null asString"); + CHECK(LLMaterialID::null.asString() == std::string("00000000-00000000-00000000-00000000")); } // set() then clear() round-trip. - template<> template<> - void llmaterialid_object_t::test<9>() + TEST_CASE_METHOD(llmaterialid_data, "llmaterialid: set() copies the bytes and clear() makes the ID null", "[llmaterialid]") { LLMaterialID id; id.set(kSampleBytes); - ensure("set copies bytes", - std::memcmp(id.get(), kSampleBytes, MATERIAL_ID_SIZE) == 0); - ensure("post-set is not null", !id.isNull()); + UNSCOPED_INFO("set copies bytes"); + CHECK(std::memcmp(id.get(), kSampleBytes, MATERIAL_ID_SIZE) == 0); + UNSCOPED_INFO("post-set is not null"); + CHECK_FALSE(id.isNull()); id.clear(); - ensure("clear yields null", id.isNull()); - ensure("clear == LLMaterialID::null", id == LLMaterialID::null); + UNSCOPED_INFO("clear yields null"); + CHECK(id.isNull()); + UNSCOPED_INFO("clear == LLMaterialID::null"); + CHECK(id == LLMaterialID::null); } // Hash consistency: equal IDs hash the same and std::hash == boost hash_value. - template<> template<> - void llmaterialid_object_t::test<10>() + TEST_CASE_METHOD(llmaterialid_data, "llmaterialid: std::hash agrees for equal IDs and with hash_value", "[llmaterialid]") { LLMaterialID a(kSampleBytes), b(kSampleBytes); - ensure_equals("equal IDs -> equal std::hash", - std::hash{}(a), std::hash{}(b)); - ensure_equals("std::hash matches hash_value", - std::hash{}(a), hash_value(b)); - ensure_equals("null hashes to 0", - std::hash{}(LLMaterialID::null), (size_t)0); + UNSCOPED_INFO("equal IDs -> equal std::hash"); + CHECK(std::hash{}(a) == std::hash{}(b)); + UNSCOPED_INFO("std::hash matches hash_value"); + CHECK(std::hash{}(a) == hash_value(b)); + UNSCOPED_INFO("null hashes to 0"); + CHECK(std::hash{}(LLMaterialID::null) == (size_t)0); } // Stream insertion delegates to asString. - template<> template<> - void llmaterialid_object_t::test<11>() + TEST_CASE_METHOD(llmaterialid_data, "llmaterialid: stream insertion delegates to asString", "[llmaterialid]") { LLMaterialID id(kSampleBytes); std::ostringstream os; os << id; - ensure_equals("operator<< matches asString", os.str(), id.asString()); + UNSCOPED_INFO("operator<< matches asString"); + CHECK(os.str() == id.asString()); } // The remaining tests exercise defensive guards against malformed input. @@ -209,34 +217,36 @@ namespace #ifndef SHOW_ASSERT // parseFromBinary (via LLSD::Binary ctor) clears on wrong-size input // instead of overreading. - template<> template<> - void llmaterialid_object_t::test<12>() + TEST_CASE_METHOD(llmaterialid_data, "llmaterialid: parseFromBinary clears on wrong-size input instead of overreading", "[llmaterialid]") { LLSD::Binary too_short(MATERIAL_ID_SIZE - 1, 0xff); - ensure("too-short binary -> null", LLMaterialID(too_short).isNull()); + UNSCOPED_INFO("too-short binary -> null"); + REQUIRE(LLMaterialID(too_short).isNull()); LLSD::Binary too_long(MATERIAL_ID_SIZE + 1, 0xff); - ensure("too-long binary -> null", LLMaterialID(too_long).isNull()); + UNSCOPED_INFO("too-long binary -> null"); + REQUIRE(LLMaterialID(too_long).isNull()); LLSD::Binary empty; - ensure("empty binary -> null", LLMaterialID(empty).isNull()); + UNSCOPED_INFO("empty binary -> null"); + REQUIRE(LLMaterialID(empty).isNull()); } // set(nullptr) does not invoke memcpy on a null source. - template<> template<> - void llmaterialid_object_t::test<13>() + TEST_CASE_METHOD(llmaterialid_data, "llmaterialid: set(nullptr) does not invoke memcpy on a null source", "[llmaterialid]") { LLMaterialID id(kSampleBytes); id.set(nullptr); - ensure("set(nullptr) clears", id.isNull()); + UNSCOPED_INFO("set(nullptr) clears"); + REQUIRE(id.isNull()); } // Non-binary LLSD (LLSD::asBinary() returns empty) clears instead of UB. - template<> template<> - void llmaterialid_object_t::test<14>() + TEST_CASE_METHOD(llmaterialid_data, "llmaterialid: a non-binary LLSD clears the ID instead of reading it", "[llmaterialid]") { LLSD not_binary("hello"); - ensure("non-binary LLSD -> null", LLMaterialID(not_binary).isNull()); + UNSCOPED_INFO("non-binary LLSD -> null"); + REQUIRE(LLMaterialID(not_binary).isNull()); } #endif } diff --git a/indra/llprimitive/tests/llmediaentry_test.cpp b/indra/llprimitive/tests/llmediaentry_test.cpp index cb4c17e061..10cfd42d2f 100644 --- a/indra/llprimitive/tests/llmediaentry_test.cpp +++ b/indra/llprimitive/tests/llmediaentry_test.cpp @@ -25,7 +25,8 @@ */ #include "linden_common.h" -#include "lltut.h" +#include +#include "altestname.h" #include @@ -135,8 +136,6 @@ \n\ " -namespace tut -{ namespace { // this is fixture data that gets created before each test and destroyed @@ -158,20 +157,9 @@ namespace LLSD defaultMediaEntryLLSD; }; - typedef test_group factory; - typedef factory::object object; -} } -namespace -{ - // this is for naming our tests to make pretty output - tut::factory tf("LLMediaEntry"); -} - -namespace tut -{ namespace { void ensure_llsd_equals(const std::string& msg, const LLSD& expected, const LLSD& actual) @@ -184,7 +172,8 @@ namespace message += "\n expected: "; message += ll_pretty_print_sd(expected); message += "\n"; - ensure(message, false); + UNSCOPED_INFO(message); + REQUIRE(false); } } @@ -211,37 +200,31 @@ namespace message += "\ncandidate_url = "; message += candidate_url; } - ensure(message, expected_pass == passed_whitelist); + UNSCOPED_INFO(message); + REQUIRE(expected_pass == passed_whitelist); } void whitelist_test(int num, const char *whitelist, const char *candidate_url, bool expected_pass) { whitelist_test(num, true, whitelist, candidate_url, expected_pass); } -} - template<> template<> - void object::test<1>() + TEST_CASE_METHOD(MediaEntry_test, "LLMediaEntry: a new entry has the default LLSD", "[LLMediaEntry]") { - set_test_name("Test LLMediaEntry Instantiation"); LLMediaEntry entry; - ensure_llsd_equals(get_test_name() + " failed", defaultMediaEntryLLSD, entry.asLLSD()); + ensure_llsd_equals(ll_test::currentTestName() + " failed", defaultMediaEntryLLSD, entry.asLLSD()); } - template<> template<> - void object::test<2>() + TEST_CASE_METHOD(MediaEntry_test, "LLMediaEntry: fromLLSD of an empty LLSD gives an empty entry", "[LLMediaEntry]") { - set_test_name("Test LLMediaEntry Instantiation from LLSD"); LLMediaEntry entry; LLSD sd; entry.fromLLSD(sd); - ensure_llsd_equals(get_test_name() + " failed", emptyMediaEntryLLSD, entry.asLLSD()); + ensure_llsd_equals(ll_test::currentTestName() + " failed", emptyMediaEntryLLSD, entry.asLLSD()); } - template<> template<> - void object::test<3>() + TEST_CASE_METHOD(MediaEntry_test, "LLMediaEntry: fromLLSD of a partial LLSD leaves the other keys empty", "[LLMediaEntry]") { - set_test_name("Test LLMediaEntry Partial Instantiation from LLSD"); LLMediaEntry entry; LLSD sd; sd[LLMediaEntry::CURRENT_URL_KEY] = "http://www.example.com"; @@ -249,13 +232,11 @@ namespace LLSD golden; std::istringstream p(PARTIAL_MEDIA_ENTRY("http://www.example.com")); LLSDSerialize::fromXML(golden,p); - ensure_llsd_equals(get_test_name() + " failed", golden, entry.asLLSD()); + ensure_llsd_equals(ll_test::currentTestName() + " failed", golden, entry.asLLSD()); } - template<> template<> - void object::test<4>() + TEST_CASE_METHOD(MediaEntry_test, "LLMediaEntry: asLLSD replaces what the LLSD already held", "[LLMediaEntry]") { - set_test_name("Test LLMediaEntry::asLLSD()"); LLMediaEntry entry; LLSD sd; // Put some cruft in the LLSD @@ -264,14 +245,12 @@ namespace whitelist.append("*.example.com"); sd[LLMediaEntry::WHITELIST_KEY] = whitelist; entry.asLLSD(sd); - ensure_llsd_equals(get_test_name() + " failed", defaultMediaEntryLLSD, sd); + ensure_llsd_equals(ll_test::currentTestName() + " failed", defaultMediaEntryLLSD, sd); } - template<> template<> - void object::test<5>() + TEST_CASE_METHOD(MediaEntry_test, "LLMediaEntry: fromLLSD of asLLSD output drops a whitelist the source lacks", "[LLMediaEntry]") { - set_test_name("Test LLMediaEntry::asLLSD() -> LLMediaEntry::fromLLSD()"); LLMediaEntry entry1, entry2; // Add a whitelist to entry2 std::vector whitelist; @@ -282,72 +261,64 @@ namespace entry1.asLLSD(sd); // "read" that LLSD into entry 2 entry2.fromLLSD(sd); - ensure_llsd_equals(get_test_name() + " failed", defaultMediaEntryLLSD, entry2.asLLSD()); + ensure_llsd_equals(ll_test::currentTestName() + " 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>() + TEST_CASE_METHOD(MediaEntry_test, "LLMediaEntry: setCurrentURL accepts a short URL and rejects one over the limit", "[LLMediaEntry]") { - set_test_name("Test Limits on setting current URL"); LLMediaEntry entry; U32 status = entry.setCurrentURL(URL_OK); - ensure(get_test_name() + " ok failed", status == LSL_STATUS_OK); + UNSCOPED_INFO(ll_test::currentTestName() + " ok failed"); + CHECK(status == LSL_STATUS_OK); status = entry.setCurrentURL(URL_TOO_BIG); - ensure(get_test_name() + " ok failed", status == LSL_STATUS_BOUNDS_ERROR); + UNSCOPED_INFO(ll_test::currentTestName() + " ok failed"); + CHECK(status == LSL_STATUS_BOUNDS_ERROR); } - template<> template<> - void object::test<7>() + TEST_CASE_METHOD(MediaEntry_test, "LLMediaEntry: setHomeURL accepts a short URL and rejects one over the limit", "[LLMediaEntry]") { - set_test_name("Test Limits on setting home URL"); LLMediaEntry entry; U32 status = entry.setHomeURL(URL_OK); - ensure(get_test_name() + " ok failed", status == LSL_STATUS_OK); + UNSCOPED_INFO(ll_test::currentTestName() + " ok failed"); + CHECK(status == LSL_STATUS_OK); status = entry.setHomeURL(URL_TOO_BIG); - ensure(get_test_name() + " ok failed", status == LSL_STATUS_BOUNDS_ERROR); + UNSCOPED_INFO(ll_test::currentTestName() + " ok failed"); + CHECK(status == LSL_STATUS_BOUNDS_ERROR); } - template<> template<> - void object::test<8>() + TEST_CASE_METHOD(MediaEntry_test, "LLMediaEntry: setWhiteList accepts a valid list", "[LLMediaEntry]") { - set_test_name("Test Limits on setting whitelist"); - // Test a valid list LLMediaEntry entry; std::vector whitelist; whitelist.push_back(std::string(URL_OK)); S32 status = entry.setWhiteList(whitelist); - ensure(get_test_name() + " invalid result", status == LSL_STATUS_OK); - ensure(get_test_name() + " failed", whitelist == entry.getWhiteList()); + UNSCOPED_INFO(ll_test::currentTestName() + " invalid result"); + CHECK(status == LSL_STATUS_OK); + UNSCOPED_INFO(ll_test::currentTestName() + " failed"); + CHECK(whitelist == entry.getWhiteList()); } - template<> template<> - void object::test<9>() + TEST_CASE_METHOD(MediaEntry_test, "LLMediaEntry: setWhiteList rejects a list with an oversized URL and stays empty", "[LLMediaEntry]") { - set_test_name("Test Limits on setting whitelist too big"); - // Test an invalid list LLMediaEntry entry; std::vector whitelist, empty; whitelist.push_back(std::string(URL_OK)); whitelist.push_back(std::string(URL_TOO_BIG)); S32 status = entry.setWhiteList(whitelist); - ensure(get_test_name() + " invalid result", status == LSL_STATUS_BOUNDS_ERROR); - ensure(get_test_name() + " failed", empty == entry.getWhiteList()); + UNSCOPED_INFO(ll_test::currentTestName() + " invalid result"); + CHECK(status == LSL_STATUS_BOUNDS_ERROR); + UNSCOPED_INFO(ll_test::currentTestName() + " failed"); + CHECK(empty == entry.getWhiteList()); } - template<> template<> - void object::test<10>() + TEST_CASE_METHOD(MediaEntry_test, "LLMediaEntry: setWhiteList rejects too many entries and stays empty", "[LLMediaEntry]") { - set_test_name("Test Limits on setting whitelist too many"); - // Test an invalid list LLMediaEntry entry; std::vector whitelist, empty; @@ -355,33 +326,31 @@ namespace whitelist.push_back("Q"); } S32 status = entry.setWhiteList(whitelist); - ensure(get_test_name() + " invalid result", status == LSL_STATUS_BOUNDS_ERROR); - ensure(get_test_name() + " failed", empty == entry.getWhiteList()); + UNSCOPED_INFO(ll_test::currentTestName() + " invalid result"); + CHECK(status == LSL_STATUS_BOUNDS_ERROR); + UNSCOPED_INFO(ll_test::currentTestName() + " failed"); + CHECK(empty == entry.getWhiteList()); } - template<> template<> - void object::test<11>() + TEST_CASE_METHOD(MediaEntry_test, "LLMediaEntry: both setWhiteList overloads agree on a valid list", "[LLMediaEntry]") { - set_test_name("Test to make sure both setWhiteList() functions behave the same"); - // Test a valid list std::vector whitelist, empty; LLSD whitelist_llsd; whitelist.push_back(std::string(URL_OK)); whitelist_llsd.append(std::string(URL_OK)); LLMediaEntry entry1, entry2; - ensure(get_test_name() + " setWhiteList(s) don't match", - entry1.setWhiteList(whitelist) == LSL_STATUS_OK && - entry2.setWhiteList(whitelist_llsd)== LSL_STATUS_OK ); - ensure(get_test_name() + " failed", - entry1.getWhiteList() == entry2.getWhiteList()); + { + INFO(ll_test::currentTestName() + " setWhiteList(s) don't match"); + CHECK(entry1.setWhiteList(whitelist) == LSL_STATUS_OK); + CHECK(entry2.setWhiteList(whitelist_llsd)== LSL_STATUS_OK); + } + UNSCOPED_INFO(ll_test::currentTestName() + " failed"); + CHECK(entry1.getWhiteList() == entry2.getWhiteList()); } - template<> template<> - void object::test<12>() + TEST_CASE_METHOD(MediaEntry_test, "LLMediaEntry: both setWhiteList overloads reject an oversized URL", "[LLMediaEntry]") { - set_test_name("Test to make sure both setWhiteList() functions behave the same"); - // Test an invalid list std::vector whitelist, empty; LLSD whitelist_llsd; @@ -390,19 +359,20 @@ namespace whitelist_llsd.append(std::string(URL_OK)); whitelist_llsd.append(std::string(URL_TOO_BIG)); LLMediaEntry entry1, entry2; - ensure(get_test_name() + " setWhiteList(s) don't match", - entry1.setWhiteList(whitelist) == LSL_STATUS_BOUNDS_ERROR && - entry2.setWhiteList(whitelist_llsd) == LSL_STATUS_BOUNDS_ERROR); - ensure(get_test_name() + " failed", - empty == entry1.getWhiteList() && - empty == entry2.getWhiteList()); + { + INFO(ll_test::currentTestName() + " setWhiteList(s) don't match"); + CHECK(entry1.setWhiteList(whitelist) == LSL_STATUS_BOUNDS_ERROR); + CHECK(entry2.setWhiteList(whitelist_llsd) == LSL_STATUS_BOUNDS_ERROR); + } + { + INFO(ll_test::currentTestName() + " failed"); + CHECK(empty == entry1.getWhiteList()); + CHECK(empty == entry2.getWhiteList()); + } } - template<> template<> - void object::test<13>() + TEST_CASE_METHOD(MediaEntry_test, "LLMediaEntry: both setWhiteList overloads reject too many entries", "[LLMediaEntry]") { - set_test_name("Test to make sure both setWhiteList() functions behave the same"); - // Test an invalid list, too many std::vector whitelist, empty; LLSD whitelist_llsd; @@ -411,16 +381,19 @@ namespace whitelist_llsd.append("Q"); } LLMediaEntry entry1, entry2; - ensure(get_test_name() + " invalid result", - entry1.setWhiteList(whitelist) == LSL_STATUS_BOUNDS_ERROR && - entry2.setWhiteList(whitelist_llsd) == LSL_STATUS_BOUNDS_ERROR); - ensure(get_test_name() + " failed", - empty == entry1.getWhiteList() && - empty == entry2.getWhiteList()); + { + INFO(ll_test::currentTestName() + " invalid result"); + CHECK(entry1.setWhiteList(whitelist) == LSL_STATUS_BOUNDS_ERROR); + CHECK(entry2.setWhiteList(whitelist_llsd) == LSL_STATUS_BOUNDS_ERROR); + } + { + INFO(ll_test::currentTestName() + " failed"); + CHECK(empty == entry1.getWhiteList()); + CHECK(empty == entry2.getWhiteList()); + } } - template<> template<> - void object::test<14>() + TEST_CASE_METHOD(MediaEntry_test, "LLMediaEntry: checkCandidateUrl matches URLs against whitelist patterns", "[LLMediaEntry]") { // Whitelist check tests int n=0; diff --git a/indra/llprimitive/tests/llprimitive_test.cpp b/indra/llprimitive/tests/llprimitive_test.cpp index d2008f43b0..a96b4d42c3 100644 --- a/indra/llprimitive/tests/llprimitive_test.cpp +++ b/indra/llprimitive/tests/llprimitive_test.cpp @@ -26,7 +26,7 @@ #include "linden_common.h" -#include "../test/lltut.h" +#include #include "../llprimitive.h" @@ -131,8 +131,6 @@ class PRIMITIVE_TEST_SETUP }; } -namespace tut -{ namespace { struct llprimitive @@ -140,138 +138,122 @@ namespace PRIMITIVE_TEST_SETUP setup_class; }; - 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>() + TEST_CASE_METHOD(llprimitive, "LLPrimitive: a default primitive constructs and destroys cleanly", "[LLPrimitive]") { - set_test_name("Test LLPrimitive Instantiation"); LLPrimitive test; } - template<> template<> - void llprimitive_object_t::test<2>() + TEST_CASE_METHOD(llprimitive, "LLPrimitive: getPCode starts at zero and returns what setPCode set", "[LLPrimitive]") { - set_test_name("Test LLPrimitive PCode setter and getter."); LLPrimitive test; - ensure_equals(test.getPCode(), 0); + CHECK(test.getPCode() == 0); LLPCode code = 1; test.setPCode(code); - ensure_equals(test.getPCode(), code); + CHECK(test.getPCode() == code); } - template<> template<> - void llprimitive_object_t::test<3>() + TEST_CASE_METHOD(llprimitive, "LLPrimitive: init_primitive sets the PCode", "[LLPrimitive]") { - set_test_name("Test llprimitive constructor and initer."); LLPCode code = 1; LLPrimitive primitive; primitive.init_primitive(code); - ensure_equals(primitive.getPCode(), code); + CHECK(primitive.getPCode() == code); } - template<> template<> - void llprimitive_object_t::test<4>() + TEST_CASE_METHOD(llprimitive, "LLPrimitive: createPrimitive makes a primitive with the PCode", "[LLPrimitive]") { - set_test_name("Test Static llprimitive constructor and initer."); LLPCode code = 1; LLPrimitive * primitive = LLPrimitive::createPrimitive(code); - ensure(primitive != NULL); - ensure_equals(primitive->getPCode(), code); + REQUIRE(primitive != NULL); + CHECK(primitive->getPCode() == code); } - template<> template<> - void llprimitive_object_t::test<5>() + TEST_CASE_METHOD(llprimitive, "LLPrimitive: setVolume of a unique volume replaces it only when params change", "[LLPrimitive]") { - set_test_name("Test setVolume creation of new unique volume."); LLPrimitive primitive; LLVolumeParams params; // Make sure volume starts off null - ensure(primitive.getVolume() == NULL); + REQUIRE(primitive.getVolume() == NULL); // Make sure we have no texture entries before setting the volume - ensure_equals(primitive.getNumTEs(), 0); + CHECK(primitive.getNumTEs() == 0); // Test that GEOMETRY has not been flagged as changed. - ensure(!primitive.isChanged(LLXform::GEOMETRY)); + CHECK_FALSE(primitive.isChanged(LLXform::GEOMETRY)); // Make sure setVolume returns true - ensure(primitive.setVolume(params, 0, true) == true); + REQUIRE(primitive.setVolume(params, 0, true)); LLVolume* new_volume = primitive.getVolume(); // make sure new volume was actually created - ensure(new_volume != NULL); + REQUIRE(new_volume != NULL); // Make sure that now that we've set the volume we have texture entries - ensure_not_equals(primitive.getNumTEs(), 0); + CHECK(primitive.getNumTEs() != 0); // Make sure that the number of texture entries equals the number of faces in the volume (should be 6) - ensure_equals(new_volume->getNumFaces(), 6); - ensure_equals(primitive.getNumTEs(), new_volume->getNumFaces()); + CHECK(new_volume->getNumFaces() == 6); + CHECK(primitive.getNumTEs() == new_volume->getNumFaces()); // Test that GEOMETRY has been flagged as changed. - ensure(primitive.isChanged(LLXform::GEOMETRY)); + CHECK(primitive.isChanged(LLXform::GEOMETRY)); // Run it twice to make sure it doesn't create a different one if params are the same - ensure(primitive.setVolume(params, 0, true) == false); - ensure(new_volume == primitive.getVolume()); + CHECK_FALSE(primitive.setVolume(params, 0, true)); + CHECK(new_volume == primitive.getVolume()); // Change the param definition and try setting it again. params.setRevolutions(4); - ensure(primitive.setVolume(params, 0, true) == true); + CHECK(primitive.setVolume(params, 0, true)); // Ensure that we now have a different volume - ensure(new_volume != primitive.getVolume()); + CHECK(new_volume != primitive.getVolume()); } - template<> template<> - void llprimitive_object_t::test<6>() + TEST_CASE_METHOD(llprimitive, "LLPrimitive: setVolume of a shared volume replaces it only when params change", "[LLPrimitive]") { - set_test_name("Test setVolume creation of new NOT-unique volume."); LLPrimitive primitive; LLVolumeParams params; // Make sure volume starts off null - ensure(primitive.getVolume() == NULL); + REQUIRE(primitive.getVolume() == NULL); // Make sure we have no texture entries before setting the volume - ensure_equals(primitive.getNumTEs(), 0); + CHECK(primitive.getNumTEs() == 0); // Test that GEOMETRY has not been flagged as changed. - ensure(!primitive.isChanged(LLXform::GEOMETRY)); + CHECK_FALSE(primitive.isChanged(LLXform::GEOMETRY)); // Make sure setVolume returns true - ensure(primitive.setVolume(params, 0, false) == true); + REQUIRE(primitive.setVolume(params, 0, false)); LLVolume* new_volume = primitive.getVolume(); // make sure new volume was actually created - ensure(new_volume != NULL); + REQUIRE(new_volume != NULL); // Make sure that now that we've set the volume we have texture entries - ensure_not_equals(primitive.getNumTEs(), 0); + CHECK(primitive.getNumTEs() != 0); // Make sure that the number of texture entries equals the number of faces in the volume (should be 6) - ensure_equals(new_volume->getNumFaces(), 6); - ensure_equals(primitive.getNumTEs(), new_volume->getNumFaces()); + CHECK(new_volume->getNumFaces() == 6); + CHECK(primitive.getNumTEs() == new_volume->getNumFaces()); // Test that GEOMETRY has been flagged as changed. - ensure(primitive.isChanged(LLXform::GEOMETRY)); + CHECK(primitive.isChanged(LLXform::GEOMETRY)); // Run it twice to make sure it doesn't create a different one if params are the same - ensure(primitive.setVolume(params, 0, false) == false); - ensure(new_volume == primitive.getVolume()); + CHECK_FALSE(primitive.setVolume(params, 0, false)); + CHECK(new_volume == primitive.getVolume()); // Change the param definition and try setting it again. params.setRevolutions(4); - ensure(primitive.setVolume(params, 0, false) == true); + CHECK(primitive.setVolume(params, 0, false)); // Ensure that we now have a different volume - ensure(new_volume != primitive.getVolume()); + CHECK(new_volume != primitive.getVolume()); } } diff --git a/indra/llxml/CMakeLists.txt b/indra/llxml/CMakeLists.txt index b1ca7f927f..d85390bae6 100644 --- a/indra/llxml/CMakeLists.txt +++ b/indra/llxml/CMakeLists.txt @@ -25,7 +25,10 @@ target_precompile_headers(llxml REUSE_FROM llprecompiled) # tests if(AL_BUILD_TESTS) - 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) + al_add_test_binary( + llxml_tests + PROJECT llxml + SOURCES tests/alxmllayermerge_test.cpp tests/llcontrol_test.cpp tests/llxmlnode_test.cpp + LIBRARIES llxml llmath llcommon + ) endif() diff --git a/indra/llxml/tests/alxmllayermerge_test.cpp b/indra/llxml/tests/alxmllayermerge_test.cpp index 16eb13f899..81f3d4d57e 100644 --- a/indra/llxml/tests/alxmllayermerge_test.cpp +++ b/indra/llxml/tests/alxmllayermerge_test.cpp @@ -30,13 +30,11 @@ #include "llfile.h" #include "lluuid.h" -#include "../test/lltut.h" +#include #include #include -namespace tut -{ namespace { // Every event, as one line: the kind, the layer, and the element or @@ -237,60 +235,83 @@ namespace } }; - 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= // against body text applied as the text, an attribute the base lacks // dropped, and two children that match nothing. - template<> template<> - void alxmllayermerge_object::test<1>() + TEST_CASE_METHOD(alxmllayermerge_data, "alxmllayermerge: merge applies an overlay and reports one event per decision", "[alxmllayermerge]") { LLXMLNodePtr base = parse(BASE); LLXMLNodePtr overlay = parse(OVERLAY); - ensure("fixture parses", base.notNull() && overlay.notNull()); + { + INFO("fixture parses"); + REQUIRE(base.notNull()); + REQUIRE(overlay.notNull()); + } RecordingObserver rec; ALXmlLayerMerge::merge(base, overlay, 1, &rec); - ensure_equals("title", attr(base, "", "title"), std::string("Titel")); - ensure_equals("the base's width is untouched", attr(base, "", "width"), std::string("100")); - ensure_equals("first of a repeated name", attr(base, "p/b", "label"), std::string("Erste")); - ensure_equals("second of a repeated name", attr(base, "p/b#1", "label"), std::string("Zweite")); - ensure_equals("text", attr(base, "p/t", "text()"), std::string("Text")); - ensure_equals("an item by value", attr(base, "p/c/v2", "label"), std::string("Zwei")); - ensure_equals("the other item is untouched", attr(base, "p/c/v1", "label"), std::string("One")); - ensure_equals("value= against body text becomes the text", attr(base, "p/u", "text()"), std::string("Wert")); - ensure_equals("and no value attribute appears", attr(base, "p/u", "value"), std::string("")); - ensure_equals("an overlay without text leaves the base's text", attr(base, "p/w", "text()"), std::string("Keep me too")); - ensure_equals("while its attribute applies", attr(base, "p/w", "font"), std::string("Serif")); - - ensure("the extra attribute was reported dropped", rec.has("dropped 1 extra=x")); - ensure_equals("one attribute dropped", rec.dropped, 1); - ensure("the value= was reported applied as text", rec.has("value-as-text 1 text[u]")); - ensure_equals("once", rec.valueAsText, 1); - ensure("the kept text was reported", rec.has("kept 1 text[w]")); - ensure_equals("one text kept", rec.kept, 1); - ensure("the missing child was reported", rec.has("nosibling 1 text[missing]")); - ensure("the unnamed child was reported", rec.has("unnamed 1 text[]")); - ensure_equals("two children unmatched", rec.unmatched, 2); + UNSCOPED_INFO("title"); + CHECK(attr(base, "", "title") == std::string("Titel")); + UNSCOPED_INFO("the base's width is untouched"); + CHECK(attr(base, "", "width") == std::string("100")); + UNSCOPED_INFO("first of a repeated name"); + CHECK(attr(base, "p/b", "label") == std::string("Erste")); + UNSCOPED_INFO("second of a repeated name"); + CHECK(attr(base, "p/b#1", "label") == std::string("Zweite")); + UNSCOPED_INFO("text"); + CHECK(attr(base, "p/t", "text()") == std::string("Text")); + UNSCOPED_INFO("an item by value"); + CHECK(attr(base, "p/c/v2", "label") == std::string("Zwei")); + UNSCOPED_INFO("the other item is untouched"); + CHECK(attr(base, "p/c/v1", "label") == std::string("One")); + UNSCOPED_INFO("value= against body text becomes the text"); + CHECK(attr(base, "p/u", "text()") == std::string("Wert")); + UNSCOPED_INFO("and no value attribute appears"); + CHECK(attr(base, "p/u", "value") == std::string("")); + UNSCOPED_INFO("an overlay without text leaves the base's text"); + CHECK(attr(base, "p/w", "text()") == std::string("Keep me too")); + UNSCOPED_INFO("while its attribute applies"); + CHECK(attr(base, "p/w", "font") == std::string("Serif")); + + UNSCOPED_INFO("the extra attribute was reported dropped"); + REQUIRE(rec.has("dropped 1 extra=x")); + UNSCOPED_INFO("one attribute dropped"); + CHECK(rec.dropped == 1); + UNSCOPED_INFO("the value= was reported applied as text"); + REQUIRE(rec.has("value-as-text 1 text[u]")); + UNSCOPED_INFO("once"); + CHECK(rec.valueAsText == 1); + UNSCOPED_INFO("the kept text was reported"); + REQUIRE(rec.has("kept 1 text[w]")); + UNSCOPED_INFO("one text kept"); + CHECK(rec.kept == 1); + UNSCOPED_INFO("the missing child was reported"); + REQUIRE(rec.has("nosibling 1 text[missing]")); + UNSCOPED_INFO("the unnamed child was reported"); + REQUIRE(rec.has("unnamed 1 text[]")); + UNSCOPED_INFO("two children unmatched"); + CHECK(rec.unmatched == 2); // p, b, b, t, u, w, c, and the item: eight matches. - ensure_equals("matches", rec.matched, 8); - ensure("the second button matched the second base button", rec.has("match 1 button[b]")); - ensure_equals("texts applied: t", rec.text, 1); + UNSCOPED_INFO("matches"); + CHECK(rec.matched == 8); + UNSCOPED_INFO("the second button matched the second base button"); + REQUIRE(rec.has("match 1 button[b]")); + UNSCOPED_INFO("texts applied: t"); + CHECK(rec.text == 1); // title; label on b, b; font on w; value and label on the item. // Names are keys, and are not written. - ensure_equals("attributes applied", rec.applied, 6); - ensure("the name is never written", !rec.has("attr 1 name=f")); + UNSCOPED_INFO("attributes applied"); + CHECK(rec.applied == 6); + UNSCOPED_INFO("the name is never written"); + REQUIRE_FALSE(rec.has("attr 1 name=f")); } // With no observer the tree comes out the same: a rule that changes // changes in one place. - template<> template<> - void alxmllayermerge_object::test<2>() + TEST_CASE_METHOD(alxmllayermerge_data, "alxmllayermerge: merge gives the same tree with or without an observer", "[alxmllayermerge]") { LLXMLNodePtr observed_base = parse(BASE); LLXMLNodePtr observed_overlay = parse(OVERLAY); @@ -301,24 +322,10 @@ namespace LLXMLNodePtr silent_overlay = parse(OVERLAY); ALXmlLayerMerge::merge(silent_base, silent_overlay); - ensure_equals("the same tree with and without an observer", serialize(silent_base), serialize(observed_base)); - ensure("and the observer saw something", !rec.events.empty()); - } - - // A repeated name matches in document order whatever lies between: - // the second overlay child of a name takes the second base child of - // it, never the first again. - template<> template<> - void alxmllayermerge_object::test<4>() - { - LLXMLNodePtr base = parse( - "