diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 87b2aeb1c69..e2cdb87cc6b 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -298,9 +298,9 @@ jobs: VIEWER_GRID: "agni" # Link Time Optimization AL_USE_LTO: ${{ matrix.configuration == 'release' && startsWith(matrix.build_variant, 'Viewer') }} - # Setting this variable directs Linden's TUT test driver code to capture - # test-program log output at the specified level, but to display it only if - # the individual test fails. + # Setting this variable directs the tests' main to capture test-program + # log output at the specified level, but to display it only if the + # individual test case fails. LOGFAIL: DEBUG master_message_template_checkout: ${{ github.workspace }}/.master-message-template # vcpkg Setup. No local binary cache: nothing reads it again on a diff --git a/CLAUDE.md b/CLAUDE.md index eb07719ace3..52d5a20dc7d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -60,9 +60,9 @@ Tests are off by default. To enable: add `-DAL_BUILD_TESTS=ON` to the configure ctest --test-dir build-- --output-on-failure -LE manual ``` -`-LE manual` leaves out the benchmarks and the pending one-process runs of Catch2 binaries (label `combined_pending`), as the build's own test target does. +`-LE manual` leaves out the benchmarks and the pending one-process runs of Catch2 binaries (label `combined_pending`), as the build's own test target does. `-L ` runs one library's tests, and `-R '^/$'` one file's. -Unit tests live alongside the library they test in `indra//tests/` directories. They are moving from TUT (Template Unit Test) to Catch2 a library at a time. TUT test targets are defined by the `LL_ADD_PROJECT_UNIT_TESTS` macro in `indra/cmake/LLAddBuildTest.cmake`; a converted library builds its files into one Catch2 binary with `al_add_test_binary` (`indra/cmake/ALAddTestBinary.cmake`), on the harness in `indra/test/`. Integration tests are in `indra/integration_tests/`. +Tests are Catch2 v3 and live alongside the library they test in `indra//tests/` directories. A library's test files build into one binary with `al_add_test_binary` (`indra/cmake/ALAddTestBinary.cmake`), on the harness in `indra/test/`, each file still a CTest entry of its own. Run one file by hand as ` "[#]~[.]"`, and add `--order rand` to shuffle. Benchmarks are files of the same binaries (`BENCH_SOURCES`), run by `ctest -L benchmark -V`. How to write a test, and the rules a test binary needs, are in `doc/TESTING.md`. Integration tests are in `indra/integration_tests/`. ## Architecture diff --git a/doc/BUILD.md b/doc/BUILD.md index 6e0d0547adf..054353116db 100644 --- a/doc/BUILD.md +++ b/doc/BUILD.md @@ -435,7 +435,7 @@ ctest --test-dir --output-on-failure -LE manual `-LE manual` leaves out what the build's own test target leaves out: the benchmarks, and each Catch2 binary's run of all its files in one process while that run is still labelled `combined_pending` (`ctest -L combined_pending` runs those). -Unit tests live alongside the library they cover in `indra//tests/`. They are moving from the TUT (Template Unit Test) framework to Catch2 a library at a time: a converted library builds its test files into one binary (`al_add_test_binary`, `indra/cmake/ALAddTestBinary.cmake`), each file still a CTest entry of its own, on the harness in `indra/test/`. Integration tests are in `indra/integration_tests/`. +Unit tests are Catch2 v3, and live alongside the library they cover in `indra//tests/`. A library builds its test files into one binary (`al_add_test_binary`, `indra/cmake/ALAddTestBinary.cmake`), each file still a CTest entry of its own, on the harness in `indra/test/`. [TESTING.md](TESTING.md) says how to run them, how to write one, and what a test binary asks of its files. Integration tests are in `indra/integration_tests/`. ## Editing XUI diff --git a/doc/TESTING.md b/doc/TESTING.md new file mode 100644 index 00000000000..ea7890e670f --- /dev/null +++ b/doc/TESTING.md @@ -0,0 +1,144 @@ +# Testing + +The viewer's tests are [Catch2](https://github.com/catchorg/Catch2) v3, from vcpkg. They live beside the library they test, in `indra//tests/`, and are built only when the build is configured with `-DAL_BUILD_TESTS=ON`. + +A library's test files build into one executable, a *test binary*, declared with `al_add_test_binary` (`indra/cmake/ALAddTestBinary.cmake`). Each file is still a CTest entry of its own, run in a process of its own. The harness every binary links is in `indra/test/`: the main, the state guard, the teardown, the matchers and what an `LL_ERRS` does. + +## Running + +``` +ctest --test-dir build-- --output-on-failure -LE manual +``` + +`-LE manual` is the default run, the one `BUILD_AND_RUN_TESTS` makes. It leaves out the benchmarks and each binary's run of all its files in one process while that run is labelled `combined_pending`. + +- **Some of them:** each entry is labelled with its library, and with `gl`, `python`, `isa`, `slow`, `combined`, `combined_pending` or `benchmark` where it is one. `ctest -L llui`, `ctest -LE gl`. +- **One file:** its entry is `/`, as `ctest -R '^llui_tests/altextview_test$'`. Or run the binary yourself, from the binary's build folder, with the tag the main gives every case from its file's name: `llui_tests "[#altextview_test]~[.]"`. The `~[.]` leaves out the file's hidden cases, such as its benchmarks, which a tag alone would choose. +- **One case:** by its name, `llui_tests "altextview: a squiggle covers what its wave works out to pixel for pixel"`. A comma in a test spec means "or", so a case name never has one. +- **In another order:** `--order rand --rng-seed `. Cases run in declared order by default. +- **Every case in a process of its own:** configure with `-DAL_TEST_PER_CASE=ON`. Each case is then its own entry, found when CTest runs. It is for chasing a case that passes alone and fails with the others. +- **The log:** `LOGTEST=DEBUG` logs everything at that level as it happens. `LOGFAIL=DEBUG` keeps it and prints a failing case's log only. `--al-log` also writes the log to `..log`. + +## Writing a test + +Add the file to the `SOURCES` of its library's binary. Its stem names its entry and its tag, so a stem is unique within a binary, and a file is in one binary. + +```cpp +#include "linden_common.h" + +#include "lldate.h" + +#include + +namespace +{ + TEST_CASE("lldate: a date round-trips through its ISO-8601 text", "[lldate]") + { + const LLDate date("2026-10-14T05:30:00Z"); + CHECK(LLDate(date.asString()) == date); + } +} +``` + +- **Names:** `": "`, unique within the binary, ignoring case, since Catch2 matches names that way and CTest accepts duplicates silently. No commas: write "twenty thousand" for 20,000. +- **Tags:** `[]` on every case, plus `[gl]`, `[python]`, `[slow]` or `[luau-solver]` where they apply. +- **`CHECK` by default:** a case goes on after a failed `CHECK`, so a run reports every failure. Use `REQUIRE` only where going on could only crash or measure nothing: a pointer, an optional, an index, a size that what follows reads through. +- **Saying why:** a scoped `INFO`, or `CAPTURE` for values. An `UNSCOPED_INFO` goes with the next assertion, whichever it is, so one made inside a helper the check calls takes it. Prefer a scoped `INFO` around a block. +- **Matchers** (`altestmatchers.h`): + - `WithinBits(expected, frac_bits)` compares floats as `is_approx_equal_fraction` does; + - `WithinDistance(expected, distance)`; + - `LLSDEquals(expected)` compares all the way down and says where two LLSDs part; + - `MemoryMatches(data, size)`. +- **Fixtures:** `TEST_CASE_METHOD(, ...)`. A fixture is made afresh for every case. A sequence whose steps depend on one another is one case with `SECTION`s. + +### Linkage + +Everything a test file defines goes in an anonymous namespace. A test binary is many files in one executable, and two files' helpers of one name would be one function to the linker. + +- **Types production code befriends by name are the exception.** Each lives in `namespace ll_test`, and is declared there by the header that befriends it: + - `llfontregistry_data` and `llfontregistry_gl_data` (`llrender/llfontregistry.h`); + - `UIImageProbe` (`llrender/lluiimage.h`); + - `VertexBufferProbe` (`llrender/llfonttextcache.h`); + - `RenderProbe` (`llrender/llrender.h`); + - `TextViewProbe` (`llui/text/altextview.h` and `altextruler.h`); + - `alincludesearch_data` (`alscript/preprocessor/alincludesearch.h`). +- **Internal is not enough for LL's name-keyed registries.** `LLSingleton`, `LLInstanceTracker` and `LLThreadLocalSingletonPointer` key on `typeid(T).name()`, which is `(anonymous namespace)::X` for every anonymous `X`. A type used with one of them needs a name unique across the tree. +- **Nothing at namespace scope may do anything.** No settings group, work queue, singleton, window, file or environment variable is made or touched there. Every file's initialisers run in every process of its binary, whichever file the process runs. Make such things in a fixture, or in a function's static in the binary's support library. + +### Fakes + +- **The real class wins over a fake:** declare a real `LLControlGroup` control rather than stubbing `getString`. +- **One home per binary:** where a fake is needed, it lives in the binary's fakes library (`_test_fakes`, a `SUPPORT` object library), one definition of each. Test files hold none. +- **A fake that would replace code another test uses** keeps its test in a binary of its own (`UNIT`, below) until the fake can go. +- **`--allow-multiple-definition`, `/FORCE` and `-z muldefs` are never used.** They are how a fake silently wins. Apple's linker only warns about a duplicate strong symbol under `-dead_strip`, and keeps the first. + +### What a case leaves behind + +The **state guard** compares a set of process facts after every case with what they were before the first. A difference fails that case, by name: "left gFloaterView null", "changed the cwd", "set env AL_X". + +- **Where the checks live:** + - **The harness's own, in every binary:** the working directory, the environment, the floating-point control state, the C locale, the APR pool, the LLTrace master recorder, the `LLApp` and LLError's fatal function. + - **Each library's:** registered in an object library, `_test_checks`, which a binary takes in `SUPPORT`. llcommon's watch the live thread pools and work queues and LLError's default level. llui's watch focus, capture, the floater view and the colour table, and also clear what a frame leaves (popovers, the mortician). +- **Put back what you change, by RAII,** so a failed `REQUIRE` puts it back too. +- **Process-lifetime state a case is allowed to make**, such as a cache filled once or a worker thread made once, is accepted with `ll_test::StateGuard::accept()` as it is made. +- **Process-wide fixtures have one owner and an explicit end.** The headless UI (`ll_test::HeadlessUI::get()`) and the shared GL context (`ll_test::sharedHeadlessGL()`) are one per process. Each ends through `ll_test::Teardown::add`, run by the main while APR and the log are still up, and never in static destruction. + +### Other harness pieces + +- **An `LL_ERRS`** throws `ll_test::FatalError` in a test, so `REQUIRE_THROWS_AS(..., ll_test::FatalError)` checks one. +- **A child process of the test's own making:** register it with `AL_TEST_HELPER(name)`, and launch the binary with `ll_test::Helper::argument(name)`. The main then runs only that helper. +- **The running case's name:** `ll_test::currentTestName()`. + +## Binaries + +``` +al_add_test_binary( PROJECT SOURCES ... + [UNIT ...] [LIBRARIES ...] [SUPPORT ...] + [INCLUDES ...] [DEFINES ...] [ENVIRONMENT ...] + [GL] [PYTHON] [ISA_TIER ] [RESOURCE_LOCK ...] [LAUNCHER ...] + [RERUN RERUN_SOURCES ... RERUN_ENVIRONMENT ...] + [BENCH_SOURCES ...] [COMBINED]) +``` + +A binary is a link set. Its files share their libraries, their fakes and their defines. The comment above the function in `ALAddTestBinary.cmake` is its reference. In short: + +- **`UNIT`** compiles the library's own files into the binary in place of linking the library, for a test that stubs what they call. Such a test is a binary of its own. +- **`SUPPORT`** names object libraries linked whole: fakes and checks. A case or listener in a static archive would be dropped by the linker. +- **`GL`** labels the entries `gl` and registers them disabled with `-DAL_ENABLE_GL_TESTS=OFF`. **`PYTHON`** gives them the interpreter, and registers them disabled without one that has `llsd`. +- **`ISA_TIER`** builds the binary at an x86-64 tier, and it skips on a host below it. +- **`RERUN`** runs some files a second time with more environment. alscript's SLua tests run again under Luau's new solver. +- **`/stray_cases`** fails when a case belongs to no file's entry, such as one written in a header. +- **`/combined`** runs every file in one process. It is labelled `combined_pending`, outside the default run, until the binary's files are known to share a process; `COMBINED` then adds it to the default run. + +## Benchmarks + +A benchmark file goes in its library's binary under `BENCH_SOURCES`. It gets one entry, `bench//`, labelled `benchmark` and `manual`, which `BUILD_AND_RUN_BENCHMARKS` runs (`ctest -L benchmark -V`). Each entry runs alone and reports to the console and to `Testing/benchmarks/_.xml`. + +```cpp +#include "altestbenchmark.h" + +namespace +{ + TEST_CASE("alregex_bench: group members with a date read from each row", "[alregex_bench][!benchmark]") + { + ll_test::skipUnlessOptimised(); + const ALRegex expression("([0-9]{1,2})/([0-9]{1,2})/([0-9]{4})"); + const std::string when = "09/23/2026"; + BENCHMARK("a date's pattern compiled once, re2") + { + ALRegexMatch result; + return expression.match(when, &result) ? result.length(3) : 0; + }; + } +} +``` + +- **Tag every case `[!benchmark]`.** It hides the case from every test run, and `ll_test::skipUnlessOptimised()` skips it outside an optimised build. +- **Return what the body made**, or pass it to `Catch::Benchmark::keep_memory`, so the optimiser cannot drop the work. +- **Setup goes before the `BENCHMARK`.** Catch2 calls the body many times: a warm-up, an estimate, then 100 samples. Each call must measure the same thing, so a body that changes what it measures puts it back, or alternates through a counter it captures: a character typed, then taken away. +- **Keep the measured code's surroundings as the real code has them.** A kernel run over arrays in a static reloads their bounds for every element, since any store might have moved them. Give each call local views. +- **Name each benchmark** for what one call covers: `"setAllDot3, 4,096 elements, cached"`. The comma rule is for case names; benchmark names are not test specs. + +## Upstream tests + +Linden Lab's own tests are TUT. One that arrives in a merge is converted by hand, to the rules above. `scripts/testing/tut2catch.py` did the bulk conversion, but it reads each file through clang with its build flags and TUT's headers, so it runs only in a checkout from before TUT was removed. diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 483e0b6a48d..8f5c96ae839 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -490,7 +490,6 @@ endforeach() # Add our test macros if(AL_BUILD_TESTS) - include(LLAddBuildTest) include(ALAddTestBinary) endif() diff --git a/indra/alscript/preprocessor/alincludesearch.h b/indra/alscript/preprocessor/alincludesearch.h index 0fb58d414fd..1eab6abe9b8 100644 --- a/indra/alscript/preprocessor/alincludesearch.h +++ b/indra/alscript/preprocessor/alincludesearch.h @@ -38,7 +38,7 @@ #include #include -namespace tut { struct alincludesearch_data; } +namespace ll_test { struct alincludesearch_data; } // What finding an include asks of the world a script is in, which only the // viewer can answer -- its inventory, what an object holds -- and a test @@ -240,7 +240,7 @@ class ALIncludeSearch private: // The require parity suite asks the search before LAD9 itself, as a // require found nowhere does (searchedBefore). - friend struct ::tut::alincludesearch_data; + friend struct ::ll_test::alincludesearch_data; // The places a SLua require is walked through, as this search sees // them now (alincludesearch.cpp). diff --git a/indra/alscript/tests/alincludesearch_test.cpp b/indra/alscript/tests/alincludesearch_test.cpp index 181454bd317..befa82b1952 100644 --- a/indra/alscript/tests/alincludesearch_test.cpp +++ b/indra/alscript/tests/alincludesearch_test.cpp @@ -187,7 +187,7 @@ namespace }; } -namespace tut +namespace ll_test { struct alincludesearch_data { diff --git a/indra/cmake/ALAddTestBinary.cmake b/indra/cmake/ALAddTestBinary.cmake index eb0b2263ba0..ddf17e4db3f 100644 --- a/indra/cmake/ALAddTestBinary.cmake +++ b/indra/cmake/ALAddTestBinary.cmake @@ -9,6 +9,17 @@ if(NOT AL_BUILD_TESTS) return() endif() +# The directories a test's shared libraries are staged in, put on the loader's +# search path for the test's run. macOS takes the same directory as an rpath +# instead, since the loader there ignores the environment. +if(WINDOWS) + set(AL_TEST_ENVIRONMENT "PATH=path_list_prepend:${SHARED_LIB_STAGING_DIR}") +elseif(LINUX) + set(AL_TEST_ENVIRONMENT "LD_LIBRARY_PATH=path_list_prepend:${SHARED_LIB_STAGING_DIR}") +else() + set(AL_TEST_ENVIRONMENT "") +endif() + # al_add_test_binary( PROJECT SOURCES ... [UNIT ...] # [LIBRARIES ...] [SUPPORT ...] # [INCLUDES ...] [DEFINES ...] [ENVIRONMENT ...] diff --git a/indra/cmake/Dependencies.cmake b/indra/cmake/Dependencies.cmake index d1636859a9f..9605594d77e 100644 --- a/indra/cmake/Dependencies.cmake +++ b/indra/cmake/Dependencies.cmake @@ -96,12 +96,11 @@ al_import(ll::tinyexr PACKAGE tinyexr CONFIG TARGETS unofficial::ti al_import(ll::vorbis PACKAGE Vorbis CONFIG TARGETS Vorbis::vorbisfile Vorbis::vorbisenc Vorbis::vorbis) al_import(ll::xxhash PACKAGE xxHash CONFIG TARGETS xxHash::xxhash) -# Header-only ports, and the two libraries kept in the tree. +# Header-only ports, and the library kept in the tree. al_import(ll::glext HEADER GL/glcorearb.h) al_import(ll::tinygltf HEADER tiny_gltf.h) al_import(ll::vhacd HEADER VHACD.h) al_import(ll::mikktspace INCLUDE ${INDRA_SOURCE_DIR}/externals/mikktspace/) -al_import(ll::tut INCLUDE ${INDRA_SOURCE_DIR}/externals/tut/) # Per platform, and per option. The target is empty where the condition is # false, so the option gates sources at the consumer and nothing else. diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake deleted file mode 100644 index fde69f0e6a5..00000000000 --- a/indra/cmake/LLAddBuildTest.cmake +++ /dev/null @@ -1,191 +0,0 @@ -# -*- cmake -*- -# -# The test harness. One function declares a TUT test executable and -# registers it with CTest. - -include_guard() - -if(NOT AL_BUILD_TESTS) - return() -endif() - -# The directories a test's shared libraries are staged in, put on the loader's -# search path for the test's run. macOS takes the same directory as an rpath -# instead, since the loader there ignores the environment. -if(WINDOWS) - set(AL_TEST_ENVIRONMENT "PATH=path_list_prepend:${SHARED_LIB_STAGING_DIR}") -elseif(LINUX) - set(AL_TEST_ENVIRONMENT "LD_LIBRARY_PATH=path_list_prepend:${SHARED_LIB_STAGING_DIR}") -else() - set(AL_TEST_ENVIRONMENT "") -endif() - -# al_add_test( PROJECT [UNIT] [DIR ] [PYTHON] [GL] [ISA_TIER ] -# [SOURCES ...] [LIBRARIES ...] [INCLUDES ...] -# [DEFINES ...] [COMMAND ...] [ENVIRONMENT ...]) -# -# Builds tests/_test.cpp into an executable and registers it as a test -# of . -# -# A UNIT test compiles the project's own .cpp into the executable and -# sees the project's include directories instead of linking the project, so -# the test decides what that one file links against; .h is listed with -# it. DIR is the folder of the project the two are in, where they are not at -# its top. Any other test links whatever LIBRARIES names. Both link the TUT runner, -# and a unit test also links llcommon and llmath, the libraries every source -# file in the tree reaches for. -# -# COMMAND runs the test through another program; "{}" stands for the test -# executable and is appended when absent. ENVIRONMENT sets variables for the -# run, VAR=value each. A PYTHON test spawns a Python peer: PYTHON is set to -# the interpreter for its run, and without one that has the llsd module the -# test is registered disabled. A GL test stands up a GL context on a hidden window: it is -# labelled gl, and where AL_ENABLE_GL_TESTS is off it is built and -# registered disabled. -# -# ISA_TIER builds the test for that x86-64 tier (baseline, v2, v3 or v4) -# rather than the tree's, against the tree's libraries: its own sources are -# compiled with that tier's flag and AL_ISA_LEVEL, so a SIMD path can be -# tested at every tier from one configure. The target and test names carry -# _, the test is labelled isa, and it exits 125 -- skipped, to CTest -- -# on a host that cannot run the tier. It links the tree's libraries, which -# were compiled at the tree's tier, so on a host below the tree's tier it -# skips whatever its own tier is. -# -# Targets are PROJECT__TEST_ for a unit test and -# INTEGRATION_TEST_ otherwise; the registered test names are -# PROJECT__TEST_ and INTEGRATION_TEST_RUNNER_. -function(al_add_test name) - cmake_parse_arguments( - PARSE_ARGV 1 - arg - "UNIT;PYTHON;GL" - "PROJECT;ISA_TIER;DIR" - "SOURCES;LIBRARIES;INCLUDES;DEFINES;COMMAND;ENVIRONMENT" - ) - if(NOT arg_PROJECT) - message(FATAL_ERROR "al_add_test(${name}): PROJECT is required") - endif() - if(arg_UNPARSED_ARGUMENTS) - message(FATAL_ERROR "al_add_test(${name}): unexpected arguments: ${arg_UNPARSED_ARGUMENTS}") - endif() - set(suffix "") - if(arg_ISA_TIER) - if(NOT arg_ISA_TIER MATCHES "^(baseline|v2|v3|v4)$") - message( - FATAL_ERROR - "al_add_test(${name}): ISA_TIER must be baseline, v2, v3 or v4, not ${arg_ISA_TIER}" - ) - endif() - set(suffix "_${arg_ISA_TIER}") - endif() - - if(arg_UNIT) - set(target PROJECT_${arg_PROJECT}_TEST_${name}${suffix}) - set(test_name ${target}) - # The unit's own file, in the folder of the project it is in where DIR - # says, beside its test. - set(dir "") - if(arg_DIR) - set(dir "${arg_DIR}/") - endif() - set(sources ${dir}${name}.cpp tests/${name}_test.cpp ${arg_SOURCES} ${dir}${name}.h) - set(libraries lltut_runner_lib llcommon ll::tut) - if(NOT arg_PROJECT STREQUAL "llmath") - list(APPEND libraries llmath) - endif() - else() - set(target INTEGRATION_TEST_${name}${suffix}) - set(test_name INTEGRATION_TEST_RUNNER_${name}${suffix}) - set(sources tests/${name}_test.cpp ${arg_SOURCES}) - set(libraries lltut_runner_lib ll::tut) - endif() - - # vcpkg's toolchain hangs an app-local copy step on every executable it - # sees declared. A test runs from the build tree with its libraries on the - # search path above, so the step has nothing to copy, and launching vcpkg - # once per test fails now and then under a parallel build when the - # executable is already open. Off for the scope of this declaration. - set(VCPKG_APPLOCAL_DEPS OFF) - add_executable(${target} ${sources}) - target_link_libraries(${target} PRIVATE al::flags ${libraries} ${arg_LIBRARIES}) - if(arg_UNIT) - # The project's include directories, since the test compiles one of its - # files without linking it. - get_property(project_includes TARGET ${arg_PROJECT} PROPERTY INTERFACE_INCLUDE_DIRECTORIES) - target_include_directories(${target} PRIVATE ${project_includes}) - endif() - target_include_directories( - ${target} - PRIVATE - ${arg_INCLUDES} - ${INDRA_SOURCE_DIR}/test - ${INDRA_SOURCE_DIR}/llmath - # llui's own folders, which its top-level headers name theirs from. - $ - ) - target_compile_definitions(${target} PRIVATE "LL_TEST=${name}" "LL_TEST_${name}" ${arg_DEFINES}) - set_target_properties(${target} PROPERTIES FOLDER "Tests/${arg_PROJECT}") - if(arg_ISA_TIER) - set_target_properties(${target} PROPERTIES AL_ISA_TIER ${arg_ISA_TIER}) - endif() - - if(WINDOWS) - set_target_properties(${target} PROPERTIES AL_SKIP_RELEASE_DEBUG_INFO ON) - elseif(DARWIN) - # A test binary is run straight from the build tree, so it is ad-hoc signed. - set_target_properties( - ${target} - PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "-" BUILD_RPATH "${SHARED_LIB_STAGING_DIR}" - ) - endif() - - set(command ${arg_COMMAND}) - list(FIND command "{}" executable_position) - if(executable_position LESS 0) - list(APPEND command "$") - else() - list(REMOVE_AT command ${executable_position}) - list(INSERT command ${executable_position} "$") - endif() - set(environment ${arg_ENVIRONMENT}) - set(labels) - set(disabled FALSE) - # Five minutes: the slowest test without a Python peer runs in under 15 - # seconds. One with a peer gets fifteen; the slowest of those runs in under - # 40. CTest's own default, 25 minutes, holds a hung test longer than the - # rest of a CI run takes. - set(timeout 300) - if(arg_ISA_TIER) - al_isa_level(${arg_ISA_TIER} ${CMAKE_SYSTEM_NAME} ${ARCH} level) - list(APPEND command "--isa-level=${level}") - list(APPEND labels isa) - endif() - if(arg_PYTHON) - set(timeout 900) - if(AL_PYTHON_PEERS) - list(APPEND environment "PYTHON=${Python3_EXECUTABLE}") - else() - set(disabled TRUE) - endif() - endif() - if(arg_GL) - list(APPEND labels gl) - if(NOT AL_ENABLE_GL_TESTS) - set(disabled TRUE) - endif() - endif() - add_test(NAME ${test_name} COMMAND ${command} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - set_tests_properties( - ${test_name} - PROPERTIES - ENVIRONMENT "${environment}" - ENVIRONMENT_MODIFICATION "${AL_TEST_ENVIRONMENT}" - LABELS "${labels}" - DISABLED ${disabled} - SKIP_RETURN_CODE 125 - TIMEOUT ${timeout} - ) - - add_dependencies(BUILD_TESTS ${target}) -endfunction() diff --git a/indra/externals/README.md b/indra/externals/README.md index 7f7774aa07e..64930abb73f 100644 --- a/indra/externals/README.md +++ b/indra/externals/README.md @@ -4,4 +4,3 @@ This directory contains vendored external resources which are unsuited for vcpkg * js - ThreeJS and JPEGEncoder * mikktspace - Blender Mikktspace - code relatively static and unchanging -* tut - TUT unit testing library - Upstream inactive \ No newline at end of file diff --git a/indra/externals/tut/LICENSE.txt b/indra/externals/tut/LICENSE.txt deleted file mode 100644 index bcd715a17b5..00000000000 --- a/indra/externals/tut/LICENSE.txt +++ /dev/null @@ -1,10 +0,0 @@ - Copyright 2002-2006 Vladimir Dyuzhev -Copyright 2007 Denis Kononenko -Copyright 2008 Micha? Rzechonek - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/indra/externals/tut/tut/tut.hpp b/indra/externals/tut/tut/tut.hpp deleted file mode 100644 index a7245e2206c..00000000000 --- a/indra/externals/tut/tut/tut.hpp +++ /dev/null @@ -1,575 +0,0 @@ -#ifndef TUT_H_GUARD -#define TUT_H_GUARD - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// these headers have all sorts of (what clang thinks are) unused functions -// -Wunused-function warnings and therefore errors on the clang (apple) build -// Leaving it as for clang only because if we ever switch to clang on other -// platforms the same issue will manifest itself. -#if __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" - #include "tut_exception.hpp" - #include "tut_result.hpp" - #include "tut_posix.hpp" - #include "tut_assert.hpp" - #include "tut_runner.hpp" -#pragma clang diagnostic pop -#else - #include "tut_exception.hpp" - #include "tut_result.hpp" - #include "tut_posix.hpp" - #include "tut_assert.hpp" - #include "tut_runner.hpp" -#endif - -#if defined(TUT_USE_SEH) -#include -#include -#endif - -/** - * Template Unit Tests Framework for C++. - * http://tut.dozen.ru - * - * @author Vladimir Dyuzhev, Vladimir.Dyuzhev@gmail.com - */ -namespace tut -{ - -/** - * Test object. Contains data test run upon and default test method - * implementation. Inherited from Data to allow tests to - * access test data as members. - */ -template -class test_object : public Data, public test_object_posix -{ -public: - - /** - * Default constructor - */ - test_object() - { - } - - void set_test_name(const std::string& current_test_name) - { - current_test_name_ = current_test_name; - } - - const std::string& get_test_name() const - { - return current_test_name_; - } - - /** - * Default do-nothing test. - */ - template - void test() - { - called_method_was_a_dummy_test_ = true; - } - - /** - * The flag is set to true by default (dummy) test. - * Used to detect usused test numbers and avoid unnecessary - * test object creation which may be time-consuming depending - * on operations described in Data::Data() and Data::~Data(). - * TODO: replace with throwing special exception from default test. - */ - bool called_method_was_a_dummy_test_; - -private: - std::string current_test_name_; -}; - - -/** - * Walks through test tree and stores address of each - * test method in group. Instantiation stops at 0. - */ -template -struct tests_registerer -{ - static void reg(Group& group) - { - group.reg(n, &Test::template test); - tests_registerer::reg(group); - } -}; - -template -struct tests_registerer -{ - static void reg(Group&) - { - } -}; - -/** - * Test group; used to recreate test object instance for - * each new test since we have to have reinitialized - * Data base class. - */ -template -class test_group : public group_base, public test_group_posix -{ - const char* name_; - - typedef void (test_object::*testmethod)(); - typedef std::map tests; - typedef typename tests::iterator tests_iterator; - typedef typename tests::const_iterator tests_const_iterator; - typedef typename tests::const_reverse_iterator - tests_const_reverse_iterator; - typedef typename tests::size_type size_type; - - tests tests_; - tests_iterator current_test_; - - /** - * Exception-in-destructor-safe smart-pointer class. - */ - template - class safe_holder - { - T* p_; - bool permit_throw_in_dtor; - - safe_holder(const safe_holder&); - safe_holder& operator=(const safe_holder&); - - public: - safe_holder() - : p_(0), - permit_throw_in_dtor(false) - { - } - - ~safe_holder() - { - release(); - } - - T* operator->() const - { - return p_; - } - - T* get() const - { - return p_; - } - - /** - * Tell ptr it can throw from destructor. Right way is to - * use std::uncaught_exception(), but some compilers lack - * correct implementation of the function. - */ - void permit_throw() - { - permit_throw_in_dtor = true; - } - - /** - * Specially treats exceptions in test object destructor; - * if test itself failed, exceptions in destructor - * are ignored; if test was successful and destructor failed, - * warning exception throwed. - */ - void release() - { - try - { - if (delete_obj() == false) - { - boost::throw_exception(warning("destructor of test object raised" - " an SEH exception")); - } - } - catch (const std::exception& ex) - { - if (permit_throw_in_dtor) - { - std::string msg = "destructor of test object raised" - " exception: "; - msg += ex.what(); - boost::throw_exception(warning(msg)); - } - } - catch( ... ) - { - if (permit_throw_in_dtor) - { - boost::throw_exception(warning("destructor of test object raised an" - " exception")); - } - } - } - - /** - * Re-init holder to get brand new object. - */ - void reset() - { - release(); - permit_throw_in_dtor = false; - p_ = new T(); - } - - bool delete_obj() - { -#if defined(TUT_USE_SEH) - __try - { -#endif - T* p = p_; - p_ = 0; - delete p; -#if defined(TUT_USE_SEH) - } - __except(handle_seh_(::GetExceptionCode())) - { - if (permit_throw_in_dtor) - { - return false; - } - } -#endif - return true; - } - }; - -public: - - typedef test_object object; - - /** - * Creates and registers test group with specified name. - */ - test_group(const char* name) - : name_(name) - { - // register itself - runner.get().register_group(name_,this); - - // register all tests - tests_registerer::reg(*this); - } - - /** - * This constructor is used in self-test run only. - */ - test_group(const char* name, test_runner& another_runner) - : name_(name) - { - // register itself - another_runner.register_group(name_, this); - - // register all tests - tests_registerer, test_group, - MaxTestsInGroup>::reg(*this); - }; - - /** - * Registers test method under given number. - */ - void reg(int n, testmethod tm) - { - tests_[n] = tm; - } - - /** - * Reset test position before first test. - */ - void rewind() - { - current_test_ = tests_.begin(); - } - - /** - * Runs next test. - */ - test_result run_next() - { - if (current_test_ == tests_.end()) - { - boost::throw_exception(no_more_tests()); - } - - // find next user-specialized test - safe_holder obj; - while (current_test_ != tests_.end()) - { - try - { - tests_iterator current_test = current_test_++; - - test_result tr = run_test_(current_test, obj); - - return tr; - } - catch (const no_such_test&) - { - continue; - } - } - - boost::throw_exception(no_more_tests()); - } - - /** - * Runs one test by position. - */ - test_result run_test(int n) - { - // beyond tests is special case to discover upper limit - if (tests_.rbegin() == tests_.rend()) - { - boost::throw_exception(beyond_last_test()); - } - - if (tests_.rbegin()->first < n) - { - boost::throw_exception(beyond_last_test()); - } - - // withing scope; check if given test exists - tests_iterator ti = tests_.find(n); - if (ti == tests_.end()) - { - boost::throw_exception(no_such_test()); - } - - safe_holder obj; - test_result tr = run_test_(ti, obj); - - return tr; - } - - - /** - * VC allows only one exception handling type per function, - * so I have to split the method. - */ - test_result run_test_(const tests_iterator& ti, safe_holder& obj) - { - std::string current_test_name; - - test_result tr(name_, ti->first, current_test_name, test_result::ok); - - try - { - if (run_test_seh_(ti->second, obj, current_test_name) == false) - { - boost::throw_exception(seh("seh")); - } - } - catch (const no_such_test&) - { - throw; - } - catch (const rethrown& ex) - { - tr = ex.tr; - tr.result = test_result::rethrown; - } - catch (const tut_error& ex) - { - tr.result = ex.result(); - tr.exception_typeid = typeid(ex).name(); - tr.message = ex.what(); - } - catch (const boost::exception& ex) - { - const tut_error* tutex = dynamic_cast(&ex); - const std::exception* stdex = dynamic_cast(&ex); - tr.exception_typeid = typeid(ex).name(); - if (tutex) - { - tr.result = tutex->result(); - tr.message = tutex->what(); - } - else - { - tr.result = test_result::ex; - if (stdex) - { - tr.message = stdex->what(); - } - else - { - tr.message = boost::diagnostic_information(ex); - } - } - } - catch (const std::exception& ex) - { - tr.result = test_result::ex; - tr.exception_typeid = typeid(ex).name(); - tr.message = ex.what(); - } - catch (...) - { - const tut_error* tutex = boost::current_exception_cast(); - const std::exception* stdex = boost::current_exception_cast(); - // test failed with unknown exception - tr.result = test_result::ex; - if (tutex) - { - tr.result = tutex->result(); - tr.exception_typeid = typeid(*tutex).name(); - tr.message = tutex->what(); - } - else if (stdex) - { - tr.exception_typeid = typeid(*stdex).name(); - tr.message = stdex->what(); - } - else - { - tr.message = boost::current_exception_diagnostic_information(); - } - } - - if (obj.get()) - { - tr.name = obj->get_test_name(); - - // try to report to parent, if exists - send_result_(obj.get(), tr); - } - else - { - tr.name = current_test_name; - } - - return tr; - } - - /** - * Runs one under SEH if platform supports it. - */ - bool run_test_seh_(testmethod tm, safe_holder& obj, - std::string& current_test_name) - { -#if defined(TUT_USE_SEH) - __try - { -#endif - if (obj.get() == 0) - { - reset_holder_(obj); - } - - obj->called_method_was_a_dummy_test_ = false; - -#if defined(TUT_USE_SEH) - - __try - { -#endif - (obj.get()->*tm)(); -#if defined(TUT_USE_SEH) - } - __except(handle_seh_(::GetExceptionCode())) - { - // throw seh("SEH"); - current_test_name = obj->get_test_name(); - return false; - } -#endif - - if (obj->called_method_was_a_dummy_test_) - { - // do not call obj.release(); reuse object - boost::throw_exception(no_such_test()); - } - - current_test_name = obj->get_test_name(); - obj.permit_throw(); - obj.release(); -#if defined(TUT_USE_SEH) - } - __except(handle_seh_(::GetExceptionCode())) - { - return false; - } -#endif - return true; - } - - void reset_holder_(safe_holder& obj) - { - try - { - obj.reset(); - } - catch (const std::exception& ex) - { - boost::throw_exception(bad_ctor(ex.what())); - } - catch (...) - { - boost::throw_exception(bad_ctor("test constructor has generated an exception;" - " group execution is terminated")); - } - } -}; - -#if defined(TUT_USE_SEH) -/** - * Decides should we execute handler or ignore SE. - */ -inline int handle_seh_(DWORD excode) -{ - switch(excode) - { - case EXCEPTION_ACCESS_VIOLATION: - case EXCEPTION_DATATYPE_MISALIGNMENT: - case EXCEPTION_BREAKPOINT: - case EXCEPTION_SINGLE_STEP: - case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: - case EXCEPTION_FLT_DENORMAL_OPERAND: - case EXCEPTION_FLT_DIVIDE_BY_ZERO: - case EXCEPTION_FLT_INEXACT_RESULT: - case EXCEPTION_FLT_INVALID_OPERATION: - case EXCEPTION_FLT_OVERFLOW: - case EXCEPTION_FLT_STACK_CHECK: - case EXCEPTION_FLT_UNDERFLOW: - case EXCEPTION_INT_DIVIDE_BY_ZERO: - case EXCEPTION_INT_OVERFLOW: - case EXCEPTION_PRIV_INSTRUCTION: - case EXCEPTION_IN_PAGE_ERROR: - case EXCEPTION_ILLEGAL_INSTRUCTION: - case EXCEPTION_NONCONTINUABLE_EXCEPTION: - case EXCEPTION_STACK_OVERFLOW: - case EXCEPTION_INVALID_DISPOSITION: - case EXCEPTION_GUARD_PAGE: - case EXCEPTION_INVALID_HANDLE: - return EXCEPTION_EXECUTE_HANDLER; - }; - - return EXCEPTION_CONTINUE_SEARCH; -} -#endif -} - -#endif - diff --git a/indra/externals/tut/tut/tut_assert.hpp b/indra/externals/tut/tut/tut_assert.hpp deleted file mode 100644 index 361307aef97..00000000000 --- a/indra/externals/tut/tut/tut_assert.hpp +++ /dev/null @@ -1,168 +0,0 @@ -#ifndef TUT_ASSERT_H_GUARD -#define TUT_ASSERT_H_GUARD - -#include "tut_exception.hpp" -#include - -#if defined(TUT_USE_POSIX) -#include -#include -#endif - -namespace tut -{ - -namespace -{ - -void fail(const std::string &msg); - -/** - * Tests provided condition. - * Throws if false. - */ -void ensure(bool cond) -{ - if (!cond) - { - // TODO: default ctor? - fail(""); - } -} - -/** - * Tests provided condition. - * Throws if true. - */ -void ensure_not(bool cond) -{ - ensure(!cond); -} - -/** - * Tests provided condition. - * Throws if false. - */ -template -void ensure(const T msg, bool cond) -{ - if (!cond) - { - fail(msg); - } -} - -/** - * Tests provided condition. - * Throws if true. - */ -template -void ensure_not(const T msg, bool cond) -{ - ensure(msg, !cond); -} - -/** - * Tests two objects for being equal. - * Throws if false. - * - * NB: both T and Q must have operator << defined somewhere, or - * client code will not compile at all! - */ -template -void ensure_equals(const std::string& msg, const Q& actual, const T& expected) -{ - if (expected != actual) - { - std::ostringstream ss; - if (! msg.empty()) - ss << msg << ": "; - ss << "expected '" - << expected - << "' actual '" - << actual - << '\''; - fail(ss.str()); - } -} - -template -void ensure_equals(const Q& actual, const T& expected) -{ - ensure_equals("", actual, expected); -} - -/** - * Tests two objects for being at most in given distance one from another. - * Borders are excluded. - * Throws if false. - * - * NB: T must have operator << defined somewhere, or - * client code will not compile at all! Also, T shall have - * operators + and -, and be comparable. - */ -template -void ensure_distance(const char* msg, const T& actual, const T& expected, - const T& distance) -{ - if (expected-distance >= actual || expected+distance <= actual) - { - std::stringstream ss; - ss << (msg ? msg : "") - << (msg? ":" : "") - << " expected (" - << expected-distance - << " - " - << expected+distance - << ") actual '" - << actual - << '\''; - fail(ss.str()); - } -} - -template -void ensure_distance(const T& actual, const T& expected, const T& distance) -{ - ensure_distance<>(0, actual, expected, distance); -} - -void ensure_errno(const char *msg, bool cond) -{ - if(!cond) - { -#if defined(TUT_USE_POSIX) - char e[512]; - std::stringstream ss; - ss << (msg ? msg : "") - << (msg? ": " : "") - << strerror_r(errno, e, sizeof(e)); - fail(ss.str()); -#else - fail(msg); -#endif - } -} - -/** - * Unconditionally fails with message. - */ -void fail(const std::string &msg) -{ - boost::throw_exception(failure(msg)); -} - -/** - * Skip test because of known failure. - */ -void skip(const std::string& msg) -{ - boost::throw_exception(skip_failure(msg)); -} - -} // end of namespace - -} - -#endif - diff --git a/indra/externals/tut/tut/tut_exception.hpp b/indra/externals/tut/tut/tut_exception.hpp deleted file mode 100644 index 85e8f0a7873..00000000000 --- a/indra/externals/tut/tut/tut_exception.hpp +++ /dev/null @@ -1,228 +0,0 @@ -#ifndef TUT_EXCEPTION_H_GUARD -#define TUT_EXCEPTION_H_GUARD - -#include -#include "tut_result.hpp" - -namespace tut -{ - -/** - * The base for all TUT exceptions. - */ -struct tut_error : public std::exception -{ - tut_error(const std::string& msg) - : err_msg(msg) - { - } - - virtual test_result::result_type result() const - { - return test_result::ex; - } - - const char* what() const throw() - { - return err_msg.c_str(); - } - - ~tut_error() throw() - { - } - -private: - - std::string err_msg; -}; - -/** - * Exception to be throwed when attempted to execute - * missed test by number. - */ -struct no_such_test : public tut_error -{ - no_such_test() - : tut_error("no such test") - { - } - - ~no_such_test() throw() - { - } -}; - -/** - * No such test and passed test number is higher than - * any test number in current group. Used in one-by-one - * test running when upper bound is not known. - */ -struct beyond_last_test : public no_such_test -{ - beyond_last_test() - { - } - - ~beyond_last_test() throw() - { - } -}; - -/** - * Group not found exception. - */ -struct no_such_group : public tut_error -{ - no_such_group(const std::string& grp) - : tut_error(grp) - { - } - - ~no_such_group() throw() - { - } -}; - -/** - * Internal exception to be throwed when - * no more tests left in group or journal. - */ -struct no_more_tests: public std::exception -{ - no_more_tests() - { - } - - const char* what() const throw() { return "no more tests"; } - - ~no_more_tests() throw() - { - } -}; - -/** - * Internal exception to be throwed when - * test constructor has failed. - */ -struct bad_ctor : public tut_error -{ - bad_ctor(const std::string& msg) - : tut_error(msg) - { - } - - test_result::result_type result() const - { - return test_result::ex_ctor; - } - - ~bad_ctor() throw() - { - } -}; - -/** - * Exception to be throwed when ensure() fails or fail() called. - */ -struct failure : public tut_error -{ - failure(const std::string& msg) - : tut_error(msg) - { - } - - test_result::result_type result() const - { - return test_result::fail; - } - - ~failure() throw() - { - } -}; - -/** - * Exception to be throwed when test desctructor throwed an exception. - */ -struct warning : public tut_error -{ - warning(const std::string& msg) - : tut_error(msg) - { - } - - test_result::result_type result() const - { - return test_result::warn; - } - - ~warning() throw() - { - } -}; - -/** - * Exception to be throwed when test issued SEH (Win32) - */ -struct seh : public tut_error -{ - seh(const std::string& msg) - : tut_error(msg) - { - } - - virtual test_result::result_type result() const - { - return test_result::term; - } - - ~seh() throw() - { - } -}; - -/** - * Exception to be throwed when child processes fail. - */ -struct rethrown : public failure -{ - explicit rethrown(const test_result &result) - : failure(result.message), tr(result) - { - } - - virtual test_result::result_type result() const - { - return test_result::rethrown; - } - - ~rethrown() throw() - { - } - - const test_result tr; -}; - -/** - * Exception to be throwed when skip_fail() is called. - */ -struct skip_failure : public failure -{ - skip_failure(const std::string& msg) - : failure(msg) - { - } - - test_result::result_type result() const - { - return test_result::skip; - } - - ~skip_failure() throw() - { - } -}; - -} - -#endif diff --git a/indra/externals/tut/tut/tut_posix.hpp b/indra/externals/tut/tut/tut_posix.hpp deleted file mode 100644 index 80643143f30..00000000000 --- a/indra/externals/tut/tut/tut_posix.hpp +++ /dev/null @@ -1,451 +0,0 @@ -#ifndef TUT_FORK_H_GUARD -#define TUT_FORK_H_GUARD - -#if defined(TUT_USE_POSIX) -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "tut_result.hpp" -#include "tut_assert.hpp" -#include "tut_runner.hpp" - -namespace tut -{ - -template -class test_group; - -template -class test_object; - -class test_group_posix -{ -private: - template - friend class test_group; - - template - void send_result_(const T *obj, const test_result &tr) - { - if(obj->get_pipe_() == -1) - { - return; - } - - if(tr.result != test_result::ok) - { - std::stringstream ss; - ss << int(tr.result) << "\n" - << tr.group << "\n" - << tr.test << "\n" - << tr.name << "\n" - << tr.exception_typeid << "\n"; - std::copy( tr.message.begin(), tr.message.end(), std::ostreambuf_iterator(ss.rdbuf()) ); - - int size = ss.str().length(); - int w = write(obj->get_pipe_(), ss.str().c_str(), size); - ensure_errno("write() failed", w == size); - } - } -}; - -template -struct tut_posix -{ - pid_t fork() - { - test_object *self = dynamic_cast< tut::test_object* >(this); - ensure("trying to call 'fork' in ctor of test object", self != NULL); - - return self->fork_(); - } - - void ensure_child_exit(pid_t pid, int exit_status = 0) - { - test_object *self = dynamic_cast< tut::test_object* >(this); - ensure("trying to call 'ensure_child_exit' in ctor of test object", self != NULL); - - int status; - self->waitpid_(pid, &status); - - self->ensure_child_exit_(status, exit_status); - } - - - void ensure_child_signal(pid_t pid, int signal = SIGTERM) - { - test_object *self = dynamic_cast< tut::test_object* >(this); - ensure("trying to call 'ensure_child_signal' in ctor of test object", self != NULL); - - int status; - self->waitpid_(pid, &status); - - self->ensure_child_signal_(status, signal); - } - - std::set get_pids() const - { - using namespace std; - - const test_object *self = dynamic_cast< const tut::test_object* >(this); - ensure("trying to call 'get_pids' in ctor of test object", self != NULL); - - return self->get_pids_(); - } - - virtual ~tut_posix() - { - } - -}; - -class test_object_posix -{ -public: - typedef std::map pid_map; - - /** - * Default constructor - */ - test_object_posix() - : pipe_(-1) - { - } - - - virtual ~test_object_posix() - { - // we have forked - if(pipe_ != -1) - { - // in child, force exit - std::exit(0); - } - - if(!pids_.empty()) - { - std::stringstream ss; - - // in parent, reap children - for(std::map::iterator i = pids_.begin(); i != pids_.end(); ++i) - { - try { - kill_child_(i->first); - } catch(const rethrown &ex) { - ss << std::endl << "child " << ex.tr.pid << " has thrown an exception: " << ex.what(); - } catch(const failure &ex) { - ss << std::endl << ex.what(); - } - } - - if(!ss.str().empty()) - { - fail(ss.str().c_str()); - } - } - } - -private: - template - friend class tut_posix; - - friend class test_group_posix; - - int get_pipe_() const - { - return pipe_; - } - - - pid_t fork_() - { - // create pipe - int fds[2]; - ensure_errno("pipe() failed", ::pipe(fds) == 0); - - pid_t pid = ::fork(); - - ensure_errno("fork() failed", pid >= 0); - - if(pid != 0) - { - // in parent, register pid - ensure("duplicated child", pids_.insert( std::make_pair(pid, fds[0]) ).second); - - // close writing side - close(fds[1]); - } - else - { - // in child, shutdown reporter - tut::runner.get().set_callback(NULL); - - // close reading side - close(fds[0]); - pipe_ = fds[1]; - } - - return pid; - } - - void kill_child_(pid_t pid) - { - int status; - - if(waitpid_(pid, &status, WNOHANG) == pid) - { - ensure_child_exit_(status, 0); - return; - } - - if(::kill(pid, SIGTERM) != 0) - { - if(errno == ESRCH) - { - // no such process - return; - } - else - { - // cannot kill, we are in trouble - std::stringstream ss; - char e[1024]; - ss << "child " << pid << " could not be killed with SIGTERM, " << strerror_r(errno, e, sizeof(e)) << std::endl; - fail(ss.str()); - } - } - - if(waitpid_(pid, &status, WNOHANG) == pid) - { - // child killed, check signal - ensure_child_signal_(status, SIGTERM); - - ensure_equals("child process exists after SIGTERM", ::kill(pid, 0), -1); - return; - } - - // child seems to be still exiting, give it some time - sleep(2); - - if(waitpid_(pid, &status, WNOHANG) != pid) - { - // child is still running, kill it - if(::kill(pid, SIGKILL) != 0) - { - if(errno == ESRCH) - { - // no such process - return; - } - else - { - std::stringstream ss; - char e[1024]; - ss << "child " << pid << " could not be killed with SIGKILL, " << strerror_r(errno, e, sizeof(e)) << std::endl; - fail(ss.str()); - } - } - - ensure_equals("wait after SIGKILL", waitpid_(pid, &status), pid); - ensure_child_signal_(status, SIGKILL); - - ensure_equals("child process exists after SIGKILL", ::kill(pid, 0), -1); - - std::stringstream ss; - ss << "child " << pid << " had to be killed with SIGKILL"; - fail(ss.str()); - } - } - - test_result receive_result_(std::istream &ss, pid_t pid) - { - test_result tr; - - int type; - ss >> type; - tr.result = test_result::result_type(type); - ss.ignore(1024, '\n'); - - std::getline(ss, tr.group); - ss >> tr.test; - ss.ignore(1024, '\n'); - std::getline(ss, tr.name); - std::getline(ss, tr.exception_typeid); - std::copy( std::istreambuf_iterator(ss.rdbuf()), - std::istreambuf_iterator(), - std::back_inserter(tr.message) ); - - tr.pid = pid; - - return tr; - } - - pid_t waitpid_(pid_t pid, int *status, int flags = 0) - { - ensure("trying to wait for unknown pid", pids_.count(pid) > 0); - - pid_t p = ::waitpid(pid, status, flags); - if( (flags & WNOHANG) && (p != pid) ) - { - return p; - } - - // read child result from pipe - fd_set fdset; - timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 0; - - FD_ZERO(&fdset); - - int pipe = pids_[pid]; - FD_SET(pipe, &fdset); - - int result = select(pipe+1, &fdset, NULL, NULL, &tv); - ensure_errno("sanity check on select() failed", result >= 0); - - if(result > 0) - { - ensure("sanity check on FD_ISSET() failed", FD_ISSET(pipe, &fdset) ); - - std::stringstream ss; - - //TODO: max failure length - char buffer[1024]; - int r = read(pipe, buffer, sizeof(buffer)); - ensure_errno("sanity check on read() failed", r >= 0); - - if(r > 0) - { - ss.write(buffer, r); - boost::throw_exception(rethrown( receive_result_(ss, pid) )); - } - } - - return pid; - } - - void ensure_child_exit_(int status, int exit_status) - { - if(WIFSIGNALED(status)) - { - std::stringstream ss; - ss << "child killed by signal " << WTERMSIG(status) - << ": expected exit with code " << exit_status; - - fail(ss.str().c_str()); - } - - if(WIFEXITED(status)) - { - if(WEXITSTATUS(status) != exit_status) - { - std::stringstream ss; - ss << "child exited, expected '" - << exit_status - << "' actual '" - << WEXITSTATUS(status) - << '\''; - - fail(ss.str().c_str()); - } - } - - if(WIFSTOPPED(status)) - { - std::stringstream ss; - ss << "child stopped by signal " << WTERMSIG(status) - << ": expected exit with code " << exit_status; - fail(ss.str().c_str()); - } - } - - void ensure_child_signal_(int status, int signal) - { - if(WIFSIGNALED(status)) - { - if(WTERMSIG(status) != signal) - { - std::stringstream ss; - ss << "child killed by signal, expected '" - << signal - << "' actual '" - << WTERMSIG(status) - << '\''; - fail(ss.str().c_str()); - } - } - - if(WIFEXITED(status)) - { - std::stringstream ss; - ss << "child exited with code " << WEXITSTATUS(status) - << ": expected signal " << signal; - - fail(ss.str().c_str()); - } - - if(WIFSTOPPED(status)) - { - std::stringstream ss; - ss << "child stopped by signal " << WTERMSIG(status) - << ": expected kill by signal " << signal; - - fail(ss.str().c_str()); - } - } - - std::set get_pids_() const - { - using namespace std; - - set pids; - for(pid_map::const_iterator i = pids_.begin(); i != pids_.end(); ++i) - { - pids.insert( i->first ); - } - - return pids; - } - - pid_map pids_; - int pipe_; -}; - -} // namespace tut - -#else - -namespace tut -{ - -struct test_object_posix -{ -}; - -struct test_group_posix -{ - template - void send_result_(const T*, const test_result &) - { - } -}; - -} // namespace tut - -#endif - - -#endif - diff --git a/indra/externals/tut/tut/tut_reporter.hpp b/indra/externals/tut/tut/tut_reporter.hpp deleted file mode 100644 index 8422bec92b4..00000000000 --- a/indra/externals/tut/tut/tut_reporter.hpp +++ /dev/null @@ -1,243 +0,0 @@ -#ifndef TUT_REPORTER -#define TUT_REPORTER - -#include - -/** - * Template Unit Tests Framework for C++. - * http://tut.dozen.ru - * - * @author Vladimir Dyuzhev, Vladimir.Dyuzhev@gmail.com - */ -namespace -{ - -std::ostream& operator<<(std::ostream& os, const tut::test_result& tr) -{ - switch(tr.result) - { - case tut::test_result::ok: - os << '.'; - break; - case tut::test_result::fail: - os << '[' << tr.test << "=F]"; - break; - case tut::test_result::ex_ctor: - os << '[' << tr.test << "=C]"; - break; - case tut::test_result::ex: - os << '[' << tr.test << "=X]"; - break; - case tut::test_result::warn: - os << '[' << tr.test << "=W]"; - break; - case tut::test_result::term: - os << '[' << tr.test << "=T]"; - break; - case tut::test_result::rethrown: - os << '[' << tr.test << "=P]"; - break; - } - - return os; -} - -} // end of namespace - -namespace tut -{ - -/** - * Default TUT callback handler. - */ -class reporter : public tut::callback -{ - std::string current_group; - typedef std::vector not_passed_list; - not_passed_list not_passed; - std::ostream& os; - -public: - - int ok_count; - int exceptions_count; - int failures_count; - int terminations_count; - int warnings_count; - - reporter() - : os(std::cout) - { - init(); - } - - reporter(std::ostream& out) - : os(out) - { - init(); - } - - void run_started() - { - init(); - } - - void test_completed(const tut::test_result& tr) - { - if (tr.group != current_group) - { - os << std::endl << tr.group << ": " << std::flush; - current_group = tr.group; - } - - os << tr << std::flush; - if (tr.result == tut::test_result::ok) - { - ok_count++; - } - else if (tr.result == tut::test_result::ex) - { - exceptions_count++; - } - else if (tr.result == tut::test_result::ex_ctor) - { - exceptions_count++; - } - else if (tr.result == tut::test_result::fail) - { - failures_count++; - } - else if (tr.result == tut::test_result::rethrown) - { - failures_count++; - } - else if (tr.result == tut::test_result::warn) - { - warnings_count++; - } - else - { - terminations_count++; - } - - if (tr.result != tut::test_result::ok) - { - not_passed.push_back(tr); - } - } - - void run_completed() - { - os << std::endl; - - if (not_passed.size() > 0) - { - not_passed_list::const_iterator i = not_passed.begin(); - while (i != not_passed.end()) - { - tut::test_result tr = *i; - - os << std::endl; - - os << "---> " << "group: " << tr.group - << ", test: test<" << tr.test << ">" - << (!tr.name.empty() ? (std::string(" : ") + tr.name) : std::string()) - << std::endl; - -#if defined(TUT_USE_POSIX) - if(tr.pid != getpid()) - { - os << " child pid: " << tr.pid << std::endl; - } -#endif - os << " problem: "; - switch(tr.result) - { - case test_result::rethrown: - os << "assertion failed in child" << std::endl; - break; - case test_result::fail: - os << "assertion failed" << std::endl; - break; - case test_result::ex: - case test_result::ex_ctor: - os << "unexpected exception" << std::endl; - if( tr.exception_typeid != "" ) - { - os << " exception typeid: " - << tr.exception_typeid << std::endl; - } - break; - case test_result::term: - os << "would be terminated" << std::endl; - break; - case test_result::warn: - os << "test passed, but cleanup code (destructor) raised" - " an exception" << std::endl; - break; - default: - break; - } - - if (!tr.message.empty()) - { - if (tr.result == test_result::fail) - { - os << " failed assertion: \"" << tr.message << "\"" - << std::endl; - } - else - { - os << " message: \"" << tr.message << "\"" - << std::endl; - } - } - - ++i; - } - } - - os << std::endl; - - os << "tests summary:"; - if (terminations_count > 0) - { - os << " terminations:" << terminations_count; - } - if (exceptions_count > 0) - { - os << " exceptions:" << exceptions_count; - } - if (failures_count > 0) - { - os << " failures:" << failures_count; - } - if (warnings_count > 0) - { - os << " warnings:" << warnings_count; - } - os << " ok:" << ok_count; - os << std::endl; - } - - bool all_ok() const - { - return not_passed.empty(); - } - -private: - - void init() - { - ok_count = 0; - exceptions_count = 0; - failures_count = 0; - terminations_count = 0; - warnings_count = 0; - not_passed.clear(); - } -}; - -} - -#endif diff --git a/indra/externals/tut/tut/tut_restartable.hpp b/indra/externals/tut/tut/tut_restartable.hpp deleted file mode 100644 index 7fa152f6875..00000000000 --- a/indra/externals/tut/tut/tut_restartable.hpp +++ /dev/null @@ -1,395 +0,0 @@ -#ifndef TUT_RESTARTABLE_H_GUARD -#define TUT_RESTARTABLE_H_GUARD - -#include -#include -#include -#include -#include - -/** - * Template Unit Tests Framework for C++. - * http://tut.dozen.ru - * - * Optional restartable wrapper for test_runner. Allows to restart test runs - * finished due to abnormal test application termination (such as segmentation - * fault or math error). - * - * @author Vladimir Dyuzhev, Vladimir.Dyuzhev@gmail.com - */ - -namespace tut -{ - -namespace util -{ - -/** - * Escapes non-alphabetical characters in string. - */ -std::string escape(const std::string& orig) -{ - std::string rc; - std::string::const_iterator i,e; - i = orig.begin(); - e = orig.end(); - - while (i != e) - { - if ((*i >= 'a' && *i <= 'z') || - (*i >= 'A' && *i <= 'Z') || - (*i >= '0' && *i <= '9') ) - { - rc += *i; - } - else - { - rc += '\\'; - rc += ('a'+(((unsigned int)*i) >> 4)); - rc += ('a'+(((unsigned int)*i) & 0xF)); - } - - ++i; - } - return rc; -} - -/** - * Un-escapes string. - */ -std::string unescape(const std::string& orig) -{ - std::string rc; - std::string::const_iterator i,e; - i = orig.begin(); - e = orig.end(); - - while (i != e) - { - if (*i != '\\') - { - rc += *i; - } - else - { - ++i; - if (i == e) - { - boost::throw_exception(std::invalid_argument("unexpected end of string")); - } - unsigned int c1 = *i; - ++i; - if (i == e) - { - boost::throw_exception(std::invalid_argument("unexpected end of string")); - } - unsigned int c2 = *i; - rc += (((c1 - 'a') << 4) + (c2 - 'a')); - } - - ++i; - } - return rc; -} - -/** - * Serialize test_result avoiding interfering with operator <<. - */ -void serialize(std::ostream& os, const tut::test_result& tr) -{ - os << escape(tr.group) << std::endl; - os << tr.test << ' '; - switch(tr.result) - { - case test_result::ok: - os << 0; - break; - case test_result::fail: - os << 1; - break; - case test_result::ex: - os << 2; - break; - case test_result::warn: - os << 3; - break; - case test_result::term: - os << 4; - break; - default: - boost::throw_exception(std::logic_error("operator << : bad result_type")); - } - os << ' ' << escape(tr.message) << std::endl; -} - -/** - * deserialization for test_result - */ -void deserialize(std::istream& is, tut::test_result& tr) -{ - std::getline(is,tr.group); - if (is.eof()) - { - boost::throw_exception(tut::no_more_tests()); - } - tr.group = unescape(tr.group); - - tr.test = -1; - is >> tr.test; - if (tr.test < 0) - { - boost::throw_exception(std::logic_error("operator >> : bad test number")); - } - - int n = -1; - is >> n; - switch(n) - { - case 0: - tr.result = test_result::ok; - break; - case 1: - tr.result = test_result::fail; - break; - case 2: - tr.result = test_result::ex; - break; - case 3: - tr.result = test_result::warn; - break; - case 4: - tr.result = test_result::term; - break; - default: - boost::throw_exception(std::logic_error("operator >> : bad result_type")); - } - - is.ignore(1); // space - std::getline(is,tr.message); - tr.message = unescape(tr.message); - if (!is.good()) - { - boost::throw_exception(std::logic_error("malformed test result")); - } -} -}; - -/** - * Restartable test runner wrapper. - */ -class restartable_wrapper -{ - test_runner& runner_; - callback* callback_; - - std::string dir_; - std::string log_; // log file: last test being executed - std::string jrn_; // journal file: results of all executed tests - -public: - /** - * Default constructor. - * @param dir Directory where to search/put log and journal files - */ - restartable_wrapper(const std::string& dir = ".") - : runner_(runner.get()), - callback_(0), - dir_(dir) - { - // dozen: it works, but it would be better to use system path separator - jrn_ = dir_ + '/' + "journal.tut"; - log_ = dir_ + '/' + "log.tut"; - } - - /** - * Stores another group for getting by name. - */ - void register_group(const std::string& name, group_base* gr) - { - runner_.register_group(name,gr); - } - - /** - * Stores callback object. - */ - void set_callback(callback* cb) - { - callback_ = cb; - } - - /** - * Returns callback object. - */ - callback& get_callback() const - { - return runner_.get_callback(); - } - - /** - * Returns list of known test groups. - */ - groupnames list_groups() const - { - return runner_.list_groups(); - } - - /** - * Runs all tests in all groups. - */ - void run_tests() const - { - // where last run was failed - std::string fail_group; - int fail_test; - read_log_(fail_group,fail_test); - bool fail_group_reached = (fail_group == ""); - - // iterate over groups - tut::groupnames gn = list_groups(); - tut::groupnames::const_iterator gni,gne; - gni = gn.begin(); - gne = gn.end(); - while (gni != gne) - { - // skip all groups before one that failed - if (!fail_group_reached) - { - if (*gni != fail_group) - { - ++gni; - continue; - } - fail_group_reached = true; - } - - // first or restarted run - int test = (*gni == fail_group && fail_test >= 0) ? fail_test + 1 : 1; - while(true) - { - // last executed test pos - register_execution_(*gni,test); - - try - { - tut::test_result tr = runner_.run_test(*gni,test); - register_test_(tr); - } - catch (const tut::beyond_last_test&) - { - break; - } - catch(const tut::no_such_test&) - { - // it's ok - } - - ++test; - } - - ++gni; - } - - // show final results to user - invoke_callback_(); - - // truncate files as mark of successful finish - truncate_(); - } - -private: - /** - * Shows results from journal file. - */ - void invoke_callback_() const - { - runner_.set_callback(callback_); - runner_.get_callback().run_started(); - - std::string current_group; - std::ifstream ijournal(jrn_.c_str()); - while (ijournal.good()) - { - // read next test result - try - { - tut::test_result tr; - util::deserialize(ijournal,tr); - runner_.get_callback().test_completed(tr); - } - catch (const no_more_tests&) - { - break; - } - } - - runner_.get_callback().run_completed(); - } - - /** - * Register test into journal. - */ - void register_test_(const test_result& tr) const - { - std::ofstream ojournal(jrn_.c_str(), std::ios::app); - util::serialize(ojournal, tr); - ojournal << std::flush; - if (!ojournal.good()) - { - boost::throw_exception(std::runtime_error("unable to register test result in file " - + jrn_)); - } - } - - /** - * Mark the fact test going to be executed - */ - void register_execution_(const std::string& grp, int test) const - { - // last executed test pos - std::ofstream olog(log_.c_str()); - olog << util::escape(grp) << std::endl << test << std::endl << std::flush; - if (!olog.good()) - { - boost::throw_exception(std::runtime_error("unable to register execution in file " - + log_)); - } - } - - /** - * Truncate tests. - */ - void truncate_() const - { - std::ofstream olog(log_.c_str()); - std::ofstream ojournal(jrn_.c_str()); - } - - /** - * Read log file - */ - void read_log_(std::string& fail_group, int& fail_test) const - { - // read failure point, if any - std::ifstream ilog(log_.c_str()); - std::getline(ilog,fail_group); - fail_group = util::unescape(fail_group); - ilog >> fail_test; - if (!ilog.good()) - { - fail_group = ""; - fail_test = -1; - truncate_(); - } - else - { - // test was terminated... - tut::test_result tr(fail_group, fail_test, "", tut::test_result::term); - register_test_(tr); - } - } -}; - -} - -#endif - diff --git a/indra/externals/tut/tut/tut_result.hpp b/indra/externals/tut/tut/tut_result.hpp deleted file mode 100644 index 6262e76e5af..00000000000 --- a/indra/externals/tut/tut/tut_result.hpp +++ /dev/null @@ -1,130 +0,0 @@ -#ifndef TUT_RESULT_H_GUARD -#define TUT_RESULT_H_GUARD - -#include - -namespace tut -{ - -#if defined(TUT_USE_POSIX) -struct test_result_posix -{ - test_result_posix() - : pid(getpid()) - { - } - - pid_t pid; -}; -#else -struct test_result_posix -{ -}; -#endif - -/** - * Return type of runned test/test group. - * - * For test: contains result of test and, possible, message - * for failure or exception. - */ -struct test_result : public test_result_posix -{ - /** - * Test group name. - */ - std::string group; - - /** - * Test number in group. - */ - int test; - - /** - * Test name (optional) - */ - std::string name; - - /** - * ok - test finished successfully - * fail - test failed with ensure() or fail() methods - * ex - test throwed an exceptions - * warn - test finished successfully, but test destructor throwed - * term - test forced test application to terminate abnormally - * skip - test skpped because it is a known failure case - */ - enum result_type - { - ok, - fail, - ex, - warn, - term, - ex_ctor, - rethrown, - skip, - }; - - result_type result; - - /** - * Exception message for failed test. - */ - std::string message; - std::string exception_typeid; - - /** - * Default constructor. - */ - test_result() - : test(0), - result(ok) - { - } - - /** - * Constructor. - */ - test_result(const std::string& grp, int pos, - const std::string& test_name, result_type res) - : group(grp), - test(pos), - name(test_name), - result(res) - { - } - - /** - * Constructor with exception. - */ - test_result(const std::string& grp,int pos, - const std::string& test_name, result_type res, - const std::exception& ex) - : group(grp), - test(pos), - name(test_name), - result(res), - message(ex.what()), - exception_typeid(typeid(ex).name()) - { - } - - /** Constructor with typeid. - */ - test_result(const std::string& grp,int pos, - const std::string& test_name, result_type res, - const std::string& ex_typeid, - const std::string& msg) - : group(grp), - test(pos), - name(test_name), - result(res), - message(msg), - exception_typeid(ex_typeid) - { - } -}; - -} - -#endif diff --git a/indra/externals/tut/tut/tut_runner.hpp b/indra/externals/tut/tut/tut_runner.hpp deleted file mode 100644 index 28083d75cf8..00000000000 --- a/indra/externals/tut/tut/tut_runner.hpp +++ /dev/null @@ -1,303 +0,0 @@ -#ifndef TUT_RUNNER_H_GUARD -#define TUT_RUNNER_H_GUARD - -#include -#include -#include "tut_exception.hpp" -#include - -namespace tut -{ - -/** - * Interface. - * Test group operations. - */ -struct group_base -{ - virtual ~group_base() - { - } - - // execute tests iteratively - virtual void rewind() = 0; - virtual test_result run_next() = 0; - - // execute one test - virtual test_result run_test(int n) = 0; -}; - - -/** - * Test runner callback interface. - * Can be implemented by caller to update - * tests results in real-time. User can implement - * any of callback methods, and leave unused - * in default implementation. - */ -struct callback -{ - /** - * Virtual destructor is a must for subclassed types. - */ - virtual ~callback() - { - } - - /** - * Called when new test run started. - */ - virtual void run_started() - { - } - - /** - * Called when a group started - * @param name Name of the group - */ - virtual void group_started(const std::string& /*name*/) - { - } - - /** - * Called when a test finished. - * @param tr Test results. - */ - virtual void test_completed(const test_result& /*tr*/) - { - } - - /** - * Called when a group is completed - * @param name Name of the group - */ - virtual void group_completed(const std::string& /*name*/) - { - } - - /** - * Called when all tests in run completed. - */ - virtual void run_completed() - { - } -}; - -/** - * Typedef for runner::list_groups() - */ -typedef std::vector groupnames; - -/** - * Test runner. - */ -class test_runner -{ - -public: - - /** - * Constructor - */ - test_runner() - : callback_(&default_callback_) - { - } - - /** - * Stores another group for getting by name. - */ - void register_group(const std::string& name, group_base* gr) - { - if (gr == 0) - { - boost::throw_exception(tut_error("group shall be non-null")); - } - - if (groups_.find(name) != groups_.end()) - { - std::string msg("attempt to add already existent group " + name); - // this exception terminates application so we use cerr also - // TODO: should this message appear in stream? - std::cerr << msg << std::endl; - boost::throw_exception(tut_error(msg)); - } - - groups_.insert( std::make_pair(name, gr) ); - } - - /** - * Stores callback object. - */ - void set_callback(callback* cb) - { - callback_ = cb == 0 ? &default_callback_ : cb; - } - - /** - * Returns callback object. - */ - callback& get_callback() const - { - return *callback_; - } - - /** - * Returns list of known test groups. - */ - const groupnames list_groups() const - { - groupnames ret; - const_iterator i = groups_.begin(); - const_iterator e = groups_.end(); - while (i != e) - { - ret.push_back(i->first); - ++i; - } - return ret; - } - - /** - * Runs all tests in all groups. - * @param callback Callback object if exists; null otherwise - */ - void run_tests() const - { - callback_->run_started(); - - const_iterator i = groups_.begin(); - const_iterator e = groups_.end(); - while (i != e) - { - callback_->group_started(i->first); - try - { - run_all_tests_in_group_(i); - } - catch (const no_more_tests&) - { - callback_->group_completed(i->first); - } - - ++i; - } - - callback_->run_completed(); - } - - /** - * Runs all tests in specified group. - */ - void run_tests(const std::string& group_name) const - { - callback_->run_started(); - - const_iterator i = groups_.find(group_name); - if (i == groups_.end()) - { - callback_->run_completed(); - boost::throw_exception(no_such_group(group_name)); - } - - callback_->group_started(group_name); - try - { - run_all_tests_in_group_(i); - } - catch (const no_more_tests&) - { - // ok - } - - callback_->group_completed(group_name); - callback_->run_completed(); - } - - /** - * Runs one test in specified group. - */ - test_result run_test(const std::string& group_name, int n) const - { - callback_->run_started(); - - const_iterator i = groups_.find(group_name); - if (i == groups_.end()) - { - callback_->run_completed(); - boost::throw_exception(no_such_group(group_name)); - } - - callback_->group_started(group_name); - try - { - test_result tr = i->second->run_test(n); - callback_->test_completed(tr); - callback_->group_completed(group_name); - callback_->run_completed(); - return tr; - } - catch (const beyond_last_test&) - { - callback_->group_completed(group_name); - callback_->run_completed(); - throw; - } - catch (const no_such_test&) - { - callback_->group_completed(group_name); - callback_->run_completed(); - throw; - } - } - -protected: - - typedef std::map groups; - typedef groups::iterator iterator; - typedef groups::const_iterator const_iterator; - groups groups_; - - callback default_callback_; - callback* callback_; - - -private: - - void run_all_tests_in_group_(const_iterator i) const - { - i->second->rewind(); - for ( ;; ) - { - test_result tr = i->second->run_next(); - callback_->test_completed(tr); - - if (tr.result == test_result::ex_ctor) - { - boost::throw_exception(no_more_tests()); - } - } - } -}; - -/** - * Singleton for test_runner implementation. - * Instance with name runner_singleton shall be implemented - * by user. - */ -class test_runner_singleton -{ -public: - - static test_runner& get() - { - static test_runner tr; - return tr; - } -}; - -extern test_runner_singleton runner; - -} - -#endif diff --git a/indra/llcommon/is_approx_equal_fraction.h b/indra/llcommon/is_approx_equal_fraction.h index 371a1307c1d..75016117ae1 100644 --- a/indra/llcommon/is_approx_equal_fraction.h +++ b/indra/llcommon/is_approx_equal_fraction.h @@ -2,9 +2,9 @@ * @file is_approx_equal_fraction.h * @author Nat Goodspeed * @date 2009-01-28 - * @brief lltut.h uses is_approx_equal_fraction(). Moved to this header - * file in llcommon so we can use lltut.h for llcommon tests without - * making llcommon depend on llmath. + * @brief The tests' float matchers use is_approx_equal_fraction(). Moved + * to this header file in llcommon so llcommon's tests can use them + * without making llcommon depend on llmath. * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp index d211dc38c27..fc0e034db47 100644 --- a/indra/llcommon/llprocess.cpp +++ b/indra/llcommon/llprocess.cpp @@ -362,8 +362,8 @@ class ReadPipeImpl : public LLProcess::ReadPipe if (!mPipe || !mPipe->is_open() || mEOF) return; - // Always read data regardless of mLimit to prevent INTEGRATION_TEST_llleap - // deadlock: stopping reads fills the OS pipe buffer and blocks the child, + // Always read data regardless of mLimit to prevent a deadlock llleap's + // test found: stopping reads fills the OS pipe buffer and blocks the child, // which prevents large (~1 MB) messages from being fully received. // mLimit only controls how many bytes appear in event notifications. auto bufs = mStreambuf.prepare(4096); diff --git a/indra/llcommon/tests/StringVec.h b/indra/llcommon/tests/StringVec.h index 1c5341d1a49..6aa4f5803ac 100644 --- a/indra/llcommon/tests/StringVec.h +++ b/indra/llcommon/tests/StringVec.h @@ -2,7 +2,7 @@ * @file StringVec.h * @author Nat Goodspeed * @date 2012-02-24 - * @brief Extend TUT ensure_equals() to handle std::vector + * @brief Print a std::vector, as a failed check shows one * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Copyright (c) 2012, Linden Research, Inc. diff --git a/indra/llcommon/tests/apply_test.cpp b/indra/llcommon/tests/apply_test.cpp index d028b2ea1c7..bf0780c85dc 100644 --- a/indra/llcommon/tests/apply_test.cpp +++ b/indra/llcommon/tests/apply_test.cpp @@ -23,12 +23,12 @@ #include #include -// for ensure_equals(std::vector): must come BEFORE lltut.h +// to print a std::vector in a failed check #include "StringVec.h" #include /***************************************************************************** -* TUT +* Tests *****************************************************************************/ namespace { diff --git a/indra/llcommon/tests/classic_callback_test.cpp b/indra/llcommon/tests/classic_callback_test.cpp index c9609809335..1a031bf47a7 100644 --- a/indra/llcommon/tests/classic_callback_test.cpp +++ b/indra/llcommon/tests/classic_callback_test.cpp @@ -89,7 +89,7 @@ struct OtherCallback } // anonymous namespace /***************************************************************************** -* TUT +* Tests *****************************************************************************/ namespace { diff --git a/indra/llcommon/tests/lazyeventapi_test.cpp b/indra/llcommon/tests/lazyeventapi_test.cpp index 5c4d3bc274e..bd5cf88b74c 100644 --- a/indra/llcommon/tests/lazyeventapi_test.cpp +++ b/indra/llcommon/tests/lazyeventapi_test.cpp @@ -69,7 +69,7 @@ class MyRegistrar: public LL::LazyEventAPI // methods. /***************************************************************************** -* TUT +* Tests *****************************************************************************/ namespace { diff --git a/indra/llcommon/tests/llcond_test.cpp b/indra/llcommon/tests/llcond_test.cpp index 399f14a6a56..d555c9f2836 100644 --- a/indra/llcommon/tests/llcond_test.cpp +++ b/indra/llcommon/tests/llcond_test.cpp @@ -21,7 +21,7 @@ #include "llcoros.h" /***************************************************************************** -* TUT +* Tests *****************************************************************************/ namespace { diff --git a/indra/llcommon/tests/lldate_test.cpp b/indra/llcommon/tests/lldate_test.cpp index 4419d4bd2d8..2b4d6763336 100644 --- a/indra/llcommon/tests/lldate_test.cpp +++ b/indra/llcommon/tests/lldate_test.cpp @@ -67,7 +67,7 @@ #define INVALID_DATE_DAY "2003-04-35T22:59:59Z" #endif -namespace tut +namespace { // Compile-time verification that LLDate's default ctor, accessors, and // comparison operators are constexpr. diff --git a/indra/llcommon/tests/lldependencies_test.cpp b/indra/llcommon/tests/lldependencies_test.cpp index 4bf0149a443..30b30cde4be 100644 --- a/indra/llcommon/tests/lldependencies_test.cpp +++ b/indra/llcommon/tests/lldependencies_test.cpp @@ -42,7 +42,7 @@ #endif /***************************************************************************** -* Display helpers: must be defined BEFORE lltut.h! +* Display helpers *****************************************************************************/ namespace { @@ -102,9 +102,6 @@ std::ostream& operator<<(std::ostream& out, const std::set& set) } // anonymous namespace -/***************************************************************************** -* Now we can #include lltut.h -*****************************************************************************/ #include /***************************************************************************** @@ -153,7 +150,7 @@ bool is_empty(const RANGE& range) } // anonymous namespace /***************************************************************************** -* tut test group +* Tests *****************************************************************************/ namespace { diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp index bf7064d68a6..c86983c5954 100644 --- a/indra/llcommon/tests/llerror_test.cpp +++ b/indra/llcommon/tests/llerror_test.cpp @@ -102,7 +102,7 @@ namespace fatalWasCalled = true; \ } -namespace tut +namespace { namespace { @@ -273,7 +273,7 @@ namespace } }; -namespace tut +namespace { TEST_CASE_METHOD(ErrorTestData, "error: the default level filters messages", "[error]") // messages are filtered based on default level @@ -490,12 +490,12 @@ namespace void require_log_name(LLError::RecorderPtr recorder, LogFromFunction f, const std::string& class_name = "") { - std::dynamic_pointer_cast(recorder)->clearMessages(); + std::dynamic_pointer_cast(recorder)->clearMessages(); std::string name = f(false); f(true); - std::string messageWithoutName = std::dynamic_pointer_cast(recorder)->message(0); - std::string messageWithName = std::dynamic_pointer_cast(recorder)->message(1); + std::string messageWithoutName = std::dynamic_pointer_cast(recorder)->message(0); + std::string messageWithName = std::dynamic_pointer_cast(recorder)->message(1); require_has(name + " logged without name", messageWithoutName, name); @@ -526,7 +526,7 @@ namespace } }; -namespace tut +namespace { TEST_CASE_METHOD(ErrorTestData, "error: multiline output leaves backslashes and line breaks unescaped", "[error]") // backslash, return, and newline are not escaped with backslashes @@ -547,7 +547,7 @@ namespace tut } } -namespace tut +namespace { // class/function information in output TEST_CASE_METHOD(ErrorTestData, "error: messages name the function and class they came from", "[error]") @@ -596,7 +596,7 @@ namespace } -namespace tut +namespace { // handle nested logging TEST_CASE_METHOD(ErrorTestData, "error: logging while building a message logs both", "[error]") @@ -638,7 +638,7 @@ namespace } } -namespace tut +namespace { // time in output (for recorders that need it) TEST_CASE_METHOD(ErrorTestData, "error: a recorder gets the time only when it asks for it", "[error]") @@ -725,7 +725,7 @@ class TestBeta static void doAll() { doDebug(); doInfo(); doWarn(); doError(); } }; -namespace tut +namespace { // filtering by class TEST_CASE_METHOD(ErrorTestData, "error: a class level overrides the default level", "[error]") @@ -857,7 +857,7 @@ namespace tut } } -namespace tut +namespace { TEST_CASE_METHOD(ErrorTestData, "error: single-line output escapes backslashes and line breaks", "[error]") // backslash, return, and newline are escaped with backslashes @@ -886,7 +886,7 @@ namespace } }; -namespace tut +namespace { TEST_CASE_METHOD(ErrorTestData, "error: a space in a tag is an error", "[error]") // space character is not allowed in a tag diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index 6de805ba982..d3d1871e5b0 100644 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -127,7 +127,7 @@ class CoroTestApp: public LLTestApp } // anonymous namespace /***************************************************************************** -* TUT +* Tests *****************************************************************************/ namespace { @@ -313,9 +313,9 @@ namespace // should immediately retrieve 'first' without waiting LL_DEBUGS() << "listener coro waiting for first" << LL_ENDL; data.append(llcoro::suspendUntilEventOnWithTimeout(pump, 0.1f, LLSD())); - // Don't use ensure() from within the coro -- ensure() failure - // throws tut::fail, which won't propagate out to the main - // test driver, which will result in an odd failure. + // Don't check from within the coro -- a failed REQUIRE + // throws, which won't propagate out to the case, which will + // result in an odd failure. // Wait for 'second' because it's not already pending. LL_DEBUGS() << "listener coro waiting for second" << LL_ENDL; data.append(llcoro::suspendUntilEventOnWithTimeout(pump, 0.1f, LLSD())); diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp index bf43e170606..8c2724f054a 100644 --- a/indra/llcommon/tests/lleventdispatcher_test.cpp +++ b/indra/llcommon/tests/lleventdispatcher_test.cpp @@ -321,9 +321,9 @@ void freenb(NPARAMSb) } // anonymous namespace /***************************************************************************** -* TUT +* Tests *****************************************************************************/ -namespace tut +namespace { static void require_contains(const std::string& outer, const std::string& inner) { @@ -670,7 +670,7 @@ namespace tut std::string call_exc(const std::string& func, const LLSD& args, const std::string& exc_frag) { - return tut::call_exc( + return ::call_exc( [this, func, args]() { if (func.empty()) @@ -687,7 +687,7 @@ namespace tut void call_logerr(const std::string& func, const LLSD& args, const std::string& frag) { - tut::call_logerr([this, func, args](){ work(func, args); }, frag); + ::call_logerr([this, func, args](){ work(func, args); }, frag); } LLSD getMetadata(const std::string& name) @@ -1363,7 +1363,7 @@ namespace tut TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: a listener error without a reply pump throws", "[lleventdispatcher]") { DispatchResult service; - tut::call_exc( + ::call_exc( [&service]() { service.post(llsd::map("op", "nosuchfunc", "reqid", 17)); }, "nosuchfunc"); @@ -1480,7 +1480,7 @@ namespace tut TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: batched map exception", "[lleventdispatcher]") { DispatchResult service; - auto error = tut::call_exc( + auto error = ::call_exc( [&service]() { service.post(llsd::map( @@ -1560,7 +1560,7 @@ namespace tut TEST_CASE_METHOD(lleventdispatcher_data, "lleventdispatcher: batched array exception", "[lleventdispatcher]") { DispatchResult service; - auto error = tut::call_exc( + auto error = ::call_exc( [&service]() { service.post(llsd::map( @@ -1581,4 +1581,4 @@ namespace tut "[1]"); require_contains(error, "unsupported"); } -} // namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/lleventfilter_test.cpp b/indra/llcommon/tests/lleventfilter_test.cpp index 3bcc4d44fbb..56b2cc17e7f 100644 --- a/indra/llcommon/tests/lleventfilter_test.cpp +++ b/indra/llcommon/tests/lleventfilter_test.cpp @@ -159,7 +159,7 @@ class TestEventThrottle: public LLEventThrottleBase } // anonymous namespace /***************************************************************************** -* TUT +* Tests *****************************************************************************/ namespace { diff --git a/indra/llcommon/tests/llevents_test.cpp b/indra/llcommon/tests/llevents_test.cpp index f06de5ead19..be834c9e09f 100644 --- a/indra/llcommon/tests/llevents_test.cpp +++ b/indra/llcommon/tests/llevents_test.cpp @@ -45,7 +45,7 @@ // external library headers #include // other Linden headers -#include "tests/listener.h" // must PRECEDE lltut.h +#include "tests/listener.h" #include #include #include "catch_and_store_what_in.h" @@ -57,7 +57,7 @@ static T make(const T& value) } /***************************************************************************** - * tut test group + * Tests *****************************************************************************/ namespace { @@ -100,7 +100,7 @@ struct events_data TEST_CASE_METHOD(events_data, "events: basic operations", "[events]") { // Having to modify this to track the statically- - // constructed pumps in other TUT modules in this giant monolithic test + // constructed pumps in other test files in this giant monolithic test // executable isn't such a hot idea. // ensure_equals("initial pump", pumps.mPumpMap.size(), 1); size_t initial_pumps(pumps.mPumpMap.size()); diff --git a/indra/llcommon/tests/llheteromap_test.cpp b/indra/llcommon/tests/llheteromap_test.cpp index 4953a326dfb..810f1913658 100644 --- a/indra/llcommon/tests/llheteromap_test.cpp +++ b/indra/llcommon/tests/llheteromap_test.cpp @@ -106,7 +106,7 @@ struct Chowdah } // anonymous namespace /***************************************************************************** -* TUT +* Tests *****************************************************************************/ namespace { diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp index f551e4de4b5..72c745486f1 100644 --- a/indra/llcommon/tests/llleap_test.cpp +++ b/indra/llcommon/tests/llleap_test.cpp @@ -91,9 +91,9 @@ static void waitfor(LLLeap* instance, int timeout=60) } /***************************************************************************** -* TUT +* Tests *****************************************************************************/ -namespace tut +namespace { struct llleap_data { @@ -651,4 +651,4 @@ namespace tut { test_or_split(PYTHON, reader_module, ll_test::currentTestName(), BUFFERED_LENGTH); } -} // namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/llmainthreadtask_test.cpp b/indra/llcommon/tests/llmainthreadtask_test.cpp index b0b09e1f663..ff4fde68dd0 100644 --- a/indra/llcommon/tests/llmainthreadtask_test.cpp +++ b/indra/llcommon/tests/llmainthreadtask_test.cpp @@ -25,7 +25,7 @@ #include "lockstatic.h" /***************************************************************************** -* TUT +* Tests *****************************************************************************/ namespace { diff --git a/indra/llcommon/tests/llpounceable_test.cpp b/indra/llcommon/tests/llpounceable_test.cpp index 22707a06986..c3cca5ca20e 100644 --- a/indra/llcommon/tests/llpounceable_test.cpp +++ b/indra/llcommon/tests/llpounceable_test.cpp @@ -72,7 +72,7 @@ LLPounceable gForward; } // anonymous namespace /***************************************************************************** -* TUT +* Tests *****************************************************************************/ namespace { diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp index d407145976f..402c03ff1a8 100644 --- a/indra/llcommon/tests/llprocess_test.cpp +++ b/indra/llcommon/tests/llprocess_test.cpp @@ -554,9 +554,9 @@ class NamedTempDir } // anonymous namespace /***************************************************************************** -* TUT +* Tests *****************************************************************************/ -namespace tut +namespace { struct llprocess_data { @@ -2086,4 +2086,4 @@ namespace tut // its own and overwritten the file with 'bad'. CHECK(readfile(out.getPath()) == "ok"); } -} // namespace tut +} // anonymous namespace diff --git a/indra/llcommon/tests/llsd_test.cpp b/indra/llcommon/tests/llsd_test.cpp index 2fa652c7e78..f5dfafeb0e1 100644 --- a/indra/llcommon/tests/llsd_test.cpp +++ b/indra/llcommon/tests/llsd_test.cpp @@ -35,7 +35,7 @@ using std::fpclassify; -namespace tut +namespace { namespace { diff --git a/indra/llcommon/tests/llsdserialize_python_test.cpp b/indra/llcommon/tests/llsdserialize_python_test.cpp index 1a5457c17b9..bbbfb813f65 100644 --- a/indra/llcommon/tests/llsdserialize_python_test.cpp +++ b/indra/llcommon/tests/llsdserialize_python_test.cpp @@ -56,7 +56,7 @@ typedef std::function FormatterFunction; typedef std::function ParserFunction; -namespace tut +namespace { // helper for TestPythonCompatible static std::string import_llsd("import os.path\n" diff --git a/indra/llcommon/tests/lltreeiterators_test.cpp b/indra/llcommon/tests/lltreeiterators_test.cpp index b1bc03d3f10..f5d8448d87b 100644 --- a/indra/llcommon/tests/lltreeiterators_test.cpp +++ b/indra/llcommon/tests/lltreeiterators_test.cpp @@ -47,7 +47,7 @@ #include /***************************************************************************** -* tut test group +* Tests *****************************************************************************/ namespace { diff --git a/indra/llcommon/tests/threadsafeschedule_test.cpp b/indra/llcommon/tests/threadsafeschedule_test.cpp index 6afc637d6d7..36468a48b0f 100644 --- a/indra/llcommon/tests/threadsafeschedule_test.cpp +++ b/indra/llcommon/tests/threadsafeschedule_test.cpp @@ -25,7 +25,7 @@ using namespace std::literals::string_literals; // s suffix using Queue = LL::ThreadSafeSchedule; /***************************************************************************** -* TUT +* Tests *****************************************************************************/ namespace { diff --git a/indra/llcommon/tests/tuple_test.cpp b/indra/llcommon/tests/tuple_test.cpp index b29251f9ad3..0ae56f398d9 100644 --- a/indra/llcommon/tests/tuple_test.cpp +++ b/indra/llcommon/tests/tuple_test.cpp @@ -20,7 +20,7 @@ #include /***************************************************************************** -* TUT +* Tests *****************************************************************************/ namespace { diff --git a/indra/llcommon/tests/workqueue_test.cpp b/indra/llcommon/tests/workqueue_test.cpp index 5a78de08b68..84d091becf3 100644 --- a/indra/llcommon/tests/workqueue_test.cpp +++ b/indra/llcommon/tests/workqueue_test.cpp @@ -33,7 +33,7 @@ using namespace std::literals::chrono_literals; // ms suffix using namespace std::literals::string_literals; // s suffix /***************************************************************************** -* TUT +* Tests *****************************************************************************/ namespace { diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 7ecea58e65a..227f0d056cb 100755 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -315,8 +315,8 @@ def handle_error(self, request, client_address): # Pass the selected port number to the subject test program via the # environment. We don't want to impose requirements on the test program's - # command-line parsing -- and anyway, for C++ integration tests, that's - # performed in TUT code rather than our own. + # command-line parsing -- and anyway, for C++ tests, that's performed in + # Catch2's code rather than our own. os.environ["LL_TEST_PORT"] = str(httpd.server_port) debug("$LL_TEST_PORT = %s", httpd.server_port) if do_valgrind: diff --git a/indra/llimage/tests/llimageworker_test.cpp b/indra/llimage/tests/llimageworker_test.cpp index ef75cc42052..d6795a52922 100644 --- a/indra/llimage/tests/llimageworker_test.cpp +++ b/indra/llimage/tests/llimageworker_test.cpp @@ -158,8 +158,6 @@ namespace // Test functions // Notes: // * Test as many as you possibly can without requiring a full blown simulation of everything - // * The tests are executed in sequence so the test instance state may change between calls - // * Remember that you cannot test private methods with tut // --------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------- diff --git a/indra/llkdu/CMakeLists.txt b/indra/llkdu/CMakeLists.txt index 64c1f70af49..8b9df57b6e4 100644 --- a/indra/llkdu/CMakeLists.txt +++ b/indra/llkdu/CMakeLists.txt @@ -14,11 +14,15 @@ if(AL_USE_KDU) # Add tests if(AL_BUILD_TESTS) get_property(llimage_include_dir TARGET llimage PROPERTY INTERFACE_INCLUDE_DIRECTORIES) - al_add_test( - llimagej2ckdu - UNIT + # The codec, compiled into its test, over the test's stubs of llimage + # and of the KDU calls it makes. Built only with KDU, which has no port + # yet: this has not been compiled since it moved to Catch2. + al_add_test_binary( + llimagej2ckdu_test PROJECT llkdu - LIBRARIES ll::kdu llcommon + SOURCES tests/llimagej2ckdu_test.cpp + UNIT llimagej2ckdu.cpp + LIBRARIES ll::kdu llmath llcommon INCLUDES ${llimage_include_dir} ) endif() diff --git a/indra/llkdu/tests/llimagej2ckdu_test.cpp b/indra/llkdu/tests/llimagej2ckdu_test.cpp index 09425fc001f..c8235c3fd71 100644 --- a/indra/llkdu/tests/llimagej2ckdu_test.cpp +++ b/indra/llkdu/tests/llimagej2ckdu_test.cpp @@ -39,8 +39,10 @@ #include "llkdumem.h" #endif #include "kdu_block_coding.h" -// Tut header -#include "lltut.h" + +#include + +#include // ------------------------------------------------------------------------------------------- // Stubbing: Declarations required to link and run the class being tested @@ -242,86 +244,53 @@ void kdu_params::operator delete(void *) {} } // namespace kdu_core // ------------------------------------------------------------------------------------------- -// TUT +// The tests // ------------------------------------------------------------------------------------------- -namespace tut +namespace { - // Test wrapper declarations - struct llimagej2ckdu_test + struct llimagej2ckdu_data { - // Derived test class + // The codec, its protected steps made callable. class LLTestImageJ2CKDU : public LLImageJ2CKDU { public: - // Provides public access to some protected methods for testing - bool callGetMetadata(LLImageJ2C &base) { return getMetadata(base); } - bool callDecodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) + bool callGetMetadata(LLImageJ2C& base) { return getMetadata(base); } + bool callDecodeImpl(LLImageJ2C& base, LLImageRaw& raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) { return decodeImpl(base, raw_image, decode_time, first_channel, max_channel_count); } - bool callEncodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text) + bool callEncodeImpl(LLImageJ2C& base, const LLImageRaw& raw_image, const char* comment_text) { return encodeImpl(base, raw_image, comment_text); } }; - // Instance to be tested - LLTestImageJ2CKDU* mImage; - // Constructor and destructor of the test wrapper - llimagej2ckdu_test() - { - mImage = new LLTestImageJ2CKDU; - } - ~llimagej2ckdu_test() - { - delete mImage; - } + std::unique_ptr mImage = std::make_unique(); }; - // Tut templating thingamagic: test group, object and test instance - typedef test_group llimagej2ckdu_t; - typedef llimagej2ckdu_t::object llimagej2ckdu_object_t; - tut::llimagej2ckdu_t tut_llimagej2ckdu("LLImageJ2CKDU"); - - // --------------------------------------------------------------------------------------- - // Test functions - // Notes: - // * Test as many as you possibly can without requiring a full blown simulation of everything - // * The tests are executed in sequence so the test instance state may change between calls - // * Remember that you cannot test private methods with tut - // --------------------------------------------------------------------------------------- - - // Test 1 : test getMetadata() - template<> template<> - void llimagej2ckdu_object_t::test<1>() + // A stream of nothing over the stubs above "works". Linked with KDU + // itself, the call throws, and getMetadata() says false. + TEST_CASE_METHOD(llimagej2ckdu_data, "llimagej2ckdu: getMetadata over the stubbed KDU succeeds", "[llimagej2ckdu]") { - LLImageJ2C* image = new LLImageJ2C(); - bool res = mImage->callGetMetadata(*image); - // Trying to set up a data stream with all NIL values and stubbed KDU will "work" and return true - // Note that is linking with KDU, that call will throw an exception and fail, returning false - ensure("getMetadata() test failed", res); + LLImageJ2C image; + CHECK(mImage->callGetMetadata(image)); } - // Test 2 : test decodeImpl() - template<> template<> - void llimagej2ckdu_object_t::test<2>() + // Decoding says true whenever there is nothing more to do, a decode that + // failed included. + TEST_CASE_METHOD(llimagej2ckdu_data, "llimagej2ckdu: decodeImpl with nothing to decode says it is done", "[llimagej2ckdu]") { - LLImageJ2C* image = new LLImageJ2C(); - LLImageRaw* raw = new LLImageRaw(); - bool res = mImage->callDecodeImpl(*image, *raw, 0.0, 0, 0); - // Decoding returns true whenever there's nothing else to do, including if decoding failed, so we'll get true here - ensure("decodeImpl() test failed", res); + LLImageJ2C image; + LLImageRaw raw; + CHECK(mImage->callDecodeImpl(image, raw, 0.0, 0, 0)); } - // Test 3 : test encodeImpl() - template<> template<> - void llimagej2ckdu_object_t::test<3>() + // Encoding says true unless KDU threw, though nothing was encoded. + TEST_CASE_METHOD(llimagej2ckdu_data, "llimagej2ckdu: encodeImpl over the stubbed KDU succeeds", "[llimagej2ckdu]") { - LLImageJ2C* image = new LLImageJ2C(); - LLImageRaw* raw = new LLImageRaw(); - bool res = mImage->callEncodeImpl(*image, *raw, NULL); - // Encoding returns true unless an exception was raised, so we'll get true here though nothing really was done - ensure("encodeImpl() test failed", res); + LLImageJ2C image; + LLImageRaw raw; + CHECK(mImage->callEncodeImpl(image, raw, nullptr)); } } diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h index 0bbbf69ee1e..9cfea511057 100644 --- a/indra/llmath/llmath.h +++ b/indra/llmath/llmath.h @@ -35,9 +35,9 @@ #include #include #include "lldefs.h" -// lltut.h uses is_approx_equal_fraction(). This was moved to its own header -// file in llcommon so we can use lltut.h for llcommon tests without making -// llcommon depend on llmath. +// The tests' float matchers use is_approx_equal_fraction(). This was moved +// to its own header file in llcommon so llcommon's tests can use them without +// making llcommon depend on llmath. #include "is_approx_equal_fraction.h" inline bool llisnan(F32 val) noexcept { return std::isnan(val); } diff --git a/indra/llmessage/tests/io_test.cpp b/indra/llmessage/tests/io_test.cpp index e373ad559c8..6772e5b469d 100644 --- a/indra/llmessage/tests/io_test.cpp +++ b/indra/llmessage/tests/io_test.cpp @@ -850,7 +850,7 @@ namespace } /* -namespace tut +namespace { struct double_construct { diff --git a/indra/llrender/llfontregistry.h b/indra/llrender/llfontregistry.h index 89fdb4f1c29..a76e2960604 100644 --- a/indra/llrender/llfontregistry.h +++ b/indra/llrender/llfontregistry.h @@ -43,10 +43,10 @@ class LLFontGL; class LLFontFreetype; // Forward-declare the test fixtures so LLFontRegistry's friend declarations -// can reference them. Defined inside namespace tut by -// indra/llrender/tests/llfontregistry_test.cpp. -namespace tut { struct llfontregistry_data; } -namespace tut { struct llfontregistry_gl_data; } +// can reference them. Defined in namespace ll_test by +// indra/llrender/tests/llfontregistry_test.cpp and llfontregistry_gl_test.cpp. +namespace ll_test { struct llfontregistry_data; } +namespace ll_test { struct llfontregistry_gl_data; } typedef std::vector string_vec_t; @@ -183,13 +183,12 @@ class LLFontRegistry // Test fixture in indra/llrender/tests/llfontregistry_test.cpp drives // resolveFontReferences / applyFamilyOverrides directly so the resolver // unit tests don't have to set up gDirUtilp + a temp skin tree just to - // reach parseFontInfo. Lives in `namespace tut`, where TUT requires - // the fixture struct. - friend struct ::tut::llfontregistry_data; + // reach parseFontInfo. + friend struct ::ll_test::llfontregistry_data; // GL-fixture sibling: needs the same private-member access to drive // simulateReload-style state wipes in tests that exercise actual // freetype loading + size-change reloads. - friend struct ::tut::llfontregistry_gl_data; + friend struct ::ll_test::llfontregistry_gl_data; // create_gl_textures - set to false for test apps with no OpenGL window, // such as llui_libtest LLFontRegistry(bool create_gl_textures); diff --git a/indra/llrender/tests/llfontgl_test.cpp b/indra/llrender/tests/llfontgl_test.cpp index a7b47a1e960..e67d89d42f8 100644 --- a/indra/llrender/tests/llfontgl_test.cpp +++ b/indra/llrender/tests/llfontgl_test.cpp @@ -75,7 +75,7 @@ namespace namespace { - // The shared GL context. TUT spins fixtures per-test method, but + // The shared GL context. Catch2 makes a fixture per test case, but // per-fixture HeadlessGL recreation poisons LLFontGL's per-getter // static fontp caches after ~10 cycles (the per-test new GL // context invalidates LLImageGL textures the fontps reference). diff --git a/indra/llrender/tests/llfontregistry_gl_test.cpp b/indra/llrender/tests/llfontregistry_gl_test.cpp index 2ff298cbd09..90c02bd9062 100644 --- a/indra/llrender/tests/llfontregistry_gl_test.cpp +++ b/indra/llrender/tests/llfontregistry_gl_test.cpp @@ -45,9 +45,8 @@ // header doesn't otherwise expose its prototype. bool init_from_xml(LLFontRegistry* registry, LLPointer node); -namespace tut +namespace ll_test { - using namespace ll_test; // A fixture that owns a local LLFontRegistry with // create_gl_textures=true over the GL context llheadlessgl_fixture.h diff --git a/indra/llrender/tests/llfontregistry_test.cpp b/indra/llrender/tests/llfontregistry_test.cpp index 1eaa882f765..9615b751db8 100644 --- a/indra/llrender/tests/llfontregistry_test.cpp +++ b/indra/llrender/tests/llfontregistry_test.cpp @@ -50,7 +50,7 @@ // gDirUtilp + a temp skin tree just to reach parseFontInfo. bool init_from_xml(LLFontRegistry* registry, LLPointer node); -namespace tut +namespace ll_test { // No FreeType/GL needed for any test in this file; the registry's only // construction-time side-effect is caching LLWindow's dynamic fallback diff --git a/indra/llrender/tests/llfonttextcache_test.cpp b/indra/llrender/tests/llfonttextcache_test.cpp index c70d48577dc..3a3d8051f37 100644 --- a/indra/llrender/tests/llfonttextcache_test.cpp +++ b/indra/llrender/tests/llfonttextcache_test.cpp @@ -66,8 +66,8 @@ namespace namespace { - // Shared GL context + LLFontGL bring-up. One per binary. TUT - // creates a fresh fixture per test method; per-fixture HeadlessGL + // Shared GL context + LLFontGL bring-up. One per binary. Catch2 + // creates a fresh fixture per test case; per-fixture HeadlessGL // creation poisons LLFontGL's process-static fontp cache, since // each test's GL context destroys the LLImageGL textures the // fontps reference (LLFontManager::cleanupClass + LLImageGL:: diff --git a/indra/llrender/tests/llheadlessgl_fixture.h b/indra/llrender/tests/llheadlessgl_fixture.h index 52f97715604..c0f6fbcc4ea 100644 --- a/indra/llrender/tests/llheadlessgl_fixture.h +++ b/indra/llrender/tests/llheadlessgl_fixture.h @@ -7,8 +7,8 @@ * shown, with the same GL the viewer runs on -- WGL on Windows, EGL on * Linux -- and where Linux has no display, SDL's offscreen driver. * - * Standing the context up is the expensive part — TUT spins a fresh - * fixture per test method, so a test that can share one takes the + * Standing the context up is the expensive part — Catch2 makes a fresh + * fixture per test case, so a test that can share one takes the * static-local instance behind sharedHeadlessGL() and keeps its own * state (font registry, atlas pages) in the fixture. A test that * needs a context of its own makes a HeadlessGL per test instead. @@ -56,10 +56,8 @@ #include "llfile.h" #include "lluuid.h" -#if AL_TEST_CATCH2 #include "alteststateguard.h" #include "altestteardown.h" -#endif #include #include @@ -105,7 +103,7 @@ namespace ll_test ALFontShaping::clearCache(); } - // RAII scope — calls resetFontState() in dtor. Embed in a TUT fixture + // RAII scope — calls resetFontState() in dtor. Embed in a fixture // struct to guarantee the shape cache is empty after every test, // regardless of how the test exited. struct FontStateScope @@ -335,9 +333,7 @@ namespace ll_test LLPointer raw = new LLImageRaw(1, 1, 4); std::memset(raw->getData(), 0xFF, 4); white = new LLImageGL(raw, false); -#if AL_TEST_CATCH2 ll_test::Teardown::add([] { white = nullptr; }); -#endif } ALTextureSlot::sWhiteTexture = white->getTexName(); // Bound once, so that the slot holds a texture: an unbind puts the @@ -558,7 +554,6 @@ namespace ll_test // from then on, so the case that first asked is not blamed for it. inline HeadlessGL& sharedHeadlessGL() { -#if AL_TEST_CATCH2 static HeadlessGL* gl = [] { HeadlessGL* made = new HeadlessGL(/*needs_vbos=*/true, @@ -570,13 +565,6 @@ namespace ll_test return made; }(); return *gl; -#else - static HeadlessGL gl(/*needs_vbos=*/true, - /*needs_imagegl=*/true, - /*needs_llrender=*/true, - /*needs_render=*/true); - return gl; -#endif } // A test's hold on the shared context. It takes sharedHeadlessGL() as it diff --git a/indra/llui/tests/alheadlessui_fixture.h b/indra/llui/tests/alheadlessui_fixture.h index e967f58b09d..ca21024f078 100644 --- a/indra/llui/tests/alheadlessui_fixture.h +++ b/indra/llui/tests/alheadlessui_fixture.h @@ -44,10 +44,8 @@ #include #include -#if AL_TEST_CATCH2 #include "alteststateguard.h" #include "altestteardown.h" -#endif #ifndef LLUI_TEST_APP_DIR # define LLUI_TEST_APP_DIR "" @@ -100,7 +98,6 @@ namespace ll_test // the case that first asked is not blamed for it. static HeadlessUI& get(bool gl_textures = false) { -#if AL_TEST_CATCH2 static HeadlessUI* made = [gl_textures] { HeadlessUI* ui = new HeadlessUI(gl_textures); @@ -109,9 +106,6 @@ namespace ll_test return ui; }(); HeadlessUI& ui = *made; -#else - static HeadlessUI ui(gl_textures); -#endif if (gl_textures != ui.mGLTextures) { LL_ERRS("HeadlessUI") << "HeadlessUI::get(gl_textures=" << gl_textures @@ -212,9 +206,7 @@ namespace ll_test static const bool loaded = [&ui] { const bool read = ui.ok() && LLUIColorTable::instance().loadFromSettings(); -#if AL_TEST_CATCH2 ll_test::StateGuard::accept("the colour table"); -#endif return read; }(); return loaded; diff --git a/indra/llui/utils/llui_libtest.cpp b/indra/llui/utils/llui_libtest.cpp index 8b35b841849..f5260376087 100644 --- a/indra/llui/utils/llui_libtest.cpp +++ b/indra/llui/utils/llui_libtest.cpp @@ -51,7 +51,7 @@ #include #include -// *TODO: switch to using TUT +// *TODO: make this a test // *TODO: teach Parabuild about this program, run automatically after full builds // I believe these must be globals, not stack variables. JC diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 11b4268f0bd..c907dc0b39f 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -2455,7 +2455,9 @@ if(AL_BUILD_TESTS) # a test links whole, since what reaches for them is a library the linker # may read after an archive of them; a test names them where it needs them. add_library(newview_test_fakes OBJECT tests/newview_test_fakes.cpp) - target_link_libraries(newview_test_fakes PRIVATE al::flags llcommon) + # The harness for its headers: the recovery writer's helper names the state + # guard and the teardown. + target_link_libraries(newview_test_fakes PRIVATE al::flags al_test_support llcommon) set_target_properties(newview_test_fakes PROPERTIES FOLDER "Tests/${test_project}") # The viewer's settings group, gSavedSettings, for a test whose code reads # it: a group made as the process starts, so linked by those tests alone. diff --git a/indra/newview/tests/aldaycyclelandmarks_test.cpp b/indra/newview/tests/aldaycyclelandmarks_test.cpp index 28c930c042b..74e8d80c222 100644 --- a/indra/newview/tests/aldaycyclelandmarks_test.cpp +++ b/indra/newview/tests/aldaycyclelandmarks_test.cpp @@ -22,9 +22,7 @@ namespace { - // File scope rather than a fixture member: TUT's test bodies reach the - // fixture through a dependent base, so a name inherited from it is not - // visible inside a lambda written in one. + // At file scope, where every case and every lambda in one sees it. constexpr F32 TWO_PI = 6.28318530718f; } diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp index 2f3671686a6..d8ad693a9e6 100644 --- a/indra/newview/tests/lllogininstance_test.cpp +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -293,7 +293,7 @@ std::string xml_escape_string(const std::string& in) } /***************************************************************************** -* TUT +* Tests *****************************************************************************/ namespace { diff --git a/indra/newview/tests/llsecapi_test.cpp b/indra/newview/tests/llsecapi_test.cpp index 2697db68ee1..122a6c19bc9 100644 --- a/indra/newview/tests/llsecapi_test.cpp +++ b/indra/newview/tests/llsecapi_test.cpp @@ -79,7 +79,7 @@ void LLSecAPIBasicHandler::removeFromCredentialMap(const std::string& storage, c void LLSecAPIBasicHandler::removeCredentialMap(const std::string& storage, const std::string& grid) {} // ------------------------------------------------------------------------------------------- -// TUT +// Tests // ------------------------------------------------------------------------------------------- namespace { diff --git a/indra/newview/tests/llsechandler_basic_test.cpp b/indra/newview/tests/llsechandler_basic_test.cpp index 090cbc080ba..589f2843523 100644 --- a/indra/newview/tests/llsechandler_basic_test.cpp +++ b/indra/newview/tests/llsechandler_basic_test.cpp @@ -140,7 +140,7 @@ LLCertException::LLCertException(const LLSD& cert_data, const std::string& msg) // ------------------------------------------------------------------------------------------- -// TUT +// Tests // ------------------------------------------------------------------------------------------- namespace { diff --git a/indra/newview/tests/llslurl_test.cpp b/indra/newview/tests/llslurl_test.cpp index 30cbbd92cd9..02423885fc3 100644 --- a/indra/newview/tests/llslurl_test.cpp +++ b/indra/newview/tests/llslurl_test.cpp @@ -174,7 +174,7 @@ static const char *gSampleGridFile = ; // ------------------------------------------------------------------------------------------- -// TUT +// Tests // ------------------------------------------------------------------------------------------- namespace { diff --git a/indra/newview/tests/lltextureinfo_test.cpp b/indra/newview/tests/lltextureinfo_test.cpp index 805ac9ddbd5..01fb975a73d 100644 --- a/indra/newview/tests/lltextureinfo_test.cpp +++ b/indra/newview/tests/lltextureinfo_test.cpp @@ -40,7 +40,6 @@ #include "../llviewercontrol.h" #include "../llvocache.h" -// Tut header #include // ------------------------------------------------------------------------------------------- @@ -87,7 +86,7 @@ LLVOCache::~LLVOCache() {} // ------------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------- -// TUT +// Tests // ------------------------------------------------------------------------------------------- namespace @@ -142,8 +141,6 @@ namespace // Test functions // Notes: // * Test as many as you possibly can without requiring a full blown simulation of everything - // * The tests are executed in sequence so the test instance state may change between calls - // * Remember that you cannot test private methods with tut // --------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------- diff --git a/indra/newview/tests/llviewercontrollistener_test.cpp b/indra/newview/tests/llviewercontrollistener_test.cpp index c698bc23324..7292b97e0c6 100644 --- a/indra/newview/tests/llviewercontrollistener_test.cpp +++ b/indra/newview/tests/llviewercontrollistener_test.cpp @@ -27,7 +27,7 @@ #include /***************************************************************************** -* TUT +* Tests *****************************************************************************/ namespace { diff --git a/indra/newview/tests/llviewernetwork_test.cpp b/indra/newview/tests/llviewernetwork_test.cpp index bcf8558eb49..de0c435ca39 100644 --- a/indra/newview/tests/llviewernetwork_test.cpp +++ b/indra/newview/tests/llviewernetwork_test.cpp @@ -166,7 +166,7 @@ static const char *gSampleGridFile = "" ; // ------------------------------------------------------------------------------------------- -// TUT +// Tests // ------------------------------------------------------------------------------------------- namespace { diff --git a/indra/newview/tests/llvocache_test.cpp b/indra/newview/tests/llvocache_test.cpp index 2f286df188c..5e46eb6a3c2 100644 --- a/indra/newview/tests/llvocache_test.cpp +++ b/indra/newview/tests/llvocache_test.cpp @@ -78,7 +78,7 @@ const LLHost& LLViewerRegion::getHost() const { static const LLHost host; return LLViewerRegion* LLWorld::getRegionFromHandle(const U64&) { return nullptr; } // ------------------------------------------------------------------------------------------- -// TUT +// Tests // ------------------------------------------------------------------------------------------- namespace { diff --git a/indra/newview/tests/llworldmap_test.cpp b/indra/newview/tests/llworldmap_test.cpp index eb959ae8ef6..7a46f872eca 100644 --- a/indra/newview/tests/llworldmap_test.cpp +++ b/indra/newview/tests/llworldmap_test.cpp @@ -36,7 +36,6 @@ #include "../llworldmapmessage.h" // Class to test #include "../llworldmap.h" -// Tut header #include // ------------------------------------------------------------------------------------------- @@ -95,7 +94,7 @@ LLControlGroup gSavedSettings("llworldmap"); // ------------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------- -// TUT +// Tests // ------------------------------------------------------------------------------------------- const F32 X_WORLD_TEST = 1000.0f * REGION_WIDTH_METERS; @@ -163,8 +162,6 @@ namespace // Test functions // Notes: // * Test as many as you possibly can without requiring a full blown simulation of everything - // * The tests are executed in sequence so the test instance state may change between calls - // * Remember that you cannot test private methods with tut // --------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------- diff --git a/indra/newview/tests/llworldmipmap_test.cpp b/indra/newview/tests/llworldmipmap_test.cpp index f8d6ec4f5da..ce061bfe592 100644 --- a/indra/newview/tests/llworldmipmap_test.cpp +++ b/indra/newview/tests/llworldmipmap_test.cpp @@ -31,7 +31,6 @@ #include "../llviewercontrol.h" // Class to test #include "../llworldmipmap.h" -// Tut header #include // ------------------------------------------------------------------------------------------- @@ -55,7 +54,7 @@ LLControlGroup gSavedSettings("llworldmipmap"); // ------------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------- -// TUT +// Tests // ------------------------------------------------------------------------------------------- namespace { @@ -85,8 +84,6 @@ namespace // Test functions // Notes: // * Test as many as you possibly can without requiring a full blown simulation of everything - // * The tests are executed in sequence so the test instance state may change between calls - // * Remember that you cannot test private methods with tut // --------------------------------------------------------------------------------------- // Test static methods TEST_CASE_METHOD(worldmipmap_test, "LLWorldMipmap: scaleToLevel is MAP_LEVELS at zero and 1 from a tile's size up", "[LLWorldMipmap]") diff --git a/indra/newview/tests/newview_test_fakes.h b/indra/newview/tests/newview_test_fakes.h index 2e2e03ae3d9..ce2513665a5 100644 --- a/indra/newview/tests/newview_test_fakes.h +++ b/indra/newview/tests/newview_test_fakes.h @@ -25,11 +25,9 @@ #ifndef AL_NEWVIEW_TEST_FAKES_H #define AL_NEWVIEW_TEST_FAKES_H -#if AL_TEST_CATCH2 #include "alrecoverystore.h" #include "alteststateguard.h" #include "altestteardown.h" -#endif namespace LL { @@ -46,7 +44,6 @@ namespace newview_test // its being made chooses nothing for the tests after. LL::WorkQueue& mainLoop(); -#if AL_TEST_CATCH2 // The thread Script Studio's recovery stores write on, ALRecoveryWriter's, // the process's, which the viewer stops in its cleanup: made by the first // fixture that makes a store, before it does, as the store would have @@ -64,7 +61,6 @@ namespace newview_test }(); (void)made; } -#endif } #endif // AL_NEWVIEW_TEST_FAKES_H diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt index 76ea30cc235..764b7dcc9b2 100644 --- a/indra/test/CMakeLists.txt +++ b/indra/test/CMakeLists.txt @@ -1,30 +1,5 @@ # -*- cmake -*- -add_library(lltut_runner_lib STATIC) - -target_sources(lltut_runner_lib PRIVATE lltut.cpp test.cpp) - -target_sources( - lltut_runner_lib - PUBLIC - FILE_SET HEADERS - FILES - catch_and_store_what_in.h - chained_callback.h - debug.h - llsdtraits.h - lltestapp.h - lltut.h - namedtempfile.h - print.h - setenv.h - sync.h -) - -target_link_libraries(lltut_runner_lib PRIVATE al::flags PUBLIC llcommon ll::tut) - -set_target_properties(lltut_runner_lib PROPERTIES FOLDER "Tests") - # The Catch2 harness: the main, what an LL_ERRS does, LOGFAIL's replay, the # state guard, the teardown, the matchers, the running case's name and the # failure type. An OBJECT library, which each test binary links directly: in @@ -59,20 +34,19 @@ target_sources( altestname.h alteststateguard.h altestteardown.h + catch_and_store_what_in.h + debug.h + llsdtraits.h + lltestapp.h + namedtempfile.h + print.h + sync.h ) # The matchers compare as llmath's is_approx_equal_fraction() does, a header # function, so the tests need llmath's headers but not its library. target_include_directories(al_test_support PUBLIC ${INDRA_SOURCE_DIR}/llmath) target_link_libraries(al_test_support PRIVATE al::flags PUBLIC llcommon ll::catch2) -# Whatever links the harness is built for Catch2: a test binary and the -# OBJECT libraries of checks it links alike. The helpers in test/ that TUT -# tests still share fail a test the way its framework does by it, and so do -# the fixtures the tests share with their checks -- the headless UI, the -# shared GL context -- whose one-per-process object is a function's static: -# an object built without it would have a body of its own, with a static of -# its own, wherever the compiler inlined it, and a second UI or context. -target_compile_definitions(al_test_support PUBLIC AL_TEST_CATCH2=1) set_target_properties(al_test_support PROPERTIES FOLDER "Tests") diff --git a/indra/test/catch_and_store_what_in.h b/indra/test/catch_and_store_what_in.h index d3dada2acf8..27c62bea6ca 100644 --- a/indra/test/catch_and_store_what_in.h +++ b/indra/test/catch_and_store_what_in.h @@ -93,7 +93,7 @@ catch (const std::runtime_error& ex) \ else \ { \ /* We don't even recognize this exception. Let it propagate */ \ - /* out to TUT to fail the test. */ \ + /* out to Catch2 to fail the test. */ \ throw; \ } \ } \ diff --git a/indra/test/chained_callback.h b/indra/test/chained_callback.h deleted file mode 100644 index 984e2f1dbcf..00000000000 --- a/indra/test/chained_callback.h +++ /dev/null @@ -1,107 +0,0 @@ -/** - * @file chained_callback.h - * @author Nat Goodspeed - * @date 2020-01-03 - * @brief Subclass of tut::callback used for chaining callbacks. - * - * $LicenseInfo:firstyear=2020&license=viewerlgpl$ - * Copyright (c) 2020, Linden Research, Inc. - * $/LicenseInfo$ - */ - -#if ! defined(LL_CHAINED_CALLBACK_H) -#define LL_CHAINED_CALLBACK_H - -#include "lltut.h" - -/** - * Derive your TUT callback from chained_callback instead of tut::callback to - * ensure that multiple such callbacks can coexist in a given test executable. - * The relevant callback method will be called for each callback instance in - * reverse order of the instance's link() methods being called: the most - * recently link()ed callback will be called first, then the previous, and so - * forth. - * - * Obviously, for this to work, all relevant callbacks must be derived from - * chained_callback instead of tut::callback. Given that, control should reach - * each of them regardless of their construction order. The chain is - * guaranteed to stop because the first link() call will link to test_runner's - * default_callback, which is simply an instance of the callback() base class. - * - * The rule for deriving from chained_callback is that you may override any of - * its virtual methods, but your override must at some point call the - * corresponding chained_callback method. - */ -class chained_callback: public tut::callback -{ -public: - /** - * Instead of calling tut::test_runner::set_callback(&your_callback), call - * your_callback.link(); - * This uses the canonical instance of tut::test_runner. - */ - void link() - { - link(tut::runner.get()); - } - - /** - * If for some reason you have a different instance of test_runner... - */ - void link(tut::test_runner& runner) - { - // Since test_runner's constructor sets a default callback, - // get_callback() will always return a reference to a valid callback - // instance. - mPrev = &runner.get_callback(); - runner.set_callback(this); - } - - /** - * Called when new test run started. - */ - virtual void run_started() - { - mPrev->run_started(); - } - - /** - * Called when a group started - * @param name Name of the group - */ - virtual void group_started(const std::string& name) - { - mPrev->group_started(name); - } - - /** - * Called when a test finished. - * @param tr Test results. - */ - virtual void test_completed(const tut::test_result& tr) - { - mPrev->test_completed(tr); - } - - /** - * Called when a group is completed - * @param name Name of the group - */ - virtual void group_completed(const std::string& name) - { - mPrev->group_completed(name); - } - - /** - * Called when all tests in run completed. - */ - virtual void run_completed() - { - mPrev->run_completed(); - } - -private: - tut::callback* mPrev; -}; - -#endif /* ! defined(LL_CHAINED_CALLBACK_H) */ diff --git a/indra/test/debug.h b/indra/test/debug.h index 1579bb9c86a..0ee40e9ae64 100644 --- a/indra/test/debug.h +++ b/indra/test/debug.h @@ -46,7 +46,7 @@ * description. * * The variable LOGTEST is used because that's the environment variable - * checked by test.cpp, our TUT main() program, to turn on LLError logging. It + * checked by altestmain.cpp, the tests' main program, to turn on LLError logging. It * is expected that Debug is solely for use in test programs. */ class Debug diff --git a/indra/test/lltut.cpp b/indra/test/lltut.cpp deleted file mode 100644 index 36c8fe4078b..00000000000 --- a/indra/test/lltut.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/** - * @file lltut.cpp - * @author Mark Lentczner - * @date 5/16/06 - * @brief MacTester - * - * $LicenseInfo:firstyear=2006&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "lltut.h" - -#include "lldate.h" -#include "llformat.h" -#include "llsd.h" -#include "lluri.h" -#include "stringize.h" - -namespace tut -{ - void ensure_equals(const std::string& msg, const LLDate& actual, - const LLDate& expected) - { - ensure_equals(msg, - actual.secondsSinceEpoch(), expected.secondsSinceEpoch()); - } - - void ensure_equals(const std::string& msg, const LLURI& actual, - const LLURI& expected) - { - ensure_equals(msg, - actual.asString(), expected.asString()); - } - - // The lexical param types here intentionally diverge from the declaration - // in our header file. In lltut.h, LLSD is only a forward-declared type; - // we have no access to its LLSD::Binary nested type, and so must restate - // it explicitly to declare this overload. However, an overload that does - // NOT match LLSD::Binary does us no good whatsoever: it would never be - // engaged. Stating LLSD::Binary for this definition at least means that - // if the LLSD::Binary type ever diverges from what we expect in lltut.h, - // that divergence will produce an error: no definition will match that - // declaration. - void ensure_equals(const std::string& msg, - const LLSD::Binary& actual, const LLSD::Binary& expected) - { - ensure_equals(msg + " size", actual.size(), expected.size()); - - LLSD::Binary::const_iterator i, j; - int k; - for (i = actual.begin(), j = expected.begin(), k = 0; - i != actual.end(); - ++i, ++j, ++k) - { - ensure_equals(msg + " field", *i, *j); - } - } - - void ensure_equals(const std::string& msg, const LLSD& actual, - const LLSD& expected) - { - ensure_equals(msg + " type", actual.type(), expected.type()); - switch (actual.type()) - { - case LLSD::TypeUndefined: - return; - - case LLSD::TypeBoolean: - ensure_equals(msg + " boolean", actual.asBoolean(), expected.asBoolean()); - return; - - case LLSD::TypeInteger: - ensure_equals(msg + " integer", actual.asInteger(), expected.asInteger()); - return; - - case LLSD::TypeReal: - ensure_equals(msg + " real", actual.asReal(), expected.asReal()); - return; - - case LLSD::TypeString: - ensure_equals(msg + " string", actual.asString(), expected.asString()); - return; - - case LLSD::TypeUUID: - ensure_equals(msg + " uuid", actual.asUUID(), expected.asUUID()); - return; - - case LLSD::TypeDate: - ensure_equals(msg + " date", actual.asDate(), expected.asDate()); - return; - - case LLSD::TypeURI: - ensure_equals(msg + " uri", actual.asURI(), expected.asURI()); - return; - - case LLSD::TypeBinary: - ensure_equals(msg + " binary", actual.asBinary(), expected.asBinary()); - return; - - case LLSD::TypeMap: - { - ensure_equals(msg + " map size", actual.size(), expected.size()); - - LLSD::map_const_iterator actual_iter = actual.beginMap(); - LLSD::map_const_iterator expected_iter = expected.beginMap(); - - while(actual_iter != actual.endMap()) - { - ensure_equals(msg + " map keys", - actual_iter->first, expected_iter->first); - ensure_equals(msg + "[" + actual_iter->first + "]", - actual_iter->second, expected_iter->second); - ++actual_iter; - ++expected_iter; - } - return; - } - case LLSD::TypeArray: - { - ensure_equals(msg + " array size", actual.size(), expected.size()); - - for(int i = 0; i < actual.size(); ++i) - { - ensure_equals(msg + llformat("[%d]", i), - actual[i], expected[i]); - } - return; - } - default: - // should never get here, but compiler produces warning if we - // don't cover this case, and at Linden warnings are fatal. - throw failure(STRINGIZE("invalid type field " << actual.type())); - } - } - - void ensure_starts_with(const std::string& msg, - const std::string& actual, const std::string& expectedStart) - { - if( actual.find(expectedStart, 0) != 0 ) - { - std::stringstream ss; - ss << msg << ": " << "expected to find '" << expectedStart - << "' at start of actual '" << actual << "'"; - throw failure(ss.str().c_str()); - } - } - - void ensure_ends_with(const std::string& msg, - const std::string& actual, const std::string& expectedEnd) - { - if( actual.size() < expectedEnd.size() - || actual.rfind(expectedEnd) - != (actual.size() - expectedEnd.size()) ) - { - std::stringstream ss; - ss << msg << ": " << "expected to find '" << expectedEnd - << "' at end of actual '" << actual << "'"; - throw failure(ss.str().c_str()); - } - } - - void ensure_contains(const std::string& msg, - const std::string& actual, const std::string& expectedSubString) - { - if( actual.find(expectedSubString, 0) == std::string::npos ) - { - std::stringstream ss; - ss << msg << ": " << "expected to find '" << expectedSubString - << "' in actual '" << actual << "'"; - throw failure(ss.str().c_str()); - } - } - - void ensure_does_not_contain(const std::string& msg, - const std::string& actual, const std::string& expectedSubString) - { - if( actual.find(expectedSubString, 0) != std::string::npos ) - { - std::stringstream ss; - ss << msg << ": " << "expected not to find " << expectedSubString - << " in actual " << actual; - throw failure(ss.str().c_str()); - } - } -} - diff --git a/indra/test/lltut.h b/indra/test/lltut.h deleted file mode 100644 index fdc1cd7dfed..00000000000 --- a/indra/test/lltut.h +++ /dev/null @@ -1,159 +0,0 @@ -/** - * @file lltut.h - * @author Phoenix - * @date 2005-09-26 - * @brief helper tut methods - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLTUT_H -#define LL_LLTUT_H - -#include "is_approx_equal_fraction.h" // instead of llmath.h -#include - -class LLDate; -class LLSD; -class LLURI; - -namespace tut -{ - void ensure_equals(const std::string& msg, - const LLDate& actual, const LLDate& expected); - - void ensure_equals(const std::string& msg, - const LLURI& actual, const LLURI& expected); - - // std::vector is the current definition of LLSD::Binary. Because - // we're only forward-declaring LLSD in this header file, we can't - // directly reference that nested type. If the build complains that - // there's no definition for this declaration, it could be that - // LLSD::Binary has changed, and that this declaration must be adjusted to - // match. - void ensure_equals(const std::string& msg, - const std::vector& actual, const std::vector& expected); - - void ensure_equals(const std::string& msg, - const LLSD& actual, const LLSD& expected); - - void ensure_starts_with(const std::string& msg, - const std::string& actual, const std::string& expectedStart); - - void ensure_ends_with(const std::string& msg, - const std::string& actual, const std::string& expectedEnd); - - void ensure_contains(const std::string& msg, - const std::string& actual, const std::string& expectedSubString); - - void ensure_does_not_contain(const std::string& msg, - const std::string& actual, const std::string& expectedSubString); -} - -// This is an odd place to #include an important contributor -- but the usual -// rules are reversed here. Instead of the overloads above referencing tut.hpp -// features, we need calls in tut.hpp template functions to dispatch to our -// overloads declared above. - -// turn off warnings about unused functions from clang for tut package -#if LL_CLANG -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" -#endif -#include -#if LL_CLANG -#pragma clang diagnostic pop -#endif - -// The functions BELOW this point actually consume tut.hpp functionality. -namespace tut -{ - inline void ensure_approximately_equals(const char* msg, F64 actual, F64 expected, U32 frac_bits) - { - if(!is_approx_equal_fraction(actual, expected, frac_bits)) - { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "not equal actual: " << actual << " expected: " << expected; - throw tut::failure(ss.str().c_str()); - } - } - - inline void ensure_approximately_equals(const char* msg, F32 actual, F32 expected, U32 frac_bits) - { - if(!is_approx_equal_fraction(actual, expected, frac_bits)) - { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "not equal actual: " << actual << " expected: " << expected; - throw tut::failure(ss.str().c_str()); - } - } - - inline void ensure_approximately_equals(F32 actual, F32 expected, U32 frac_bits) - { - ensure_approximately_equals(NULL, actual, expected, frac_bits); - } - - inline void ensure_approximately_equals_range(const char *msg, F32 actual, F32 expected, F32 delta) - { - if (fabs(actual-expected)>delta) - { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "not equal actual: " << actual << " expected: " << expected << " tolerance: " << delta; - throw tut::failure(ss.str().c_str()); - } - } - - inline void ensure_memory_matches(const char* msg,const void* actual, U32 actual_len, const void* expected,U32 expected_len) - { - if((expected_len != actual_len) || - (std::memcmp(actual, expected, actual_len) != 0)) - { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "not equal"; - throw tut::failure(ss.str().c_str()); - } - } - - inline void ensure_memory_matches(const void* actual, U32 actual_len, const void* expected,U32 expected_len) - { - ensure_memory_matches("", actual, actual_len, expected, expected_len); - } - - template - void ensure_not_equals(const char* msg,const Q& actual,const T& expected) - { - if( expected == actual ) - { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "both equal " << expected; - throw tut::failure(ss.str().c_str()); - } - } - - template - void ensure_not_equals(const Q& actual,const T& expected) - { - ensure_not_equals(NULL, actual, expected); - } -} - -#endif // LL_LLTUT_H diff --git a/indra/test/setenv.h b/indra/test/setenv.h deleted file mode 100644 index a691697adb1..00000000000 --- a/indra/test/setenv.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - * @file setenv.h - * @author Nat Goodspeed - * @date 2020-04-01 - * @brief Provide a way for a particular test program to alter the - * environment before entry to main(). - * - * $LicenseInfo:firstyear=2020&license=viewerlgpl$ - * Copyright (c) 2020, Linden Research, Inc. - * $/LicenseInfo$ - */ - -#if ! defined(LL_SETENV_H) -#define LL_SETENV_H - -#include // setenv() - -/** - * Our test.cpp main program responds to environment variables LOGTEST and - * LOGFAIL. But if you set (e.g.) LOGTEST=DEBUG before a viewer build, @em - * every test program in the build emits debug log output. This can be so - * voluminous as to slow down the build. - * - * With an integration test program, you can specifically build (e.g.) the - * INTEGRATION_TEST_llstring target, and set any environment variables you - * want for that. But with a unit test program, since executing the program is - * a side effect rather than an explicit target, specifically building (e.g.) - * PROJECT_lllogin_TEST_lllogin only builds the executable without running it. - * - * To set an environment variable for a particular test program, declare a - * static instance of SetEnv in its .cpp file. SetEnv's constructor takes - * pairs of strings, e.g. - * - * @code - * static SetEnv sLOGGING("LOGTEST", "INFO"); - * @endcode - * - * Declaring a static instance of SetEnv is important because that ensures - * that the environment variables are set before main() is entered, since it - * is main() that examines LOGTEST and LOGFAIL. - */ -struct SetEnv -{ - // degenerate constructor, terminate recursion - SetEnv() {} - - /** - * SetEnv() accepts an arbitrary number of pairs of strings: variable - * name, value, variable name, value ... Entering the constructor sets - * those variables in the process environment using Posix setenv(), - * overriding any previous value. If static SetEnv declarations in - * different translation units specify overlapping sets of variable names, - * it is indeterminate which instance will "win." - */ - template - SetEnv(VAR&& var, VAL&& val, ARGS&&... rest): - // constructor forwarding handles the tail of the list - SetEnv(std::forward(rest)...) - { - // set just the first (variable, value) pair - // 1 means override previous value if any - setenv(std::forward(var), std::forward(val), 1); - } -}; - -#endif /* ! defined(LL_SETENV_H) */ diff --git a/indra/test/sync.h b/indra/test/sync.h index b7dd613adfd..b7bae5bd2ea 100644 --- a/indra/test/sync.h +++ b/indra/test/sync.h @@ -21,14 +21,8 @@ #include "llerror.h" #include "llcoros.h" -// A timeout fails the test the way the test's framework does, until the -// last TUT test that uses this is converted: al_add_test_binary defines -// AL_TEST_CATCH2 for a Catch2 binary. -#if AL_TEST_CATCH2 +// A timeout fails the test case, as a FAIL does. #include -#else -#include "lltut.h" -#endif /** * Instantiate Sync in any test in which we need to suspend one coroutine @@ -113,14 +107,8 @@ class Sync LL_DEBUGS() << name << " yield_until(" << until << ") suspending" << LL_ENDL; if (! mCond.wait_for_equal(mTimeout, until)) { -#if AL_TEST_CATCH2 FAIL(name << ' ' << func << '(' << arg << ") timed out after " << int(mTimeout.value()) << "ms (expected " << until << ", actual " << mCond.get() << ')'); -#else - tut::fail(STRINGIZE(name << ' ' << func << '(' << arg << ") timed out after " - << int(mTimeout.value()) << "ms (expected " << until - << ", actual " << mCond.get() << ')')); -#endif } // each time we wake up, bump mCond bump(); diff --git a/indra/test/test.cpp b/indra/test/test.cpp deleted file mode 100644 index f03385067dd..00000000000 --- a/indra/test/test.cpp +++ /dev/null @@ -1,550 +0,0 @@ -/** - * @file test.cpp - * @author Phoenix - * @date 2005-09-26 - * @brief Entry point for the test app. - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -/** - * - * You can add tests by creating a new cpp file in this directory, and - * rebuilding. There are at most 50 tests per testgroup without a - * little bit of template parameter and makefile tweaking. - * - */ - -#include "linden_common.h" -#include "llerrorcontrol.h" -#include "lltut.h" -#include "chained_callback.h" -#include "stringize.h" -#include "namedtempfile.h" -#include "lltrace.h" -#include "lltracethreadrecorder.h" -#include "llprocessor.h" - -#include "llapr.h" -#include "apr_pools.h" -#include "apr_getopt.h" - -#include -#include - -#include - -void wouldHaveCrashed(const std::string& message); - -namespace tut -{ - test_runner_singleton runner; -} - -class LLReplayLog -{ -public: - LLReplayLog() {} - virtual ~LLReplayLog() {} - - virtual void reset() {} - virtual void replay(std::ostream&) {} -}; - -class RecordToTempFile : public LLError::Recorder -{ -public: - RecordToTempFile(const RecordToTempFile&) = delete; - RecordToTempFile& operator=(const RecordToTempFile&) = delete; - - RecordToTempFile() - : LLError::Recorder(), - mTempFile("log", ""), - mFile(mTempFile.getPath()) - { - } - - virtual ~RecordToTempFile() - { - mFile.close(); - } - - virtual void recordMessage(LLError::ELevel level, const std::string& message) - { - LL_PROFILE_ZONE_SCOPED; - mFile << message << std::endl; - } - - void reset() - { - mFile.close(); - mFile.open(mTempFile.getPath()); - } - - void replay(std::ostream& out) - { - mFile.close(); - llifstream inf(mTempFile.getPath()); - std::string line; - while (std::getline(inf, line)) - { - out << line << std::endl; - } - } - -private: - NamedTempFile mTempFile; - llofstream mFile; -}; - -class LLReplayLogReal: public LLReplayLog -{ -public: - LLReplayLogReal(const LLReplayLogReal&) = delete; - LLReplayLogReal& operator=(const LLReplayLogReal&) = delete; - - LLReplayLogReal(LLError::ELevel level) - : LLReplayLog(), - mOldSettings(LLError::saveAndResetSettings()), - mRecorder(new RecordToTempFile()) - { - LLError::setFatalFunction(wouldHaveCrashed); - LLError::setDefaultLevel(level); - LLError::addRecorder(mRecorder); - } - - virtual ~LLReplayLogReal() - { - LLError::removeRecorder(mRecorder); - LLError::restoreSettings(mOldSettings); - } - - virtual void reset() - { - std::dynamic_pointer_cast(mRecorder)->reset(); - } - - virtual void replay(std::ostream& out) - { - std::dynamic_pointer_cast(mRecorder)->replay(out); - } - -private: - LLError::SettingsStoragePtr mOldSettings; - LLError::RecorderPtr mRecorder; -}; - -class LLTestCallback : public chained_callback -{ - typedef chained_callback super; - -public: - LLTestCallback(bool verbose_mode, std::ostream *stream, - std::shared_ptr replayer) : - mVerboseMode(verbose_mode), - mTotalTests(0), - mPassedTests(0), - mFailedTests(0), - mSkippedTests(0), - // By default, capture a shared_ptr to std::cout, with a no-op "deleter" - // so that destroying the shared_ptr makes no attempt to delete std::cout. - mStream(std::shared_ptr(&std::cout, [](std::ostream*){})), - mReplayer(replayer) - { - if (stream) - { - // We want a boost::iostreams::tee_device that will stream to two - // std::ostreams. - typedef boost::iostreams::tee_device TeeDevice; - // More than that, though, we want an actual stream using that - // device. - typedef boost::iostreams::stream TeeStream; - // Allocate and assign in two separate steps, per Herb Sutter. - // (Until we turn on C++11 support, have to wrap *stream with - // boost::ref() due to lack of perfect forwarding.) - std::shared_ptr pstream(new TeeStream(std::cout, boost::ref(*stream))); - mStream = pstream; - } - } - - ~LLTestCallback() - { - } - - virtual void run_started() - { - //std::cout << "run_started" << std::endl; - LL_INFOS("TestRunner")<<"Test Started"<< LL_ENDL; - super::run_started(); - } - - virtual void group_started(const std::string& name) { - LL_INFOS("TestRunner")<<"Unit test group_started name=" << name << LL_ENDL; - *mStream << "Unit test group_started name=" << name << std::endl; - super::group_started(name); - } - - virtual void group_completed(const std::string& name) { - LL_INFOS("TestRunner")<<"Unit test group_completed name=" << name << LL_ENDL; - *mStream << "Unit test group_completed name=" << name << std::endl; - super::group_completed(name); - } - - virtual void test_completed(const tut::test_result& tr) - { - ++mTotalTests; - - // If this test failed, dump requested log messages BEFORE stating the - // test result. - if (tr.result != tut::test_result::ok && tr.result != tut::test_result::skip) - { - mReplayer->replay(*mStream); - } - // Either way, clear stored messages in preparation for next test. - mReplayer->reset(); - - std::ostringstream out; - out << "[" << tr.group << ", " << tr.test; - if (! tr.name.empty()) - out << ": " << tr.name; - out << "] "; - switch(tr.result) - { - case tut::test_result::ok: - ++mPassedTests; - out << "ok"; - break; - case tut::test_result::fail: - ++mFailedTests; - out << "fail"; - break; - case tut::test_result::ex: - ++mFailedTests; - out << "exception: " << LLError::Log::demangle(tr.exception_typeid.c_str()); - break; - case tut::test_result::warn: - ++mFailedTests; - out << "test destructor throw"; - break; - case tut::test_result::term: - ++mFailedTests; - out << "abnormal termination"; - break; - case tut::test_result::skip: - ++mSkippedTests; - out << "skipped known failure"; - break; - default: - ++mFailedTests; - out << "unknown (tr.result == " << tr.result << ")"; - } - if(mVerboseMode || (tr.result != tut::test_result::ok)) - { - *mStream << out.str(); - if(!tr.message.empty()) - { - *mStream << ": '" << tr.message << "'"; - LL_WARNS("TestRunner") << "not ok : "< 0) - { - *mStream << "\tSkipped known failures:\t" << mSkippedTests - << std::endl; - } - - if(mFailedTests > 0) - { - *mStream << "*********************************" << std::endl; - *mStream << "Failed Tests:\t" << mFailedTests << std::endl; - *mStream << "Please report or fix the problem." << std::endl; - *mStream << "*********************************" << std::endl; - } - super::run_completed(); - } - -protected: - bool mVerboseMode; - int mTotalTests; - int mPassedTests; - int mFailedTests; - int mSkippedTests; - std::shared_ptr mStream; - std::shared_ptr mReplayer; -}; - -static const apr_getopt_option_t TEST_CL_OPTIONS[] = -{ - {"help", 'h', 0, "Print the help message."}, - {"list", 'l', 0, "List available test groups."}, - {"verbose", 'v', 0, "Verbose output."}, - {"group", 'g', 1, "Run test group specified by option argument."}, - {"output", 'o', 1, "Write output to the named file."}, - {"wait", 'w', 0, "Wait for input before exit."}, - {"debug", 'd', 0, "Emit full debug logs."}, - {"suitename", 'x', 1, "Run tests using this suitename"}, - {"isa-level", 'i', 1, "Exit 125, which CTest reads as skipped, unless the host runs this x86-64 level: 2 for SSE4.2, 3 for AVX2, 4 for AVX-512."}, - {0, 0, 0, 0} -}; - -// Whether the host can run a binary compiled for an x86-64 level. A level of -// 0 is the baseline, which anything the viewer builds for can run. -static bool host_runs_isa_level(int level) -{ - if (level <= 0) - { - return true; - } - LLProcessorInfo cpu; - switch (level) - { - case 2: return cpu.hasSSE42(); - case 3: return cpu.hasAVX2(); - case 4: return cpu.hasAVX512F(); - default: return false; - } -} - -void stream_usage(std::ostream& s, const char* app) -{ - s << "Usage: " << app << " [OPTIONS]" << std::endl - << std::endl; - - s << "This application runs the unit tests." << std::endl << std::endl; - - s << "Options: " << std::endl; - const apr_getopt_option_t* option = &TEST_CL_OPTIONS[0]; - while(option->name) - { - s << " "; - s << " -" << (char)option->optch << ", --" << option->name - << std::endl; - s << "\t" << option->description << std::endl << std::endl; - ++option; - } - - s << app << " is also sensitive to environment variables:\n" - << "LOGTEST=level : for all tests, emit log messages at level 'level'\n" - << "LOGFAIL=level : only for failed tests, emit log messages at level 'level'\n" - << "where 'level' is one of ALL, DEBUG, INFO, WARN, ERROR, NONE.\n" - << "--debug is like LOGTEST=DEBUG, but --debug overrides LOGTEST,\n" - << "while LOGTEST overrides LOGFAIL.\n\n"; - - s << "Examples:" << std::endl; - s << " " << app << " --verbose" << std::endl; - s << "\tRun all the tests and report all results." << std::endl; - s << " " << app << " --list" << std::endl; - s << "\tList all available test groups." << std::endl; - s << " " << app << " --group=uuid" << std::endl; - s << "\tRun the test group 'uuid'." << std::endl; - - s << "\n\n" - << "In any event, logs are recorded in the build directory by appending\n" - << "the suffix '.log' to the full path name of this application.\n" - << "If no level is specified as described above, these log files are at\n" - << "DEBUG level.\n" - ; -} - -void stream_groups(std::ostream& s, const char* app) -{ - s << "Registered test groups:" << std::endl; - tut::groupnames gl = tut::runner.get().list_groups(); - tut::groupnames::const_iterator it = gl.begin(); - tut::groupnames::const_iterator end = gl.end(); - for(; it != end; ++it) - { - s << " " << *(it) << std::endl; - } -} - -void wouldHaveCrashed(const std::string& message) -{ - tut::fail("llerrs message: " + message); -} - -static LLTrace::ThreadRecorder* sMasterThreadRecorder = NULL; - -int main(int argc, char **argv) -{ - // Call Tracy first thing to have it allocate memory - // https://github.com/wolfpld/tracy/issues/196 - LL_PROFILER_FRAME_END; - LL_PROFILER_SET_THREAD_NAME("App"); - - ll_init_apr(); - apr_getopt_t* os = NULL; - if(APR_SUCCESS != apr_getopt_init(&os, gAPRPoolp, argc, argv)) - { - std::cerr << "apr_getopt_init() failed" << std::endl; - return 1; - } - - // values used for controlling application - bool verbose_mode = false; - bool wait_at_exit = false; - std::string test_group; - std::string suite_name; - - // LOGTEST overrides default, but can be overridden by --debug. - const char* LOGTEST = getenv("LOGTEST"); - - // values used for options parsing - apr_status_t apr_err; - const char* opt_arg = NULL; - int opt_id = 0; - std::unique_ptr output; - - while(true) - { - apr_err = apr_getopt_long(os, TEST_CL_OPTIONS, &opt_id, &opt_arg); - if(APR_STATUS_IS_EOF(apr_err)) break; - if(apr_err) - { - char buf[255]; /* Flawfinder: ignore */ - std::cerr << "Error parsing options: " - << apr_strerror(apr_err, buf, 255) << std::endl; - return 1; - } - switch (opt_id) - { - case 'g': - test_group.assign(opt_arg); - break; - case 'h': - stream_usage(std::cout, argv[0]); - return 0; - break; - case 'l': - stream_groups(std::cout, argv[0]); - return 0; - case 'v': - verbose_mode = true; - break; - case 'o': - output.reset(new llofstream); - output->open(opt_arg); - break; - case 'w': - wait_at_exit = true; - break; - case 'd': - LOGTEST = "DEBUG"; - break; - case 'x': - suite_name.assign(opt_arg); - break; - case 'i': - if (!host_runs_isa_level(atoi(opt_arg))) - { - std::cout << "Skipped: this host does not run x86-64 level " << opt_arg << std::endl; - return 125; - } - break; - default: - stream_usage(std::cerr, argv[0]); - return 1; - break; - } - } - - // set up logging - const char* LOGFAIL = getenv("LOGFAIL"); - std::shared_ptr replayer{std::make_shared()}; - - // Testing environment variables for both 'set' and 'not empty' allows a - // user to suppress a pre-existing environment variable by forcing empty. - if (LOGTEST && *LOGTEST) - { - LLError::initForApplication(".", ".", true /* log to stderr */); - LLError::setDefaultLevel(LLError::decodeLevel(LOGTEST)); - } - else - { - LLError::initForApplication(".", ".", false /* do not log to stderr */); - LLError::setDefaultLevel(LLError::LEVEL_DEBUG); - if (LOGFAIL && *LOGFAIL) - { - LLError::ELevel level = LLError::decodeLevel(LOGFAIL); - replayer.reset(new LLReplayLogReal(level)); - } - } - LLError::setFatalFunction(wouldHaveCrashed); - std::string test_app_name(argv[0]); - std::string test_log = test_app_name + ".log"; - LLFile::remove(test_log); - LLError::logToFile(test_log); - - if (!sMasterThreadRecorder) - { - sMasterThreadRecorder = new LLTrace::ThreadRecorder(); - LLTrace::set_master_thread_recorder(sMasterThreadRecorder); - } - - // run the tests - - LLTestCallback* mycallback = new LLTestCallback(verbose_mode, output.get(), replayer); - - // a chained_callback subclass must be linked with previous - mycallback->link(); - - if(test_group.empty()) - { - tut::runner.get().run_tests(); - } - else - { - tut::runner.get().run_tests(test_group); - } - - bool success = (mycallback->getFailedTests() == 0); - - if (wait_at_exit) - { - std::cerr << "Press return to exit..." << std::endl; - std::cin.get(); - } - - ll_cleanup_apr(); - - int retval = (success ? 0 : 1); - return retval; - - //delete mycallback; -}