pax_global_header00006660000000000000000000000064151165576770014536gustar00rootroot0000000000000052 comment=a69c7866d6e1af01d14aab81cf790b9b210cabe2 openalgz-ut-a69c786/000077500000000000000000000000001511655767700143545ustar00rootroot00000000000000openalgz-ut-a69c786/.clang-format000066400000000000000000000073201511655767700167310ustar00rootroot00000000000000--- Language: Cpp AccessModifierOffset: -1 AlignAfterOpenBracket: Align AlignConsecutiveMacros: false AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlines: Left AlignOperands: true AlignTrailingComments: false AllowAllArgumentsOnNextLine: true AllowAllConstructorInitializersOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: Never AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: All AllowShortLambdasOnASingleLine: All AllowShortIfStatementsOnASingleLine: WithoutElse AllowShortLoopsOnASingleLine: true AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: Yes BinPackArguments: true BinPackParameters: true BraceWrapping: AfterCaseLabel: false AfterClass: true AfterControlStatement: false AfterEnum: false AfterFunction: true AfterNamespace: true AfterObjCDeclaration: false AfterStruct: true AfterUnion: false AfterExternBlock: false BeforeCatch: true BeforeElse: true IndentBraces: false SplitEmptyFunction: false SplitEmptyRecord: false SplitEmptyNamespace: false BreakBeforeBinaryOperators: None BreakBeforeBraces: Custom BreakBeforeInheritanceComma: false BreakInheritanceList: BeforeColon BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false BreakConstructorInitializers: BeforeColon BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true ColumnLimit: 120 CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 3 ContinuationIndentWidth: 3 Cpp11BracedListStyle: true DeriveLineEnding: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: false IncludeBlocks: Regroup IncludeCategories: - Regex: ^ Priority: 2 SortPriority: 0 - Regex: ^<.*\.h> Priority: 1 SortPriority: 0 - Regex: ^<.* Priority: 2 SortPriority: 0 - Regex: .* Priority: 3 SortPriority: 0 IncludeIsMainRegex: ([-_](test|unittest))?$ IncludeIsMainSourceRegex: "" IndentCaseLabels: false IndentGotoLabels: true IndentPPDirectives: None IndentWidth: 3 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: false MacroBlockBegin: "" MacroBlockEnd: "" MaxEmptyLinesToKeep: 1 NamespaceIndentation: All ObjCBinPackProtocolList: Never ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true PenaltyBreakAssignment: 2 PenaltyBreakBeforeFirstCallParameter: 1 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 200 PointerAlignment: Left ReflowComments: true SortIncludes: true SortUsingDeclarations: true SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyBlock: false SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInConditionalStatement: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false SpaceBeforeSquareBrackets: false Standard: Auto TabWidth: 8 UseCRLF: false UseTab: Never SpaceBeforeCaseColon: false BitFieldColonSpacing: Both EmptyLineAfterAccessModifier: Never EmptyLineBeforeAccessModifier: Always QualifierAlignment: Left ReferenceAlignment: Left openalgz-ut-a69c786/.github/000077500000000000000000000000001511655767700157145ustar00rootroot00000000000000openalgz-ut-a69c786/.github/workflows/000077500000000000000000000000001511655767700177515ustar00rootroot00000000000000openalgz-ut-a69c786/.github/workflows/clang-format.yml000066400000000000000000000011631511655767700230470ustar00rootroot00000000000000name: clang-format on: push: branches: - main paths-ignore: - '**.md' workflow_dispatch: jobs: build: runs-on: ubuntu-latest permissions: contents: write # Grant write permission to the repository content steps: - uses: actions/checkout@v4 - uses: DoozyX/clang-format-lint-action@v0.16.2 with: source: '.' exclude: '' extensions: 'h,hpp,cpp' clangFormatVersion: 16 style: file inplace: True - uses: EndBug/add-and-commit@v9 with: message: 'Committing clang-format changes' openalgz-ut-a69c786/.github/workflows/clang-linux.yml000066400000000000000000000022141511655767700227140ustar00rootroot00000000000000name: clang-linux on: push: branches: - main - feature/* paths-ignore: - '**.md' pull_request: branches: [main] paths-ignore: - '**.md' workflow_dispatch: jobs: build: runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: clang: [17, 18] build_type: [Debug] std: [23] env: CC: clang-${{matrix.clang}} CXX: clang++-${{matrix.clang}} steps: - uses: actions/checkout@v4 - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y libc++-dev libc++abi-dev - name: Configure CMake run: | cmake -B ${{github.workspace}}/build \ -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ -DCMAKE_CXX_STANDARD=${{matrix.std}} \ -DCMAKE_C_COMPILER=${{env.CC}} \ -DCMAKE_CXX_COMPILER=${{env.CXX}} \ -DCMAKE_CXX_FLAGS="-stdlib=libc++" \ -DCMAKE_EXE_LINKER_FLAGS="-stdlib=libc++ -lc++abi" - name: Build run: cmake --build build -j $(nproc) - name: Test working-directory: build run: ctest -j $(nproc) --output-on-failure openalgz-ut-a69c786/.github/workflows/clang.yml000066400000000000000000000011461511655767700215620ustar00rootroot00000000000000name: clang on: push: branches: - main - feature/* paths-ignore: - '**.md' pull_request: branches: [main] paths-ignore: - '**.md' workflow_dispatch: env: BUILD_TYPE: Debug jobs: build: runs-on: macos-latest steps: - uses: actions/checkout@v4 - name: Configure CMake run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} - name: Build run: cmake --build build --config ${{env.BUILD_TYPE}} -j 3 - name: Test working-directory: build run: ctest -C ${{env.BUILD_TYPE}} -j 3 --output-on-failure openalgz-ut-a69c786/.github/workflows/gcc.yml000066400000000000000000000014171511655767700212330ustar00rootroot00000000000000name: gcc on: push: branches: - main - feature/* paths-ignore: - '**.md' pull_request: branches: [main] paths-ignore: - '**.md' workflow_dispatch: jobs: build: runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: gcc: [13, 14] build_type: [Debug] std: [23] env: CC: gcc-${{matrix.gcc}} CXX: g++-${{matrix.gcc}} steps: - uses: actions/checkout@v4 - name: Configure CMake run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_STANDARD=${{matrix.std}} - name: Build run: cmake --build build -j $(nproc) - name: Test working-directory: build run: ctest -j $(nproc) --output-on-failure openalgz-ut-a69c786/.github/workflows/msvc.yml000066400000000000000000000014031511655767700214420ustar00rootroot00000000000000name: msvc on: push: branches: - main - 'feature/*' paths-ignore: - '**.md' pull_request: branches: [main] paths-ignore: - '**.md' workflow_dispatch: env: CTEST_OUTPUT_ON_FAILURE: 1 BUILD_TYPE: Debug jobs: build: runs-on: windows-latest timeout-minutes: 10 strategy: matrix: cpp_version: [23] steps: - uses: actions/checkout@v4 - name: Configure CMake run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_CXX_STANDARD=${{matrix.cpp_version}} - name: Build run: cmake --build build --config ${{env.BUILD_TYPE}} --parallel - name: Test working-directory: build run: ctest --build-config ${{env.BUILD_TYPE}} openalgz-ut-a69c786/.gitignore000066400000000000000000000000671511655767700163470ustar00rootroot00000000000000build* compile_commands.json .vscode .cache .DS_Store openalgz-ut-a69c786/CMakeLists.txt000077500000000000000000000022641511655767700171230ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.24) include(cmake/prelude.cmake) project( ut VERSION 1.1.0 LANGUAGES CXX ) include(cmake/project-is-top-level.cmake) include(cmake/variables.cmake) add_library(${PROJECT_NAME}_${PROJECT_NAME} INTERFACE) add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}_${PROJECT_NAME}) if (MSVC) string(REGEX MATCH "\/cl(.exe)?$" matched_cl ${CMAKE_CXX_COMPILER}) if (matched_cl) # for a C++ standards compliant preprocessor, not needed for clang-cl target_compile_options(${PROJECT_NAME}_${PROJECT_NAME} INTERFACE "/Zc:preprocessor" /GL /permissive- /Zc:lambda) target_link_options(${PROJECT_NAME}_${PROJECT_NAME} INTERFACE /LTCG /INCREMENTAL:NO) endif() endif() set_property(TARGET ${PROJECT_NAME}_${PROJECT_NAME} PROPERTY EXPORT_NAME ${PROJECT_NAME}) target_compile_features(${PROJECT_NAME}_${PROJECT_NAME} INTERFACE cxx_std_23) target_include_directories( ${PROJECT_NAME}_${PROJECT_NAME} ${warning_guard} INTERFACE "$" ) if(NOT CMAKE_SKIP_INSTALL_RULES) include(cmake/install-rules.cmake) endif() if (PROJECT_IS_TOP_LEVEL) include(cmake/dev-mode.cmake) endif()openalgz-ut-a69c786/LICENSE000066400000000000000000000020511511655767700153570ustar00rootroot00000000000000MIT License Copyright (c) 2024 openalgz Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. openalgz-ut-a69c786/README.md000066400000000000000000000033731511655767700156410ustar00rootroot00000000000000## UT: run-time and compile-time C++ unit-testing A simple and fast compiling unit test library. ## Tools - `suite` - Write a collection of tests - `"name"_test` - Declare a test - `test("name")` - Declare a test with a runtime name - `expect` - Check a boolean - `throws(func)` - Require a call to throw ### Features - Single header To enable compile time testing you must define the macro: `UT_COMPILE_TIME` Runtime testing is always enabled. ### Running Specific Tests Use the `UT_RUN` environment variable to run specific tests by name: ```bash # Single test UT_RUN="test name" ./my_tests # Multiple tests UT_RUN="[test1,test2,test3]" ./my_tests ``` ### Requirements - C++23 ## Example ```c++ #include "ut/ut.hpp" using namespace ut; suite tests = [] { "double"_test = [] { double v = 42.1; expect(42.1 == v) << "v is not 42.1"; expect[42.1 == v] << "a fatal error!"; }; "double mutable"_test = []() mutable { double v = 42.1; expect(42.1 == v) << "v is not 42.1"; expect[42.1 == v] << "oh no!"; }; "int"_test = [] { expect(5 + 4 == 9) << "bad"; expect[5 + 4 == 9] << "fatal"; }; "int consteval"_test = []() consteval { expect(5 + 4 == 9) << "bad"; expect[5 + 4 == 9] << "fatal"; }; "string"_test = [] { std::string_view v = "Hello World"; expect(v == "Hello World"); expect[v == "Hello World"]; }; test("runtime named test") = [] { std::string_view v = "Hello World"; expect(v == "Hello World"); expect[v == "Hello World"]; }; "throws"_test = []() mutable { expect(throws([] { throw std::runtime_error("I throw!"); })); }; "no throw"_test = []() mutable { expect(not throws([] { return 55; })); }; }; int main() {} ``` openalgz-ut-a69c786/cmake/000077500000000000000000000000001511655767700154345ustar00rootroot00000000000000openalgz-ut-a69c786/cmake/dev-mode.cmake000066400000000000000000000054101511655767700201360ustar00rootroot00000000000000enable_language(CXX) # Avoid warning about DOWNLOAD_EXTRACT_TIMESTAMP in CMake 3.24: if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") cmake_policy(SET CMP0135 NEW) endif() set_property(GLOBAL PROPERTY USE_FOLDERS YES) include(CTest) if(BUILD_TESTING) #add_subdirectory(tests) endif() # Done in developer mode only, so users won't be bothered by this :) file(GLOB_RECURSE headers CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/*.hpp") source_group(TREE "${PROJECT_SOURCE_DIR}/include" PREFIX headers FILES ${headers}) file(GLOB_RECURSE sources CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src/*.cpp") source_group(TREE "${PROJECT_SOURCE_DIR}/src" PREFIX sources FILES ${sources}) add_executable(${PROJECT_NAME}_ide ${sources} ${headers}) target_link_libraries(${PROJECT_NAME}_ide PRIVATE ${PROJECT_NAME}::${PROJECT_NAME}) set_target_properties(${PROJECT_NAME}_${PROJECT_NAME} ${PROJECT_NAME}_ide PROPERTIES FOLDER ProjectTargets) add_test(NAME ${PROJECT_NAME}_ide COMMAND ${PROJECT_NAME}_ide) # UT_RUN feature tests add_executable(ut_run_tests "${PROJECT_SOURCE_DIR}/tests/ut_run_tests.cpp") target_link_libraries(ut_run_tests PRIVATE ${PROJECT_NAME}::${PROJECT_NAME}) # Test: No filter - all 5 tests should run add_test(NAME ut_run_no_filter COMMAND ut_run_tests) set_tests_properties(ut_run_no_filter PROPERTIES PASS_REGULAR_EXPRESSION "tests: 5 \\(5 passed" ) # Test: Single test filter add_test(NAME ut_run_single COMMAND ut_run_tests) set_tests_properties(ut_run_single PROPERTIES ENVIRONMENT "UT_RUN=alpha" PASS_REGULAR_EXPRESSION "tests: 1 \\(1 passed" ) # Test: Single test with space in name add_test(NAME ut_run_single_with_space COMMAND ut_run_tests) set_tests_properties(ut_run_single_with_space PROPERTIES ENVIRONMENT "UT_RUN=delta test" PASS_REGULAR_EXPRESSION "tests: 1 \\(1 passed" ) # Test: Array filter with two tests add_test(NAME ut_run_array_two COMMAND ut_run_tests) set_tests_properties(ut_run_array_two PROPERTIES ENVIRONMENT "UT_RUN=[alpha,beta]" PASS_REGULAR_EXPRESSION "tests: 2 \\(2 passed" ) # Test: Array filter with three tests add_test(NAME ut_run_array_three COMMAND ut_run_tests) set_tests_properties(ut_run_array_three PROPERTIES ENVIRONMENT "UT_RUN=[alpha,beta,gamma]" PASS_REGULAR_EXPRESSION "tests: 3 \\(3 passed" ) # Test: Array filter with tests containing spaces add_test(NAME ut_run_array_with_spaces COMMAND ut_run_tests) set_tests_properties(ut_run_array_with_spaces PROPERTIES ENVIRONMENT "UT_RUN=[delta test,epsilon test]" PASS_REGULAR_EXPRESSION "tests: 2 \\(2 passed" ) # Test: Non-matching filter - no tests should run add_test(NAME ut_run_no_match COMMAND ut_run_tests) set_tests_properties(ut_run_no_match PROPERTIES ENVIRONMENT "UT_RUN=nonexistent" PASS_REGULAR_EXPRESSION "tests: 0 \\(0 passed" ) openalgz-ut-a69c786/cmake/install-config.cmake000066400000000000000000000001011511655767700213370ustar00rootroot00000000000000include("${CMAKE_CURRENT_LIST_DIR}/${PROJECT_NAME}Targets.cmake")openalgz-ut-a69c786/cmake/install-rules.cmake000066400000000000000000000030711511655767700212350ustar00rootroot00000000000000if(PROJECT_IS_TOP_LEVEL) set(CMAKE_INSTALL_INCLUDEDIR include/${PROJECT_NAME} CACHE PATH "") endif() # Project is configured with no languages, so tell GNUInstallDirs the lib dir set(CMAKE_INSTALL_LIBDIR lib CACHE PATH "") include(CMakePackageConfigHelpers) include(GNUInstallDirs) # find_package() call for consumers to find this project set(package ${PROJECT_NAME}) install( DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" COMPONENT ${PROJECT_NAME}_Development ) install( TARGETS ${PROJECT_NAME}_${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" ) write_basic_package_version_file( "${package}ConfigVersion.cmake" COMPATIBILITY SameMajorVersion ARCH_INDEPENDENT ) # Allow package maintainers to freely override the path for the configs set( zb8_INSTALL_CMAKEDIR "${CMAKE_INSTALL_DATADIR}/${package}" CACHE PATH "CMake package config location relative to the install prefix" ) mark_as_advanced(${PROJECT_NAME}_INSTALL_CMAKEDIR) install( FILES cmake/install-config.cmake DESTINATION "${zb8_INSTALL_CMAKEDIR}" RENAME "${package}Config.cmake" COMPONENT ${PROJECT_NAME}_Development ) install( FILES "${PROJECT_BINARY_DIR}/${package}ConfigVersion.cmake" DESTINATION "${zb8_INSTALL_CMAKEDIR}" COMPONENT ${PROJECT_NAME}_Development ) install( EXPORT ${PROJECT_NAME}Targets NAMESPACE ${PROJECT_NAME}:: DESTINATION "${zb8_INSTALL_CMAKEDIR}" COMPONENT ${PROJECT_NAME}_Development ) if(PROJECT_IS_TOP_LEVEL) include(CPack) endif() openalgz-ut-a69c786/cmake/prelude.cmake000066400000000000000000000004731511655767700201020ustar00rootroot00000000000000# ---- In-source guard ---- if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) message( FATAL_ERROR "In-source builds are not supported. " "Please read the BUILDING document before trying to build this project. " "You may need to delete 'CMakeCache.txt' and 'CMakeFiles/' first." ) endif() openalgz-ut-a69c786/cmake/project-is-top-level.cmake000066400000000000000000000002321511655767700224170ustar00rootroot00000000000000# This variable is set by project() in CMake 3.21+ string( COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}" PROJECT_IS_TOP_LEVEL ) openalgz-ut-a69c786/cmake/variables.cmake000066400000000000000000000020401511655767700204020ustar00rootroot00000000000000# ---- Developer mode ---- # Developer mode enables targets and code paths in the CMake scripts that are # only relevant for the developer(s) of this project # Targets necessary to build the project must be provided unconditionally, so # consumers can trivially build and package the project if(PROJECT_IS_TOP_LEVEL) option(${PROJECT_NAME}_DEVELOPER_MODE "Enable developer mode" OFF) endif() # ---- Warning guard ---- # target_include_directories with the SYSTEM modifier will request the compiler # to omit warnings from the provided paths, if the compiler supports that # This is to provide a user experience similar to find_package when # add_subdirectory or FetchContent is used to consume this project set(warning_guard "") if(NOT PROJECT_IS_TOP_LEVEL) option( ${PROJECT_NAME}_INCLUDES_WITH_SYSTEM "Use SYSTEM modifier for ${PROJECT_NAME}'s includes, disabling warnings" ON ) mark_as_advanced(${PROJECT_NAME}_INCLUDES_WITH_SYSTEM) if(${PROJECT_NAME}_INCLUDES_WITH_SYSTEM) set(warning_guard SYSTEM) endif() endif() openalgz-ut-a69c786/include/000077500000000000000000000000001511655767700157775ustar00rootroot00000000000000openalgz-ut-a69c786/include/ut/000077500000000000000000000000001511655767700164275ustar00rootroot00000000000000openalgz-ut-a69c786/include/ut/ut.hpp000066400000000000000000000314461511655767700176000ustar00rootroot00000000000000// Refactored from: // Copyright (c) 2024 Kris Jusiak (kris at jusiak dot net) // Distributed under the Boost Software License, Version 1.0. // (See http://www.boost.org/LICENSE_1_0.txt) // // UT: A simple C++23 unit testing library with compile-time and run-time support. // // Running specific tests: // Set the UT_RUN environment variable to run only specific tests by name. // Single test: UT_RUN="my test" ./my_tests // Multiple tests: UT_RUN="[test1,test2,test3]" ./my_tests // If UT_RUN is not set, all tests run (default behavior). #pragma once #include #include #include #include #include #include namespace ut { namespace detail { constexpr bool fatal = true; template constexpr auto is_mutable_lambda_v = false; template constexpr auto is_mutable_lambda_v = true; template constexpr auto is_mutable_lambda_v = false; template constexpr auto has_capture_lambda_v = sizeof(Fn) > 1ul; template struct identity { using type = T; }; } template struct fixed_string { constexpr fixed_string(const char (&str)[Size]) { for (size_t i = 0; i < Size; ++i) { storage[i] = str[i]; } } [[nodiscard]] constexpr auto operator[](const auto i) const { return storage[i]; } [[nodiscard]] constexpr auto data() const { return storage; } [[nodiscard]] static constexpr auto size() { return Size - 1; } [[nodiscard]] constexpr operator std::string_view() const { return {storage, Size - 1}; } constexpr friend auto operator<<(auto& os, const fixed_string& fs) -> decltype(auto) { return os << std::string_view{fs.storage, fs.size()}; } char storage[Size]{}; }; namespace events { enum class mode { run_time, compile_time }; template struct test_begin { std::string_view file_name{}; uint_least32_t line{}; std::string_view name{}; }; template struct test_end { std::string_view file_name{}; uint_least32_t line{}; std::string_view name{}; enum { FAILED, PASSED, COMPILE_TIME } result{}; }; struct assertion { bool passed{}; std::string_view file_name{}; uint_least32_t line{}; }; struct fatal {}; template struct log { const Msg& msg; bool result{}; }; struct summary { enum { FAILED, PASSED, COMPILE_TIME }; size_t asserts[2]{}; /* FAILED, PASSED */ size_t tests[3]{}; /* FAILED, PASSED, COMPILE_TIME */ }; } // namespace events template struct outputter { template constexpr auto on(const events::test_begin&) {} constexpr auto on(const events::test_begin& event) { current_test = event; } template constexpr auto on(const events::test_end&) {} constexpr auto on(const events::assertion& event) { if (not event.passed && not std::is_constant_evaluated()) { if (initial_new_line == '\n') { os << initial_new_line; } else { initial_new_line = '\n'; } os << "FAILED \"" << current_test.name << "\" "; const auto n = event.file_name.size(); const auto start = n <= 32 ? 0 : n - 32; if (start > 0) { os << "..."; } os << event.file_name.substr(start, n) << ":" << event.line << '\n'; } } constexpr auto on(const events::fatal&) {} template constexpr auto on(const events::log& event) { if (!std::is_constant_evaluated() && !event.result) { os << ' ' << event.msg; } } constexpr auto on(const events::summary& event) { using namespace events; if (!std::is_constant_evaluated()) { if (event.asserts[summary::FAILED] || event.tests[summary::FAILED]) { os << "\nFAILED\n"; } else { os << "\nPASSED\n"; } os << "tests: " << (event.tests[summary::PASSED] + event.tests[summary::FAILED]) << " (" << event.tests[summary::PASSED] << " passed, " << event.tests[summary::FAILED] << " failed, " << event.tests[summary::COMPILE_TIME] << " compile-time)\n" << "asserts: " << (event.asserts[summary::PASSED] + event.asserts[summary::FAILED]) << " (" << event.asserts[summary::PASSED] << " passed, " << event.asserts[summary::FAILED] << " failed)\n"; } } OStream& os; events::test_begin current_test{}; char initial_new_line{}; }; template struct reporter { constexpr auto on(const events::test_begin& event) { asserts_failed[current++] = summary.asserts[events::summary::FAILED]; outputter.on(event); } constexpr auto on(const events::test_end& event) { const auto result = summary.asserts[events::summary::FAILED] == asserts_failed[--current]; ++summary.tests[result]; events::test_end te{event}; te.result = static_cast(result); outputter.on(te); } constexpr auto on(const events::test_begin&) { ++summary.tests[events::summary::COMPILE_TIME]; } constexpr auto on(const events::test_end&) {} constexpr auto on(const events::assertion& event) { if (event.passed) { ++summary.asserts[events::summary::PASSED]; } else { ++summary.asserts[events::summary::FAILED]; } outputter.on(event); } constexpr auto on(const events::fatal& event) { ++summary.tests[events::summary::FAILED]; outputter.on(event); outputter.on(summary); std::exit(1); } ~reporter() { // non constexpr outputter.on(summary); if (summary.asserts[events::summary::FAILED]) { std::exit(1); } } Outputter& outputter; events::summary summary{}; size_t asserts_failed[MaxDepth]{}; size_t current{}; }; template struct runner { template constexpr auto on(Test test, const std::string_view file_name, uint_least32_t line, const std::string_view name) -> bool { if (std::is_constant_evaluated()) { if constexpr (requires { requires detail::is_mutable_lambda_v; }) { return false; } else { test(); return true; } } else { static const std::string_view filter = []() -> std::string_view { if (const char* env = std::getenv("UT_RUN")) return env; return {}; }(); auto matches_filter = [](std::string_view test_name, std::string_view f) { if (f.empty()) return true; // Array format: [test1,test2,test3] if (f.starts_with('[') && f.ends_with(']')) { auto content = f.substr(1, f.size() - 2); size_t pos = 0; while (pos < content.size()) { auto comma = content.find(',', pos); auto token = (comma == std::string_view::npos) ? content.substr(pos) : content.substr(pos, comma - pos); if (token == test_name) return true; if (comma == std::string_view::npos) break; pos = comma + 1; } return false; } // Single test name return test_name == f; }; if (!matches_filter(name, filter)) { return false; } #if defined(UT_COMPILE_TIME) if constexpr (!requires { requires detail::is_mutable_lambda_v; } && !detail::has_capture_lambda_v) { reporter.on(events::test_begin{file_name, line, name}); static_assert((test(), "[FAILED]")); reporter.on(events::test_end{file_name, line, name}); } #endif reporter.on(events::test_begin{file_name, line, name}); test(); reporter.on(events::test_end{file_name, line, name}); } return true; } Reporter& reporter; }; } namespace ut { inline struct { struct { friend constexpr decltype(auto) operator<<([[maybe_unused]] auto& os, [[maybe_unused]] const auto& t) { static_assert(requires { std::clog << t; }); return (std::clog << t); } } stream; ut::outputter outputter{stream}; ut::reporter reporter{outputter}; ut::runner runner{reporter}; } cfg; constexpr struct { template struct eval final { template requires std::convertible_to constexpr eval(T&& test_passed, auto&& loc) : passed(static_cast(test_passed)) { if (std::is_constant_evaluated()) { if (not passed) { std::abort(); } } else { cfg.reporter.on(events::assertion{passed, loc.file_name(), loc.line()}); if (not passed) { if constexpr (Fatal) { cfg.reporter.on(events::fatal{}); } } } } bool passed{}; }; template requires std::convertible_to constexpr auto operator()(T&& test_passed, const std::source_location& loc = std::source_location::current()) const { return log{eval{test_passed, loc}.passed}; } #if __cplusplus >= 202300L // if we have C++23 template requires std::convertible_to constexpr auto operator[](T&& test_passed, const std::source_location& loc = std::source_location::current()) const { return log{eval{test_passed, loc}.passed}; } #else template requires std::convertible_to constexpr auto operator[](T&& test_passed) const { return log{eval{test_passed, std::source_location::current()}.passed}; } #endif private: struct log final { bool passed{}; template constexpr const auto& operator<<(const Msg& msg) const { cfg.outputter.on(events::log{msg, passed}); return *this; } }; } expect{}; struct suite final { suite(auto&& tests) { tests(); } }; namespace detail { template struct test final { constexpr auto operator=(auto test) const { const auto& loc = std::source_location::current(); return cfg.runner.on(test, loc.file_name(), loc.line(), Name); } }; struct runtime_test final { std::string_view name{}; constexpr auto operator=(auto test) const { const auto& loc = std::source_location::current(); return cfg.runner.on(test, loc.file_name(), loc.line(), name); } }; } constexpr auto test(const std::string_view name) { return detail::runtime_test{name}; } template [[nodiscard]] constexpr auto operator""_test() { return detail::test{}; } #if __cpp_exceptions template constexpr auto throws(Callable&& c, Args&&... args) { try { std::forward(c)(std::forward(args)...); } catch (...) { return true; } return false; } #endif } using ut::operator""_test; openalgz-ut-a69c786/src/000077500000000000000000000000001511655767700151435ustar00rootroot00000000000000openalgz-ut-a69c786/src/main.cpp000066400000000000000000000026421511655767700165770ustar00rootroot00000000000000#define UT_COMPILE_TIME #include "ut/ut.hpp" using namespace ut; #include #include suite tests = [] { "double"_test = [] { double v = 42.1; expect(42.1 == v) << "v is not 42.1"; expect[42.1 == v] << "a fatal error!"; }; // a runtime-only test because the lambda is mutable "double mutable"_test = []() mutable { double v = 42.1; expect(42.1 == v) << "v is not 42.1"; expect[42.1 == v] << "oh no!"; }; "int"_test = [] { expect(5 + 4 == 9) << "bad"; expect[5 + 4 == 9] << "fatal"; }; // a compile-time only test because the lambda is consteval "int consteval"_test = []() consteval { expect(5 + 4 == 9) << "bad"; expect[5 + 4 == 9] << "fatal"; }; "string"_test = [] { std::string_view v = "Hello World"; expect(v == "Hello World"); expect[v == "Hello World"]; }; test("runtime named test") = [] { std::string_view v = "Hello World"; expect(v == "Hello World"); expect[v == "Hello World"]; }; "throws"_test = []() mutable { expect(throws([] { throw std::runtime_error("I throw!"); })); }; "no throw"_test = []() mutable { expect(not throws([] { return 55; })); }; }; struct boolean_type { bool b = true; operator bool() const { return b; } }; suite bool_convertible = [] { "boolean_type"_test = []() mutable { expect(boolean_type{}); }; }; int main() {} openalgz-ut-a69c786/tests/000077500000000000000000000000001511655767700155165ustar00rootroot00000000000000openalgz-ut-a69c786/tests/ut_run_tests.cpp000066400000000000000000000007601511655767700207630ustar00rootroot00000000000000// Tests for UT_RUN environment variable filtering feature // This file is used by CTest to verify the UT_RUN functionality #include "ut/ut.hpp" using namespace ut; // Define exactly 5 tests with known names for predictable testing suite filter_tests = [] { "alpha"_test = [] { expect(true); }; "beta"_test = [] { expect(true); }; "gamma"_test = [] { expect(true); }; test("delta test") = [] { expect(true); }; test("epsilon test") = [] { expect(true); }; }; int main() {}