diff --git a/.github/workflows/cmake-linux-amd64.yml b/.github/workflows/cmake-linux-amd64.yml index 1c863c83..f99da9ed 100644 --- a/.github/workflows/cmake-linux-amd64.yml +++ b/.github/workflows/cmake-linux-amd64.yml @@ -23,7 +23,7 @@ jobs: - name: Add to PATH shell: bash run: | - echo "/usr/lib/llvm-19/bin" >> $GITHUB_PATH + echo "/usr/lib/llvm-21/bin" >> $GITHUB_PATH - name: Set reusable strings # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. @@ -40,8 +40,7 @@ jobs: - name: Build # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). - run: > - core_count=$(nproc) + run: > cmake --build ${{ steps.strings.outputs.build-output-dir }} --config Release diff --git a/.github/workflows/cmake-linux-arm64.yml b/.github/workflows/cmake-linux-arm64.yml index f3ecf94b..bb4a6a15 100644 --- a/.github/workflows/cmake-linux-arm64.yml +++ b/.github/workflows/cmake-linux-arm64.yml @@ -6,24 +6,27 @@ on: pull_request: branches: [ "main"] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: - runs-on: [self-hosted,linux,arm64,agx] - + runs-on: [self-hosted,linux,arm64] + timeout-minutes: 10 # Increase this timeout value as needed strategy: + matrix: + build_type: [Release] + cpp_compiler: [g++,clang++] # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. - fail-fast: false - - matrix: - cpp_compiler: [g++,clang++] - + fail-fast: true steps: - uses: actions/checkout@v4 - name: Add to PATH shell: bash run: | - echo "/usr/lib/llvm-19/bin" >> $GITHUB_PATH + echo "/usr/lib/llvm-21/bin" >> $GITHUB_PATH - name: Set reusable strings # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. @@ -36,13 +39,12 @@ jobs: cmake -G "Ninja" -B ${{steps.strings.outputs.build-output-dir}} -DCMAKE_CXX_COMPILER=${{matrix.cpp_compiler}} -DCUDA_ARCH="87;" - -DCMAKE_BUILD_TYPE=Release + -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -S ${{github.workspace}}; - name: Build # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). - run: > - core_count=$(nproc) + run: > cmake --build ${{ steps.strings.outputs.build-output-dir }} --config Release @@ -50,5 +52,5 @@ jobs: working-directory: ${{ steps.strings.outputs.build-output-dir }} # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest --build-config Release --output-junit test_results.xml + run: ctest --build-config ${{ matrix.build_type }} --output-junit test_results.xml \ No newline at end of file diff --git a/.github/workflows/cmake-windows-amd64.yml b/.github/workflows/cmake-windows-amd64.yml index 917dd561..8536e063 100644 --- a/.github/workflows/cmake-windows-amd64.yml +++ b/.github/workflows/cmake-windows-amd64.yml @@ -14,7 +14,7 @@ jobs: # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. fail-fast: false matrix: - toolset: ["cl,14.16,11.8","cl,14.44,12.9","llvm,19.1.7,12.9"] + toolset: ["cl,14.16,11.8","cl,14.44,12.9","llvm,21.1.0,12.9"] steps: diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c33d54d..f247036c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,11 +7,11 @@ set (PROJECT_VERSION_REV 11) set (PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_REV}) include (cmake/cmake_init.cmake) include (cmake/doxygen.cmake) -include (cmake/targets/virtualfolders.cmake) include (cmake/deploy/deploy_dependencies.cmake) include (cmake/archflags.cmake) include (cmake/generators/version_header.cmake) +include (cmake/generators/export_header.cmake) project(FusedKernelLibrary VERSION ${PROJECT_VERSION} LANGUAGES CXX DESCRIPTION "Implementation of a methodology that allows all sorts of user defined GPU kernel fusion, for non CUDA programmers." HOMEPAGE_URL "https://github.com/morousg/FusedKernelLibrary" ) @@ -36,20 +36,22 @@ else() message(STATUS "CUDA compiler not found, CUDA support will be disabled.") endif() +add_subdirectory(include) +add_subdirectory(lib) - +option (BUILD_TEST "build standard tests" ON) option (BUILD_UTEST "build standard unit tests" ON) option (ENABLE_BENCHMARK "build benchmarking unit tests" OFF) - - -add_subdirectory(include) -add_subdirectory(lib) +if (${BUILD_TEST}) + include (cmake/tests/discover_tests.cmake) + enable_testing() + add_subdirectory(tests) +endif() if (${BUILD_UTEST}) include (cmake/tests/discover_tests.cmake) - enable_testing() - add_subdirectory(tests) + enable_testing() add_subdirectory(utests) endif() diff --git a/cmake/cmake_init.cmake b/cmake/cmake_init.cmake index 9075657a..7f21d02b 100644 --- a/cmake/cmake_init.cmake +++ b/cmake/cmake_init.cmake @@ -19,7 +19,7 @@ set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO "RelWithDebInfo;Release;") set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install") set (TEMPLATE_DEPTH "1000" CACHE STRING "template depth") - +set (FUNDAMENTAL_TYPES uchar char ushort short uint int ulong long ulonglong longlong float double) cmake_policy(SET CMP0111 NEW) # ensure all targets provide shared libs location diff --git a/cmake/discover_tests.cmake b/cmake/discover_tests.cmake new file mode 100644 index 00000000..03f6bd9f --- /dev/null +++ b/cmake/discover_tests.cmake @@ -0,0 +1,84 @@ +set (LAUNCH_SOURCES "${CMAKE_SOURCE_DIR}/tests/main.cpp;${CMAKE_SOURCE_DIR}/tests/main.h") +if (WIN32) + list(APPEND LAUNCH_SOURCES "${CMAKE_SOURCE_DIR}/manifest.xml") #for utf8 codepage +endif() + +function(add_cuda_to_test TARGET_NAME) + add_cuda_to_target(${TARGET_NAME} "") + set_target_cuda_arch_flags(${TARGET_NAME}) + + if(${ENABLE_DEBUG}) + add_cuda_debug_support_to_target(${TARGET_NAME}) + endif() + if(${ENABLE_NVTX}) + add_nvtx_support_to_target(${TARGET_NAME}) + endif() +endfunction() + +function (add_generated_test TARGET_NAME TEST_SOURCE EXTENSION DIR) + + set(TEST_GENERATED_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}_${EXTENSION}/launcher.${EXTENSION}") #use the same name as the target ) + + configure_file(${CMAKE_SOURCE_DIR}/tests/launcher.in ${TEST_GENERATED_SOURCE} @ONLY) #replace variables in the test source file + set (TARGET_NAME_EXT "${TARGET_NAME}_${EXTENSION}") + #message(STATUS "Adding test: ${TARGET_NAME_EXT} from ${TEST_GENERATED_SOURCE} and ${TEST_SOURCE}") + add_executable(${TARGET_NAME_EXT} "${TEST_GENERATED_SOURCE};${TEST_SOURCE}" ) + target_sources(${TARGET_NAME_EXT} PRIVATE ${LAUNCH_SOURCES}) + + if(${ENABLE_BENCHMARK}) + target_compile_definitions(${TARGET_NAME_EXT} PRIVATE ENABLE_BENCHMARK) + endif() + + set_target_properties(${TARGET_NAME_EXT} PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES CXX_EXTENSIONS NO) + target_include_directories(${TARGET_NAME_EXT} PRIVATE "${CMAKE_SOURCE_DIR}") + target_include_directories(${TARGET_NAME_EXT} PRIVATE "${DIR}") + target_link_libraries(${TARGET_NAME_EXT} PRIVATE FKL::FKL) + target_link_libraries(${TARGET_NAME_EXT} PRIVATE CUDA::cuda_driver) + if (NVRTC_ENABLE) + target_link_libraries(${TARGET_NAME_EXT} PRIVATE ${NVRTC_LIBRARIES}) + target_compile_definitions(${TARGET_NAME_EXT} PRIVATE NVRTC_ENABLE) + if (MSVC) + if (NVRTC_STATIC_LINK) + set_target_properties(${TARGET_NAME_EXT} PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + endif() + endif() + endif() + if (MSVC) + target_compile_options(${TARGET_NAME_EXT} PRIVATE $<$:/diagnostics:caret>) + endif() + + add_optimization_flags(${TARGET_NAME_EXT}) + + add_test(NAME ${TARGET_NAME_EXT} COMMAND ${TARGET_NAME_EXT}) + cmake_path(SET path2 "${DIR}") + cmake_path(GET path2 FILENAME DIR_NAME) + set_property(TARGET "${TARGET_NAME_EXT}" PROPERTY FOLDER "tests/${EXTENSION}/${DIR_NAME}") + +endfunction() + +function (discover_tests DIR) + file( + GLOB_RECURSE + TEST_SOURCES + CONFIGURE_DEPENDS + "${DIR}/*.h" + ) + + foreach(test_source ${TEST_SOURCES}) + + get_filename_component(TARGET_NAME ${test_source} NAME_WE) + cmake_path(GET test_source PARENT_PATH DIR_NAME) #get the directory name of the test source file + string(FIND ${DIR_NAME} "cudabug" POS) + if (${POS} EQUAL -1) #if the directory name does not contain "cudabug" + if (${ENABLE_CPU}) + add_generated_test("${TARGET_NAME}" "${test_source}" "cpp" "${DIR_NAME}") + endif() + endif() + if (CMAKE_CUDA_COMPILER AND ENABLE_CUDA) + add_generated_test("${TARGET_NAME}" "${test_source}" "cu" "${DIR_NAME}") + add_cuda_to_test("${TARGET_NAME}_cu") + endif() + + endforeach() +endfunction() + \ No newline at end of file diff --git a/cmake/generators/export_header.cmake b/cmake/generators/export_header.cmake new file mode 100644 index 00000000..8ccd05e9 --- /dev/null +++ b/cmake/generators/export_header.cmake @@ -0,0 +1,9 @@ + + +macro(add_generated_export_header_to_target TARGET_NAME) + include(GenerateExportHeader) + generate_export_header(${TARGET_NAME} EXPORT_FILE_NAME "${CMAKE_BINARY_DIR}/exports/${TARGET_NAME}_export.h") + target_include_directories(${TARGET_NAME} PUBLIC "${CMAKE_BINARY_DIR}/exports/") + set_target_properties(${TARGET_NAME} PROPERTIES CXX_VISIBILITY_PRESET default) + target_sources(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/exports/${TARGET_NAME}_export.h") +endmacro() diff --git a/cmake/targets/virtualfolders.cmake b/cmake/targets/virtualfolders.cmake deleted file mode 100644 index d8851ab5..00000000 --- a/cmake/targets/virtualfolders.cmake +++ /dev/null @@ -1,23 +0,0 @@ -# ###################################################################################################################### -# virtual folders: Projects and source code is not located in the same subfolder Thus, we need to set virtual folders. -# Those, have the same tree folder structure -# ###################################################################################################################### -macro(set_virtual_folders SRCS) - # add the version resource file to the root folder - source_group("" FILES ${VERSION_RC}) - foreach(f ${SRCS}) - # Get the relative path of the file - file(RELATIVE_PATH SRCGR ${CMAKE_CURRENT_SOURCE_DIR} ${f}) - string(FIND "${SRCGR}" "/" POS) - # Extract the folder if exists, (Remove the filename part) - string(REGEX REPLACE "(.*)(/[^/]*)$" "\\1" SRCGR ${SRCGR}) - - if(${POS} GREATER -1) - # Source_group expects \\ (double antislash), not / (slash) - string(REPLACE / \\ SRCGR ${SRCGR}) - source_group("${SRCGR}" FILES ${f}) - else() - source_group("" FILES ${f}) - endif() - endforeach() -endmacro() diff --git a/cmake/tests/add_generated_test.cmake b/cmake/tests/add_generated_test.cmake index a2b42ec9..6eaf7290 100644 --- a/cmake/tests/add_generated_test.cmake +++ b/cmake/tests/add_generated_test.cmake @@ -2,7 +2,7 @@ set (LAUNCH_SOURCES "${CMAKE_SOURCE_DIR}/tests/main.cpp;${CMAKE_SOURCE_DIR}/test if (WIN32) list(APPEND LAUNCH_SOURCES "${CMAKE_SOURCE_DIR}/utf8cp.manifest") #for utf8 codepage endif() - +include (cmake/generators/export_header.cmake) function(add_cuda_to_test TARGET_NAME) add_cuda_to_target(${TARGET_NAME} "") set_target_cuda_arch_flags(${TARGET_NAME}) @@ -15,46 +15,64 @@ function(add_cuda_to_test TARGET_NAME) endif() endfunction() -function (add_generated_test TARGET_NAME TEST_SOURCE EXTENSION DIR) - set(TEST_GENERATED_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}_${EXTENSION}/launcher.${EXTENSION}") #use the same name as the target ) - - configure_file(${CMAKE_SOURCE_DIR}/tests/launcher.in ${TEST_GENERATED_SOURCE} @ONLY) #replace variables in the test source file - set (TARGET_NAME_EXT "${TARGET_NAME}_${EXTENSION}") - #message(STATUS "Adding test: ${TARGET_NAME_EXT} from ${TEST_GENERATED_SOURCE} and ${TEST_SOURCE}") - add_executable(${TARGET_NAME_EXT} "${TEST_GENERATED_SOURCE};${TEST_SOURCE}" ) - target_sources(${TARGET_NAME_EXT} PRIVATE ${LAUNCH_SOURCES}) +function(configure_test_target_flags TARGET_NAME TEST_SOURCE DIR) + set(TEST_GENERATED_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}_${EXTENSION}/launcher.${EXTENSION}") #use the same name as the target ) + configure_file(${CMAKE_SOURCE_DIR}/tests/launcher.in ${TEST_GENERATED_SOURCE} @ONLY) #replace variables in the test source file + #message(STATUS "Adding test: ${TARGET_NAME} from ${TEST_GENERATED_SOURCE} and ${TEST_SOURCE}") + if(${ENABLE_BENCHMARK}) - target_compile_definitions(${TARGET_NAME_EXT} PRIVATE ENABLE_BENCHMARK) + target_compile_definitions(${TARGET_NAME} PRIVATE ENABLE_BENCHMARK) endif() - set_target_properties(${TARGET_NAME_EXT} PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES CXX_EXTENSIONS NO) - target_include_directories(${TARGET_NAME_EXT} PUBLIC "${CMAKE_SOURCE_DIR}") - target_include_directories(${TARGET_NAME_EXT} PUBLIC "${DIR}") - target_link_libraries(${TARGET_NAME_EXT} PUBLIC FKL::FKL) + set_target_properties(${TARGET_NAME} PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES CXX_EXTENSIONS NO) + target_include_directories(${TARGET_NAME} PUBLIC "${CMAKE_SOURCE_DIR}") + target_include_directories(${TARGET_NAME} PUBLIC "${DIR}") + target_link_libraries(${TARGET_NAME} PUBLIC FKL::FKL) if (MSVC) - target_compile_options(${TARGET_NAME_EXT} PRIVATE $<$:/diagnostics:caret>) - target_compile_options(${TARGET_NAME_EXT} PRIVATE $<$:/bigobj>) + target_compile_options(${TARGET_NAME} PRIVATE $<$:/diagnostics:caret>) + target_compile_options(${TARGET_NAME} PRIVATE $<$:/bigobj>) # add_compile_options("$<$:/utf-8>") - #target_link_libraries(${TARGET_NAME_EXT} -manifest:embed -manifestinput:"${PROJECT_SOURCE_DIR}/myapp.manifest" + #target_link_libraries(${TARGET_NAME} -manifest:embed -manifestinput:"${PROJECT_SOURCE_DIR}/myapp.manifest" endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - target_compile_options(${TARGET_NAME_EXT} PRIVATE $<$:-Wno-c++11-narrowing>) + target_compile_options(${TARGET_NAME} PRIVATE $<$:-Wno-c++11-narrowing>) + target_compile_options(${TARGET_NAME} PRIVATE $<$:-Wconversion>) + endif() - - add_optimization_flags(${TARGET_NAME_EXT}) - - add_test(NAME ${TARGET_NAME_EXT} COMMAND ${TARGET_NAME_EXT}) - cmake_path(SET path2 "${DIR}") - cmake_path(GET path2 FILENAME DIR_NAME) - cmake_path(GET path2 PARENT_PATH DIR_PARENT_PATH) - if (${EXTENSION} STREQUAL "cu") + add_optimization_flags(${TARGET_NAME}) +endfunction() + +function (set_ide_target_folder TARGET_NAME DIR_PARENT_PATH EXTENSION) + if (${EXTENSION} STREQUAL "cu") set(FKL_BACKEND "cuda") elseif(${EXTENSION} STREQUAL "cpp") set(FKL_BACKEND "cpu") else() message(FATAL_ERROR "Unknown extension: ${EXTENSION}") endif() - set_property(TARGET "${TARGET_NAME_EXT}" PROPERTY FOLDER "${DIR_PARENT_PATH}/${FKL_BACKEND}/") - + set_property(TARGET "${TARGET_NAME}" PROPERTY FOLDER "${DIR_PARENT_PATH}/${FKL_BACKEND}/") +endfunction() + + +function (add_generated_test_stub TARGET_NAME_EXT TEST_SOURCE DIR) + set(TEST_GENERATED_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}_${EXTENSION}/launcher.${EXTENSION}") #use the same name as the target ) + configure_file(${CMAKE_SOURCE_DIR}/tests/launcher.in ${TEST_GENERATED_SOURCE} @ONLY) #replace variables in the test source file + add_executable(${TARGET_NAME_EXT} "${TEST_GENERATED_SOURCE};${TEST_SOURCE}" ) + configure_test_target_flags("${TARGET_NAME_EXT}" "${TEST_SOURCE}" "${DIR}") + target_sources(${TARGET_NAME_EXT} PRIVATE ${LAUNCH_SOURCES}) + add_test(NAME ${TARGET_NAME_EXT} COMMAND ${TARGET_NAME_EXT}) + cmake_path(SET path2 "${DIR}") + cmake_path(GET path2 FILENAME DIR_NAME) + cmake_path(GET path2 PARENT_PATH DIR_PARENT_PATH) + string(REPLACE " " "" DIR_PARENT_PATH "${DIR_PARENT_PATH}") #for windows + + set_ide_target_folder("${TARGET_NAME_EXT}" "${DIR_PARENT_PATH}" "${EXTENSION}") + +endfunction() + +function (add_generated_test TARGET_NAME TEST_SOURCE EXTENSION DIR) + set(TEST_GENERATED_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}_${EXTENSION}/launcher.${EXTENSION}") #use the same name as the target ) + set (TARGET_NAME_EXT "${TARGET_NAME}_${EXTENSION}") + add_generated_test_stub("${TARGET_NAME_EXT}" "${TEST_SOURCE}" "${DIR}") endfunction() diff --git a/cmake/tests/add_shared_test_libs.cmake b/cmake/tests/add_shared_test_libs.cmake new file mode 100644 index 00000000..a88e7ec4 --- /dev/null +++ b/cmake/tests/add_shared_test_libs.cmake @@ -0,0 +1,36 @@ +function (add_generated_lib TARGET_NAME TEST_SOURCES DIR) + add_library(${TARGET_NAME} SHARED "${TEST_SOURCES}" ) + set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE CXX) + add_generated_export_header_to_target(${TARGET_NAME}) + configure_test_target_flags("${TARGET_NAME}" "${TEST_SOURCES}" "${DIR}") + set_property(TARGET "${TARGET_NAME}" PROPERTY FOLDER "${DIR}") +endfunction() + +function (add_shared_target TARGET_BASE_NAME EXTENSION FUNDAMENTAL_TYPE DIR) + string(TOUPPER ${FUNDAMENTAL_TYPE} FUNDAMENTAL_TYPE_UPPER) + string(TOUPPER ${EXTENSION} EXTENSION_UPPER) + set (GEN_DIR ${TARGET_BASE_NAME}_${FUNDAMENTAL_TYPE}_${EXTENSION}) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${TARGET_BASE_NAME}_ftype.h.in + ${CMAKE_BINARY_DIR}/generated/${GEN_DIR}/${GEN_DIR}.h) + + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${TARGET_BASE_NAME}_ftype.${EXTENSION}.in + ${CMAKE_BINARY_DIR}/generated/${GEN_DIR}/${GEN_DIR}.${EXTENSION}) + + set(SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/${TARGET_BASE_NAME}_common.h" + ${CMAKE_BINARY_DIR}/generated/${GEN_DIR}/${GEN_DIR}.h + ${CMAKE_BINARY_DIR}/generated/${GEN_DIR}/${GEN_DIR}.${EXTENSION}) + + add_generated_lib("${TARGET_NAME}_${EXTENSION}" "${SOURCES}" "${DIR}") + target_include_directories("${TARGET_NAME}_${EXTENSION}" PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/") #testcommon + target_include_directories("${TARGET_NAME}_${EXTENSION}" PUBLIC "${CMAKE_BINARY_DIR}/generated/${GEN_DIR}/") #testcommon + +endfunction() + +function (add_shared_test_lib TARGET_BASE_NAME DIR EXTENSION FUNDAMENTAL_TYPE) + set(TARGET_NAME "${TARGET_BASE_NAME}_${FUNDAMENTAL_TYPE}") + add_shared_target("${TARGET_BASE_NAME}" "${EXTENSION}" "${FUNDAMENTAL_TYPE}" "${DIR}") + if ("${EXTENSION}" STREQUAL "cu") + add_cuda_to_test("${TARGET_NAME}_${EXTENSION}") + endif() + +endfunction() \ No newline at end of file diff --git a/cmake/tests/discover_tests.cmake b/cmake/tests/discover_tests.cmake index 5f26adb2..57416626 100644 --- a/cmake/tests/discover_tests.cmake +++ b/cmake/tests/discover_tests.cmake @@ -1,4 +1,5 @@ include (${CMAKE_SOURCE_DIR}/cmake/tests/add_generated_test.cmake) + function (discover_tests DIR) file( @@ -7,30 +8,30 @@ function (discover_tests DIR) CONFIGURE_DEPENDS "${DIR}/*.h" ) - - foreach(test_source ${TEST_SOURCES}) - get_filename_component(TARGET_NAME ${test_source} NAME_WE) - file (READ ${test_source} TEST_SOURCE_CONTENTS ) #read the contents of the test source file + list(FILTER TEST_SOURCES EXCLUDE REGEX ".*_common.*") + foreach(TEST_SOURCE ${TEST_SOURCES}) + get_filename_component(TARGET_NAME ${TEST_SOURCE} NAME_WE) + file (READ ${TEST_SOURCE} TEST_SOURCE_CONTENTS ) #read the contents of the test source file string(FIND "${TEST_SOURCE_CONTENTS}" "ONLY_CU" POS_ONLY_CU) string(FIND "${TEST_SOURCE_CONTENTS}" "ONLY_CPU" POS_ONLY_CPU) - cmake_path(GET test_source RELATIVE_PART DIR_RELATIVE_PATH) - string(REPLACE "${PROJECT_NAME}/" " " DIR_RELATIVE_PATH "${DIR_RELATIVE_PATH}") #remove the project name from the relative path + set (TEST_SOURCE1 "${TEST_SOURCE}") + string(REPLACE ${CMAKE_SOURCE_DIR} "" TEST_SOURCE1 "${TEST_SOURCE1}") #make the path relative to the current directory) + cmake_path(GET TEST_SOURCE1 RELATIVE_PART DIR_RELATIVE_PATH) + if (${POS_ONLY_CU} EQUAL -1) #if the source file does not contain "__ONLY_CU__" - if (${ENABLE_CPU}) - add_generated_test("${TARGET_NAME}" "${test_source}" "cpp" "${DIR_RELATIVE_PATH}") - endif() + if (${ENABLE_CPU}) + add_generated_test("${TARGET_NAME}" "${TEST_SOURCE}" "cpp" "${DIR_RELATIVE_PATH}") + endif() endif() if (CMAKE_CUDA_COMPILER AND ENABLE_CUDA) - if (${POS_ONLY_CPU} EQUAL -1) #if the source file does not contain "__ONLY_CPU__" - add_generated_test("${TARGET_NAME}" "${test_source}" "cu" "${DIR_RELATIVE_PATH}") + if (${POS_ONLY_CPU} EQUAL -1) #if the source file does not contain "__ONLY_CPU__" + add_generated_test("${TARGET_NAME}" "${TEST_SOURCE}" "cu" "${DIR_RELATIVE_PATH}") add_cuda_to_test("${TARGET_NAME}_cu") endif() - endif() - - - endforeach() + endif() + endforeach() endfunction() \ No newline at end of file diff --git a/utests/CMakeLists.txt b/utests/CMakeLists.txt index bffc07e3..a6d73935 100644 --- a/utests/CMakeLists.txt +++ b/utests/CMakeLists.txt @@ -1,8 +1,12 @@ set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUT_DIR}) + + MACRO(SUBDIRLIST result curdir) FILE(GLOB children ${curdir}/*) # + + SET(dirlist "") FOREACH(child ${children}) IF(IS_DIRECTORY ${child}) @@ -12,12 +16,23 @@ ENDFOREACH() SET(${result} ${dirlist} ) ENDMACRO() + +#remove shared libs, we will build targets separa + set (LIST_OF_DIRS "") SUBDIRLIST(LIST_DIRS ${CMAKE_CURRENT_SOURCE_DIR}) - + foreach(DIR ${LIST_DIRS}) set (ROOT_TEST_DIR "utests") discover_tests(${DIR}) endforeach() + +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/algorithm/image_processing/utest_saturate) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/core/utils/utest_cuda_vector_utils) + + + + + diff --git a/utests/algorithm/image_processing/utest_saturate.h b/utests/algorithm/image_processing/utest_saturate.h deleted file mode 100644 index d2f39f7c..00000000 --- a/utests/algorithm/image_processing/utest_saturate.h +++ /dev/null @@ -1,134 +0,0 @@ -/* Copyright 2025 Oscar Amoros Huguet - Copyright 2025 Grup Mediapro S.L.U - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. */ - -#include -#include -#include -#include -#include -#include - -inline std::string niceType(const std::string& input) { - // Map "unsigned type" to specific type names - static const std::unordered_map unsignedTypeMap = { - {"unsigned char", "uchar"}, - {"unsigned short", "ushort"}, - {"unsigned int", "uint"}, - {"unsigned long", "ulong"}, - {"unsigned longlong", "ulonglong"}, - {"__int64", "longlong"}, - {"unsigned __int64", "ulonglong"} - }; - - // Check if the input matches any key in the map - auto it = unsignedTypeMap.find(input); - if (it != unsignedTypeMap.end()) { - return it->second; // Return the mapped type name - } - return input; -} - -template -constexpr OutputType expectedMinValue() { - if constexpr (cxp::cmp_less_equal(fk::minValue>, fk::minValue>)) { - return fk::minValue; - } else { - return fk::Cast::exec(fk::minValue); - } -} - -template -constexpr T halfPositiveRange() { - return fk::make_set(fk::maxValue> / 2); -} - -template -constexpr OutputType expectedPositiveValue(const InputType& input) { - if (cxp::cmp_greater(fk::vectorAt<0>(input), fk::maxValue>)) { - return fk::maxValue; - } else { - return fk::Cast::exec(input); - } -} - -template -void addOneTest() { - // minValue <= minValue -> output{ fk::minValue, ... } - // minValue > minValue -> output{ fk::Cast::exec(fk::minValue), ... } - constexpr OutputType expectedMinVal = expectedMinValue(); - - // maxValue < maxValue -> output{ ... , fk::Cast::exec(fk::maxValue) } - // maxValue >= maxValue -> output{ ... , fk::maxValue } - constexpr OutputType expectedMaxVal = expectedPositiveValue(fk::maxValue); - - // halfPositiveRange() < maxValue -> output{ ... , fk::Cast::exec(fk::maxValue) } - // halfPositiveRange() >= maxValue -> output{ ... , fk::maxValue } - constexpr OutputType expectedHalfMaxValue = expectedPositiveValue(halfPositiveRange()); - - constexpr std::array inputVals{ fk::minValue, halfPositiveRange(), fk::maxValue }; - constexpr std::array outputVals{ expectedMinVal, expectedHalfMaxValue, expectedMaxVal}; - - TestCaseBuilder>::addTest(testCases, inputVals, outputVals); -} - -template -void addOneTestAllChannels() { - // Base Type - addOneTest(); - - // Vector of 1 - using Input1 = typename fk::VectorType::type_v; - using Output1 = typename fk::VectorType::type_v; - addOneTest(); - - // Vector of 2 - using Input2 = fk::VectorType_t; - using Output2 = fk::VectorType_t; - addOneTest(); - - // Vector of 3 - using Input3 = fk::VectorType_t; - using Output3 = fk::VectorType_t; - addOneTest(); - - // Vector of 4 - using Input4 = fk::VectorType_t; - using Output4 = fk::VectorType_t; - addOneTest(); -} - -template -void addAllTestsFor_helper(const std::index_sequence&) { - static_assert(fk::validCUDAVec || std::is_fundamental_v, "Type must be either a cuda vector or a fundamental type."); - static_assert(fk::isTypeList, "TypeList_ must be a valid TypeList."); - // For each type in TypeList_, add tests with Type - (addOneTestAllChannels, Type>(), ...); -} - -template -void addAllTestsFor(const std::index_sequence&) { - // For each type in TypeList_, add tests with each type in TypeList_ - (addAllTestsFor_helper>(std::make_index_sequence{}), ...); -} - -START_ADDING_TESTS -using Fundamental = fk::RemoveType_t<0, fk::StandardTypes>; -addAllTestsFor(std::make_index_sequence()); -STOP_ADDING_TESTS - -// You can add more tests for other type combinations as needed. -int launch() { - RUN_ALL_TESTS -}; \ No newline at end of file diff --git a/utests/algorithm/image_processing/utest_saturate/CMakeLists.txt b/utests/algorithm/image_processing/utest_saturate/CMakeLists.txt new file mode 100644 index 00000000..ea9fba20 --- /dev/null +++ b/utests/algorithm/image_processing/utest_saturate/CMakeLists.txt @@ -0,0 +1,49 @@ +include (${CMAKE_SOURCE_DIR}/cmake/tests/add_shared_test_libs.cmake) +#shared tests libs first +set(TARGET_NAME utest_saturate) +if (${ENABLE_CPU}) + set(EXTENSION cpp) + string(TOUPPER ${EXTENSION} EXTENSION_UPPER) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${TARGET_NAME}.h.in + ${CMAKE_BINARY_DIR}/${TARGET_NAME}/${TARGET_NAME}_${EXTENSION}.h) + + add_generated_test_stub("${TARGET_NAME}_${EXTENSION}" + "${CMAKE_BINARY_DIR}/${TARGET_NAME}/${TARGET_NAME}_${EXTENSION}.h" + "" + ) + set_ide_target_folder(${TARGET_NAME}_${EXTENSION} "/utests/algorithm/image_processing" ${EXTENSION}) + + foreach(FUNDAMENTAL_TYPE ${FUNDAMENTAL_TYPES}) + add_shared_test_lib(${TARGET_NAME} "" "${EXTENSION}" "${FUNDAMENTAL_TYPE}") + set_property(TARGET "${TARGET_NAME}_${FUNDAMENTAL_TYPE}_${EXTENSION}" PROPERTY FOLDER "/utests/algorithm/image_processing/cpu/${TARGET_NAME}") + target_link_libraries(${TARGET_NAME}_${EXTENSION} PRIVATE "${TARGET_NAME}_${FUNDAMENTAL_TYPE}_${EXTENSION}") + endforeach() + +endif() + +if (CMAKE_CUDA_COMPILER AND ENABLE_CUDA) + set(EXTENSION cu) + string(TOUPPER ${EXTENSION} EXTENSION_UPPER) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${TARGET_NAME}.h.in + ${CMAKE_BINARY_DIR}/${TARGET_NAME}/${TARGET_NAME}_${EXTENSION}.h) #replace variables in the test source file + + add_generated_test_stub("${TARGET_NAME}_cu" + "${CMAKE_BINARY_DIR}/${TARGET_NAME}/${TARGET_NAME}_${EXTENSION}.h" + "") + set_ide_target_folder(${TARGET_NAME}_${EXTENSION} "/utests/algorithm/image_processing" "${EXTENSION}") + + add_cuda_to_test("${TARGET_NAME}_cu") + foreach(FUNDAMENTAL_TYPE ${FUNDAMENTAL_TYPES}) + add_shared_test_lib(${TARGET_NAME} + "" + "${EXTENSION}" + "${FUNDAMENTAL_TYPE}" + ) + set_property(TARGET "${TARGET_NAME}_${FUNDAMENTAL_TYPE}_${EXTENSION}" PROPERTY FOLDER "/utests/algorithm/image_processing/cuda/${TARGET_NAME}") + target_link_libraries(${TARGET_NAME}_${EXTENSION} PRIVATE "${TARGET_NAME}_${FUNDAMENTAL_TYPE}_${EXTENSION}") + endforeach() + +endif() + + + \ No newline at end of file diff --git a/utests/algorithm/image_processing/utest_saturate/utest_saturate.h.in b/utests/algorithm/image_processing/utest_saturate/utest_saturate.h.in new file mode 100644 index 00000000..b14f9164 --- /dev/null +++ b/utests/algorithm/image_processing/utest_saturate/utest_saturate.h.in @@ -0,0 +1,46 @@ +/* Copyright 2025 Oscar Amoros Huguet + Copyright 2025 Grup Mediapro S.L.U + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ +#ifndef FK_UTEST_SATURATE_@EXTENSION_UPPER@_H +#define FK_UTEST_SATURATE_@EXTENSION_UPPER@_H +#include "utest_saturate_char_@EXTENSION@.h" +#include "utest_saturate_double_@EXTENSION@.h" +#include "utest_saturate_float_@EXTENSION@.h" +#include "utest_saturate_int_@EXTENSION@.h" +#include "utest_saturate_long_@EXTENSION@.h" +#include "utest_saturate_longlong_@EXTENSION@.h" +#include "utest_saturate_short_@EXTENSION@.h" +#include "utest_saturate_uchar_@EXTENSION@.h" +#include "utest_saturate_uint_@EXTENSION@.h" +#include "utest_saturate_ulong_@EXTENSION@.h" +#include "utest_saturate_ulonglong_@EXTENSION@.h" +#include "utest_saturate_ushort_@EXTENSION@.h" + +#include +#include +int launch() { + const std::vector v{ + fk::utest_saturate_char_@EXTENSION@::launch(), fk::utest_saturate_double_@EXTENSION@::launch(), + fk::utest_saturate_float_@EXTENSION@::launch(), fk::utest_saturate_int_@EXTENSION@::launch(), + fk::utest_saturate_long_@EXTENSION@::launch(), fk::utest_saturate_longlong_@EXTENSION@::launch(), + fk::utest_saturate_short_@EXTENSION@::launch(), fk::utest_saturate_uchar_@EXTENSION@::launch(), + fk::utest_saturate_uint_@EXTENSION@::launch(), fk::utest_saturate_ulong_@EXTENSION@::launch(), + fk::utest_saturate_ulonglong_@EXTENSION@::launch(), fk::utest_saturate_ushort_@EXTENSION@::launch(), + }; + + bool zeros = std::all_of(v.begin(), v.end(), [](int i) { return i == 0; }); + return zeros ? 0 : -1; +} + +#endif \ No newline at end of file diff --git a/utests/algorithm/image_processing/utest_saturate/utest_saturate_common.h b/utests/algorithm/image_processing/utest_saturate/utest_saturate_common.h new file mode 100644 index 00000000..fbd26552 --- /dev/null +++ b/utests/algorithm/image_processing/utest_saturate/utest_saturate_common.h @@ -0,0 +1,89 @@ +#ifndef UTEST_COMMON_H +#define UTEST_COMMON_H + +#include +#include +#include +#include +#include +#include +#include + +template constexpr OutputType expectedMinValue() { + if constexpr (cxp::cmp_less_equal(fk::minValue>, fk::minValue>)) { + return fk::minValue; + } else { + return fk::Cast::exec(fk::minValue); + } +} + +template constexpr T halfPositiveRange() { return fk::make_set(fk::maxValue> / 2); } + +template constexpr OutputType expectedPositiveValue(const InputType &input) { + if (cxp::cmp_greater(fk::vectorAt<0>(input), fk::maxValue>)) { + return fk::maxValue; + } else { + return fk::Cast::exec(input); + } +} + +template void addOneTest() { + constexpr OutputType expectedMinVal = expectedMinValue(); + + constexpr OutputType expectedMaxVal = expectedPositiveValue(fk::maxValue); + + constexpr OutputType expectedHalfMaxValue = expectedPositiveValue(halfPositiveRange()); + + constexpr std::array inputVals{fk::minValue, halfPositiveRange(), + fk::maxValue}; + constexpr std::array outputVals{expectedMinVal, expectedHalfMaxValue, expectedMaxVal}; + + TestCaseBuilder>::addTest(testCases, inputVals, outputVals); +} + +template void addOneTestAllChannels() { + // Base Type + addOneTest(); + + // Vector of 1 + using Input1 = typename fk::VectorType::type_v; + using Output1 = typename fk::VectorType::type_v; + addOneTest(); + + // Vector of 2 + using Input2 = fk::VectorType_t; + using Output2 = fk::VectorType_t; + addOneTest(); + + // Vector of 3 + using Input3 = fk::VectorType_t; + using Output3 = fk::VectorType_t; + addOneTest(); + + // Vector of 4 + using Input4 = fk::VectorType_t; + using Output4 = fk::VectorType_t; + addOneTest(); +} + +template +void addAllTestsFor_helper(const std::index_sequence &) { + static_assert(fk::validCUDAVec || std::is_fundamental_v, + "Type must be either a cuda vector or a fundamental type."); + static_assert(fk::isTypeList, "TypeList_ must be a valid TypeList."); + // For each type in TypeList_, add tests with Type + (addOneTestAllChannels, Type>(), ...); +} + +template void addAllTestsFor(const std::index_sequence &) { + // For each type in TypeList_, add tests with each type in TypeList_ + (addAllTestsFor_helper>(std::make_index_sequence{}), ...); +} + +template +void addAllOutputTestsForInput(const std::index_sequence &) { + // For each OutputType in OutputTypeList, add tests with fixed InputType + (addOneTestAllChannels>(), ...); +} + +#endif // ! UTEST_COMMON_H \ No newline at end of file diff --git a/utests/algorithm/image_processing/utest_saturate/utest_saturate_ftype.cpp.in b/utests/algorithm/image_processing/utest_saturate/utest_saturate_ftype.cpp.in new file mode 100644 index 00000000..de1d26d8 --- /dev/null +++ b/utests/algorithm/image_processing/utest_saturate/utest_saturate_ftype.cpp.in @@ -0,0 +1,26 @@ +/* Copyright 2025 Oscar Amoros Huguet + Copyright 2025 Grup Mediapro S.L.U + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#include "utest_saturate_@FUNDAMENTAL_TYPE@_@EXTENSION@.h" + +START_ADDING_TESTS +using Fundamental = fk::RemoveType_t<0, fk::StandardTypes>; +addAllOutputTestsForInput(std::make_index_sequence{}); +STOP_ADDING_TESTS + +int fk::utest_saturate_@FUNDAMENTAL_TYPE@_@EXTENSION@::launch() { + RUN_ALL_TESTS + return 0; +} diff --git a/utests/algorithm/image_processing/utest_saturate/utest_saturate_ftype.cu.in b/utests/algorithm/image_processing/utest_saturate/utest_saturate_ftype.cu.in new file mode 100644 index 00000000..811a5942 --- /dev/null +++ b/utests/algorithm/image_processing/utest_saturate/utest_saturate_ftype.cu.in @@ -0,0 +1,26 @@ +/* Copyright 2025 Oscar Amoros Huguet + Copyright 2025 Grup Mediapro S.L.U + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#include "utest_saturate_@FUNDAMENTAL_TYPE@_@EXTENSION@.h" + +START_ADDING_TESTS +using Fundamental = fk::RemoveType_t<0, fk::StandardTypes>; +addAllOutputTestsForInput(std::make_index_sequence{}); +STOP_ADDING_TESTS + +int fk::utest_saturate_@FUNDAMENTAL_TYPE@_@EXTENSION@::launch() { + RUN_ALL_TESTS + return 0; +} \ No newline at end of file diff --git a/utests/algorithm/image_processing/utest_saturate/utest_saturate_ftype.h.in b/utests/algorithm/image_processing/utest_saturate/utest_saturate_ftype.h.in new file mode 100644 index 00000000..2ca04cd5 --- /dev/null +++ b/utests/algorithm/image_processing/utest_saturate/utest_saturate_ftype.h.in @@ -0,0 +1,25 @@ +/* Copyright 2025 Oscar Amoros Huguet + Copyright 2025 Grup Mediapro S.L.U + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#ifndef FK_UTEST_SATURATE_@FUNDAMENTAL_TYPE_UPPER@_H +#define FK_UTEST_SATURATE_@FUNDAMENTAL_TYPE_UPPER@_H + +#include "utest_saturate_@FUNDAMENTAL_TYPE@_@EXTENSION@_export.h" +#include "utest_saturate_common.h" + +namespace fk::utest_saturate_@FUNDAMENTAL_TYPE@_@EXTENSION@{ + int UTEST_SATURATE_@FUNDAMENTAL_TYPE_UPPER@_@EXTENSION_UPPER@_EXPORT launch(); +#endif +} diff --git a/utests/core/utils/utest_cuda_vector_utils.h b/utests/core/utils/utest_cuda_vector_utils.h deleted file mode 100644 index 101957ec..00000000 --- a/utests/core/utils/utest_cuda_vector_utils.h +++ /dev/null @@ -1,530 +0,0 @@ -/* Copyright 2025 Oscar Amoros Huguet - Copyright 2025 Albert Andaluz - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. */ - -#define __ONLY_CPU__ - -#ifndef FK_UTEST_CUDA_VECTOR_UTILS_H -#define FK_UTEST_CUDA_VECTOR_UTILS_H - -#include -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) - // MSVC - // C4804: 'operation': unsafe use of type 'bool' in operation - // C4805: 'operation': unsafe mix of type 'type' and type 'bool' in operation - // C4806: 'case': unsafe case statement that forces value to bool 'true' or 'false' -#pragma warning(disable : 4804 4805 4806) - -#elif defined(__clang__) - // Clang -#pragma clang diagnostic ignored "-Wbool-compare" // Compares a bool with an integer other than 0 or 1 -#pragma clang diagnostic ignored "-Wbool-operation" // e.g., using bitwise operations on bools -#pragma clang diagnostic ignored "-Wswitch-bool" // e.g., switch(myBool) { case 123: ... } - -#elif defined(__GNUC__) - // GCC -#pragma GCC diagnostic ignored "-Wbool-compare" // Compares a bool with an integer other than 0 or 1 -// Note: GCC does not have direct equivalents for C4804 and C4806. -// The broader -Wconversion flag might catch some cases but is often too aggressive to disable globally. -#endif - -namespace fk::test { - // Track compilation results - std::vector unexpected_failed_compilations; - - // SFINAE-based test helpers - template - struct can_unary_minus : std::false_type {}; - - template - struct can_unary_minus())>> : std::true_type {}; - - template - struct can_unary_not : std::false_type {}; - - template - struct can_unary_not())>> : std::true_type {}; - - template - struct can_unary_bitwise_not : std::false_type {}; - - template - struct can_unary_bitwise_not())>> : std::true_type {}; - - template - struct can_add : std::false_type {}; - - template - struct can_add() + std::declval())>> : std::true_type {}; - - template - struct can_subtract : std::false_type {}; - - template - struct can_subtract() - std::declval())>> : std::true_type {}; - - template - struct can_multiply : std::false_type {}; - - template - struct can_multiply() * std::declval())>> : std::true_type {}; - - template - struct can_divide : std::false_type {}; - - template - struct can_divide() / std::declval())>> : std::true_type {}; - - template - struct can_equal : std::false_type {}; - - template - struct can_equal() == std::declval())>> : std::true_type {}; - - template - struct can_not_equal : std::false_type {}; - - template - struct can_not_equal() != std::declval())>> : std::true_type {}; - - template - struct can_less : std::false_type {}; - - template - struct can_less() < std::declval())>> : std::true_type {}; - - template - struct can_less_equal : std::false_type {}; - - template - struct can_less_equal() <= std::declval())>> : std::true_type {}; - - template - struct can_greater : std::false_type {}; - - template - struct can_greater() > std::declval())>> : std::true_type {}; - - template - struct can_greater_equal : std::false_type {}; - - template - struct can_greater_equal() >= std::declval())>> : std::true_type {}; - - template - struct can_logical_and : std::false_type {}; - - template - struct can_logical_and() && std::declval())>> : std::true_type {}; - - template - struct can_logical_or : std::false_type {}; - - template - struct can_logical_or() || std::declval())>> : std::true_type {}; - - template - struct can_bitwise_and : std::false_type {}; - - template - struct can_bitwise_and() & std::declval())>> : std::true_type {}; - - template - struct can_bitwise_or : std::false_type {}; - - template - struct can_bitwise_or() | std::declval())>> : std::true_type {}; - - template - struct can_bitwise_xor : std::false_type {}; - - template - struct can_bitwise_xor() ^ std::declval())>> : std::true_type {}; - - // Compound assignment operators - template - struct can_add_assign : std::false_type {}; - - template - struct can_add_assign() += std::declval())>> : std::true_type {}; - - template - struct can_sub_assign : std::false_type {}; - - template - struct can_sub_assign() -= std::declval())>> : std::true_type {}; - - template - struct can_mul_assign : std::false_type {}; - - template - struct can_mul_assign() *= std::declval())>> : std::true_type {}; - - template - struct can_div_assign : std::false_type {}; - - template - struct can_div_assign() /= std::declval())>> : std::true_type {}; - - template - struct can_and_assign : std::false_type {}; - - template - struct can_and_assign() &= std::declval())>> : std::true_type {}; - - template - struct can_or_assign : std::false_type {}; - - template - struct can_or_assign() |= std::declval())>> : std::true_type {}; - - using VecAndStdTypes = TypeListCat_t; - - template - void detectUnaryUnexpectedCompilationErrors() { - if constexpr (can_unary_minus::value != can_unary_minus>::value) { - unexpected_failed_compilations.push_back("unaryMinus_" + typeToString()); - } - if constexpr (can_unary_not::value != can_unary_not>::value) { - unexpected_failed_compilations.push_back("unaryNot_" + typeToString()); - } - if constexpr (can_unary_bitwise_not::value != can_unary_bitwise_not>::value) { - unexpected_failed_compilations.push_back("unaryBitwiseNot_" + typeToString()); - } - } - - template - void detectBinaryUnexpectedCompilationErrors() { - if constexpr (cn == cn) { - if constexpr (can_add::value != can_add, VBase>::value) { - unexpected_failed_compilations.push_back("binaryAdd_" + typeToString() + "_" + typeToString()); - } - if constexpr (can_subtract::value != can_subtract, VBase>::value) { - unexpected_failed_compilations.push_back("binaryMinus_" + typeToString() + "_" + typeToString()); - } - if constexpr (can_multiply::value != can_multiply, VBase>::value) { - unexpected_failed_compilations.push_back("binaryMul_" + typeToString() + "_" + typeToString()); - } - if constexpr (can_divide::value != can_divide, VBase>::value) { - unexpected_failed_compilations.push_back("binaryDiv_" + typeToString() + "_" + typeToString()); - } - if constexpr (can_equal::value != can_equal, VBase>::value) { - unexpected_failed_compilations.push_back("binaryEqual_" + typeToString() + "_" + typeToString()); - } - if constexpr (can_not_equal::value != can_not_equal, VBase>::value) { - unexpected_failed_compilations.push_back("binaryNotEqual_" + typeToString() + "_" + typeToString()); - } - if constexpr (can_less::value != can_less, VBase>::value) { - unexpected_failed_compilations.push_back("binaryLess_" + typeToString() + "_" + typeToString()); - } - if constexpr (can_less_equal::value != can_less_equal, VBase>::value) { - unexpected_failed_compilations.push_back("binaryLessEqual_" + typeToString() + "_" + typeToString()); - } - if constexpr (can_greater::value != can_greater, VBase>::value) { - unexpected_failed_compilations.push_back("binaryGreater_" + typeToString() + "_" + typeToString()); - } - if constexpr (can_greater_equal::value != can_greater_equal, VBase>::value) { - unexpected_failed_compilations.push_back("binaryGreaterEqual_" + typeToString() + "_" + typeToString()); - } - if constexpr (can_logical_and::value != can_logical_and, VBase>::value) { - unexpected_failed_compilations.push_back("logicalAnd_" + typeToString() + "_" + typeToString()); - } - if constexpr (can_logical_or::value != can_logical_or, VBase>::value) { - unexpected_failed_compilations.push_back("logicalOr_" + typeToString() + "_" + typeToString()); - } - if constexpr (can_bitwise_and::value != can_bitwise_and, VBase>::value) { - unexpected_failed_compilations.push_back("bitwiseAnd_" + typeToString() + "_" + typeToString()); - } - if constexpr (can_bitwise_or::value != can_bitwise_or, VBase>::value) { - unexpected_failed_compilations.push_back("bitwiseOr_" + typeToString() + "_" + typeToString()); - } - if constexpr (can_bitwise_xor::value != can_bitwise_xor, VBase>::value) { - unexpected_failed_compilations.push_back("bitwiseXor_" + typeToString() + "_" + typeToString()); - } - } - } - - template - void detectCompoundUnexpectedCompilationErrors() { - if constexpr (fk::AreVVEqCN::value || fk::AreVS::value) { - // The case scalar += vector is not supported (same for the other compound operators) - if constexpr (can_add_assign::value != can_add_assign, VBase>::value) { - unexpected_failed_compilations.push_back("addAssign_" + typeToString() + "_" + typeToString()); - } - if constexpr (can_sub_assign::value != can_sub_assign, VBase>::value) { - unexpected_failed_compilations.push_back("subAssign_" + typeToString() + "_" + typeToString()); - } - if constexpr (can_mul_assign::value != can_mul_assign, VBase>::value) { - unexpected_failed_compilations.push_back("mulAssign_" + typeToString() + "_" + typeToString()); - } - if constexpr (can_div_assign::value != can_div_assign, VBase>::value) { - unexpected_failed_compilations.push_back("divAssign_" + typeToString() + "_" + typeToString()); - } - if constexpr (can_and_assign::value != can_and_assign, VBase>::value) { - unexpected_failed_compilations.push_back("andAssign_" + typeToString() + "_" + typeToString()); - } - if constexpr (can_or_assign::value != can_or_assign, VBase>::value) { - unexpected_failed_compilations.push_back("orAssign_" + typeToString() + "_" + typeToString()); - } - } - } - - template - constexpr inline bool testUnaryMinus() { - if constexpr (can_unary_minus::value) { - constexpr VBase base_val{ static_cast>(5) }; - constexpr T val = make_set(base_val); - constexpr auto result = -val; // Test unary minus - constexpr auto expectedBase = -base_val; - using ExpectedBaseType = std::decay_t; - using ExpectedType = typename VectorType>::type_v; - constexpr ExpectedType expectedResult = make_set(expectedBase); - return expectedResult == result; - } else { - return true; - } - } - - template - constexpr inline bool testUnaryNot() { - if constexpr (can_unary_not::value) { - constexpr VBase base_val{ static_cast>(5) }; - constexpr T val = make_set(base_val); - constexpr auto result = !val; // Test unary not - constexpr auto expectedBase = !base_val; - using ExpectedBaseType = std::decay_t; - using ExpectedType = typename VectorType>::type_v; - constexpr ExpectedType expectedResult = make_set(expectedBase); - return expectedResult == result; - } else { - return true; - } - } - - template - constexpr inline bool testUnaryBitwiseNot() { - if constexpr (can_unary_bitwise_not::value) { - constexpr VBase base_val{ static_cast>(5) }; - constexpr T val = make_set(base_val); - constexpr auto result = ~val; // Test unary bitwise not - constexpr auto expectedBase = ~base_val; - using ExpectedBaseType = std::decay_t; - using ExpectedType = typename VectorType>::type_v; - constexpr ExpectedType expectedResult = make_set(expectedBase); - return expectedResult == result; - } else { - return true; - } - } - template - bool testUnaryOperators() { - bool correct{ true }; - if (!testUnaryMinus()) { - std::cout << "Failed unaryMinus test for type: " << typeToString() << std::endl; - correct = false; - } - if (!testUnaryNot()) { - std::cout << "Failed unaryNot test for type: " << typeToString() << std::endl; - correct = false; - } - if (!testUnaryBitwiseNot()) { - std::cout << "Failed unaryBitwiseNot test for type: " << typeToString() << std::endl; - correct = false; - } - return correct; - } - -#define BINARY_OP_TEST(OP_NAME, OP) \ - template \ - constexpr inline bool binary ## OP_NAME() { \ - if constexpr (can_ ## OP_NAME::value) { \ - constexpr VBase base_val1{ static_cast>(5) }; \ - constexpr VBase base_val2{ static_cast>(3) }; \ - constexpr I1 val1 = make_set(base_val1); \ - constexpr I2 val2 = make_set(base_val2); \ - constexpr auto result = val1 OP val2; \ - constexpr auto expectedBase = base_val1 OP base_val2; \ - using ExpectedBaseType = std::decay_t; \ - using ExpectedType = typename VectorType > cn ? cn : cn)>::type_v; \ - constexpr ExpectedType expectedResult = make_set(expectedBase); \ - return expectedResult == result; \ - } else { \ - return true; \ - } \ - } - -BINARY_OP_TEST(add, +) -BINARY_OP_TEST(subtract, -) -BINARY_OP_TEST(multiply, *) -BINARY_OP_TEST(divide, /) -BINARY_OP_TEST(equal, ==) -BINARY_OP_TEST(not_equal, !=) -BINARY_OP_TEST(less, <) -BINARY_OP_TEST(less_equal, <=) -BINARY_OP_TEST(greater, >) -BINARY_OP_TEST(greater_equal, >=) -BINARY_OP_TEST(logical_and, &&) -BINARY_OP_TEST(logical_or, ||) -BINARY_OP_TEST(bitwise_and, &) -BINARY_OP_TEST(bitwise_or, |) -BINARY_OP_TEST(bitwise_xor, ^) - -#undef BINARY_OP_TEST - - template - bool testBinaryOperators() { - constexpr std::array binaryOperatorTestNames - { "binaryAdd", "binaryMinus", "binaryMul", "binaryDiv", - "binaryEqual", "binaryNotEqual", "binaryLess", "binaryLessEqual", - "binaryGreater", "binaryGreaterEqual", "binaryLogicalAnd", "binaryLogicalOr", - "binaryBitwiseAnd", "binaryBitwiseOr", "binaryBitwiseXor" }; - constexpr std::array results = { - binaryadd(), binarysubtract(), binarymultiply(), binarydivide(), - binaryequal(), binarynot_equal(), binaryless(), - binaryless_equal(), binarygreater(), binarygreater_equal(), - binarylogical_and(), binarylogical_or(), binarybitwise_and(), - binarybitwise_or(), binarybitwise_xor() - }; - bool correct{ true }; - for (int i = 0; i < 15; ++i) { - if (!results[i]) { - std::cout << "Failed " << binaryOperatorTestNames[i] << " test for types: " - << typeToString() << " and " << typeToString() << std::endl; - correct = false; - } - } - return correct; - } - -#define COMPOUND_OP_TEST(OP_NAME, OP) \ - template \ - constexpr inline bool compound ## OP_NAME() { \ - if constexpr (can_ ## OP_NAME::value) { \ - VBase base_val1{ static_cast>(5) }; \ - constexpr VBase base_val2{ static_cast>(3) }; \ - I1 val1 = make_set(base_val1); \ - constexpr I2 val2 = make_set(base_val2); \ - val1 OP val2; \ - base_val1 OP base_val2; \ - using ExpectedBaseType = VBase; \ - using ExpectedType = I1; \ - ExpectedType expectedResult = make_set(base_val1); \ - return expectedResult == val1; \ - } else { \ - return true; \ - } \ - } - -COMPOUND_OP_TEST(add_assign, +=) -COMPOUND_OP_TEST(sub_assign, -=) -COMPOUND_OP_TEST(mul_assign, *=) -COMPOUND_OP_TEST(div_assign, /=) -COMPOUND_OP_TEST(and_assign, &=) -COMPOUND_OP_TEST(or_assign, |=) - -#undef COMPOUND_OP_TEST - - // Test compound operators - template - bool testCompoundOperators() { - constexpr std::array compoundOperatorTestNames - { "compoundAddAssign", "compoundSubAssign", "compoundMulAssign", - "compoundDivAssign", "compoundAndAssign", "compoundOrAssign" }; - const std::array results{ compoundadd_assign(), compoundsub_assign(), - compoundmul_assign(), compounddiv_assign(), - compoundand_assign(), compoundor_assign() }; - bool correct{ true }; - for (int i = 0; i < 6; ++i) { - if (!results[i]) { - std::cout << "Failed " << compoundOperatorTestNames[i] << " test for types: " - << typeToString() << " and " << typeToString() << std::endl; - correct = false; - } - } - return correct; - } - - template - struct UnaryTest; - - template - struct UnaryTest> { - static bool execute() { - (detectUnaryUnexpectedCompilationErrors(), ...); - return (testUnaryOperators() && ...); - } - }; - - template - struct BinaryTests; - - template - struct BinaryTests, TypeList> { - static bool execute() { - if constexpr (sizeof...(Types1) == 0) { - (detectBinaryUnexpectedCompilationErrors(), ...); - return (testBinaryOperators() && ...); - } else { - (detectBinaryUnexpectedCompilationErrors(), ...); - const bool result = (testBinaryOperators() && ...); - return result && BinaryTests, TypeList>::execute(); - } - } - }; - - template - struct CompoundTests; - - template - struct CompoundTests, TypeList> { - static bool execute() { - if constexpr (sizeof...(Types1) == 0) { - (detectCompoundUnexpectedCompilationErrors(), ...); - return (testCompoundOperators() && ...); - } else { - (detectCompoundUnexpectedCompilationErrors(), ...); - const bool result = (testCompoundOperators() && ...); - return result && CompoundTests, TypeList>::execute(); - } - } - }; - -} // namespace fk::test - -int launch() { - using namespace fk::test; - - bool passed = UnaryTest::execute(); - passed &= BinaryTests::execute(); - passed &= CompoundTests::execute(); - - if (!unexpected_failed_compilations.empty()) { - std::cout << "ERROR: Unexpected compilation failures that did occur:\n"; - for (const auto& failure : unexpected_failed_compilations) { - std::cout << "- " << failure << std::endl; - } - return -1; - } - - return passed ? 0 : -1; -} - -#endif // FK_UTEST_CUDA_VECTOR_UTILS_H \ No newline at end of file diff --git a/utests/core/utils/utest_cuda_vector_utils/CMakeLists.txt b/utests/core/utils/utest_cuda_vector_utils/CMakeLists.txt new file mode 100644 index 00000000..57b84cb3 --- /dev/null +++ b/utests/core/utils/utest_cuda_vector_utils/CMakeLists.txt @@ -0,0 +1,23 @@ +include (${CMAKE_SOURCE_DIR}/cmake/tests/add_shared_test_libs.cmake) +if (${ENABLE_CPU}) + set(EXTENSION cpp) + set(TARGET_NAME utest_cuda_vector_utils) + string(TOUPPER ${EXTENSION} EXTENSION_UPPER) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/utest_cuda_vector_utils.h.in + ${CMAKE_BINARY_DIR}/utest_cuda_vector_utils/utest_cuda_vector_utils_${EXTENSION}.h) + #replace variables in the test source file + + add_generated_test_stub("utest_cuda_vector_utils_${EXTENSION}" + "${CMAKE_BINARY_DIR}/utest_cuda_vector_utils/utest_cuda_vector_utils_${EXTENSION}.h" + "") + set_ide_target_folder(${TARGET_NAME}_${EXTENSION} "/utests/core/utils" ${EXTENSION}) + foreach(FUNDAMENTAL_TYPE ${FUNDAMENTAL_TYPES}) + add_shared_test_lib(${TARGET_NAME} "" ${EXTENSION} ${FUNDAMENTAL_TYPE}) + set_property(TARGET "${TARGET_NAME}_${FUNDAMENTAL_TYPE}_${EXTENSION}" PROPERTY FOLDER "utests/core/utils/cpu/${TARGET_NAME}") + target_link_libraries(${TARGET_NAME}_${EXTENSION} PRIVATE "${TARGET_NAME}_${FUNDAMENTAL_TYPE}_${EXTENSION}") + endforeach() +endif() + + + + \ No newline at end of file diff --git a/utests/core/utils/utest_cuda_vector_utils/utest_cuda_vector_utils.h.in b/utests/core/utils/utest_cuda_vector_utils/utest_cuda_vector_utils.h.in new file mode 100644 index 00000000..158b25c5 --- /dev/null +++ b/utests/core/utils/utest_cuda_vector_utils/utest_cuda_vector_utils.h.in @@ -0,0 +1,51 @@ +/* Copyright 2025 Oscar Amoros Huguet + Copyright 2025 Albert Andaluz + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#define __ONLY_CPU__ + +#ifndef FK_UTEST_CUDA_VECTOR_UTILS_@EXTENSION@_H +#define FK_UTEST_CUDA_VECTOR_UTILS_@EXTENSION@_H + +#include "utest_cuda_vector_utils_char_@EXTENSION@.h" +#include "utest_cuda_vector_utils_double_@EXTENSION@.h" +#include "utest_cuda_vector_utils_float_@EXTENSION@.h" +#include "utest_cuda_vector_utils_int_@EXTENSION@.h" +#include "utest_cuda_vector_utils_long_@EXTENSION@.h" +#include "utest_cuda_vector_utils_longlong_@EXTENSION@.h" +#include "utest_cuda_vector_utils_short_@EXTENSION@.h" +#include "utest_cuda_vector_utils_uchar_@EXTENSION@.h" +#include "utest_cuda_vector_utils_uint_@EXTENSION@.h" +#include "utest_cuda_vector_utils_ulong_@EXTENSION@.h" +#include "utest_cuda_vector_utils_ulonglong_@EXTENSION@.h" +#include "utest_cuda_vector_utils_ushort_@EXTENSION@.h" + +#include +#include + +int launch() { + const std::vector v{ + fk::utest_cuda_vector_utils_char_@EXTENSION@::launch(), fk::utest_cuda_vector_utils_double_@EXTENSION@::launch(), + fk::utest_cuda_vector_utils_float_@EXTENSION@::launch(), fk::utest_cuda_vector_utils_int_@EXTENSION@::launch(), + fk::utest_cuda_vector_utils_long_@EXTENSION@::launch(), fk::utest_cuda_vector_utils_longlong_@EXTENSION@::launch(), + fk::utest_cuda_vector_utils_short_@EXTENSION@::launch(), fk::utest_cuda_vector_utils_uchar_@EXTENSION@::launch(), + fk::utest_cuda_vector_utils_uint_@EXTENSION@::launch(), fk::utest_cuda_vector_utils_ulong_@EXTENSION@::launch(), + fk::utest_cuda_vector_utils_ulonglong_@EXTENSION@::launch(), fk::utest_cuda_vector_utils_ushort_@EXTENSION@::launch(), + }; + bool zeros = std::all_of(v.begin(), v.end(), [](int i) { return i == 0; }); + + return zeros ? 0 : -1; +} + +#endif // FK_UTEST_CUDA_VECTOR_UTILS_H \ No newline at end of file diff --git a/utests/core/utils/utest_cuda_vector_utils/utest_cuda_vector_utils_common.h b/utests/core/utils/utest_cuda_vector_utils/utest_cuda_vector_utils_common.h new file mode 100644 index 00000000..433da0d9 --- /dev/null +++ b/utests/core/utils/utest_cuda_vector_utils/utest_cuda_vector_utils_common.h @@ -0,0 +1,430 @@ +// Track compilation results +#ifndef FK_UTEST_CUDA_VECTOR_UTILS_COMMON_H +#define FK_UTEST_CUDA_VECTOR_UTILS_COMMON_H + +#include +#include +#include +#include +#include +#include +#include +using namespace fk; +std::vector unexpected_failed_compilations; + +// SFINAE-based test helpers +template struct can_unary_minus : std::false_type {}; + +template struct can_unary_minus())>> : std::true_type {}; + +template struct can_unary_not : std::false_type {}; + +template struct can_unary_not())>> : std::true_type {}; + +template struct can_unary_bitwise_not : std::false_type {}; + +template struct can_unary_bitwise_not())>> : std::true_type {}; + +template struct can_add : std::false_type {}; + +template +struct can_add() + std::declval())>> : std::true_type {}; + +template struct can_subtract : std::false_type {}; + +template +struct can_subtract() - std::declval())>> : std::true_type {}; + +template struct can_multiply : std::false_type {}; + +template +struct can_multiply() * std::declval())>> : std::true_type {}; + +template struct can_divide : std::false_type {}; + +template +struct can_divide() / std::declval())>> : std::true_type {}; + +template struct can_equal : std::false_type {}; + +template +struct can_equal() == std::declval())>> : std::true_type {}; + +template struct can_not_equal : std::false_type {}; + +template +struct can_not_equal() != std::declval())>> : std::true_type {}; + +template struct can_less : std::false_type {}; + +template +struct can_less() < std::declval())>> : std::true_type {}; + +template struct can_less_equal : std::false_type {}; + +template +struct can_less_equal() <= std::declval())>> : std::true_type {}; + +template struct can_greater : std::false_type {}; + +template +struct can_greater() > std::declval())>> : std::true_type {}; + +template struct can_greater_equal : std::false_type {}; + +template +struct can_greater_equal() >= std::declval())>> : std::true_type {}; + +template struct can_logical_and : std::false_type {}; + +template +struct can_logical_and() && std::declval())>> : std::true_type {}; + +template struct can_logical_or : std::false_type {}; + +template +struct can_logical_or() || std::declval())>> : std::true_type {}; + +template struct can_bitwise_and : std::false_type {}; + +template +struct can_bitwise_and() & std::declval())>> : std::true_type {}; + +template struct can_bitwise_or : std::false_type {}; + +template +struct can_bitwise_or() | std::declval())>> : std::true_type {}; + +template struct can_bitwise_xor : std::false_type {}; + +template +struct can_bitwise_xor() ^ std::declval())>> : std::true_type {}; + +// Compound assignment operators +template struct can_add_assign : std::false_type {}; + +template +struct can_add_assign() += std::declval())>> : std::true_type {}; + +template struct can_sub_assign : std::false_type {}; + +template +struct can_sub_assign() -= std::declval())>> : std::true_type {}; + +template struct can_mul_assign : std::false_type {}; + +template +struct can_mul_assign() *= std::declval())>> : std::true_type {}; + +template struct can_div_assign : std::false_type {}; + +template +struct can_div_assign() /= std::declval())>> : std::true_type {}; + +template struct can_and_assign : std::false_type {}; + +template +struct can_and_assign() &= std::declval())>> : std::true_type {}; + +template struct can_or_assign : std::false_type {}; + +template +struct can_or_assign() |= std::declval())>> : std::true_type {}; + +using VecAndStdTypes = TypeListCat_t; + +template void detectUnaryUnexpectedCompilationErrors() { + if constexpr (can_unary_minus::value != can_unary_minus>::value) { + unexpected_failed_compilations.push_back("unaryMinus_" + typeToString()); + } + if constexpr (can_unary_not::value != can_unary_not>::value) { + unexpected_failed_compilations.push_back("unaryNot_" + typeToString()); + } + if constexpr (can_unary_bitwise_not::value != can_unary_bitwise_not>::value) { + unexpected_failed_compilations.push_back("unaryBitwiseNot_" + typeToString()); + } +} + +template void detectBinaryUnexpectedCompilationErrors() { + if constexpr (cn == cn) { + if constexpr (can_add::value != can_add, VBase>::value) { + unexpected_failed_compilations.push_back("binaryAdd_" + typeToString() + "_" + typeToString()); + } + if constexpr (can_subtract::value != can_subtract, VBase>::value) { + unexpected_failed_compilations.push_back("binaryMinus_" + typeToString() + "_" + typeToString()); + } + if constexpr (can_multiply::value != can_multiply, VBase>::value) { + unexpected_failed_compilations.push_back("binaryMul_" + typeToString() + "_" + typeToString()); + } + if constexpr (can_divide::value != can_divide, VBase>::value) { + unexpected_failed_compilations.push_back("binaryDiv_" + typeToString() + "_" + typeToString()); + } + if constexpr (can_equal::value != can_equal, VBase>::value) { + unexpected_failed_compilations.push_back("binaryEqual_" + typeToString() + "_" + typeToString()); + } + if constexpr (can_not_equal::value != can_not_equal, VBase>::value) { + unexpected_failed_compilations.push_back("binaryNotEqual_" + typeToString() + "_" + typeToString()); + } + if constexpr (can_less::value != can_less, VBase>::value) { + unexpected_failed_compilations.push_back("binaryLess_" + typeToString() + "_" + typeToString()); + } + if constexpr (can_less_equal::value != can_less_equal, VBase>::value) { + unexpected_failed_compilations.push_back("binaryLessEqual_" + typeToString() + "_" + + typeToString()); + } + if constexpr (can_greater::value != can_greater, VBase>::value) { + unexpected_failed_compilations.push_back("binaryGreater_" + typeToString() + "_" + typeToString()); + } + if constexpr (can_greater_equal::value != can_greater_equal, VBase>::value) { + unexpected_failed_compilations.push_back("binaryGreaterEqual_" + typeToString() + "_" + + typeToString()); + } + if constexpr (can_logical_and::value != can_logical_and, VBase>::value) { + unexpected_failed_compilations.push_back("logicalAnd_" + typeToString() + "_" + typeToString()); + } + if constexpr (can_logical_or::value != can_logical_or, VBase>::value) { + unexpected_failed_compilations.push_back("logicalOr_" + typeToString() + "_" + typeToString()); + } + if constexpr (can_bitwise_and::value != can_bitwise_and, VBase>::value) { + unexpected_failed_compilations.push_back("bitwiseAnd_" + typeToString() + "_" + typeToString()); + } + if constexpr (can_bitwise_or::value != can_bitwise_or, VBase>::value) { + unexpected_failed_compilations.push_back("bitwiseOr_" + typeToString() + "_" + typeToString()); + } + if constexpr (can_bitwise_xor::value != can_bitwise_xor, VBase>::value) { + unexpected_failed_compilations.push_back("bitwiseXor_" + typeToString() + "_" + typeToString()); + } + } +} + +template void detectCompoundUnexpectedCompilationErrors() { + if constexpr (fk::AreVVEqCN::value || fk::AreVS::value) { + // The case scalar += vector is not supported (same for the other compound operators) + if constexpr (can_add_assign::value != can_add_assign, VBase>::value) { + unexpected_failed_compilations.push_back("addAssign_" + typeToString() + "_" + typeToString()); + } + if constexpr (can_sub_assign::value != can_sub_assign, VBase>::value) { + unexpected_failed_compilations.push_back("subAssign_" + typeToString() + "_" + typeToString()); + } + if constexpr (can_mul_assign::value != can_mul_assign, VBase>::value) { + unexpected_failed_compilations.push_back("mulAssign_" + typeToString() + "_" + typeToString()); + } + if constexpr (can_div_assign::value != can_div_assign, VBase>::value) { + unexpected_failed_compilations.push_back("divAssign_" + typeToString() + "_" + typeToString()); + } + if constexpr (can_and_assign::value != can_and_assign, VBase>::value) { + unexpected_failed_compilations.push_back("andAssign_" + typeToString() + "_" + typeToString()); + } + if constexpr (can_or_assign::value != can_or_assign, VBase>::value) { + unexpected_failed_compilations.push_back("orAssign_" + typeToString() + "_" + typeToString()); + } + } +} + +template constexpr inline bool testUnaryMinus() { + if constexpr (can_unary_minus::value) { + constexpr VBase base_val{static_cast>(5)}; + constexpr T val = make_set(base_val); + constexpr auto result = -val; // Test unary minus + constexpr auto expectedBase = -base_val; + using ExpectedBaseType = std::decay_t; + using ExpectedType = typename VectorType>::type_v; + constexpr ExpectedType expectedResult = make_set(expectedBase); + return expectedResult == result; + } else { + return true; + } +} + +template constexpr inline bool testUnaryNot() { + if constexpr (can_unary_not::value) { + constexpr VBase base_val{static_cast>(5)}; + constexpr T val = make_set(base_val); + constexpr auto result = !val; // Test unary not + constexpr auto expectedBase = !base_val; + using ExpectedBaseType = std::decay_t; + using ExpectedType = typename VectorType>::type_v; + constexpr ExpectedType expectedResult = make_set(expectedBase); + return expectedResult == result; + } else { + return true; + } +} + +template constexpr inline bool testUnaryBitwiseNot() { + if constexpr (can_unary_bitwise_not::value) { + constexpr VBase base_val{static_cast>(5)}; + constexpr T val = make_set(base_val); + constexpr auto result = ~val; // Test unary bitwise not + constexpr auto expectedBase = ~base_val; + using ExpectedBaseType = std::decay_t; + using ExpectedType = typename VectorType>::type_v; + constexpr ExpectedType expectedResult = make_set(expectedBase); + return expectedResult == result; + } else { + return true; + } +} +template bool testUnaryOperators() { + bool correct{true}; + if (!testUnaryMinus()) { + std::cout << "Failed unaryMinus test for type: " << typeToString() << std::endl; + correct = false; + } + if (!testUnaryNot()) { + std::cout << "Failed unaryNot test for type: " << typeToString() << std::endl; + correct = false; + } + if (!testUnaryBitwiseNot()) { + std::cout << "Failed unaryBitwiseNot test for type: " << typeToString() << std::endl; + correct = false; + } + return correct; +} + +#define BINARY_OP_TEST(OP_NAME, OP) \ + template constexpr inline bool binary##OP_NAME() { \ + if constexpr (can_##OP_NAME::value) { \ + constexpr VBase base_val1{static_cast>(5)}; \ + constexpr VBase base_val2{static_cast>(3)}; \ + constexpr I1 val1 = make_set(base_val1); \ + constexpr I2 val2 = make_set(base_val2); \ + constexpr auto result = val1 OP val2; \ + constexpr auto expectedBase = base_val1 OP base_val2; \ + using ExpectedBaseType = std::decay_t; \ + using ExpectedType = typename VectorType > cn ? cn : cn)>::type_v; \ + constexpr ExpectedType expectedResult = make_set(expectedBase); \ + return expectedResult == result; \ + } else { \ + return true; \ + } \ + } + +BINARY_OP_TEST(add, +) +BINARY_OP_TEST(subtract, -) +BINARY_OP_TEST(multiply, *) +BINARY_OP_TEST(divide, /) +BINARY_OP_TEST(equal, ==) +BINARY_OP_TEST(not_equal, !=) +BINARY_OP_TEST(less, <) +BINARY_OP_TEST(less_equal, <=) +BINARY_OP_TEST(greater, >) +BINARY_OP_TEST(greater_equal, >=) +BINARY_OP_TEST(logical_and, &&) +BINARY_OP_TEST(logical_or, ||) +BINARY_OP_TEST(bitwise_and, &) +BINARY_OP_TEST(bitwise_or, |) +BINARY_OP_TEST(bitwise_xor, ^) + +#undef BINARY_OP_TEST + +template bool testBinaryOperators() { + constexpr std::array binaryOperatorTestNames{ + "binaryAdd", "binaryMinus", "binaryMul", "binaryDiv", "binaryEqual", + "binaryNotEqual", "binaryLess", "binaryLessEqual", "binaryGreater", "binaryGreaterEqual", + "binaryLogicalAnd", "binaryLogicalOr", "binaryBitwiseAnd", "binaryBitwiseOr", "binaryBitwiseXor"}; + constexpr std::array results = { + binaryadd(), binarysubtract(), binarymultiply(), + binarydivide(), binaryequal(), binarynot_equal(), + binaryless(), binaryless_equal(), binarygreater(), + binarygreater_equal(), binarylogical_and(), binarylogical_or(), + binarybitwise_and(), binarybitwise_or(), binarybitwise_xor()}; + bool correct{true}; + for (int i = 0; i < 15; ++i) { + if (!results[i]) { + std::cout << "Failed " << binaryOperatorTestNames[i] << " test for types: " << typeToString() << " and " + << typeToString() << std::endl; + correct = false; + } + } + return correct; +} + +#define COMPOUND_OP_TEST(OP_NAME, OP) \ + template constexpr inline bool compound##OP_NAME() { \ + if constexpr (can_##OP_NAME::value) { \ + VBase base_val1{static_cast>(5)}; \ + constexpr VBase base_val2{static_cast>(3)}; \ + I1 val1 = make_set(base_val1); \ + constexpr I2 val2 = make_set(base_val2); \ + val1 OP val2; \ + base_val1 OP base_val2; \ + using ExpectedBaseType = VBase; \ + using ExpectedType = I1; \ + ExpectedType expectedResult = make_set(base_val1); \ + return expectedResult == val1; \ + } else { \ + return true; \ + } \ + } + +COMPOUND_OP_TEST(add_assign, +=) +COMPOUND_OP_TEST(sub_assign, -=) +COMPOUND_OP_TEST(mul_assign, *=) +COMPOUND_OP_TEST(div_assign, /=) +COMPOUND_OP_TEST(and_assign, &=) +COMPOUND_OP_TEST(or_assign, |=) + +#undef COMPOUND_OP_TEST + +// Test compound operators +template bool testCompoundOperators() { + constexpr std::array compoundOperatorTestNames{"compoundAddAssign", "compoundSubAssign", + "compoundMulAssign", "compoundDivAssign", + "compoundAndAssign", "compoundOrAssign"}; + const std::array results{compoundadd_assign(), compoundsub_assign(), + compoundmul_assign(), compounddiv_assign(), + compoundand_assign(), compoundor_assign()}; + bool correct{true}; + for (int i = 0; i < 6; ++i) { + if (!results[i]) { + std::cout << "Failed " << compoundOperatorTestNames[i] << " test for types: " << typeToString() + << " and " << typeToString() << std::endl; + correct = false; + } + } + return correct; +} + +template struct UnaryTest; + +template struct UnaryTest> { + static bool execute() { + (detectUnaryUnexpectedCompilationErrors(), ...); + return (testUnaryOperators() && ...); + } +}; + +template struct BinaryTests; + +template +struct BinaryTests, TypeList> { + static bool execute() { + if constexpr (sizeof...(Types1) == 0) { + (detectBinaryUnexpectedCompilationErrors(), ...); + return (testBinaryOperators() && ...); + } else { + (detectBinaryUnexpectedCompilationErrors(), ...); + const bool result = (testBinaryOperators() && ...); + return result && BinaryTests, TypeList>::execute(); + } + } +}; + +template struct CompoundTests; + +template +struct CompoundTests, TypeList> { + static bool execute() { + if constexpr (sizeof...(Types1) == 0) { + (detectCompoundUnexpectedCompilationErrors(), ...); + return (testCompoundOperators() && ...); + } else { + (detectCompoundUnexpectedCompilationErrors(), ...); + const bool result = (testCompoundOperators() && ...); + return result && CompoundTests, TypeList>::execute(); + } + } +}; + +#endif \ No newline at end of file diff --git a/utests/core/utils/utest_cuda_vector_utils/utest_cuda_vector_utils_ftype.cpp.in b/utests/core/utils/utest_cuda_vector_utils/utest_cuda_vector_utils_ftype.cpp.in new file mode 100644 index 00000000..00470be6 --- /dev/null +++ b/utests/core/utils/utest_cuda_vector_utils/utest_cuda_vector_utils_ftype.cpp.in @@ -0,0 +1,37 @@ +/* Copyright 2025 Oscar Amoros Huguet + Copyright 2025 Albert Andaluz + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#define __ONLY_CPU__ +#include "utest_cuda_vector_utils_@FUNDAMENTAL_TYPE@_@EXTENSION@.h" +#include "utest_cuda_vector_utils_common.h" + +int fk::utest_cuda_vector_utils_@FUNDAMENTAL_TYPE@_@EXTENSION@::launch() { + + using VecAndStdTypes1 = fk::TypeList<@FUNDAMENTAL_TYPE@>; + + bool passed = UnaryTest::execute(); + passed &= BinaryTests::execute(); + passed &= CompoundTests::execute(); + + if (!unexpected_failed_compilations.empty()) { + std::cout << "ERROR: Unexpected compilation failures that did occur:\n"; + for (const auto &failure : unexpected_failed_compilations) { + std::cout << "- " << failure << std::endl; + } + return -1; + } + + return passed ? 0 : -1; +} diff --git a/utests/core/utils/utest_cuda_vector_utils/utest_cuda_vector_utils_ftype.h.in b/utests/core/utils/utest_cuda_vector_utils/utest_cuda_vector_utils_ftype.h.in new file mode 100644 index 00000000..48f63975 --- /dev/null +++ b/utests/core/utils/utest_cuda_vector_utils/utest_cuda_vector_utils_ftype.h.in @@ -0,0 +1,28 @@ +/* Copyright 2025 Oscar Amoros Huguet + Copyright 2025 Albert Andaluz + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#define __ONLY_CPU__ + +#ifndef FK_UTEST_CUDA_VECTOR_UTILS_@FUNDAMENTAL_TYPE_UPPER@_H +#define FK_UTEST_CUDA_VECTOR_UTILS_@FUNDAMENTAL_TYPE_UPPER@_H + +#include "utest_cuda_vector_utils_@FUNDAMENTAL_TYPE@_@EXTENSION@_export.h" + +namespace fk::utest_cuda_vector_utils_@FUNDAMENTAL_TYPE@_@EXTENSION@ { + + int UTEST_CUDA_VECTOR_UTILS_@FUNDAMENTAL_TYPE_UPPER@_@EXTENSION_UPPER@_EXPORT launch(); + +#endif // FK_UTEST_CUDA_VECTOR_UTILS_@FUNDAMENTAL_TYPE_UPPER@_H +} // namespace fk::test