diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 00000000..cd8a2a4b --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,40 @@ +Checks: >- + boost-*, + bugprone-*, + cert-*, + clang-analyzer-*, + cppcoreguidelines-*, + google-*, + hicpp-*, + llvm-*, + misc-*, + modernize-*, + performance-*, + portability-*, + readability-*, + -boost-use-ranges, + -clang-analyzer-core.uninitialized.Assign, + -cppcoreguidelines-avoid-const-or-ref-data-members, + -cppcoreguidelines-use-default-member-init, + -google-readability-avoid-underscore-in-googletest-name, + -google-readability-todo, + -llvm-header-guard, + -modernize-concat-nested-namespaces, + -modernize-type-traits, + -modernize-use-constraints, + -modernize-use-default-member-init, + -modernize-use-designated-initializers, + -modernize-use-nodiscard, + -modernize-use-ranges, + -readability-avoid-const-params-in-decls, + -readability-identifier-length, + -*-use-trailing-return-type, + -*-named-parameter, +CheckOptions: + - key: readability-function-cognitive-complexity.Threshold + value: '90' + - key: readability-magic-numbers.IgnoredIntegerValues + value: '1;2;3;4;5;8;10;16;20;32;60;64;100;128;256;500;512;1000' +WarningsAsErrors: '*' +HeaderFilterRegex: 'include/cetl/.*\.hpp' +FormatStyle: file diff --git a/.github/workflows/cetlvast.yml b/.github/workflows/cetlvast.yml index a9e5ce71..a8869dde 100644 --- a/.github/workflows/cetlvast.yml +++ b/.github/workflows/cetlvast.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest container: ghcr.io/opencyphal/toolshed:ts24.4.3 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Cache ext modules id: cetlvast-ext uses: actions/cache@v4 @@ -52,7 +52,7 @@ jobs: toolchain: ["gcc-native-32", "clang-native"] standard: ["cpp-14", "cpp-17", "cpp-20", "cpp-23"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Cache ext modules uses: actions/cache@v4 with: @@ -93,7 +93,7 @@ jobs: toolchain: ["gcc-native", "clang-native"] standard: ["cpp-14", "cpp-17", "cpp-20"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Cache ext modules uses: actions/cache@v4 with: @@ -130,7 +130,7 @@ jobs: needs: - cache-warmup steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Cache ext modules uses: actions/cache@v4 with: @@ -148,7 +148,7 @@ jobs: uses: actions/configure-pages@v5 - name: Upload docs if: ${{ github.event_name != 'pull_request' }} - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v4 with: path: "cetlvast/build/suites/docs/html/" @@ -168,7 +168,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis by sonarqube. - name: Cache ext modules @@ -188,25 +188,8 @@ jobs: working-directory: cetlvast run: cmake --workflow --preset manual-coverage-gcc-native-cpp-14-offline - name: SonarQube Scan - uses: SonarSource/sonarqube-scan-action@v5.3.0 + uses: SonarSource/sonarqube-scan-action@v5.3.1 if: ${{ env.SONAR_TOKEN != '' }} - with: - args: > - -Dsonar.organization=opencyphal - -Dsonar.projectKey=OpenCyphal_CETL - -Dsonar.projectName=CETLVaSt - -Dsonar.verbose=true - -Dsonar.cfamily.compile-commands=cetlvast/build/compile_commands.json - -Dsonar.cfamily.cobertura.reportPaths=cetlvast/build/suites/unittest/coverage.xml - -Dsonar.testExecutionReportPaths=cetlvast/build/suites/unittest/unittest-sonarqube.xml - -Dsonar.sources="include,cetlvast/suites/unittest/sonar.cpp" - -Dsonar.tests="cetlvast/suites/unittest" - -Dsonar.test.inclusions="**/test_*.cpp" - -Dsonar.cfamily.ignoreHeaderComments=false - -Dsonar.coverage.exclusions="cetlvast/**/*,**/sonar.cpp" - -Dsonar.cpd.exclusions="cetlvast/**/*,**/sonar.cpp" - -Dsonar.cfamily.reportingCppStandardOverride=c++14 - deploy-docs: if: > (github.event_name == 'release' && !github.event.release.prerelease) || diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 80d14da8..30236cb8 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -30,7 +30,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.gitignore b/.gitignore index 58e8db97..536daa19 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,6 @@ Vagrantfile cmake-build-*/ *.gcov + +# clangd +.cache diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 985c340e..446b3a69 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -7,7 +7,7 @@ "xaver.clang-format", "vadimcn.vscode-lldb", "matepek.vscode-catch2-test-adapter", - "ms-vscode.hexeditor", - "Jme797.prettier-vscode-extension" + "sonarsource.sonarlint-vscode", + "llvm-vs-code-extensions.vscode-clangd" ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index e385410a..eef5a329 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,8 @@ "cmake.configureOnEdit": false, "cmake.sourceDirectory": "${workspaceFolder}/cetlvast", "cmake.buildBeforeRun": true, + "C_Cpp.intelliSenseEngine": "disabled", + "C_Cpp.autoAddFileAssociations": false, "editor.wordWrapColumn": 120, "files.insertFinalNewline": true, "files.trimFinalNewlines": true, @@ -30,7 +32,7 @@ "pattern": "${workspaceFolder}/cetlvast/build/suites/benchmark/**/benchmark_*" } ], - "sonarlint.pathToCompileCommands": "${workspaceFolder}/build/compile_commands.json", + "sonarlint.pathToCompileCommands": "${workspaceFolder}/cetlvast/build/compile_commands.json", "sonarlint.connectedMode.project": { "connectionId": "opencyphal", "projectKey": "OpenCyphal_CETL" @@ -56,110 +58,6 @@ }, "files.associations": { "*.in": "cmake", - "Config": "perl", - "memory_resource": "cpp", - "any": "cpp", - "array": "cpp", - "atomic": "cpp", - "bit": "cpp", - "*.tcc": "cpp", - "bitset": "cpp", - "cctype": "cpp", - "chrono": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "codecvt": "cpp", - "compare": "cpp", - "concepts": "cpp", - "condition_variable": "cpp", - "cstdarg": "cpp", - "cstddef": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "deque": "cpp", - "forward_list": "cpp", - "list": "cpp", - "map": "cpp", - "set": "cpp", - "string": "cpp", - "unordered_map": "cpp", - "unordered_set": "cpp", - "vector": "cpp", - "exception": "cpp", - "algorithm": "cpp", - "functional": "cpp", - "iterator": "cpp", - "memory": "cpp", - "numeric": "cpp", - "optional": "cpp", - "random": "cpp", - "ratio": "cpp", - "string_view": "cpp", - "system_error": "cpp", - "tuple": "cpp", - "type_traits": "cpp", - "utility": "cpp", - "fstream": "cpp", - "future": "cpp", - "initializer_list": "cpp", - "iomanip": "cpp", - "iosfwd": "cpp", - "iostream": "cpp", - "istream": "cpp", - "limits": "cpp", - "mutex": "cpp", - "new": "cpp", - "numbers": "cpp", - "ostream": "cpp", - "regex": "cpp", - "semaphore": "cpp", - "shared_mutex": "cpp", - "span": "cpp", - "sstream": "cpp", - "stdexcept": "cpp", - "stop_token": "cpp", - "streambuf": "cpp", - "thread": "cpp", - "cinttypes": "cpp", - "typeinfo": "cpp", - "variant": "cpp", - "__bit_reference": "cpp", - "__bits": "cpp", - "__config": "cpp", - "__debug": "cpp", - "__errc": "cpp", - "__hash_table": "cpp", - "__locale": "cpp", - "__mutex_base": "cpp", - "__node_handle": "cpp", - "__split_buffer": "cpp", - "__threading_support": "cpp", - "__tree": "cpp", - "__tuple": "cpp", - "__verbose_abort": "cpp", - "ios": "cpp", - "locale": "cpp", - "queue": "cpp", - "version": "cpp", - "cassert": "cpp", - "__memory": "cpp", - "__nullptr": "cpp", - "__string": "cpp", - "ranges": "cpp", - "complex": "cpp", - "stack": "cpp", - "strstream": "cpp", - "typeindex": "cpp", - "charconv": "cpp", - "csignal": "cpp", - "format": "cpp", - "execution": "cpp", - "filesystem": "cpp", - "print": "cpp" + "*.tcc": "cpp" } } diff --git a/.vscode/words-cetl.txt b/.vscode/words-cetl.txt index 36575a50..81c4e7ea 100644 --- a/.vscode/words-cetl.txt +++ b/.vscode/words-cetl.txt @@ -1,9 +1,10 @@ - arity +arity AUTOSAR builddir cetl cetlpf cetlvast +cfamily COMPILETEST_PRECHECK copydoc ctest @@ -32,6 +33,7 @@ NOLINT NOLINTBEGIN NOLINTEND NOLINTNEXTLINE +NOSONAR NOTFOUND opencyphal Pavel diff --git a/cetlvast/CMakeLists.txt b/cetlvast/CMakeLists.txt index d4866c09..d09c509d 100644 --- a/cetlvast/CMakeLists.txt +++ b/cetlvast/CMakeLists.txt @@ -206,12 +206,16 @@ else() ) endif() -add_custom_target( - docs - DEPENDS - lint - generate_CETL_docs -) +if(DEFINED Doxygen_FOUND) +if(${Doxygen_FOUND}) + add_custom_target( + docs + DEPENDS + lint + generate_CETL_docs + ) +endif() +endif() add_custom_target( build diff --git a/cetlvast/CMakePresets.json b/cetlvast/CMakePresets.json index 1c119335..e9fc8898 100644 --- a/cetlvast/CMakePresets.json +++ b/cetlvast/CMakePresets.json @@ -502,20 +502,6 @@ "gcovr_sonarqube_report_for_examples" ] }, - { - "name": "manual-Coverage-no-scan-gcc-native-cpp-14-offline", - "configurePreset": "configure-gcc-native-cpp-14-offline", - "configuration": "Coverage", - "targets": [ - "build", - "run_unittests", - "gcovr_html_report_for_unittest", - "gcovr_sonarqube_report_for_unittest", - "run_examples", - "gcovr_html_report_for_examples", - "gcovr_sonarqube_report_for_examples" - ] - }, { "name": "build-Release-gcc-native-cpp-14-online", "configurePreset": "configure-gcc-native-cpp-14-online", @@ -2112,21 +2098,6 @@ } ] }, - { - "name": "manual-sonar-scan-gcc-native-cpp-14-offline", - "displayName": "gcc native cpp 14 offline (Coverage only).", - "description": "Hand-managed workflow for generating coverage data for sonarqube scans.", - "steps": [ - { - "type": "configure", - "name": "configure-gcc-native-cpp-14-offline" - }, - { - "type": "build", - "name": "manual-Coverage-gcc-native-cpp-14-offline" - } - ] - }, { "name": "manual-coverage-gcc-native-cpp-14-offline", "displayName": "gcc native cpp 14 offline (Coverage only).", @@ -2138,7 +2109,7 @@ }, { "type": "build", - "name": "manual-Coverage-no-scan-gcc-native-cpp-14-offline" + "name": "manual-Coverage-gcc-native-cpp-14-offline" } ] }, diff --git a/cetlvast/cmake/modules/ExternalDependenciesGitHub.cmake.in b/cetlvast/cmake/modules/ExternalDependenciesGitHub.cmake.in index 6d63f6ef..39d80a04 100644 --- a/cetlvast/cmake/modules/ExternalDependenciesGitHub.cmake.in +++ b/cetlvast/cmake/modules/ExternalDependenciesGitHub.cmake.in @@ -13,4 +13,4 @@ set(GIT_TAG_benchmark "v1.9.1") set(GIT_TAG_googletest "v1.16.0") -set(GIT_TAG_o1heap "aa3c253923db36eee7d73a4a5e30884a5fe7a6eb") +set(GIT_TAG_o1heap "2.2.0") diff --git a/cetlvast/cmake/modules/FindSonarScanner.cmake b/cetlvast/cmake/modules/FindSonarScanner.cmake deleted file mode 100644 index 5f50e449..00000000 --- a/cetlvast/cmake/modules/FindSonarScanner.cmake +++ /dev/null @@ -1,143 +0,0 @@ -# -# Copyright (C) OpenCyphal Development Team -# Copyright Amazon.com Inc. or its affiliates. -# SPDX-License-Identifier: MIT -# - -# -# Fins sonar-scanner and define a scan target -# - -find_program(SONAR_SCANNER sonar-scanner) - -include(FindPackageHandleStandardArgs) - -find_package_handle_standard_args(SonarScanner - REQUIRED_VARS SONAR_SCANNER -) - -find_package(Git) - -set(SONARSCANNER_SONARCLOUD_URL "https://sonarcloud.io") -set(SONARSCANNER_DEFAULT_SOURCE_ENCODING "UTF-8") - -# -# :function: define_sonar_scan_target -# Create a target that runs sonar-scanner. -# -# N.B.: If you are scanning a header-only library this target will refuse to index the headers unless you have at least -# one source file. This is a (dumb) limitation of sonarscanner. To work around this simply add a dummy source file -# (e.g. a .cpp file with nothing in it) to your project but be sure it shows up in the compile_commands.json file. -# -# :param path: ROOT_DIRECTORY - The root directory of the project. Default is "${CMAKE_SOURCE_DIR}" -# :param string: ORGANIZATION - The organization name on SonarCloud. Default is "opencyphal" -# :param string: PROJECT_KEY - The unique key of the project on SonarCloud. -# :param string: PROJECT_NAME - The name of the project on SonarCloud. -# :param string: CPP_VERSION - C++ version number (Just the number. e.g. 14, 17, or 20). -# :param string: PROJECT_VERSION - The version of the project on SonarCloud. Default is "1.0" -# :param list[target]: DEPENDS - A list of targets to depend on. -# :param path: COMPILE_COMMANDS - The path to the compile_commands.json file. -# :param list[path]: SOURCES - The source directories to scan. -# :param list[path]: TESTS - The test directories to scan. -# :param glob: TEST_EXCLUSIONS - A glob pattern to define exclusions from the test report. -# :param glob: TEST_INCLUSIONS - A glob pattern to define inclusions from the test report. -# :param list[path]: COVERAGE_REPORTS - A list of coverage reports to use. -# :param list[path]: TEST_REPORTS - A list of test reports to use. -# :param globpattern: EXCLUDE_COVERAGE - A glob pattern to define exclusions from the coverage report. -# :param globpattern: EXCLUDE_CPD - A glob pattern to define exclusions from the code duplication report. -# :param string: BRANCH_NAME - The name of the branch for ROOT_DIRECTORY. Default uses git to find the -# current branch. -function (define_sonar_cloud_scan_target_for_c_cpp) - #+-[input]----------------------------------------------------------------+ - set(options "") - set(singleValueArgs - ROOT_DIRECTORY - ORGANIZATION - PROJECT_KEY - PROJECT_NAME - PROJECT_VERSION - EXCLUDE_COVERAGE - EXCLUDE_CPD - TEST_EXCLUSIONS - TEST_INCLUSIONS - COMPILE_COMMANDS - CPP_VERSION - BRANCH_NAME) - set(multiValueArgs DEPENDS SOURCES TESTS COVERAGE_REPORTS TEST_REPORTS) - cmake_parse_arguments(PARSE_ARGV 0 ARG "${options}" "${singleValueArgs}" "${multiValueArgs}") - - if(NOT ARG_ROOT_DIRECTORY) - set(ARG_ROOT_DIRECTORY "${CMAKE_SOURCE_DIR}") - endif() - - if(NOT ARG_ORGANIZATION) - set(ARG_ORGANIZATION "opencyphal") - endif() - - if(NOT ARG_PROJECT_VERSION) - set(ARG_PROJECT_VERSION "1.0") - endif() - - if(NOT ARG_COMPILE_COMMANDS) - message(FATAL_ERROR "COMPILE_COMMANDS argument is required.") - endif() - - if(NOT ARG_BRANCH_NAME) - if(NOT Git_FOUND) - message(WARNING "Git not found. Cannot determine branch name.") - set(ARG_BRANCH_NAME "develop/local") - else() - execute_process( - COMMAND git rev-parse --abbrev-ref HEAD - WORKING_DIRECTORY ${ARG_ROOT_DIRECTORY} - OUTPUT_VARIABLE ARG_BRANCH_NAME - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - message(STATUS "sonarscan will use branch name from git: ${ARG_BRANCH_NAME}") - endif() - endif() - - if(NOT DEFINED ENV{SONAR_TOKEN}) - message(WARNING "SONAR_TOKEN environment variable is not set.") - else() - set(LOCAL_SONAR_TOKEN $ENV{SONAR_TOKEN}) - endif() - - #+-[body]-----------------------------------------------------------------+ - - list(JOIN ARG_SOURCES "," LOCAL_SOURCES) - list(JOIN ARG_TESTS "," LOCAL_TESTS) - list(JOIN ARG_COVERAGE_REPORTS "," LOCAL_COVERAGE_REPORTS) - list(JOIN ARG_TEST_REPORTS "," LOCAL_TEST_REPORTS) - - add_custom_target(sonar-cloud-scan-${ARG_PROJECT_KEY} - DEPENDS ${ARG_DEPENDS} - USES_TERMINAL - COMMAND ${SONAR_SCANNER} - --define sonar.verbose=true - --define sonar.organization=${ARG_ORGANIZATION} - --define sonar.projectKey=${ARG_PROJECT_KEY} - --define sonar.projectName=${ARG_PROJECT_NAME} - --define sonar.projectVersion=${ARG_PROJECT_VERSION} - --define sonar.sources=${LOCAL_SOURCES} - --define sonar.tests=${LOCAL_TESTS} - --define sonar.test.exclusions=${ARG_TEST_EXCLUSIONS} - --define sonar.test.inclusions=${ARG_TEST_INCLUSIONS} - --define sonar.sourceEncoding=${SONARSCANNER_DEFAULT_SOURCE_ENCODING} - --define sonar.host.url=${SONARSCANNER_SONARCLOUD_URL} - --define sonar.cfamily.ignoreHeaderComments=false - --define sonar.coverage.exclusions=${ARG_EXCLUDE_COVERAGE} - --define sonar.cpd.exclusions=${ARG_EXCLUDE_CPD} - --define sonar.cfamily.compile-commands=${ARG_COMPILE_COMMANDS} - --define sonar.cfamily.reportingCppStandardOverride=c++${ARG_CPP_VERSION} - --define sonar.cfamily.cobertura.reportPaths=${LOCAL_COVERAGE_REPORTS} - --define sonar.testExecutionReportPaths=${LOCAL_TEST_REPORTS} - --define sonar.branch.name=${ARG_BRANCH_NAME} - --define sonar.token=${LOCAL_SONAR_TOKEN} - WORKING_DIRECTORY ${ARG_ROOT_DIRECTORY} - VERBATIM - COMMENT "Running sonar-scanner using sonarcloud for ${ARG_PROJECT_KEY}") - - #+-[output]---------------------------------------------------------------+ - -endfunction(define_sonar_cloud_scan_target_for_c_cpp) diff --git a/cetlvast/cmake/modules/Findclangformat.cmake b/cetlvast/cmake/modules/Findclangformat.cmake index d8964b28..5a9930ee 100644 --- a/cetlvast/cmake/modules/Findclangformat.cmake +++ b/cetlvast/cmake/modules/Findclangformat.cmake @@ -56,9 +56,9 @@ function(enable_clang_format_check_for_directory) cmake_path(APPEND ARG_DIRECTORY "${ARG_GLOB_PATTERN}" OUTPUT_VARIABLE LOCAL_GLOB_PATTERN_WITH_PATH) if (ARG_ADD_TO_ALL) - set(LOCAL_ALL "ALL") + set(LOCAL_ALL "ALL") else() - set(LOCAL_ALL "") + set(LOCAL_ALL "") endif() file(GLOB_RECURSE LOCAL_SOURCE_FILES diff --git a/cetlvast/cmake/modules/ProjectLibrary.cmake b/cetlvast/cmake/modules/ProjectLibrary.cmake index c4ff6a40..e7a12954 100644 --- a/cetlvast/cmake/modules/ProjectLibrary.cmake +++ b/cetlvast/cmake/modules/ProjectLibrary.cmake @@ -10,7 +10,7 @@ # Function: add_project_library # -# Add and install a library. This method combines all the most common setup for libcyphal library definitions +# Add and install a library. This method combines all the most common setup for cetlvast library definitions # including defining install rules. Additional properties, dependencies, etc can be added after this function # using the library target named by the `NAME` argument you passed in. # diff --git a/cetlvast/include/cetlvast/helpers_gtest.hpp b/cetlvast/include/cetlvast/helpers_gtest.hpp index 477dd121..166cbc8b 100644 --- a/cetlvast/include/cetlvast/helpers_gtest.hpp +++ b/cetlvast/include/cetlvast/helpers_gtest.hpp @@ -15,7 +15,7 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -#if CETL_ENABLE_DEBUG_ASSERT +#if defined(CETL_ENABLE_DEBUG_ASSERT) && CETL_ENABLE_DEBUG_ASSERT /// Workaround limitation in googletest to enable coverage data from the /// forked or cloned processes used by google death-tests. diff --git a/cetlvast/suites/docs/CMakeLists.txt b/cetlvast/suites/docs/CMakeLists.txt index 5d65d896..68794962 100644 --- a/cetlvast/suites/docs/CMakeLists.txt +++ b/cetlvast/suites/docs/CMakeLists.txt @@ -152,6 +152,8 @@ endfunction(create_docs_target) if (${Doxygen_FOUND}) +set(Doxygen_FOUND TRUE PARENT_SCOPE) + file(GLOB_RECURSE DOXYGEN_INPUT_LIST LIST_DIRECTORIES false CONFIGURE_DEPENDS diff --git a/cetlvast/suites/docs/examples/CMakeLists.txt b/cetlvast/suites/docs/examples/CMakeLists.txt index a0aefe50..5ae4216d 100644 --- a/cetlvast/suites/docs/examples/CMakeLists.txt +++ b/cetlvast/suites/docs/examples/CMakeLists.txt @@ -31,6 +31,7 @@ set(NATIVE_EXAMPLES example_08_variable_length_array_vs_vector.cpp example_09_variant.cpp example_10_unbounded_variant.cpp + example_11_memory_resource_o1heap.cpp ) set(ALL_EXAMPLES "") diff --git a/cetlvast/suites/docs/examples/example_11_memory_resource_o1heap.cpp b/cetlvast/suites/docs/examples/example_11_memory_resource_o1heap.cpp new file mode 100644 index 00000000..9e107477 --- /dev/null +++ b/cetlvast/suites/docs/examples/example_11_memory_resource_o1heap.cpp @@ -0,0 +1,38 @@ +/// @file +/// Example of using CETL memory_resource types. +/// +/// This file demonstrates using cetl::pmr::UnsynchronizedO1HeapMemoryResourceDelegate +/// +/// @copyright +/// Copyright (C) OpenCyphal Development Team +/// SPDX-License-Identifier: MIT +/// +#include "cetl/pmr/o1heap_memory_resource_delegate.hpp" + +#include + +namespace cetl +{ +namespace pf17 +{ + +TEST(example_11_memory_resource_o1heap, main) +{ + //! [main] + // We'll use the aligned-storage helper class to defined properly aligned storage for the 01-heap areana. As this + // is a large chunk we'll make it static to keep it off the stack. + static cetl::pmr::O1HeapAlignedStorage<0x100000> large_buffer{}; + + // Now we can use our test subject to allocate and deallocate memory. + cetl::pmr::UnsynchronizedO1HeapMemoryResourceDelegate test_subject{large_buffer}; + + // Note that, until https://github.com/pavel-kirienko/o1heap/issues/13 is fixed the alignment doesn't have + // any effect on allocation requests. + void* mem = test_subject.allocate(8); + ASSERT_NE(nullptr, mem); + test_subject.deallocate(mem, 8); + //! [main] +} + +} // namespace pf17 +} // namespace cetl diff --git a/cetlvast/suites/unittest/CMakeLists.txt b/cetlvast/suites/unittest/CMakeLists.txt index f280c1d9..757bef05 100644 --- a/cetlvast/suites/unittest/CMakeLists.txt +++ b/cetlvast/suites/unittest/CMakeLists.txt @@ -208,45 +208,3 @@ add_executable(cetlvast_sonar target_link_libraries(cetlvast_sonar PRIVATE cetl ) - -find_package(SonarScanner) - -if (${SonarScanner_FOUND}) - - # This is allows local debugging of the github action that runs sonarqube. - # It's not supposed to be part of the normal development process. To use sonarqube - # as part of your normal development process, you should use SonarSource.sonarlint-vscode - # in vscode. - # - # WARNING: If you are a project admin and you have a SONAR_TOKEN, this will upload - # the measure to the CETL project on sonarcloud.io. Be sure you are on a branch - # if you do this. - - message(STATUS "SonarScanner found. Adding a local Sonar Scanner target.") - - define_sonar_cloud_scan_target_for_c_cpp( - ROOT_DIRECTORY ${CETL_ROOT} - PROJECT_KEY OpenCyphal_CETL - PROJECT_NAME CETLVaSt - DEPENDS - unittest-sonarqube.xml - ${LOCAL_COVERAGE_REPORT_INDICES} - cetlvast_sonar - SOURCES - "${CETL_ROOT}/include" - "${CMAKE_CURRENT_SOURCE_DIR}/sonar.cpp" - TESTS - "${CMAKE_CURRENT_SOURCE_DIR}" - TEST_INCLUSIONS - "**/test_*.cpp" - CPP_VERSION ${CETLVAST_CPP_STANDARD} - COMPILE_COMMANDS - "${CMAKE_BINARY_DIR}/compile_commands.json" - COVERAGE_REPORTS - "${CMAKE_CURRENT_BINARY_DIR}/coverage.xml" - TEST_REPORTS - "${CMAKE_CURRENT_BINARY_DIR}/unittest-sonarqube.xml" - ) -else() - message(STATUS "SonarScanner not found. Skipping SonarCloud integration.") -endif() diff --git a/cetlvast/suites/unittest/test_buffer_memory_resource.cpp b/cetlvast/suites/unittest/test_buffer_memory_resource.cpp index e1a3cfb4..05e5bea5 100644 --- a/cetlvast/suites/unittest/test_buffer_memory_resource.cpp +++ b/cetlvast/suites/unittest/test_buffer_memory_resource.cpp @@ -256,7 +256,7 @@ TEST(UnsynchronizedBufferMemoryResourceDelegateTest, TestAllocateAllocateDealloc // +----------------------------------------------------------------------+ // | ☠️ DEATH TESTS ☠️ // +----------------------------------------------------------------------+ -#if CETL_ENABLE_DEBUG_ASSERT +#if defined(CETL_ENABLE_DEBUG_ASSERT) && CETL_ENABLE_DEBUG_ASSERT static void TestNullUpstreamInCtor() { diff --git a/cetlvast/suites/unittest/test_o1heap_memory_resource.cpp b/cetlvast/suites/unittest/test_o1heap_memory_resource.cpp index 93713eff..a56925bc 100644 --- a/cetlvast/suites/unittest/test_o1heap_memory_resource.cpp +++ b/cetlvast/suites/unittest/test_o1heap_memory_resource.cpp @@ -8,41 +8,117 @@ /// #include "cetl/cetl.hpp" -#include "cetlvast/helpers_gtest.hpp" +#include "cetlvast/helpers_gtest.hpp" // NOLINT(clangd-unused-includes) #include "cetl/pmr/o1heap_memory_resource_delegate.hpp" -constexpr std::size_t TestBufferSize = 0x100000; -static cetl::pmr::O1HeapAlignedStorage large_buffer{}; +#include +#include // for std::bad_alloc +#include -TEST(UnsynchronizedO1HeapMemoryResourceTest, TestDefault) +namespace cetl +{ +namespace test { - cetl::pmr::UnsynchronizedO1HeapMemoryResourceDelegate test_subject{large_buffer}; - void* mem = test_subject.allocate(8); - ASSERT_NE(nullptr, mem); - test_subject.deallocate(mem, 8); -} -TEST(UnsynchronizedO1HeapMemoryResourceTest, O1HeapAlignedStorageTest) +class O1HeapMemoryResourceTest : public ::testing::Test { - cetl::pmr::O1HeapAlignedStorage<4096> aligned_storage{}; - cetl::pmr::UnsynchronizedO1HeapMemoryResourceDelegate test_subject{aligned_storage}; + static constexpr std::size_t TestBufferSize = 4096; + + void SetUp() override + { + // Fill the buffer with a known pattern to help identify uninitialized memory usage + memset(&large_buffer, 0xAA, sizeof(large_buffer)); // NOLINT + } + +protected: + static cetl::pmr::O1HeapAlignedStorage large_buffer; +}; + +cetl::pmr::O1HeapAlignedStorage O1HeapMemoryResourceTest::large_buffer; + +TEST_F(O1HeapMemoryResourceTest, O1HeapAlignedStorageTest) +{ + cetl::pmr::UnsynchronizedO1HeapMemoryResourceDelegate test_subject{large_buffer}; void* mem = test_subject.allocate(16); ASSERT_NE(nullptr, mem); test_subject.deallocate(mem, 16); } +TEST_F(O1HeapMemoryResourceTest, TestAllocationFailureThrowsBadAlloc) +{ + // Use a small buffer to easily exhaust memory + cetl::pmr::O1HeapAlignedStorage<1024> small_buffer{}; + cetl::pmr::UnsynchronizedO1HeapMemoryResourceDelegate test_subject{small_buffer}; + + // Allocate all available memory to force the next allocation to fail + std::vector allocations; + +#if defined(__cpp_exceptions) + try + { + // Keep allocating until we exhaust the heap + while (true) + { + void* mem = test_subject.allocate(64); // Allocate in reasonable chunks + allocations.push_back(mem); + } + // If we get here, allocation didn't fail as expected + FAIL() << "Expected std::bad_alloc to be thrown when heap was exhausted"; + } catch (const std::bad_alloc&) + { + // This is expected when the heap is exhausted - this tests line 77 + SUCCEED() << "std::bad_alloc was thrown as expected when heap was exhausted"; + } +#else + // When exceptions are disabled, allocate until we get nullptr + void* mem = nullptr; + do + { + mem = test_subject.allocate(64); + if (mem != nullptr) + { + allocations.push_back(mem); + } + } while (mem != nullptr); + + // We should have gotten at least some allocations before failing + EXPECT_GT(allocations.size(), 0u) << "Should have been able to allocate some memory before exhaustion"; + + // Try one more allocation to ensure it returns nullptr + void* final_alloc = test_subject.allocate(64); + EXPECT_EQ(nullptr, final_alloc) << "Allocation should return nullptr when heap is exhausted"; +#endif + // Don't worry about deallocating memory since the allocator and small_buffer both go out of scope with the test. +} + +} // namespace test +} // namespace cetl + // +----------------------------------------------------------------------+ -#if CETL_ENABLE_DEBUG_ASSERT +#if defined(CETL_ENABLE_DEBUG_ASSERT) && CETL_ENABLE_DEBUG_ASSERT static void TestNullBufferInCtor() { flush_coverage_on_death(); - cetl::pmr::UnsynchronizedO1HeapMemoryResourceDelegate test_subject{nullptr, 0}; + cetl::pmr::UnsynchronizedO1HeapMemoryResourceDelegate test_subject{nullptr, 0xFFFFFFFF}; } TEST(DeathTestUnsynchronizedO1HeapMemoryResourceAssertions, TestNullBufferInCtor) { EXPECT_DEATH(TestNullBufferInCtor(), "CDE_o1h_001"); } + +static void TestArenaSizeTooSmall() +{ + flush_coverage_on_death(); + std::array small_storage{}; + CETL_DEBUG_ASSERT(small_storage.size() < o1heapMinArenaSize, "Test setup error"); + cetl::pmr::UnsynchronizedO1HeapMemoryResourceDelegate test_subject{small_storage.data(), small_storage.size()}; +} + +TEST(DeathTestUnsynchronizedO1HeapMemoryResourceAssertions, TestArenaSizeTooSmall) +{ + EXPECT_DEATH(TestArenaSizeTooSmall(), "CDE_o1h_002"); +} #endif diff --git a/cetlvast/suites/unittest/test_pf17_polymorphic_allocator.cpp b/cetlvast/suites/unittest/test_pf17_polymorphic_allocator.cpp index ddee5ac2..341642b3 100644 --- a/cetlvast/suites/unittest/test_pf17_polymorphic_allocator.cpp +++ b/cetlvast/suites/unittest/test_pf17_polymorphic_allocator.cpp @@ -376,7 +376,7 @@ TYPED_TEST(TestPolymorphicAllocatorMoveOnlyProtocols, TestEmplace) // | DEATH TESTS // +----------------------------------------------------------------------+ -#if CETL_ENABLE_DEBUG_ASSERT +#if defined(CETL_ENABLE_DEBUG_ASSERT) && CETL_ENABLE_DEBUG_ASSERT static void TestNullResourceToCtor() { diff --git a/cetlvast/suites/unittest/test_pf20_span_asserts.cpp b/cetlvast/suites/unittest/test_pf20_span_asserts.cpp index 8ccb9e69..11472f18 100644 --- a/cetlvast/suites/unittest/test_pf20_span_asserts.cpp +++ b/cetlvast/suites/unittest/test_pf20_span_asserts.cpp @@ -16,7 +16,7 @@ namespace // +----------------------------------------------------------------------+ // | DEBUG ASSERT TESTS // +----------------------------------------------------------------------+ -#if CETL_ENABLE_DEBUG_ASSERT +#if defined(CETL_ENABLE_DEBUG_ASSERT) && CETL_ENABLE_DEBUG_ASSERT static void TestStaticSpanWithWrongSize() diff --git a/cetlvast/suites/unittest/test_variable_length_array_debug_asserts.cpp b/cetlvast/suites/unittest/test_variable_length_array_debug_asserts.cpp index 0d0c191a..e7ef1930 100644 --- a/cetlvast/suites/unittest/test_variable_length_array_debug_asserts.cpp +++ b/cetlvast/suites/unittest/test_variable_length_array_debug_asserts.cpp @@ -29,7 +29,7 @@ namespace // +----------------------------------------------------------------------+ // | DEBUG ASSERT TESTS // +----------------------------------------------------------------------+ -#if CETL_ENABLE_DEBUG_ASSERT +#if defined(CETL_ENABLE_DEBUG_ASSERT) && CETL_ENABLE_DEBUG_ASSERT static void TestBoolSpecLastByteBitFillTooLarge() { diff --git a/include/cetl/cetl.hpp b/include/cetl/cetl.hpp index 3ca0e5cb..40961d6b 100644 --- a/include/cetl/cetl.hpp +++ b/include/cetl/cetl.hpp @@ -31,6 +31,7 @@ /// - @subpage example_08_variable_length_array_vs_vector /// - @subpage example_09_variant /// - @subpage example_10_unbounded_variant +/// - @subpage example_11_memory_resource_o1heap /// /// @page example_01_polyfill_20 Example 1: CETL C++20 Polyfill Header /// Full example for @ref cetl/pf20/cetlpf.hpp @@ -73,6 +74,10 @@ /// Full example for cetl::unbounded_variant /// @include example_10_unbounded_variant.cpp /// +/// @page example_11_memory_resource_o1heap Example 11: Using the 01heap memory_resource +/// Full example for cetl::pmr::UnsynchronizedO1HeapMemoryResourceDelegate +/// @include example_11_memory_resource_o1heap.cpp +/// #ifndef CETL_H_INCLUDED #define CETL_H_INCLUDED @@ -95,18 +100,18 @@ /// and minor version. A patch version number change will only occur if library source code is changed. /// Documentation or test suite changes will not require a change to `cetl/cetl.hpp` and will not bump /// the patch version. -#define CETL_VERSION_PATCH 2 +#define CETL_VERSION_PATCH 0 // NOSONAR cpp:5028 /// @def CETL_VERSION_MINOR /// CETL minor version. /// Minor versions shall only add to CETL or modify it in a backwards compatible way. -#define CETL_VERSION_MINOR 4 +#define CETL_VERSION_MINOR 5 // NOSONAR cpp:5028 /// @def CETL_VERSION_MAJOR /// CETL Major version. /// New major versions shall be rare. No overarching guarantees are made about compatibility /// between major versions. -#define CETL_VERSION_MAJOR 1 +#define CETL_VERSION_MAJOR 1 // NOSONAR cpp:5028 /// @} @@ -125,7 +130,7 @@ /// in production code is strongly discouraged. /// #if defined NDEBUG && defined CETL_ENABLE_DEBUG_ASSERT -# undef CETL_ENABLE_DEBUG_ASSERT +# undef CETL_ENABLE_DEBUG_ASSERT // NOSONAR cpp:s959 #endif // Intentional violation of Sonar: the assertions check macro cannot be replaced with a function definition. @@ -138,7 +143,7 @@ // Make the standard exceptions available only if exceptions are enabled. #if defined(__cpp_exceptions) -# include +# include // NOLINT #endif /// @defgroup CETL_CPP_STANDARD Guaranteed CETL c++ standard numbers @@ -162,7 +167,7 @@ /// #include /// #endif /// ``` -#define CETL_CPP_STANDARD_14 201402L +#define CETL_CPP_STANDARD_14 201402L // NOSONAR cpp:5028 /// @def CETL_CPP_STANDARD_17 /// Provides the proper value to test against `__cplusplus` for c++14. @@ -172,7 +177,7 @@ /// #include /// #endif /// ``` -#define CETL_CPP_STANDARD_17 201703L +#define CETL_CPP_STANDARD_17 201703L // NOSONAR cpp:5028 /// @def CETL_CPP_STANDARD_20 /// Provides the proper value to test against `__cplusplus` for c++14. @@ -183,7 +188,7 @@ /// #include /// #endif /// ``` -#define CETL_CPP_STANDARD_20 202002L +#define CETL_CPP_STANDARD_20 202002L // NOSONAR cpp:5028 /// @} diff --git a/include/cetl/pf17/memory_resource.hpp b/include/cetl/pf17/memory_resource.hpp index 9590cc8b..78aff38f 100644 --- a/include/cetl/pf17/memory_resource.hpp +++ b/include/cetl/pf17/memory_resource.hpp @@ -767,7 +767,7 @@ class basic_monotonic_buffer_resource : public memory_resource void* do_allocate_from_current_buffer(std::size_t size_bytes, std::size_t alignment) { void* result = nullptr; - if (current_buffer_->buffer && current_buffer_->remaining_buffer_size >= size_bytes) + if ((nullptr != current_buffer_->buffer) && (current_buffer_->remaining_buffer_size >= size_bytes)) { CETL_DEBUG_ASSERT(current_buffer_->buffer_size >= current_buffer_->remaining_buffer_size, "remaining_buffer_size exceeded total buffer size? We have corrupt internal logic."); diff --git a/include/cetl/pf17/optional.hpp b/include/cetl/pf17/optional.hpp index 1dc3fd3b..a9feafc1 100644 --- a/include/cetl/pf17/optional.hpp +++ b/include/cetl/pf17/optional.hpp @@ -90,7 +90,7 @@ struct base_destruction { if (m_engaged) { - new (std::addressof(m_value)) T(std::forward(other).m_value); + new (std::addressof(m_value)) T(std::forward(other).m_value); // NOSONAR cpp:S984 } } void reset() noexcept diff --git a/include/cetl/pmr/o1heap_memory_resource_delegate.hpp b/include/cetl/pmr/o1heap_memory_resource_delegate.hpp index 1197d854..c7b799e7 100644 --- a/include/cetl/pmr/o1heap_memory_resource_delegate.hpp +++ b/include/cetl/pmr/o1heap_memory_resource_delegate.hpp @@ -4,7 +4,7 @@ /// include cetl/pf17/cetlpf.hpp or provide the memory_resource definition you want this class to use. /// You'll also need to provide an include path to o1heap.h and compile in o1heap.c when using this type. /// -/// TODO: examples +/// @snippet{trimleft} example_11_memory_resource_o1heap.cpp main /// /// @copyright /// Copyright (C) OpenCyphal Development Team @@ -19,7 +19,8 @@ # include "cetl/cetl.hpp" #endif -#include // for aligned_storage +#include // for max_align_t +#include // for std::bad_alloc #include "o1heap.h" @@ -33,13 +34,14 @@ struct O1HeapAlignedStorage { static constexpr std::size_t size_bytes = StorageSizeBytes; static constexpr std::size_t alignment = O1HEAP_ALIGNMENT; + static constexpr std::size_t arena_size = ((size_bytes + (alignment - 1)) / alignment) * alignment; static_assert(O1HEAP_ALIGNMENT >= alignof(std::max_align_t), "O1HEAP_ALIGNMENT is too small for this platform."); - struct alignas(alignment) type + struct alignas(alignment) type // NOSONAR cpp:5945 { - unsigned char data[((size_bytes + alignment - 1) / alignment) * alignment]; - } storage[1]; + unsigned char data[arena_size]; // NOSONAR cpp:3646 + } storage[1]; // NOSONAR cpp:3646 }; class UnsynchronizedO1HeapMemoryResourceDelegate @@ -47,9 +49,8 @@ class UnsynchronizedO1HeapMemoryResourceDelegate public: UnsynchronizedO1HeapMemoryResourceDelegate(void* buffer, std::size_t buffer_size_bytes) : o1heap_{o1heapInit(buffer, buffer_size_bytes)} - , max_size_bytes_{buffer_size_bytes} { - // TODO: https://github.com/pavel-kirienko/o1heap/issues/17 + CETL_DEBUG_ASSERT(o1heapMinArenaSize <= buffer_size_bytes, "CDE_o1h_002: buffer_size_bytes is too small."); CETL_DEBUG_ASSERT(nullptr != o1heap_, "CDE_o1h_001: o1heapInit failed."); } @@ -90,14 +91,11 @@ class UnsynchronizedO1HeapMemoryResourceDelegate std::size_t max_size() const noexcept { - // TODO: https://github.com/pavel-kirienko/o1heap/issues/18 - return max_size_bytes_; + return o1heapGetMaxAllocationSize(o1heap_); } private: O1HeapInstance* o1heap_; - // TODO: remove when https://github.com/pavel-kirienko/o1heap/issues/18 is fixed. - std::size_t max_size_bytes_; }; } // namespace pmr diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 00000000..8547a529 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,28 @@ +# SonarQube project configuration for CETL +sonar.organization=opencyphal +sonar.projectKey=OpenCyphal_CETL +sonar.projectName=CETLVaSt +sonar.verbose=true + +# C/C++ specific settings +sonar.cfamily.compile-commands=cetlvast/build/compile_commands.json +sonar.cfamily.cobertura.reportPaths=cetlvast/build/suites/unittest/coverage.xml +sonar.cfamily.ignoreHeaderComments=false +sonar.cfamily.reportingCppStandardOverride=c++14 + +# Test execution reports +sonar.testExecutionReportPaths=cetlvast/build/suites/unittest/unittest-sonarqube.xml + +# Source and test directories +sonar.sources=include,cetlvast/suites/unittest/sonar.cpp +sonar.tests=cetlvast/suites/unittest +sonar.test.inclusions=**/test_*.cpp + +# Coverage and duplication exclusions +sonar.coverage.exclusions=cetlvast/**/*,**/sonar.cpp +sonar.cpd.exclusions=cetlvast/**/*,**/sonar.cpp + +# Rule exclusions +sonar.issue.ignore.multicriteria=e1 +sonar.issue.ignore.multicriteria.e1.ruleKey=cpp:S994,cpp:S799 +sonar.issue.ignore.multicriteria.e1.resourceKey=**/*