diff --git a/.clang-tidy b/.clang-tidy index e925d82..7fb79fd 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,3 +1,12 @@ -Checks: '-*,bugprone-*,cert-*,clang-analyzer-*,cppcoreguidelines-*,hicpp-*,misc-*,modernize-*,performance-*,portability-*,readability-*' -AnalyzeTemporaryDtors: false -FormatStyle: "file" +{ + "Checks": "-*,bugprone-*,cert-*,clang-analyzer-*,cppcoreguidelines-*,hicpp-*,misc-*,modernize-*,performance-*,portability-*,readability-*,-modernize-use-nullptr,-hicpp-use-nullptr", + "HeaderFilterRegex": ".*", + "ExcludeHeaderFilterRegex": "boost/.*", + "WarningsAsErrors": "*", + "FormatStyle": "none", + "SystemHeaders": false, + "ExtraArgs": [ + "-Iinclude", + "-I3rdparty/include" + ] +} diff --git a/.github/workflows/cmake-linux-pack-nost.yml b/.github/workflows/cmake-linux-pack-nost.yml index d9ad7b5..0aa17ca 100644 --- a/.github/workflows/cmake-linux-pack-nost.yml +++ b/.github/workflows/cmake-linux-pack-nost.yml @@ -59,6 +59,7 @@ jobs: - name: Pack working-directory: ${{ steps.strings.outputs.build-output-dir }} run: | + cpack -G TXZ cpack -G DEB cpack -G RPM diff --git a/.github/workflows/cmake-linux-pack-st.yml b/.github/workflows/cmake-linux-pack-st.yml index d0998ea..3715090 100644 --- a/.github/workflows/cmake-linux-pack-st.yml +++ b/.github/workflows/cmake-linux-pack-st.yml @@ -60,6 +60,7 @@ jobs: - name: Pack working-directory: ${{ steps.strings.outputs.build-output-dir }} run: | + cpack -G TXZ cpack -G DEB cpack -G RPM diff --git a/.github/workflows/cmake-multi-platform-st.yml b/.github/workflows/cmake-multi-platform-st.yml index 489bcb2..ae70bf8 100644 --- a/.github/workflows/cmake-multi-platform-st.yml +++ b/.github/workflows/cmake-multi-platform-st.yml @@ -74,6 +74,7 @@ jobs: run: | cpack --config CPackConfig.cmake -C ${{ matrix.build_type }} cpack --config CPackConfig.cmake -C ${{ matrix.build_type }} -G ZIP + cpack --config CPackConfig.cmake -C ${{ matrix.build_type }} -G 7Z - name: 'Artifact Storage' uses: actions/upload-artifact@v4 diff --git a/.github/workflows/cmake-static-analysis-rls.yml b/.github/workflows/cmake-static-analysis-rls.yml deleted file mode 100644 index 163a824..0000000 --- a/.github/workflows/cmake-static-analysis-rls.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: TestCPP Static analysis - Release - -on: - # Will run on push when merging to 'branches'. The output will be shown in the console - push: - branches: - - main - - # 'pull_request_target' allows this Action to also run on forked repositories - # The output will be shown in PR comments (unless the 'force_console_print' flag is used) - pull_request_target: - branches: - - "*" - -jobs: - static_analysis: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Run static analysis - uses: JacobDomagala/StaticAnalysis@master - with: - language: c++ - - # Exclude any issues found in ${Project_root_dir}/lib - exclude_dir: lib - - use_cmake: true - cmake_args: -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -Wno-dev - - cppcheck_args: --enable=all --suppress=missingIncludeSystem --std=c++11 diff --git a/.github/workflows/cmake-static-analysis-dbg.yml b/.github/workflows/cmake-static-analysis.yml similarity index 55% rename from .github/workflows/cmake-static-analysis-dbg.yml rename to .github/workflows/cmake-static-analysis.yml index a5748e6..408b296 100644 --- a/.github/workflows/cmake-static-analysis-dbg.yml +++ b/.github/workflows/cmake-static-analysis.yml @@ -1,4 +1,4 @@ -name: TestCPP Static analysis - Debug +name: TestCPP Static analysis - cppcheck/clang-tidy on: # Will run on push when merging to 'branches'. The output will be shown in the console @@ -18,16 +18,15 @@ jobs: steps: - uses: actions/checkout@v4 - - - name: Run static analysis - uses: JacobDomagala/StaticAnalysis@master + - name: Static analysis for C++(Clang-19)/Python project + uses: eljonny/StaticAnalysis@clang-19 with: language: c++ - # Exclude any issues found in ${Project_root_dir}/lib - exclude_dir: lib + # Exclude any issues found in ${Project_root_dir}/3rdparty + exclude_dir: 3rdparty use_cmake: true cmake_args: -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -Wno-dev - cppcheck_args: --enable=all --suppress=missingIncludeSystem --std=c++11 + cppcheck_args: --quiet --enable=all --inconclusive --suppress=missingIncludeSystem --suppress=unusedFunction --suppress=checkersReport --suppress='*:3rdparty/*' --std=c++11 --inline-suppr --force --check-level=exhaustive diff --git a/.github/workflows/cmake-windows-pack-nost.yml b/.github/workflows/cmake-windows-pack-nost.yml new file mode 100644 index 0000000..b150bca --- /dev/null +++ b/.github/workflows/cmake-windows-pack-nost.yml @@ -0,0 +1,66 @@ +name: TestCPP CMake WIX Pack - No Stacktraces + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + # 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 + + # Set up a matrix to run the following 3 configurations: + # 1. + # 2. + # 3. + # + # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. + matrix: + os: [windows-latest] + build_type: [Release, Debug] + c_compiler: [cl] + include: + - os: windows-latest + c_compiler: cl + cpp_compiler: cl + + steps: + - uses: actions/checkout@v4 + + - 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. + id: strings + shell: bash + run: | + echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: > + cmake -B ${{ steps.strings.outputs.build-output-dir }} + -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} + -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} + -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: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} + + - name: Pack + working-directory: ${{ steps.strings.outputs.build-output-dir }} + run: | + cpack -G WIX -C ${{ matrix.build_type }} + + - name: 'Artifact Storage' + uses: actions/upload-artifact@v4 + with: + name: linux-packages-${{ matrix.os }}-${{ matrix.c_compiler }}-${{ matrix.build_type }} + path: ${{ github.workspace }}/_packages/** + overwrite: true diff --git a/.github/workflows/cmake-windows-pack-st.yml b/.github/workflows/cmake-windows-pack-st.yml new file mode 100644 index 0000000..6148184 --- /dev/null +++ b/.github/workflows/cmake-windows-pack-st.yml @@ -0,0 +1,67 @@ +name: TestCPP CMake WIX Pack + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + # 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 + + # Set up a matrix to run the following 3 configurations: + # 1. + # 2. + # 3. + # + # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. + matrix: + os: [windows-latest] + build_type: [Release, Debug] + c_compiler: [cl] + include: + - os: windows-latest + c_compiler: cl + cpp_compiler: cl + + steps: + - uses: actions/checkout@v4 + + - 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. + id: strings + shell: bash + run: | + echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: > + cmake -B ${{ steps.strings.outputs.build-output-dir }} + -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} + -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -DTESTCPP_STACKTRACE_ENABLED=1 + -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: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} + + - name: Pack + working-directory: ${{ steps.strings.outputs.build-output-dir }} + run: | + cpack -G WIX -C ${{ matrix.build_type }} + + - name: 'Artifact Storage' + uses: actions/upload-artifact@v4 + with: + name: linux-packages-${{ matrix.os }}-${{ matrix.c_compiler }}-${{ matrix.build_type }} + path: ${{ github.workspace }}/_packages/** + overwrite: true diff --git a/.gitignore b/.gitignore index a6880ae..048d48f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ build-debug build-release _packages test_package/build +CMakeUserPresets.json +/.vs +out/ +**/cppcheck-checkers.report diff --git a/CMakeLists.txt b/CMakeLists.txt index 04f62a9..ce2e566 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required (VERSION 3.16) set (PROJECT_NAME "TestCPP") set (PROJECT_GROUP_NAME "cpptesting") -project (${PROJECT_NAME} VERSION 0.2.1 LANGUAGES CXX) +project (${PROJECT_NAME} VERSION 1.0.0 LANGUAGES CXX) set (CMAKE_CXX_STANDARD 11) set (CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..4311e28 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,1216 @@ +{ + "version": 6, + "cmakeMinimumRequired": { + "major": 3, + "minor": 25, + "patch": 0 + }, + "configurePresets": [ + { + "name": "base", + "description": "Base configuration that defines Cache Variables and Dev Warnings, using the Debug build options.", + "hidden": true, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_EXPORT_COMPILE_COMMANDS": "0", + "TESTCPP_STACKTRACE_ENABLED": "0", + "TESTCPP_ANALYSIS_ENABLED": "0", + "TESTCPP_TEST_ENABLED": "0", + "TESTCPP_DEMO_ENABLED": "0", + "TESTCPP_COVERAGE_ENABLED": "0" + }, + "warnings": { + "dev": false, + "deprecated": false, + "uninitialized": true, + "unusedCli": false, + "systemVars": false + } + }, + { + "name": "export-cc-nost-base", + "description": "Base configuration ensures Compile Commands are exported, using the Debug build options, with no stacktraces (strict C++11).", + "inherits": "base", + "hidden": true, + "cacheVariables": { + "CMAKE_EXPORT_COMPILE_COMMANDS": "1" + } + }, + { + "name": "export-cc-st-base", + "description": "Base configuration ensures Compile Commands are exported and Stack Traces are enabled, using the Debug build options.", + "inherits": "export-cc-nost-base", + "hidden": true, + "cacheVariables": { + "TESTCPP_STACKTRACE_ENABLED": "1" + } + }, + { + "name": "windows-msvc", + "inherits": "export-cc-st-base", + "hidden": true, + "description": "Configure the build for MSVC CL on Windows, with stacktraces enabled.", + "generator": "Ninja", + "toolchainFile": "${sourceDir}/cmake/toolchains/Windows.MSVC.toolchain.cmake", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "windows-msvc-nost", + "inherits": "export-cc-nost-base", + "hidden": true, + "description": "Configure the build for MSVC CL on Windows, with no stacktraces.", + "generator": "Ninja", + "toolchainFile": "${sourceDir}/cmake/toolchains/Windows.MSVC.toolchain.cmake", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "windows-clangcl", + "inherits": "export-cc-nost-base", + "hidden": true, + "description": "Configure the build for ClangCL on Windows, only used for running clang-tidy on Windows.", + "generator": "Ninja", + "toolchainFile": "${sourceDir}/cmake/toolchains/Windows.Clang.toolchain.cmake", + "cacheVariables": { + "CMAKE_C_COMPILER_FRONTEND_VARIANT": "MSVC", + "CMAKE_CXX_COMPILER_FRONTEND_VARIANT": "MSVC" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "base-dev-checks", + "displayName": "CMake - Dev Checks", + "description": "Base config preset with dev checks enabled, using the Debug build options.", + "inherits": "base", + "warnings": { + "dev": true, + "deprecated": true, + "uninitialized": true, + "unusedCli": true, + "systemVars": true + } + }, + { + "name": "win32-base", + "displayName": "Generate - x86 - Debug - Windows - Core Lib", + "description": "Configure the base Windows Minimal build with the x86 toolchain architecture, using the Debug build options.", + "inherits": "windows-msvc", + "binaryDir": "${sourceDir}/out/build/Debug/win32-base", + "installDir": "${sourceDir}/out/install/Debug/win32-base", + "cacheVariables": { + "CMAKE_SYSTEM_PROCESSOR": "X86" + } + }, + { + "name": "win32-base-rel", + "displayName": "Generate - x86 - Release - Windows - Core Lib", + "description": "Configure the base Windows Minimal build with the x86 toolchain architecture, using the Release build options.", + "inherits": "win32-base", + "binaryDir": "${sourceDir}/out/build/Release/win32-base", + "installDir": "${sourceDir}/out/install/Release/win32-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "win32-nost-base", + "displayName": "Generate - x86 - Debug - Windows - Core Lib - No Stacktraces", + "description": "Configure the base Windows Minimal build with the x86 toolchain architecture, using the Debug build options, but with no stacktraces (strict C++11).", + "inherits": "windows-msvc-nost", + "binaryDir": "${sourceDir}/out/build/Debug/win32-nost-base", + "installDir": "${sourceDir}/out/install/Debug/win32-nost-base", + "cacheVariables": { + "CMAKE_SYSTEM_PROCESSOR": "X86" + } + }, + { + "name": "win32-nost-base-rel", + "displayName": "Generate - x86 - Release - Windows - Core Lib - No Stacktraces", + "description": "Configure the base Windows Minimal build with the x86 toolchain architecture, using the Release build options, but with no stacktraces (strict C++11).", + "inherits": "win32-nost-base", + "binaryDir": "${sourceDir}/out/build/Release/win32-nost-base", + "installDir": "${sourceDir}/out/install/Release/win32-nost-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "win32-msanalysis", + "displayName": "Generate - x86 - Windows - MSVC Analysis", + "description": "Configure the base Windows Minimal build to prepare for running the MSVC++ analyzer.", + "inherits": "win32-nost-base", + "binaryDir": "${sourceDir}/out/build/win32-msanalysis", + "installDir": "${sourceDir}/out/install/win32-msanalysis", + "cacheVariables": { + "CMAKE_C_FLAGS": "/analyze /analyze:external- /analyze:autolog /analyze:ruleset NativeRecommendedRules.ruleset", + "CMAKE_CXX_FLAGS": "/analyze /analyze:external- /analyze:autolog /analyze:ruleset NativeRecommendedRules.ruleset" + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { + "intelliSenseMode": "windows-msvc-x86", + "enableMicrosoftCodeAnalysis": true, + "disableExternalAnalysis": true, + "intelliSenseOptions": { + "additionalCompilerArgs": [ + "/analyze", + "/analyze:external-", + "/analyze:autolog", + "/analyze:ruleset NativeRecommendedRules.ruleset" + ] + } + } + } + }, + { + "name": "win32-msanalysis-sarif", + "displayName": "Generate - x86 - Windows - MSVC Analysis - SARIF Output", + "description": "Configure the base Windows Minimal build to prepare for running the MSVC++ analyzer, and output the results to a SARIF format.", + "inherits": "win32-msanalysis", + "binaryDir": "${sourceDir}/out/build/win32-msanalysis-sarif", + "installDir": "${sourceDir}/out/install/win32-msanalysis-sarif", + "cacheVariables": { + "CMAKE_C_FLAGS": "/analyze /analyze:external- /analyze:autolog /analyze:ruleset NativeRecommendedRules.ruleset /analyze:log:format:sarif", + "CMAKE_CXX_FLAGS": "/analyze /analyze:external- /analyze:autolog /analyze:ruleset NativeRecommendedRules.ruleset /analyze:log:format:sarif" + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { + "intelliSenseOptions": { + "additionalCompilerArgs": [ + "/analyze:log:format:sarif" + ] + } + } + } + }, + { + "name": "win32-msanalysis-xml", + "displayName": "Generate - x86 - Windows - MSVC Analysis - XML Output", + "description": "Configure the base Windows Minimal build to prepare for running the MSVC++ analyzer, and output the results to a XML format.", + "inherits": "win32-msanalysis", + "binaryDir": "${sourceDir}/out/build/win32-msanalysis-xml", + "installDir": "${sourceDir}/out/install/win32-msanalysis-xml", + "cacheVariables": { + "CMAKE_C_FLAGS": "/analyze /analyze:external- /analyze:autolog /analyze:ruleset NativeRecommendedRules.ruleset /analyze:log:format:xml", + "CMAKE_CXX_FLAGS": "/analyze /analyze:external- /analyze:autolog /analyze:ruleset NativeRecommendedRules.ruleset /analyze:log:format:xml" + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { + "intelliSenseOptions": { + "additionalCompilerArgs": [ + "/analyze:log:format:xml" + ] + } + } + } + }, + { + "name": "win32-with-test", + "displayName": "Generate - x86 - Debug - Windows - Core Lib With Tests", + "description": "Configure the Windows build with Core and Test Library build set and the x86 toolchain architecture, using the Debug build options.", + "inherits": "win32-base", + "binaryDir": "${sourceDir}/out/build/Debug/win32-withtest", + "installDir": "${sourceDir}/out/install/Debug/win32-withtest", + "cacheVariables": { + "TESTCPP_TEST_ENABLED": "1" + } + }, + { + "name": "win32-with-test-rel", + "displayName": "Generate - x86 - Release - Windows - Core Lib With Tests", + "description": "Configure the Windows build with Core and Test Library build set and the x86 toolchain architecture, using the Release build options.", + "inherits": "win32-base-rel", + "binaryDir": "${sourceDir}/out/build/Release/win32-withtest", + "installDir": "${sourceDir}/out/install/Release/win32-withtest", + "cacheVariables": { + "TESTCPP_TEST_ENABLED": "1" + } + }, + { + "name": "win32-nost-with-test", + "displayName": "Generate - x86 - Debug - Windows - Core Lib With Tests - No Stacktraces", + "description": "Configure the Windows build with Core and Test Library build set and the x86 toolchain architecture, using the Debug build options, but with no stacktraces (strict C++11).", + "inherits": "win32-nost-base", + "binaryDir": "${sourceDir}/out/build/Debug/win32-nost-withtest", + "installDir": "${sourceDir}/out/install/Debug/win32-nost-withtest", + "cacheVariables": { + "TESTCPP_TEST_ENABLED": "1" + } + }, + { + "name": "win32-nost-with-test-rel", + "displayName": "Generate - x86 - Release - Windows - Core Lib With Tests - No Stacktraces", + "description": "Configure the Windows build with Core and Test Library build set and the x86 toolchain architecture, using the Release build options, but with no stacktraces (strict C++11).", + "inherits": "win32-nost-base-rel", + "binaryDir": "${sourceDir}/out/build/Release/win32-nost-withtest", + "installDir": "${sourceDir}/out/install/Release/win32-nost-withtest", + "cacheVariables": { + "TESTCPP_TEST_ENABLED": "1" + } + }, + { + "name": "win32-with-demo", + "displayName": "Generate - x86 - Debug - Windows - Core Lib With Demo", + "description": "Configure the Windows build with Core and Demo set and the x86 toolchain architecture, using the Debug build options.", + "inherits": "win32-base", + "binaryDir": "${sourceDir}/out/build/Debug/win32-withdemo", + "installDir": "${sourceDir}/out/install/Debug/win32-withdemo", + "cacheVariables": { + "TESTCPP_DEMO_ENABLED": "1" + } + }, + { + "name": "win32-with-demo-rel", + "displayName": "Generate - x86 - Release - Windows - Core Lib With Demo", + "description": "Configure the Windows build with Core and Demo set and the x86 toolchain architecture, using the Release build options.", + "inherits": "win32-base-rel", + "binaryDir": "${sourceDir}/out/build/Release/win32-withdemo", + "installDir": "${sourceDir}/out/install/Release/win32-withdemo", + "cacheVariables": { + "TESTCPP_DEMO_ENABLED": "1" + } + }, + { + "name": "win32-nost-with-demo", + "displayName": "Generate - x86 - Debug - Windows - Core Lib With Demo - No Stacktraces", + "description": "Configure the Windows build with Core and Demo library build set and the x86 toolchain architecture, using the Debug build options, but with no stacktraces (strict C++11).", + "inherits": "win32-nost-base", + "binaryDir": "${sourceDir}/out/build/Debug/win32-nost-withdemo", + "installDir": "${sourceDir}/out/install/Debug/win32-nost-withdemo", + "cacheVariables": { + "TESTCPP_DEMO_ENABLED": "1" + } + }, + { + "name": "win32-nost-with-demo-rel", + "displayName": "Generate - x86 - Release - Windows - Core Lib With Demo - No Stacktraces", + "description": "Configure the Windows build with Core and Demo library build set and the x86 toolchain architecture, using the Release build options, but with no stacktraces (strict C++11).", + "inherits": "win32-nost-base-rel", + "binaryDir": "${sourceDir}/out/build/Release/win32-nost-withdemo", + "installDir": "${sourceDir}/out/install/Release/win32-nost-withdemo", + "cacheVariables": { + "TESTCPP_DEMO_ENABLED": "1" + } + }, + { + "name": "win32-all", + "displayName": "Generate - x86 - Debug - Windows - All Components - Core, Tests, and Demo", + "description": "Configure the Windows build with all Library options set and the x86 toolchain architecture, using the Debug build options.", + "inherits": "win32-with-test", + "binaryDir": "${sourceDir}/out/build/Debug/win32-all", + "installDir": "${sourceDir}/out/install/Debug/win32-all", + "cacheVariables": { + "TESTCPP_DEMO_ENABLED": "1" + } + }, + { + "name": "win32-all-rel", + "displayName": "Generate - x86 - Release - Windows - All Components - Core, Tests, and Demo", + "description": "Configure the Windows build with all Library options set and the x86 toolchain architecture, using the Release build options.", + "inherits": "win32-with-test-rel", + "binaryDir": "${sourceDir}/out/build/Release/win32-all", + "installDir": "${sourceDir}/out/install/Release/win32-all", + "cacheVariables": { + "TESTCPP_DEMO_ENABLED": "1" + } + }, + { + "name": "win32-nost-all", + "displayName": "Generate - x86 - Debug - Windows - All Components - Core, Tests, and Demo - No Stacktraces", + "description": "Configure the Windows build with all Library options set and the x86 toolchain architecture, using the Debug build options, but with no stacktraces (strict C++11).", + "inherits": "win32-nost-with-test", + "binaryDir": "${sourceDir}/out/build/Debug/win32-nost-all", + "installDir": "${sourceDir}/out/install/Debug/win32-nost-all", + "cacheVariables": { + "TESTCPP_DEMO_ENABLED": "1" + } + }, + { + "name": "win32-nost-all-rel", + "displayName": "Generate - x86 - Release - Windows - All Components - Core, Tests, and Demo - No Stacktraces", + "description": "Configure the Windows build with all Library options set and the x86 toolchain architecture, using the Release build options, but with no stacktraces (strict C++11).", + "inherits": "win32-nost-with-test-rel", + "binaryDir": "${sourceDir}/out/build/Release/win32-nost-all", + "installDir": "${sourceDir}/out/install/Release/win32-nost-all", + "cacheVariables": { + "TESTCPP_DEMO_ENABLED": "1" + } + }, + { + "name": "win32-ct", + "displayName": "Generate - x86 - Windows - Clang Tidy analysis", + "description": "Configure the Windows build with clang-tidy analysis and the x86 toolchain architecture.", + "inherits": "windows-clangcl", + "binaryDir": "${sourceDir}/out/build/win32-ct", + "installDir": "${sourceDir}/out/install/win32-ct", + "cacheVariables": { + "CMAKE_C_FLAGS": "-v -m32", + "CMAKE_CXX_FLAGS": "-v -m32", + "CMAKE_EXE_LINKER_FLAGS": "/machine:x86", + "CMAKE_SYSTEM_PROCESSOR": "X86", + "TESTCPP_ANALYSIS_ENABLED": "1" + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { + "intelliSenseMode": "windows-clangcl-x86", + "clangTidyChecks": "-*,bugprone-*,cert-*,clang-analyzer-*,cppcoreguidelines-*,hicpp-*,misc-*,modernize-*,performance-*,portability-*,readability-*,-modernize-use-nullptr,-hicpp-use-nullptr", + "enableClangTidyCodeAnalysis": true + } + } + }, + { + "name": "win64-base", + "displayName": "Generate - x64 - Debug - Windows - Core Lib", + "description": "Configure the base Windows build with the x64 toolchain architecture, using the Debug build options.", + "inherits": "win32-base", + "binaryDir": "${sourceDir}/out/build/Debug/win64-base", + "installDir": "${sourceDir}/out/install/Debug/win64-base", + "cacheVariables": { + "CMAKE_SYSTEM_PROCESSOR": "AMD64" + } + }, + { + "name": "win64-base-rel", + "displayName": "Generate - x64 - Release - Windows - Core Lib", + "description": "Configure the base Windows build with the x64 toolchain architecture, using the Release build options.", + "inherits": "win32-base-rel", + "binaryDir": "${sourceDir}/out/build/Release/win64-base", + "installDir": "${sourceDir}/out/install/Release/win64-base", + "cacheVariables": { + "CMAKE_SYSTEM_PROCESSOR": "AMD64" + } + }, + { + "name": "win64-nost-base", + "displayName": "Generate - x64 - Debug - Windows - Core Lib - No Stacktraces", + "description": "Configure the base Windows build with the x64 toolchain architecture, using the Debug build options, but with no stacktraces (strict C++11).", + "inherits": "win32-nost-base", + "binaryDir": "${sourceDir}/out/build/Debug/win64-nost-base", + "installDir": "${sourceDir}/out/install/Debug/win64-nost-base", + "cacheVariables": { + "CMAKE_SYSTEM_PROCESSOR": "AMD64" + } + }, + { + "name": "win64-nost-base-rel", + "displayName": "Generate - x64 - Release - Windows - Core Lib - No Stacktraces", + "description": "Configure the base Windows build with the x64 toolchain architecture, using the Release build options, but with no stacktraces (strict C++11).", + "inherits": "win32-nost-base-rel", + "binaryDir": "${sourceDir}/out/build/Release/win64-nost-base", + "installDir": "${sourceDir}/out/install/Release/win64-nost-base", + "cacheVariables": { + "CMAKE_SYSTEM_PROCESSOR": "AMD64" + } + }, + { + "name": "win64-msanalysis", + "displayName": "Generate - x64 - Windows - MSVC Analysis", + "description": "Configure the base Windows Minimal build with the x64 toolchain architecture, then run the MSVC++ analyzer.", + "inherits": "win32-msanalysis", + "binaryDir": "${sourceDir}/out/build/win64-msanalysis", + "installDir": "${sourceDir}/out/install/win64-msanalysis", + "cacheVariables": { + "CMAKE_C_FLAGS": "/analyze /analyze:external- /analyze:autolog /analyze:ruleset NativeRecommendedRules.ruleset", + "CMAKE_CXX_FLAGS": "/analyze /analyze:external- /analyze:autolog /analyze:ruleset NativeRecommendedRules.ruleset", + "CMAKE_SYSTEM_PROCESSOR": "AMD64" + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { + "intelliSenseMode": "windows-msvc-x64" + } + } + }, + { + "name": "win64-msanalysis-sarif", + "displayName": "Generate - x64 - Windows - MSVC Analysis - SARIF Output", + "description": "Configure the base Windows Minimal build with the x64 toolchain architecture, then run the MSVC++ analyzer, and output the results to a SARIF format.", + "inherits": "win32-msanalysis-sarif", + "binaryDir": "${sourceDir}/out/build/win64-msanalysis-sarif", + "installDir": "${sourceDir}/out/install/win64-msanalysis-sarif", + "cacheVariables": { + "CMAKE_C_FLAGS": "/analyze /analyze:external- /analyze:autolog /analyze:ruleset NativeRecommendedRules.ruleset /analyze:log:format:sarif", + "CMAKE_CXX_FLAGS": "/analyze /analyze:external- /analyze:autolog /analyze:ruleset NativeRecommendedRules.ruleset /analyze:log:format:sarif", + "CMAKE_SYSTEM_PROCESSOR": "AMD64" + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { + "intelliSenseMode": "windows-msvc-x64" + } + } + }, + { + "name": "win64-msanalysis-xml", + "displayName": "Generate - x64 - Windows - MSVC Analysis - XML Output", + "description": "Configure the base Windows Minimal build with the x64 toolchain architecture, then run the MSVC++ analyzer, and output the results to a XML format.", + "inherits": "win32-msanalysis-xml", + "binaryDir": "${sourceDir}/out/build/win64-msanalysis-xml", + "installDir": "${sourceDir}/out/install/win64-msanalysis-xml", + "cacheVariables": { + "CMAKE_C_FLAGS": "/analyze /analyze:external- /analyze:autolog /analyze:ruleset NativeRecommendedRules.ruleset /analyze:log:format:xml", + "CMAKE_CXX_FLAGS": "/analyze /analyze:external- /analyze:autolog /analyze:ruleset NativeRecommendedRules.ruleset /analyze:log:format:xml", + "CMAKE_SYSTEM_PROCESSOR": "AMD64" + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { + "intelliSenseMode": "windows-msvc-x64" + } + } + }, + { + "name": "win64-with-test", + "displayName": "Generate - x64 - Debug - Windows - Core Lib With Tests", + "description": "Configure the Windows build with Core and Test Library build set and the x64 toolchain architecture, using the Debug build options.", + "inherits": "win64-base", + "binaryDir": "${sourceDir}/out/build/Debug/win64-withtest", + "installDir": "${sourceDir}/out/install/Debug/win64-withtest", + "cacheVariables": { + "TESTCPP_TEST_ENABLED": "1" + } + }, + { + "name": "win64-with-test-rel", + "displayName": "Generate - x64 - Release - Windows - Core Lib With Tests", + "description": "Configure the Windows build with Core and Test Library build set and the x64 toolchain architecture, using the Release build options.", + "inherits": "win64-base-rel", + "binaryDir": "${sourceDir}/out/build/Release/win64-withtest", + "installDir": "${sourceDir}/out/install/Release/win64-withtest", + "cacheVariables": { + "TESTCPP_TEST_ENABLED": "1" + } + }, + { + "name": "win64-nost-with-test", + "displayName": "Generate - x64 - Debug - Windows - Core Lib With Tests - No Stacktraces", + "description": "Configure the Windows build with Core and Test Library build set and the x64 toolchain architecture, using the Debug build options, but with no stacktraces (strict C++11).", + "inherits": "win64-nost-base", + "binaryDir": "${sourceDir}/out/build/Debug/win64-nost-withtest", + "installDir": "${sourceDir}/out/install/Debug/win64-nost-withtest", + "cacheVariables": { + "TESTCPP_TEST_ENABLED": "1" + } + }, + { + "name": "win64-nost-with-test-rel", + "displayName": "Generate - x64 - Release - Windows - Core Lib With Tests - No Stacktraces", + "description": "Configure the Windows build with Core and Test Library build set and the x64 toolchain architecture, using the Release build options, but with no stacktraces (strict C++11).", + "inherits": "win64-nost-base-rel", + "binaryDir": "${sourceDir}/out/build/Release/win64-nost-withtest", + "installDir": "${sourceDir}/out/install/Release/win64-nost-withtest", + "cacheVariables": { + "TESTCPP_TEST_ENABLED": "1" + } + }, + { + "name": "win64-with-demo", + "displayName": "Generate - x64 - Debug - Windows - Core Lib With Demo", + "description": "Configure the Windows build with Core and Demo set and the x64 toolchain architecture, using the Debug build options.", + "inherits": "win64-base", + "binaryDir": "${sourceDir}/out/build/Debug/win64-withdemo", + "installDir": "${sourceDir}/out/install/Debug/win64-withdemo", + "cacheVariables": { + "TESTCPP_DEMO_ENABLED": "1" + } + }, + { + "name": "win64-with-demo-rel", + "displayName": "Generate - x64 - Release - Windows - Core Lib With Demo", + "description": "Configure the Windows build with Core and Demo set and the x64 toolchain architecture, using the Release build options.", + "inherits": "win64-base-rel", + "binaryDir": "${sourceDir}/out/build/Release/win64-withdemo", + "installDir": "${sourceDir}/out/install/Release/win64-withdemo", + "cacheVariables": { + "TESTCPP_DEMO_ENABLED": "1" + } + }, + { + "name": "win64-nost-with-demo", + "displayName": "Generate - x64 - Debug - Windows - Core Lib With Demo - No Stacktraces", + "description": "Configure the Windows build with Core and Demo library build set and the x64 toolchain architecture, using the Debug build options, but with no stacktraces (strict C++11).", + "inherits": "win64-nost-base", + "binaryDir": "${sourceDir}/out/build/Debug/win64-nost-withdemo", + "installDir": "${sourceDir}/out/install/Debug/win64-nost-withdemo", + "cacheVariables": { + "TESTCPP_DEMO_ENABLED": "1" + } + }, + { + "name": "win64-nost-with-demo-rel", + "displayName": "Generate - x64 - Release - Windows - Core Lib With Demo - No Stacktraces", + "description": "Configure the Windows build with Core and Demo library build set and the x64 toolchain architecture, using the Release build options, but with no stacktraces (strict C++11).", + "inherits": "win64-nost-base-rel", + "binaryDir": "${sourceDir}/out/build/Release/win64-nost-withdemo", + "installDir": "${sourceDir}/out/install/Release/win64-nost-withdemo", + "cacheVariables": { + "TESTCPP_DEMO_ENABLED": "1" + } + }, + { + "name": "win64-all", + "displayName": "Generate - x64 - Debug - Windows - All Components - Core, Tests, and Demo", + "description": "Configure the Windows build with all Library options set and the x64 toolchain architecture, using the Debug build options.", + "inherits": "win64-with-test", + "binaryDir": "${sourceDir}/out/build/Debug/win64-all", + "installDir": "${sourceDir}/out/install/Debug/win64-all", + "cacheVariables": { + "TESTCPP_DEMO_ENABLED": "1" + } + }, + { + "name": "win64-all-rel", + "displayName": "Generate - x64 - Release - Windows - All Components - Core, Tests, and Demo", + "description": "Configure the Windows build with all Library options set and the x64 toolchain architecture, using the Release build options.", + "inherits": "win64-with-test-rel", + "binaryDir": "${sourceDir}/out/build/Release/win64-all", + "installDir": "${sourceDir}/out/install/Release/win64-all", + "cacheVariables": { + "TESTCPP_DEMO_ENABLED": "1" + } + }, + { + "name": "win64-nost-all", + "displayName": "Generate - x64 - Debug - Windows - All Components - Core, Tests, and Demo - No Stacktraces", + "description": "Configure the Windows build with all Library options set and the x64 toolchain architecture, using the Debug build options, but with no stacktraces (strict C++11).", + "inherits": "win64-nost-with-test", + "binaryDir": "${sourceDir}/out/build/Debug/win64-nost-all", + "installDir": "${sourceDir}/out/install/Debug/win64-nost-all", + "cacheVariables": { + "TESTCPP_DEMO_ENABLED": "1" + } + }, + { + "name": "win64-nost-all-rel", + "displayName": "Generate - x64 - Release - Windows - All Components - Core, Tests, and Demo - No Stacktraces", + "description": "Configure the Windows build with all Library options set and the x64 toolchain architecture, using the Release build options, but with no stacktraces (strict C++11).", + "inherits": "win64-nost-with-test-rel", + "binaryDir": "${sourceDir}/out/build/Release/win64-nost-all", + "installDir": "${sourceDir}/out/install/Release/win64-nost-all", + "cacheVariables": { + "TESTCPP_DEMO_ENABLED": "1" + } + }, + { + "name": "win64-ct", + "displayName": "Generate - x64 - Windows - Clang Tidy analysis", + "description": "Configure the Windows build with clang-tidy analysis and the x64 toolchain architecture.", + "inherits": "win32-ct", + "binaryDir": "${sourceDir}/out/build/win64-ct", + "installDir": "${sourceDir}/out/install/win64-ct", + "cacheVariables": { + "CMAKE_C_FLAGS": "-v", + "CMAKE_CXX_FLAGS": "-v", + "CMAKE_EXE_LINKER_FLAGS": "/machine:x64", + "CMAKE_SYSTEM_PROCESSOR": "AMD64" + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { + "intelliSenseMode": "windows-clangcl-x64" + } + } + } + ], + "buildPresets": [ + { + "name": "win32-base-build", + "displayName": "Build - x86 - Debug - Windows - Core Lib", + "description": "Build only the Core Lib with MSVC-x86 with the necessary Ninja options in Debug mode.", + "configurePreset": "win32-base", + "resolvePackageReferences": "on", + "nativeToolOptions": [ + "-v" + ], + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "win32-base-clean-build", + "displayName": "Clean Build - x86 - Debug - Windows - Core Lib", + "description": "Build only the Core Lib with MSVC-x86 with the necessary Ninja options in Debug mode, and clean up the previous build first.", + "inherits": "win32-base-build", + "cleanFirst": true + }, + { + "name": "win32-base-build-rel", + "displayName": "Build - x86 - Release - Windows - Core Lib", + "description": "Build only the Core Lib with MSVC-x86 with the necessary Ninja options in Release mode.", + "configurePreset": "win32-base-rel", + "inherits": "win32-base-build" + }, + { + "name": "win32-base-clean-build-rel", + "displayName": "Clean Build - x86 - Release - Windows - Core Lib", + "description": "Build only the Core Lib with MSVC-x86 with the necessary Ninja options in Release mode, and clean up the previous build first.", + "inherits": "win32-base-build-rel", + "cleanFirst": true + }, + { + "name": "win32-nost-base-build", + "displayName": "Build - x86 - Debug - Windows - Core Lib - No Stacktraces", + "description": "Build only the Core Lib with MSVC-x86 with the necessary Ninja options in Debug mode, but with no stacktraces (strict C++11).", + "configurePreset": "win32-nost-base", + "inherits": "win32-base-build" + }, + { + "name": "win32-nost-base-clean-build", + "displayName": "Clean Build - x86 - Debug - Windows - Core Lib - No Stacktraces", + "description": "Build only the Core Lib with MSVC-x86 with the necessary Ninja options in Debug mode, but with no stacktraces (strict C++11), and clean up the previous build first.", + "inherits": "win32-nost-base-build", + "cleanFirst": true + }, + { + "name": "win32-nost-base-build-rel", + "displayName": "Build - x86 - Release - Windows - Core Lib - No Stacktraces", + "description": "Build only the Core Lib with MSVC-x86 with the necessary Ninja options in Release mode, but with no stacktraces (strict C++11).", + "configurePreset": "win32-nost-base-rel", + "inherits": "win32-base-build" + }, + { + "name": "win32-nost-base-clean-build-rel", + "displayName": "Clean Build - x86 - Release - Windows - Core Lib - No Stacktraces", + "description": "Build only the Core Lib with MSVC-x86 with the necessary Ninja options in Release mode, but with no stacktraces (strict C++11), and clean up the previous build first.", + "inherits": "win32-nost-base-build-rel", + "cleanFirst": true + }, + { + "name": "win32-with-test-build", + "displayName": "Build - x86 - Debug - Windows - Core Lib With Tests", + "description": "Build the Core Lib, and Tests, with MSVC-x86 with the necessary Ninja options in Debug mode.", + "configurePreset": "win32-with-test", + "inherits": "win32-base-build" + }, + { + "name": "win32-with-test-clean-build", + "displayName": "Clean Build - x86 - Debug - Windows - Core Lib With Tests", + "description": "Build the Core Lib, and Tests, with MSVC-x86 with the necessary Ninja options in Debug mode, and clean up the previous build first.", + "inherits": "win32-with-test-build", + "cleanFirst": true + }, + { + "name": "win32-with-test-build-rel", + "displayName": "Build - x86 - Release - Windows - Core Lib With Tests", + "description": "Build the Core Lib, and Tests, with MSVC-x86 with the necessary Ninja options in Release mode.", + "configurePreset": "win32-with-test-rel", + "inherits": "win32-base-build" + }, + { + "name": "win32-with-test-clean-build-rel", + "displayName": "Clean Build - x86 - Release - Windows - Core Lib With Tests", + "description": "Build the Core Lib, and Tests, with MSVC-x86 with the necessary Ninja options in Release mode, and clean up the previous build first.", + "inherits": "win32-with-test-build-rel", + "cleanFirst": true + }, + { + "name": "win32-nost-with-test-build", + "displayName": "Build - x86 - Debug - Windows - Core Lib With Tests - No Stacktraces", + "description": "Build the Core Lib, and Tests, with MSVC-x86 with the necessary Ninja options in Debug mode, but with no stacktraces (strict C++11).", + "configurePreset": "win32-nost-with-test", + "inherits": "win32-base-build" + }, + { + "name": "win32-nost-with-test-clean-build", + "displayName": "Clean Build - x86 - Debug - Windows - Core Lib With Tests - No Stacktraces", + "description": "Build the Core Lib, and Tests, with MSVC-x86 with the necessary Ninja options in Debug mode, but with no stacktraces (strict C++11), and clean up the previous build first.", + "inherits": "win32-nost-with-test-build", + "cleanFirst": true + }, + { + "name": "win32-nost-with-test-build-rel", + "displayName": "Build - x86 - Release - Windows - Core Lib With Tests - No Stacktraces", + "description": "Build the Core Lib, and Tests, with MSVC-x86 with the necessary Ninja options in Release mode, but with no stacktraces (strict C++11).", + "configurePreset": "win32-nost-with-test-rel", + "inherits": "win32-base-build" + }, + { + "name": "win32-nost-with-test-clean-build-rel", + "displayName": "Clean Build - x86 - Release - Windows - Core Lib With Tests - No Stacktraces", + "description": "Build the Core Lib, and Tests, with MSVC-x86 with the necessary Ninja options in Release mode, but with no stacktraces (strict C++11), and clean up the previous build first.", + "inherits": "win32-nost-with-test-build-rel", + "cleanFirst": true + }, + { + "name": "win32-with-demo-build", + "displayName": "Build - x86 - Debug - Windows - Core Lib With Demo", + "description": "Build the Core Lib, and Demo, with MSVC-x86 with the necessary Ninja options in Debug mode.", + "configurePreset": "win32-with-demo", + "inherits": "win32-base-build" + }, + { + "name": "win32-with-demo-clean-build", + "displayName": "Clean Build - x86 - Debug - Windows - Core Lib With Demo", + "description": "Build the Core Lib, and Demo, with MSVC-x86 with the necessary Ninja options in Debug mode, and clean up the previous build first.", + "inherits": "win32-with-demo-build", + "cleanFirst": true + }, + { + "name": "win32-with-demo-build-rel", + "displayName": "Build - x86 - Release - Windows - Core Lib With Demo", + "description": "Build the Core Lib, and Demo, with MSVC-x86 with the necessary Ninja options in Release mode.", + "configurePreset": "win32-with-demo-rel", + "inherits": "win32-base-build" + }, + { + "name": "win32-with-demo-clean-build-rel", + "displayName": "Clean Build - x86 - Release - Windows - Core Lib With Demo", + "description": "Build the Core Lib, and Demo, with MSVC-x86 with the necessary Ninja options in Release mode, and clean up the previous build first.", + "inherits": "win32-with-demo-build-rel", + "cleanFirst": true + }, + { + "name": "win32-nost-with-demo-build", + "displayName": "Build - x86 - Debug - Windows - Core Lib With Demo - No Stacktraces", + "description": "Build the Core Lib, and Demo, with MSVC-x86 with the necessary Ninja options in Debug mode, but with no stacktraces (strict C++11).", + "configurePreset": "win32-nost-with-demo", + "inherits": "win32-base-build" + }, + { + "name": "win32-nost-with-demo-clean-build", + "displayName": "Clean Build - x86 - Debug - Windows - Core Lib With Demo - No Stacktraces", + "description": "Build the Core Lib, and Demo, with MSVC-x86 with the necessary Ninja options in Debug mode, but with no stacktraces (strict C++11), and clean up the previous build first.", + "inherits": "win32-nost-with-demo-build", + "cleanFirst": true + }, + { + "name": "win32-nost-with-demo-build-rel", + "displayName": "Build - x86 - Release - Windows - Core Lib With Demo - No Stacktraces", + "description": "Build the Core Lib, and Demo, with MSVC-x86 with the necessary Ninja options in Release mode, but with no stacktraces (strict C++11).", + "configurePreset": "win32-nost-with-demo-rel", + "inherits": "win32-base-build" + }, + { + "name": "win32-nost-with-demo-clean-build-rel", + "displayName": "Clean Build - x86 - Release - Windows - Core Lib With Demo - No Stacktraces", + "description": "Build the Core Lib, and Demo, with MSVC-x86 with the necessary Ninja options in Release mode, but with no stacktraces (strict C++11), and clean up the previous build first.", + "inherits": "win32-nost-with-demo-build-rel", + "cleanFirst": true + }, + { + "name": "win32-all-build", + "displayName": "Build - x86 - Debug - Windows - All Components - Core, Tests, and Demo", + "description": "Build the Core Lib, Tests, and Demo, with MSVC-x86 with the necessary Ninja options in Debug mode.", + "configurePreset": "win32-all", + "inherits": "win32-base-build" + }, + { + "name": "win32-all-clean-build", + "displayName": "Clean Build - x86 - Debug - Windows - All Components - Core, Tests, and Demo", + "description": "Build the Core Lib, Tests, and Demo, with MSVC-x86 with the necessary Ninja options in Debug mode, and clean up the previous build first.", + "inherits": "win32-all-build", + "cleanFirst": true + }, + { + "name": "win32-all-build-rel", + "displayName": "Build - x86 - Release - Windows - All Components - Core, Tests, and Demo", + "description": "Build the Core Lib, Tests, and Demo, with MSVC-x86 with the necessary Ninja options in Release mode.", + "configurePreset": "win32-all-rel", + "inherits": "win32-base-build" + }, + { + "name": "win32-all-clean-build-rel", + "displayName": "Clean Build - x86 - Release - Windows - All Components - Core, Tests, and Demo", + "description": "Build the Core Lib, Tests, and Demo, with MSVC-x86 with the necessary Ninja options in Release mode, and clean up the previous build first.", + "inherits": "win32-all-build-rel", + "cleanFirst": true + }, + { + "name": "win32-nost-all-build", + "displayName": "Build - x86 - Debug - Windows - All Components - Core, Tests, and Demo - No Stacktraces", + "description": "Build the Core Lib, Tests, and Demo, with MSVC-x86 with the necessary Ninja options in Debug mode, but with no stacktraces (strict C++11).", + "configurePreset": "win32-nost-all", + "inherits": "win32-base-build" + }, + { + "name": "win32-nost-all-clean-build", + "displayName": "Clean Build - x86 - Debug - Windows - All Components - Core, Tests, and Demo - No Stacktraces", + "description": "Build the Core Lib, Tests, and Demo, with MSVC-x86 with the necessary Ninja options in Debug mode, but with no stacktraces (strict C++11), and clean up the previous build first.", + "inherits": "win32-nost-all-build", + "cleanFirst": true + }, + { + "name": "win32-nost-all-build-rel", + "displayName": "Build - x86 - Release - Windows - All Components - Core, Tests, and Demo - No Stacktraces", + "description": "Build the Core Lib, Tests, and Demo, with MSVC-x86 with the necessary Ninja options in Release mode, but with no stacktraces (strict C++11).", + "configurePreset": "win32-nost-all-rel", + "inherits": "win32-base-build" + }, + { + "name": "win32-nost-all-clean-build-rel", + "displayName": "Clean Build - x86 - Release - Windows - All Components - Core, Tests, and Demo - No Stacktraces", + "description": "Build the Core Lib, Tests, and Demo, with MSVC-x86 with the necessary Ninja options in Release mode, but with no stacktraces (strict C++11), and clean up the previous build first.", + "inherits": "win32-nost-all-build-rel", + "cleanFirst": true + }, + { + "name": "win32-msanalysis-build", + "displayName": "Analyze - x86 - Windows", + "description": "Analyze the code with MSVC CL using the x86 toolchain architecture.", + "inherits": "win32-base-clean-build", + "configurePreset": "win32-msanalysis", + "cleanFirst": true + }, + { + "name": "win32-msanalysis-xml-build", + "displayName": "Analyze - x86 - Windows - XML output", + "description": "Analyze the code with MSVC CL using the x86 toolchain architecture, then output the analysis results in XML format.", + "inherits": "win32-msanalysis-build", + "configurePreset": "win32-msanalysis-xml" + }, + { + "name": "win32-msanalysis-sarif-build", + "displayName": "Analyze - x86 - Windows - SARIF output", + "description": "Analyze the code with MSVC CL using the x86 toolchain architecture, then output the analysis results in SARIF format.", + "inherits": "win32-msanalysis-build", + "configurePreset": "win32-msanalysis-sarif" + }, + { + "name": "win32-ct-build", + "displayName": "Analyze - x86 - Windows - Clang Tidy", + "description": "Analyze the code with Clang Tidy using the x86 toolchain architecture.", + "configurePreset": "win32-ct", + "cleanFirst": true, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "win64-base-build", + "displayName": "Build - x64 - Debug - Windows - Core Lib", + "description": "Build with MSVC-x64 with the necessary Ninja options in Debug mode.", + "configurePreset": "win64-base", + "inherits": "win32-base-build" + }, + { + "name": "win64-base-clean-build", + "displayName": "Clean Build - x64 - Debug - Windows - Core Lib", + "description": "Build with MSVC-x64 with the necessary Ninja options in Debug mode, and clean up the previous build first.", + "inherits": "win64-base-build", + "cleanFirst": true + }, + { + "name": "win64-base-build-rel", + "displayName": "Build - x64 - Release - Windows - Core Lib", + "description": "Build with MSVC-x64 with the necessary Ninja options in Release mode.", + "configurePreset": "win64-base-rel", + "inherits": "win32-base-build-rel" + }, + { + "name": "win64-base-clean-build-rel", + "displayName": "Clean Build - x64 - Release - Windows - Core Lib", + "description": "Build with MSVC-x64 with the necessary Ninja options in Release mode, and clean up the previous build first.", + "inherits": "win64-base-build-rel", + "cleanFirst": true + }, + { + "name": "win64-nost-base-build", + "displayName": "Build - x64 - Debug - Windows - Core Lib - No Stacktraces", + "description": "Build only the Core Lib with MSVC-x64 with the necessary Ninja options in Debug mode, but with no stacktraces (strict C++11).", + "configurePreset": "win64-nost-base", + "inherits": "win64-base-build" + }, + { + "name": "win64-nost-base-clean-build", + "displayName": "Clean Build - x64 - Debug - Windows - Core Lib - No Stacktraces", + "description": "Build only the Core Lib with MSVC-x64 with the necessary Ninja options in Debug mode, but with no stacktraces (strict C++11), and clean up the previous build first.", + "inherits": "win64-nost-base-build", + "cleanFirst": true + }, + { + "name": "win64-nost-base-build-rel", + "displayName": "Build - x64 - Release - Windows - Core Lib - No Stacktraces", + "description": "Build only the Core Lib with MSVC-x64 with the necessary Ninja options in Release mode, but with no stacktraces (strict C++11).", + "configurePreset": "win64-nost-base-rel", + "inherits": "win64-base-build" + }, + { + "name": "win64-nost-base-clean-build-rel", + "displayName": "Clean Build - x64 - Release - Windows - Core Lib - No Stacktraces", + "description": "Build only the Core Lib with MSVC-x64 with the necessary Ninja options in Release mode, but with no stacktraces (strict C++11), and clean up the previous build first.", + "inherits": "win64-nost-base-build-rel", + "cleanFirst": true + }, + { + "name": "win64-with-test-build", + "displayName": "Build - x64 - Debug - Windows - Core Lib With Tests", + "description": "Build the Core Lib, and Tests, with MSVC-x64 with the necessary Ninja options in Debug mode.", + "configurePreset": "win64-with-test", + "inherits": "win64-base-build" + }, + { + "name": "win64-with-test-clean-build", + "displayName": "Clean Build - x64 - Debug - Windows - Core Lib With Tests", + "description": "Build the Core Lib, and Tests, with MSVC-x64 with the necessary Ninja options in Debug mode, and clean up the previous build first.", + "inherits": "win64-with-test-build", + "cleanFirst": true + }, + { + "name": "win64-with-test-build-rel", + "displayName": "Build - x64 - Release - Windows - Core Lib With Tests", + "description": "Build the Core Lib, and Tests, with MSVC-x64 with the necessary Ninja options in Release mode.", + "configurePreset": "win64-with-test-rel", + "inherits": "win64-base-build" + }, + { + "name": "win64-with-test-clean-build-rel", + "displayName": "Clean Build - x64 - Release - Windows - Core Lib With Tests", + "description": "Build the Core Lib, and Tests, with MSVC-x64 with the necessary Ninja options in Release mode, and clean up the previous build first.", + "inherits": "win64-with-test-build-rel", + "cleanFirst": true + }, + { + "name": "win64-nost-with-test-build", + "displayName": "Build - x64 - Debug - Windows - Core Lib With Tests - No Stacktraces", + "description": "Build the Core Lib, and Tests, with MSVC-x64 with the necessary Ninja options in Debug mode, but with no stacktraces (strict C++11).", + "configurePreset": "win64-nost-with-test", + "inherits": "win64-base-build" + }, + { + "name": "win64-nost-with-test-clean-build", + "displayName": "Clean Build - x64 - Debug - Windows - Core Lib With Tests - No Stacktraces", + "description": "Build the Core Lib, and Tests, with MSVC-x64 with the necessary Ninja options in Debug mode, but with no stacktraces (strict C++11), and clean up the previous build first.", + "inherits": "win64-nost-with-test-build", + "cleanFirst": true + }, + { + "name": "win64-nost-with-test-build-rel", + "displayName": "Build - x64 - Release - Windows - Core Lib With Tests - No Stacktraces", + "description": "Build the Core Lib, and Tests, with MSVC-x64 with the necessary Ninja options in Release mode, but with no stacktraces (strict C++11).", + "configurePreset": "win64-nost-with-test-rel", + "inherits": "win64-base-build" + }, + { + "name": "win64-nost-with-test-clean-build-rel", + "displayName": "Clean Build - x64 - Release - Windows - Core Lib With Tests - No Stacktraces", + "description": "Build the Core Lib, and Tests, with MSVC-x64 with the necessary Ninja options in Release mode, but with no stacktraces (strict C++11), and clean up the previous build first.", + "inherits": "win64-nost-with-test-build-rel", + "cleanFirst": true + }, + { + "name": "win64-with-demo-build", + "displayName": "Build - x64 - Debug - Windows - Core Lib With Demo", + "description": "Build the Core Lib, and Demo, with MSVC-x64 with the necessary Ninja options in Debug mode.", + "configurePreset": "win64-with-demo", + "inherits": "win64-base-build" + }, + { + "name": "win64-with-demo-clean-build", + "displayName": "Clean Build - x64 - Debug - Windows - Core Lib With Demo", + "description": "Build the Core Lib, and Demo, with MSVC-x64 with the necessary Ninja options in Debug mode, and clean up the previous build first.", + "inherits": "win64-with-demo-build", + "cleanFirst": true + }, + { + "name": "win64-with-demo-build-rel", + "displayName": "Build - x64 - Release - Windows - Core Lib With Demo", + "description": "Build the Core Lib, and Demo, with MSVC-x64 with the necessary Ninja options in Release mode.", + "configurePreset": "win64-with-demo-rel", + "inherits": "win64-base-build" + }, + { + "name": "win64-with-demo-clean-build-rel", + "displayName": "Clean Build - x64 - Release - Windows - Core Lib With Demo", + "description": "Build the Core Lib, and Demo, with MSVC-x64 with the necessary Ninja options in Release mode, and clean up the previous build first.", + "inherits": "win64-with-demo-build-rel", + "cleanFirst": true + }, + { + "name": "win64-nost-with-demo-build", + "displayName": "Build - x64 - Debug - Windows - Core Lib With Demo - No Stacktraces", + "description": "Build the Core Lib, and Demo, with MSVC-x64 with the necessary Ninja options in Debug mode, but with no stacktraces (strict C++11).", + "configurePreset": "win64-nost-with-demo", + "inherits": "win64-base-build" + }, + { + "name": "win64-nost-with-demo-clean-build", + "displayName": "Clean Build - x64 - Debug - Windows - Core Lib With Demo - No Stacktraces", + "description": "Build the Core Lib, and Demo, with MSVC-x64 with the necessary Ninja options in Debug mode, but with no stacktraces (strict C++11), and clean up the previous build first.", + "inherits": "win64-nost-with-demo-build", + "cleanFirst": true + }, + { + "name": "win64-nost-with-demo-build-rel", + "displayName": "Build - x64 - Release - Windows - Core Lib With Demo - No Stacktraces", + "description": "Build the Core Lib, and Demo, with MSVC-x64 with the necessary Ninja options in Release mode, but with no stacktraces (strict C++11).", + "configurePreset": "win64-nost-with-demo-rel", + "inherits": "win64-base-build" + }, + { + "name": "win64-nost-with-demo-clean-build-rel", + "displayName": "Clean Build - x64 - Release - Windows - Core Lib With Demo - No Stacktraces", + "description": "Build the Core Lib, and Demo, with MSVC-x64 with the necessary Ninja options in Release mode, but with no stacktraces (strict C++11), and clean up the previous build first.", + "inherits": "win64-nost-with-demo-build-rel", + "cleanFirst": true + }, + { + "name": "win64-all-build", + "displayName": "Build - x64 - Debug - Windows - All Components - Core, Tests, and Demo", + "description": "Build the Core Lib, Tests, and Demo, with MSVC-x64 with the necessary Ninja options in Debug mode.", + "configurePreset": "win64-all", + "inherits": "win64-base-build" + }, + { + "name": "win64-all-clean-build", + "displayName": "Clean Build - x64 - Debug - Windows - All Components - Core, Tests, and Demo", + "description": "Build the Core Lib, Tests, and Demo, with MSVC-x64 with the necessary Ninja options in Debug mode, and clean up the previous build first.", + "inherits": "win64-all-build", + "cleanFirst": true + }, + { + "name": "win64-all-build-rel", + "displayName": "Build - x64 - Release - Windows - All Components - Core, Tests, and Demo", + "description": "Build the Core Lib, Tests, and Demo, with MSVC-x64 with the necessary Ninja options in Release mode.", + "configurePreset": "win64-all-rel", + "inherits": "win64-base-build" + }, + { + "name": "win64-all-clean-build-rel", + "displayName": "Clean Build - x64 - Release - Windows - All Components - Core, Tests, and Demo", + "description": "Build the Core Lib, Tests, and Demo, with MSVC-x64 with the necessary Ninja options in Release mode, and clean up the previous build first.", + "inherits": "win64-all-build-rel", + "cleanFirst": true + }, + { + "name": "win64-nost-all-build", + "displayName": "Build - x64 - Debug - Windows - All Components - Core, Tests, and Demo - No Stacktraces", + "description": "Build the Core Lib, Tests, and Demo, with MSVC-x64 with the necessary Ninja options in Debug mode, but with no stacktraces (strict C++11).", + "configurePreset": "win64-nost-all", + "inherits": "win64-base-build" + }, + { + "name": "win64-nost-all-clean-build", + "displayName": "Clean Build - x64 - Debug - Windows - All Components - Core, Tests, and Demo - No Stacktraces", + "description": "Build the Core Lib, Tests, and Demo, with MSVC-x64 with the necessary Ninja options in Debug mode, but with no stacktraces (strict C++11), and clean up the previous build first.", + "inherits": "win64-nost-all-build", + "cleanFirst": true + }, + { + "name": "win64-nost-all-build-rel", + "displayName": "Build - x64 - Release - Windows - All Components - Core, Tests, and Demo - No Stacktraces", + "description": "Build the Core Lib, Tests, and Demo, with MSVC-x64 with the necessary Ninja options in Release mode, but with no stacktraces (strict C++11).", + "configurePreset": "win64-nost-all-rel", + "inherits": "win64-base-build" + }, + { + "name": "win64-nost-all-clean-build-rel", + "displayName": "Clean Build - x64 - Release - Windows - All Components - Core, Tests, and Demo - No Stacktraces", + "description": "Build the Core Lib, Tests, and Demo, with MSVC-x64 with the necessary Ninja options in Release mode, but with no stacktraces (strict C++11), and clean up the previous build first.", + "inherits": "win64-nost-all-build-rel", + "cleanFirst": true + }, + { + "name": "win64-msanalysis-build", + "displayName": "Analyze - x64 - Windows", + "description": "Analyze the code with MSVC CL using the x64 toolchain architecture.", + "inherits": "win32-msanalysis-build", + "configurePreset": "win64-msanalysis" + }, + { + "name": "win64-msanalysis-xml-build", + "displayName": "Analyze - x64 - Windows - XML output", + "description": "Analyze the code with MSVC CL using the x64 toolchain architecture, then output the analysis results in XML format.", + "inherits": "win64-msanalysis-build", + "configurePreset": "win64-msanalysis-xml" + }, + { + "name": "win64-msanalysis-sarif-build", + "displayName": "Analyze - x64 - Windows - SARIF output", + "description": "Analyze the code with MSVC CL using the x64 toolchain architecture, then output the analysis results in SARIF format.", + "inherits": "win64-msanalysis-build", + "configurePreset": "win64-msanalysis-sarif" + }, + { + "name": "win64-ct-build", + "displayName": "Analyze - x64 - Windows - Clang Tidy", + "description": "Analyze the code with Clang Tidy using the x64 toolchain architecture.", + "inherits": "win32-ct-build", + "configurePreset": "win64-ct" + } + ], + "testPresets": [ + { + "name": "win32-test", + "displayName": "Test - x86 - Debug - Windows", + "description": "Test with MSVC-x86 with the necessary Ninja options in Debug mode.", + "configurePreset": "win32-with-test", + "output": { + "shortProgress": true, + "verbosity": "verbose", + "outputOnFailure": true, + "outputLogFile": "testcpp-tests.log", + "maxFailedTestOutputSize": 104857600, + "maxPassedTestOutputSize": 104857600, + "testOutputTruncation": "tail" + }, + "execution": { + "noTestsAction": "error", + "stopOnFailure": false + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "win32-test-rel", + "displayName": "Test - x86 - Release - Windows", + "description": "Test with MSVC-x86 with the necessary Ninja options in Release mode.", + "configurePreset": "win32-with-test-rel", + "inherits": "win32-test" + }, + { + "name": "win32-nost-test", + "displayName": "Test - x86 - Debug - Windows - No Stacktraces", + "description": "Test with MSVC-x86 with the necessary Ninja options in Debug mode, but with no stacktraces (strict C++11).", + "configurePreset": "win32-nost-with-test", + "inherits": "win32-test" + }, + { + "name": "win32-nost-test-rel", + "displayName": "Test - x86 - Release - Windows - No Stacktraces", + "description": "Test with MSVC-x86 with the necessary Ninja options in Release mode, but with no stacktraces (strict C++11).", + "configurePreset": "win32-nost-with-test-rel", + "inherits": "win32-test" + }, + { + "name": "win64-test", + "displayName": "Test - x64 - Debug - Windows", + "description": "Test with MSVC-x64 with the necessary Ninja options in Debug mode.", + "configurePreset": "win64-with-test", + "inherits": "win32-test" + }, + { + "name": "win64-test-rel", + "displayName": "Test - x64 - Release - Windows", + "description": "Test with MSVC-x64 with the necessary Ninja options in Release mode.", + "configurePreset": "win64-with-test-rel", + "inherits": "win32-test" + }, + { + "name": "win64-nost-test", + "displayName": "Test - x64 - Debug - Windows - No Stacktraces", + "description": "Test with MSVC-x64 with the necessary Ninja options in Debug mode, but with no stacktraces (strict C++11).", + "configurePreset": "win64-nost-with-test", + "inherits": "win32-nost-test" + }, + { + "name": "win64-nost-test-rel", + "displayName": "Test - x64 - Release - Windows - No Stacktraces", + "description": "Test with MSVC-x64 with the necessary Ninja options in Release mode, but with no stacktraces (strict C++11).", + "configurePreset": "win64-nost-with-test-rel", + "inherits": "win32-nost-test" + } + ] +} diff --git a/LICENSE.rtf b/LICENSE.rtf new file mode 100644 index 0000000..c6d5184 --- /dev/null +++ b/LICENSE.rtf @@ -0,0 +1,62 @@ +{\rtf1\ansi\deff3\adeflang1025 +{\fonttbl{\f0\froman\fprq2\fcharset0 Times New Roman;}{\f1\froman\fprq2\fcharset2 Symbol;}{\f2\fswiss\fprq2\fcharset0 Arial;}{\f3\froman\fprq2\fcharset0 Liberation Serif{\*\falt Times New Roman};}{\f4\fswiss\fprq2\fcharset0 Liberation Sans{\*\falt Arial};}{\f5\fnil\fprq2\fcharset0 Cascadia Mono;}{\f6\fnil\fprq2\fcharset0 Microsoft YaHei;}{\f7\fnil\fprq2\fcharset0 Lucida Sans;}{\f8\fswiss\fprq0\fcharset128 Lucida Sans;}} +{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;\red0\green136\blue0;\red163\green21\blue21;} +{\stylesheet{\s0\snext0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052 Normal;} +{\s15\sbasedon0\snext16\rtlch\af7\afs28 \ltrch\hich\af4\loch\sb240\sa120\keepn\f4\fs28\dbch\af6 Heading;} +{\s16\sbasedon0\snext16\loch\sl276\slmult1\sb0\sa140 Body Text;} +{\s17\sbasedon16\snext17\rtlch\af8 \ltrch\loch\sl276\slmult1\sb0\sa140 List;} +{\s18\sbasedon0\snext18\rtlch\af8\afs24\ai \ltrch\loch\sb120\sa120\noline\fs24\i caption;} +{\s19\sbasedon0\snext19\rtlch\af8 \ltrch\loch\noline Index;} +}{\*\generator LibreOffice/24.8.4.2$Windows_X86_64 LibreOffice_project/bb3cfa12c7b1bf994ecc5649a80400d06cd71002}{\info{\creatim\yr0\mo0\dy0\hr0\min0}{\revtim\yr2025\mo2\dy17\hr14\min55}{\printim\yr0\mo0\dy0\hr0\min0}}{\*\userprops}\deftab709 +\hyphauto1\viewscale140\formshade\paperh15840\paperw12240\margl1134\margr1134\margt1134\margb1134\sectd\sbknone\sftnnar\saftnnrlc\sectunlocked1\pgwsxn12240\pghsxn15840\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\ftnbj\ftnstart1\ftnrstcont\ftnnar\fet\aftnrstcont\aftnstart1\aftnnrlc +{\*\ftnsep\chftnsep}\pgndec\pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar{\hich\af5\loch\cf1\fs19\highlight8\f5\loch +This is free and unencumbered software released into the public domain.} +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar\hich\af5\loch\cf1\fs19\highlight8\f5\loch + +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar{\hich\af5\loch\cf1\fs19\highlight8\f5\loch +Anyone is free to copy, modify, publish, use, compile, sell, or} +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar{\hich\af5\loch\cf1\fs19\highlight8\f5\loch +distribute this software, either in source code form or as a compiled} +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar{\hich\af5\loch\cf1\fs19\highlight8\f5\loch +binary, for any purpose, commercial or non-commercial, and by any} +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar{\hich\af5\loch\cf1\fs19\highlight8\f5\loch +means.} +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar\hich\af5\loch\cf1\fs19\highlight8\f5\loch + +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar{\hich\af5\loch\cf1\fs19\highlight8\f5\loch +In jurisdictions that recognize copyright laws, the author or authors} +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar{\hich\af5\loch\cf1\fs19\highlight8\f5\loch +of this software dedicate any and all copyright interest in the} +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar{\hich\af5\loch\cf1\fs19\highlight8\f5\loch +software to the public domain. We make this dedication for the benefit} +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar{\hich\af5\loch\cf1\fs19\highlight8\f5\loch +of the public at large and to the detriment of our heirs and} +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar{\hich\af5\loch\cf1\fs19\highlight8\f5\loch +successors. We intend this dedication to be an overt act of} +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar{\hich\af5\loch\cf1\fs19\highlight8\f5\loch +relinquishment in perpetuity of all present and future rights to this} +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar{\hich\af5\loch\cf1\fs19\highlight8\f5\loch +software under copyright law.} +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar\hich\af5\loch\cf1\fs19\highlight8\f5\loch + +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar{\hich\af5\loch\cf1\fs19\highlight8\f5\loch +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,} +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar{\hich\af5\loch\cf1\fs19\highlight8\f5\loch +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF} +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar{\hich\af5\loch\cf1\fs19\highlight8\f5\loch +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.} +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar{\hich\af5\loch\cf1\fs19\highlight8\f5\loch +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR} +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar{\hich\af5\loch\cf1\fs19\highlight8\f5\loch +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,} +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar{\hich\af5\loch\cf1\fs19\highlight8\f5\loch +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR} +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar{\hich\af5\loch\cf1\fs19\highlight8\f5\loch +OTHER DEALINGS IN THE SOFTWARE.} +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar\hich\af5\loch\cf1\fs19\highlight8\f5\loch + +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar{\hich\af5\loch\cf1\fs19\highlight8\f5\loch +For more information, please refer to } +\par \pard\plain \s0\rtlch\af7\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\nowidctlpar\hyphpar0\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af9\langfe2052\ql\ltrpar\loch + +\par } \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index 114f98f..0000000 --- a/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -.PHONY: clean All - -All: - @echo "----------Building project:[ TestFramework - Debug ]----------" - @"$(MAKE)" -f "TestFramework.mk" -clean: - @echo "----------Cleaning project:[ TestFramework - Debug ]----------" - @"$(MAKE)" -f "TestFramework.mk" clean diff --git a/README.md b/README.md index 85c6f53..01969c8 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,11 @@ [![Test-NoStacktraces](https://github.com/eljonny/TestCPP/actions/workflows/cmake-build-test-nost.yml/badge.svg?event=push)](https://github.com/eljonny/TestCPP/actions/workflows/cmake-build-test-nost.yml) [![RPM_DEB-Packaging-WithStacktraces](https://github.com/eljonny/TestCPP/actions/workflows/cmake-linux-pack-st.yml/badge.svg?event=push)](https://github.com/eljonny/TestCPP/actions/workflows/cmake-linux-pack-st.yml) [![RPM_DEB-Packaging-NoStacktraces](https://github.com/eljonny/TestCPP/actions/workflows/cmake-linux-pack-nost.yml/badge.svg?event=push)](https://github.com/eljonny/TestCPP/actions/workflows/cmake-linux-pack-nost.yml) +[![WIX-Packaging-WithStacktraces](https://github.com/eljonny/TestCPP/actions/workflows/cmake-windows-pack-st.yml/badge.svg?event=push)](https://github.com/eljonny/TestCPP/actions/workflows/cmake-windows-pack-st.yml) +[![WIX-Packaging-NoStacktraces](https://github.com/eljonny/TestCPP/actions/workflows/cmake-windows-pack-nost.yml/badge.svg?event=push)](https://github.com/eljonny/TestCPP/actions/workflows/cmake-windows-pack-nost.yml) [![Coverage](https://github.com/eljonny/TestCPP/actions/workflows/cmake-build-cov-st.yml/badge.svg?event=push)](https://github.com/eljonny/TestCPP/actions/workflows/cmake-build-cov-st.yml) [![Security](https://github.com/eljonny/TestCPP/actions/workflows/codeql.yml/badge.svg?event=push)](https://github.com/eljonny/TestCPP/actions/workflows/codeql.yml) -[![StaticAnalysis-Debug](https://github.com/eljonny/TestCPP/actions/workflows/cmake-static-analysis-dbg.yml/badge.svg?event=push)](https://github.com/eljonny/TestCPP/actions/workflows/cmake-static-analysis-dbg.yml) -[![StaticAnalysis-Release](https://github.com/eljonny/TestCPP/actions/workflows/cmake-static-analysis-rls.yml/badge.svg?event=push)](https://github.com/eljonny/TestCPP/actions/workflows/cmake-static-analysis-rls.yml) +[![StaticAnalysis](https://github.com/eljonny/TestCPP/actions/workflows/cmake-static-analysis.yml/badge.svg?event=push)](https://github.com/eljonny/TestCPP/actions/workflows/cmake-static-analysis.yml) [![codecov](https://codecov.io/gh/eljonny/TestCPP/graph/badge.svg?token=WFG41QY4BB)](https://codecov.io/gh/eljonny/TestCPP) # Introduction @@ -15,8 +16,8 @@ This is an implementation of a very simple test framework. It can be built with no dependencies (other than the C++ standard library) and can rely on only C++11 features. -It supports stack traces on test failures as of 0.2.1-beta.3, which uses - Boost.StackTrace, but this is not required to use the framework. +It supports stack traces on test failures as of 0.2.1-beta.3, which + uses Boost.StackTrace, but this is not required to use the framework. The library is most useful for educational projects or small personal/internal projects, as many features required for @@ -24,7 +25,7 @@ The library is most useful for educational projects or small For testing more complex projects I would recommend using [GoogleTest](https://github.com/google/googletest). -The code is <1k lines. +The code is ~2k lines. The Release builds are optimized for speed over size, so the build could probably be tweaked to get a smaller binary if desired. @@ -73,6 +74,8 @@ Then add it to your test executable target through ### vcpkg +*In process, not working yet* + As of 0.1.0-beta.1, you might soon be able to also include it in your `vcpkg` project by running the following in the root of your project (pending vcpkg PR approval that I have in right @@ -83,6 +86,8 @@ As of 0.1.0-beta.1, you might soon be able to also include it ### Conan +*In process, not working yet* + As of 0.1.1-beta.2, you might soon be able to also include it in your `conan` project (pending approval from the Conan community). @@ -181,13 +186,18 @@ To build within the CodeLite IDE: deploy the library, cmake-rls or cmake-nost-rls) In the Custom Targets... context menu option there are also, depending - on the build configuration chose, ctest, cpack, clean, clang-tidy, - cmake-nost-rls, and cmake-rls targets, which will, respectively: + on the build configuration chose, ctest, cpack, cppcheck, clean, + clang-tidy, cmake-nost-rls, and cmake-rls targets, which will, + respectively: - Run the test suite for the library and, if the Debug configuration is selected, generate code coverage reports for the library code. - Create binary and/or source packages (depending on the build configuration selected), and if you're on a Linux distro that supports building RPM and/or DEB packages, will build those too. + - Runs CMake without actually running the build so the build system + is generated with compile_commands.json, then runs cppcheck against + what is defined in compile_commands.json. In this case, this is + only the core library header and source files. - Runs the CMake clean command and completely removes the build directory, based on the selected configuration. - Runs the build with the TESTCPP_ANALYSIS_ENABLED build variable @@ -203,12 +213,66 @@ In the Custom Targets... context menu option there are also, depending causes the library to have link-time dependencies as explained in other elements of this README. +## Building in Visual Studio + +I have been able to successfully configure and build the project in + [Visual Studio 2022 Community](https://visualstudio.microsoft.com/vs/community/) + using the built-in CMake project integration, so this does work if you + would like to proceed this way. + +There are a number of CMake Presets defined in CMakePresets.json that + align with different build configurations and analysis profiles, + including all build feature variations (with/without the Demo project, + with/without the tests, and subsequently with/without stacktrace + support via Boost.StackTrace). + +To get started, open the root project folder in Visual Studio 2022 and + select the desired CMake Preset from the Build Configurations dropdown + which will be populated with all applicable buildPresets once the + project has fully loaded. Visual Studio automatically detects the + presence of the CMakePresets.json and CMakeLists.txt files and will + configure the project with the lexicographically-first build preset. +Subsequently, you can build the project from the Build menu or by + pressing F7 after successful configuration. + ## CMake build structure and variables The CMake build is split into components that get included into the main build file to make modifying each piece more logical and manageable. +The top-level components are as follows: +- CMake Build File: + The main build file that includes all the other components + (CMakeLists.txt). +- Code Analysis: + Configuring code analysis tools (Analysis.cmake). + Currently, this includes clang-tidy in the build process. + - CPPCheck is also included in the build process, but it is + not included in the Analysis.cmake file because it is + run as part of the static analysis GitHub Action (see + .github/workflows/cmake-static-analysis.yml). +- Build Type Handling: + Handling the build type which sets compile options for the platform + on which the build is being run based on the build type (Debug, + Release, I still need to work on getting RelWithDebugInfo + configured properly) (BuildTypeHandling.cmake). +- Target Includes: + Which includes should be applied to which targets (Includes.cmake). +- Installing: + Configuring installation of the library (Installing.cmake). +- Target Link Libraries: + Which libraries need to be linked in which build targets, based on + the result of VarChecks in terms of code coverage (Linking.cmake). +- Packaging: + Configuring library packaging parameters, enable CPack + (Packing.cmake). +- Build Targets Configuration: + Setting up the build targets (Targets.cmake). + Lists each translation unit that applies to each target. +- Testing: + Testing configuration, based on the result of VarChecks + (Testing.cmake). - Variable Checks: Checking the variables to enable or disable certain sections of the build (VarChecks.cmake). @@ -252,38 +316,62 @@ The CMake build is split into components that get included into the - dbgeng - Non-Windows - libdl -- Build Targets Configuration: - Setting up the build targets (Targets.cmake). - Lists each translation unit that applies to each target. -- Build Type Handling: - Handling the build type which sets compile options for the platform - on which the build is being run based on the build type (Debug, - Release, I still need to work on getting RelWithDebugInfo - configured properly) (BuildTypeHandling.cmake). -- Target Includes: - Which includes should be applied to which targets (Includes.cmake). -- Target Link Libraries: - Which libraries need to be linked in which build targets, based on - the result of VarChecks in terms of code coverage (Linking.cmake). -- Testing: - Testing configuration, based on the result of VarChecks - (Testing.cmake). -- Installing: - Configuring installation of the library (Installing.cmake). -- Packaging: - Configuring library packaging parameters, enable CPack - (Packing.cmake). + +There are a number of CMake modules in the cmake directory, including + the following: +- Build definitions: + - CompileDefs.cmake + Defines compile definitions for all configurations of the project. + - DebugCompileDefs.cmake + Defines compile definitions specific to the Debug configuration of + the project. + - GCCClangDebug.cmake + Defines compiler flags specific to the Debug configuration of the + project when building with GCC or Clang. + - GCCClangRelease.cmake + Defines compiler flags specific to the Release configuration of the + project when building with GCC or Clang. + - GCCCoverage.cmake + Defines compiler flags specific to the Debug configuration of the + project when building with GCC and generating code coverage data. + - MSVCDebug.cmake + Defines compiler flags specific to the Debug configuration of the + project when building with MSVC. + - MSVCRelease.cmake + Defines compiler flags specific to the Release configuration of the + project when building with MSVC. +- Linking definitions: + - Demo.cmake + Defines the linking for the demo targets. + - TestCPPWithCoverage.cmake + Defines the linking for the TestCPP library when code coverage is + enabled. + - Tests.cmake + Defines the linking for the test targets. + - TestsWithCoverage.cmake + Defines the linking for the test targets when code coverage is + enabled. +- Toolchain files: + - Windows + - Windows.Clang.toolchain.cmake + Toolchain file for Visual Studio-installed Clang on Windows. + - Windows.MSVC.toolchain.cmake + Toolchain file for MSVC on Windows. + - Windows.Kits.cmake + Toolchain file for Windows SDKs. + - VSWhere.cmake + Toolchain file for finding Visual Studio installations. ## Building outside the IDE To build outside of the CodeLite IDE, you can run the following commands, after cloning the repo, for the debug build: ``` - cd TestCPP - mkdir build-debug - cd build-debug - cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=1 - make +cd TestCPP +mkdir build-debug +cd build-debug +cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=1 +make ``` The Debug configuration supports testing and code coverage. To enable those in the build, add one or both of the following flags to `cmake`: @@ -292,11 +380,11 @@ The Debug configuration supports testing and code coverage. To enable For the release build: ``` - cd TestCPP - mkdir build-release - cd build-release - cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=1 - make +cd TestCPP +mkdir build-release +cd build-release +cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=1 +make ``` The Release configuration supports testing. To enable generation of the test target in the build, add the following flag to `cmake`: @@ -313,6 +401,57 @@ Both Release and Debug configurations support building with stack trace functionality with the folloeing flag: - `-DTESTCPP_STACKTRACE_ENABLED` +# Static Analysis of TestCPP + +You can run cppcheck and clang-tidy on the generated + compile_commands.json. + +For clang-tidy, you can also use the CMake flag to have CMake run + clang-tidy during the build. + +clang-tidy uses the .clang-tidy configuration file so you don't need to + specify any options unless you want to run it with different checks/ + behavior from what I usually run for this project. + +My suggested options for cppcheck are: +- --project=compile_commands.json + - Uses the generated CMake output to define a check project; you need + to adjust the argument to this parameter to match the path to the + compile_commands.json file that was generated. +- --enable=all + - Enables all CPPCheck checks applicable to the project. +- --suppress=missingIncludeSystem + - Then suppress missing system includes, there are no <>-enclosed + includes that need to be checked for this project. +- --suppress=unusedFunction + - Then suppress checking for unused functions. Most API functions + are not used within the library itself, and CPPCheck will generate + numerous errors saying the functions are unused. +- --suppress=checkersReport + - Then suppress the checkers report, which is a summary of the checks + that were run and how many were skipped. +- --suppress='\*:3rdparty/\*' + - Ignore all checks in the 3rdparty directory, which is where the + Boost headers are stored for this project. +- --inconclusive + - Allow inconclusive results to be reported, which can identify more + potential issues in the code. +- --force + - Forces checking of all preprocessor configurations +- --check-level=exhaustive + - Analysis covers all possible paths and more expensive checks +- --inline-suppr + - Obeys inline suppression of errors in the code, which are used in + this project to suppress specific instances of errors/warnings that + are not applicable or are false positives. +- --quiet + - Suppresses all output except for errors and warnings +- --std=c++11 + - Specifies the C++ standard to use for checking the code + +I would recommend against using the -j parameter with cppcheck because + it disables certain checks that are used in the project. + # Testing and Code Coverage The library is tested using itself. @@ -352,14 +491,14 @@ After building, run the variant of the commands that align with your build configuration (starting from the project directory). For the debug build: ``` - cd build-debug - sudo cmake --install . +cd build-debug +sudo cmake --install . ``` For the release build: ``` - cd build-release - sudo cmake --install . +cd build-release +sudo cmake --install . ``` # Packaging @@ -375,6 +514,10 @@ To create basic binary packages that can be used on the or build-release): - `cpack --config CPackConfig.cmake` +Building the basic binary packages on Windows will generate an NSIS + executable installer while building on Linux will generate multiple + tarballs and an executable sh installer file. + To create source packages, run the following command from the CMake build directory (either build-debug or build-release): - `cpack --config CPackSourceConfig.cmake` @@ -386,6 +529,11 @@ If you are building on Linux, the build supports generating - `cpack -G DEB` - `cpack -G RPM` +If you are building on Windows, the build supports generating MSI + packages. To build those (make sure you have the WiX toolkit installed + first), run the following command from the CMake build directory: + - `cpack -G WIX` + Both Windows and Linux builds also support generating a zip package by running the following command from the CMake build directory (either build-debug or build-release): @@ -402,3 +550,40 @@ Please submit an issue on the repo if there are problems I am semi-actively developing this library/framework, if anyone would like to contribute please do so by submitting pull requests on GitHub. + +## GitHub Actions + +The project is configured to use GitHub Actions for CI/CD. +There are several workflows that are used to build, test, and package + the library. +The workflows are as follows: +- cmake-multi-platform-st.yml + - Builds the library on Windows and Linux with stack traces enabled. +- cmake-multi-platform-nost.yml + - Builds the library on Windows and Linux with stack traces disabled. +- cmake-build-test-st.yml + - Builds and tests the library on Windows and Linux with stack traces + enabled. +- cmake-build-test-nost.yml + - Builds and tests the library on Windows and Linux with stack traces + disabled. +- cmake-linux-pack-st.yml + - Builds and packages the library on Linux with stack traces enabled. +- cmake-linux-pack-nost.yml + - Builds and packages the library on Linux with stack traces + disabled. +- cmake-windows-pack-st.yml + - Builds and packages the library on Windows with stack traces + enabled. +- cmake-windows-pack-nost.yml + - Builds and packages the library on Windows with stack traces + disabled. +- cmake-build-cov-st.yml + - Builds and tests the library on Linux with stack traces enabled and + generates code coverage reports that are then pushed to CodeCov for + helpful visualizations and reporting. +- codeql.yml + - Runs the CodeQL static analysis tool on the library code. +- cmake-static-analysis.yml + - Runs clang-tidy and cppcheck on the library code using + JacobDomagala/StaticAnalysis@master diff --git a/TestFramework.mk b/TestFramework.mk deleted file mode 100644 index f66e224..0000000 --- a/TestFramework.mk +++ /dev/null @@ -1,118 +0,0 @@ -## -## Auto Generated makefile by CodeLite IDE -## any manual changes will be erased -## -## Debug -ProjectName :=TestFramework -ConfigurationName :=Debug -WorkspacePath :=/home/jhyry/Dropbox/CSU-Fullerton/C++Cert/C++Fundamentals/TestFramework -ProjectPath :=/home/jhyry/Dropbox/CSU-Fullerton/C++Cert/C++Fundamentals/TestFramework -IntermediateDirectory :=./Debug -OutDir := $(IntermediateDirectory) -CurrentFileName := -CurrentFilePath := -CurrentFileFullPath := -User :=Jonathan Hyry -Date :=09/03/24 -CodeLitePath :=/home/jhyry/.codelite -LinkerName :=/usr/bin/i586-linux-gnu-g++ -SharedObjectLinkerName :=/usr/bin/i586-linux-gnu-g++ -shared -fPIC -ObjectSuffix :=.o -DependSuffix :=.o.d -PreprocessSuffix :=.i -DebugSwitch :=-g -IncludeSwitch :=-I -LibrarySwitch :=-l -OutputSwitch :=-o -LibraryPathSwitch :=-L -PreprocessorSwitch :=-D -SourceSwitch :=-c -OutputFile :=$(IntermediateDirectory)/$(ProjectName) -Preprocessors := -ObjectSwitch :=-o -ArchiveOutputSwitch := -PreprocessOnlySwitch :=-E -ObjectsFileList :="TestFramework.txt" -PCHCompileFlags := -MakeDirCommand :=mkdir -p -LinkOptions := -O0 -IncludePath := $(IncludeSwitch). $(IncludeSwitch). -IncludePCH := -RcIncludePath := -Libs := -ArLibs := -LibPath := $(LibraryPathSwitch). $(LibraryPathSwitch). $(LibraryPathSwitch)Debug - -## -## Common variables -## AR, CXX, CC, AS, CXXFLAGS and CFLAGS can be overriden using an environment variables -## -AR := /usr/bin/i586-linux-gnu-ar rcu -CXX := /usr/bin/i586-linux-gnu-g++ -CC := /usr/bin/i586-linux-gnu-gcc -CXXFLAGS := -g -Wall $(Preprocessors) -CFLAGS := $(Preprocessors) -ASFLAGS := -AS := /usr/bin/i586-linux-gnu-as - - -## -## User defined environment variables -## -CodeLiteDir:=/usr/share/codelite -Objects0=$(IntermediateDirectory)/Test.cpp$(ObjectSuffix) $(IntermediateDirectory)/main.cpp$(ObjectSuffix) - - - -Objects=$(Objects0) - -## -## Main Build Targets -## -.PHONY: all clean PreBuild PrePreBuild PostBuild MakeIntermediateDirs -all: $(OutputFile) - -$(OutputFile): $(IntermediateDirectory)/.d $(Objects) - @$(MakeDirCommand) $(@D) - @echo "" > $(IntermediateDirectory)/.d - @echo $(Objects0) > $(ObjectsFileList) - $(LinkerName) $(OutputSwitch)$(OutputFile) @$(ObjectsFileList) $(LibPath) $(Libs) $(LinkOptions) - -MakeIntermediateDirs: - @test -d ./Debug || $(MakeDirCommand) ./Debug - - -$(IntermediateDirectory)/.d: - @test -d ./Debug || $(MakeDirCommand) ./Debug - -PreBuild: - - -## -## Objects -## -$(IntermediateDirectory)/Test.cpp$(ObjectSuffix): Test.cpp $(IntermediateDirectory)/Test.cpp$(DependSuffix) - $(CXX) $(IncludePCH) $(SourceSwitch) "/home/jhyry/Dropbox/CSU-Fullerton/C++Cert/C++Fundamentals/TestFramework/Test.cpp" $(CXXFLAGS) $(ObjectSwitch)$(IntermediateDirectory)/Test.cpp$(ObjectSuffix) $(IncludePath) -$(IntermediateDirectory)/Test.cpp$(DependSuffix): Test.cpp - @$(CXX) $(CXXFLAGS) $(IncludePCH) $(IncludePath) -MG -MP -MT$(IntermediateDirectory)/Test.cpp$(ObjectSuffix) -MF$(IntermediateDirectory)/Test.cpp$(DependSuffix) -MM Test.cpp - -$(IntermediateDirectory)/Test.cpp$(PreprocessSuffix): Test.cpp - $(CXX) $(CXXFLAGS) $(IncludePCH) $(IncludePath) $(PreprocessOnlySwitch) $(OutputSwitch) $(IntermediateDirectory)/Test.cpp$(PreprocessSuffix) Test.cpp - -$(IntermediateDirectory)/main.cpp$(ObjectSuffix): main.cpp $(IntermediateDirectory)/main.cpp$(DependSuffix) - $(CXX) $(IncludePCH) $(SourceSwitch) "/home/jhyry/Dropbox/CSU-Fullerton/C++Cert/C++Fundamentals/TestFramework/main.cpp" $(CXXFLAGS) $(ObjectSwitch)$(IntermediateDirectory)/main.cpp$(ObjectSuffix) $(IncludePath) -$(IntermediateDirectory)/main.cpp$(DependSuffix): main.cpp - @$(CXX) $(CXXFLAGS) $(IncludePCH) $(IncludePath) -MG -MP -MT$(IntermediateDirectory)/main.cpp$(ObjectSuffix) -MF$(IntermediateDirectory)/main.cpp$(DependSuffix) -MM main.cpp - -$(IntermediateDirectory)/main.cpp$(PreprocessSuffix): main.cpp - $(CXX) $(CXXFLAGS) $(IncludePCH) $(IncludePath) $(PreprocessOnlySwitch) $(OutputSwitch) $(IntermediateDirectory)/main.cpp$(PreprocessSuffix) main.cpp - - --include $(IntermediateDirectory)/*$(DependSuffix) -## -## Clean -## -clean: - $(RM) -r ./Debug/ - - diff --git a/TestFramework.project b/TestFramework.project index b21de56..879283e 100644 --- a/TestFramework.project +++ b/TestFramework.project @@ -3,12 +3,22 @@ - + + + + - + + + + + + + + @@ -48,6 +58,10 @@ + + + + @@ -59,6 +73,10 @@ + + + + @@ -71,8 +89,7 @@ - - + @@ -573,6 +590,8 @@ mkdir -p coverage && cd coverage find .. -name '*.gcda' | xargs gcov -abcfmu find . -type f -not -name 'Test*.gcov' | xargs rm + cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -Wno-dev +cppcheck --project=compile_commands.json --enable=all --suppress=missingIncludeSystem --suppress=unusedFunction --std=c++11 --inline-suppr cpack --config CPackConfig.cmake cpack --config CPackSourceConfig.cmake cpack -G DEB diff --git a/TestFramework.txt b/TestFramework.txt deleted file mode 100644 index 4304079..0000000 --- a/TestFramework.txt +++ /dev/null @@ -1 +0,0 @@ -./Debug/Test.cpp.o ./Debug/main.cpp.o diff --git a/UNLICENSE b/UNLICENSE deleted file mode 100644 index 68a49da..0000000 --- a/UNLICENSE +++ /dev/null @@ -1,24 +0,0 @@ -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or -distribute this software, either in source code form or as a compiled -binary, for any purpose, commercial or non-commercial, and by any -means. - -In jurisdictions that recognize copyright laws, the author or authors -of this software dedicate any and all copyright interest in the -software to the public domain. We make this dedication for the benefit -of the public at large and to the detriment of our heirs and -successors. We intend this dedication to be an overt act of -relinquishment in perpetuity of all present and future rights to this -software under copyright law. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -For more information, please refer to diff --git a/cmake/Analyzing.cmake b/cmake/Analyzing.cmake index 2da8883..6ec0c22 100644 --- a/cmake/Analyzing.cmake +++ b/cmake/Analyzing.cmake @@ -1,5 +1,10 @@ if (TESTCPP_ANALYSIS_ENABLED) - find_program (CLANG_TIDY_COMMAND NAMES clang-tidy) + find_program ( + CLANG_TIDY_COMMAND + NAMES + clang-tidy + NO_CACHE + ) if (NOT CLANG_TIDY_COMMAND) message (WARNING "clang-tidy is not found!") @@ -8,8 +13,9 @@ if (TESTCPP_ANALYSIS_ENABLED) else() set ( CLANG_TIDY_COMMAND -"${CLANG_TIDY_COMMAND};--header-filter=.*;--format-style=file;\ --p=${CMAKE_BINARY_DIR}" +"${CLANG_TIDY_COMMAND};--config-file=${CMAKE_SOURCE_DIR}/.clang-tidy;-p=${CMAKE_BINARY_DIR}" + CACHE STRING "Clang Tidy command" + FORCE ) set_target_properties ( diff --git a/cmake/BuildTypeHandling.cmake b/cmake/BuildTypeHandling.cmake index eba34cd..de82312 100644 --- a/cmake/BuildTypeHandling.cmake +++ b/cmake/BuildTypeHandling.cmake @@ -1,275 +1,38 @@ -list ( - APPEND - MSVC_RELEASE_BUILD_OPTS - /Wall # Enable all warnings and treat them as errors - /WX # Treat linker warnings as errors also - /O2 # Do optimizations for the Release build - /wd4668 # There are undefined preprocessor macros in the Windows - # SDK in internal headers. This is a workaround until - # these issues are fixed by Microsoft. - /wd4710 # It's ok if functions are not inlined - /wd4711 # It's also ok if functions are inlined - /wd4820 # Struct/class data member padding is ok, we're concerned - # about speed, not space, and the natural alignment - # is generally fastest. Let the compiler add the padding. - /wd4100 # Unused parameters occur in the Release build in debugLog - /wd5045 # Warns about code that can cause the Spectre - # vulnerability to become exploitable, does not - # go away when /Qspectre is specified. Applied this - # warning suppression since /Qspectre is specified. - /Qspectre # Instructs the compiler to apply Spectre - # vulnerability mitigations. -) -list ( - APPEND - MSVC_DEBUG_BUILD_OPTS - /Wall /WX /Od - /wd4820 /wd4668 /wd5045 - /Qspectre -) +list (APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/build") -if (${TESTCPP_STACKTRACE_ENABLED}) - list ( - APPEND - MSVC_RELEASE_BUILD_OPTS - /wd5039 # There's a function or two in the Windows SDK that are - # passed to extern 'C' APIs that are not marked as - # noexcept which causes this warning, which is treated - # as an error. There's nothing we can do about this so - # this is added as a workaround. - /wd4625 # In the Boost.StackTrace code, there are copy - # constructors that are implicitly deleted. Since this - # is in an external library, suppress the warning to - # work around this to ensure clean compilation. - /wd4626 # In the Boost.StackTrace code, there are copy - # assignment operators that are implicitly deleted. - # Since this is in an external library, suppress the - # warning to work around this to ensure clean - # compilation. - /wd5026 # In the Boost.StackTrace code, there are move - # constructors that are implicitly deleted. Since this - # is in an external library, suppress the warning to - # work around this to ensure clean compilation. - /wd5027 # In the Boost.StackTrace code, there are move - # assignment operators that are implicitly deleted. - # Since this is in an external library, suppress the - # warning to work around this to ensure clean - # compilation. - ) - list ( - APPEND - MSVC_DEBUG_BUILD_OPTS - /wd5039 /wd4625 /wd4626 /wd5026 /wd5027 - ) +if (NOT CMAKE_BUILD_TYPE) + set (CMAKE_BUILD_TYPE ${CMAKE_CONFIGURATION_TYPES}) endif () -if (BUILD_TESTING) - list ( - APPEND - MSVC_RELEASE_BUILD_OPTS - /wd4514 # It's ok if the compiler removes unreferenced inline - # functions. - ) - list ( - APPEND - MSVC_DEBUG_BUILD_OPTS - /wd4514 - ) -endif () - -list ( - APPEND - GCC_CLANG_RELEASE_BUILD_OPTS - -O3 # Optimize the Release build - -Wall # Enable most warnings - -Wextra # Enable even more warnings - -Wpedantic # Enable most of the rest of the warnings - -Werror # Treat all warnings as errors - -Wno-unused-parameter # Unused parameters occur in the Release - # build in debugLog -) -list ( - APPEND - GCC_CLANG_DEBUG_BUILD_OPTS - -g # Enable all debugging information - -Og # Ensure the compiler doesn't use optimizations that would harm - # debuggability of the resulting code - -Wall -Wextra -Wpedantic -Werror -) -list ( - APPEND - COVERAGE_BUILD_OPTS - -g -Og -Wall -Wextra -Wpedantic -Werror - -fprofile-arcs # Enable profile points that help with code - # coverage - -ftest-coverage # Enable core code coverage compilation -) - if (${CMAKE_BUILD_TYPE} STREQUAL "Release") message (STATUS "Release build compilation options enabled") - if (MSVC) - target_compile_options ( - ${PROJECT_NAME} - PUBLIC - ${MSVC_RELEASE_BUILD_OPTS} - ) - - if (${TESTCPP_DEMO_ENABLED}) - target_compile_options ( - ${PROJECT_NAME}_run - PUBLIC - ${MSVC_RELEASE_BUILD_OPTS} - ) - endif () - - if (BUILD_TESTING) - target_compile_options ( - ${PROJECT_NAME}_TestCase_test - PUBLIC - ${MSVC_RELEASE_BUILD_OPTS} - ) + if (MSVC AND NOT ("${CMAKE_VS_PLATFORM_TOOLSET}" STREQUAL "ClangCL")) + message (STATUS "MSVC Release build compilation options enabled") - target_compile_options ( - ${PROJECT_NAME}_TestSuite_test - PUBLIC - ${MSVC_RELEASE_BUILD_OPTS} - ) - endif () + include (MSVCRelease) else () - target_compile_options ( - ${PROJECT_NAME} - PUBLIC - ${GCC_CLANG_RELEASE_BUILD_OPTS} - ) + message (STATUS "GCC/Clang Release build compilation options enabled") - if (${TESTCPP_DEMO_ENABLED}) - target_compile_options ( - ${PROJECT_NAME}_run - PUBLIC - ${GCC_CLANG_RELEASE_BUILD_OPTS} - ) - endif () - - if (BUILD_TESTING) - target_compile_options ( - ${PROJECT_NAME}_TestCase_test - PUBLIC - ${GCC_CLANG_RELEASE_BUILD_OPTS} - ) - - target_compile_options ( - ${PROJECT_NAME}_TestSuite_test - PUBLIC - ${GCC_CLANG_RELEASE_BUILD_OPTS} - ) - endif () + include (GCCClangRelease) endif () else () message (STATUS "Debug build compilation options enabled") - target_compile_definitions ( - ${PROJECT_NAME} - PUBLIC - DEBUG_LOG - ) + include (DebugCompileDefs) + + if (MSVC AND NOT ("${CMAKE_VS_PLATFORM_TOOLSET}" STREQUAL "ClangCL")) + message (STATUS "MSVC Debug build compilation options enabled") - if (${TESTCPP_DEMO_ENABLED}) - target_compile_definitions ( - ${PROJECT_NAME}_run - PUBLIC - DEBUG_LOG - ) - endif () - - if (BUILD_TESTING) - target_compile_definitions ( - ${PROJECT_NAME}_TestCase_test - PUBLIC - DEBUG_LOG - ) - target_compile_definitions ( - ${PROJECT_NAME}_TestSuite_test - PUBLIC - DEBUG_LOG - ) - endif () - - if (MSVC) - target_compile_options ( - ${PROJECT_NAME} - PUBLIC - ${MSVC_DEBUG_BUILD_OPTS} - ) - - if (${TESTCPP_DEMO_ENABLED}) - target_compile_options ( - ${PROJECT_NAME}_run - PUBLIC - ${MSVC_DEBUG_BUILD_OPTS} - ) - endif () - - if (BUILD_TESTING) - target_compile_options ( - ${PROJECT_NAME}_TestCase_test - PUBLIC - ${MSVC_DEBUG_BUILD_OPTS} - ) - - target_compile_options ( - ${PROJECT_NAME}_TestSuite_test - PUBLIC - ${MSVC_DEBUG_BUILD_OPTS} - ) - endif () + include (MSVCDebug) else () - if (${TESTCPP_DEMO_ENABLED}) - target_compile_options ( - ${PROJECT_NAME}_run - PUBLIC - ${GCC_CLANG_DEBUG_BUILD_OPTS} - ) - endif () - - if (BUILD_TESTING AND ${TESTCPP_COVERAGE_ENABLED}) - message (STATUS "Coverage compilation options enabled") + message (STATUS "GCC/Clang Debug build compilation options enabled") - target_compile_options ( - ${PROJECT_NAME} - PUBLIC - ${COVERAGE_BUILD_OPTS} - ) - - target_compile_options ( - ${PROJECT_NAME}_TestCase_test - PUBLIC - ${COVERAGE_BUILD_OPTS} - ) - - target_compile_options ( - ${PROJECT_NAME}_TestSuite_test - PUBLIC - ${COVERAGE_BUILD_OPTS} - ) - - else () - target_compile_options ( - ${PROJECT_NAME} - PUBLIC - ${GCC_CLANG_DEBUG_BUILD_OPTS} - ) - endif () + include (GCCClangDebug) endif () endif () -if (${TESTCPP_STACKTRACE_ENABLED}) - target_compile_definitions ( - ${PROJECT_NAME} - PUBLIC - TESTCPP_STACKTRACE_ENABLED - ) -endif () +include (CompileDefs) diff --git a/cmake/Includes.cmake b/cmake/Includes.cmake index 200a7ae..a34ba82 100644 --- a/cmake/Includes.cmake +++ b/cmake/Includes.cmake @@ -32,4 +32,10 @@ if (BUILD_TESTING) test/include include ) + + target_include_directories ( + ${PROJECT_NAME}_Assertions_test PRIVATE + test/include + include + ) endif () diff --git a/cmake/Installing.cmake b/cmake/Installing.cmake index 39ac2b2..7606865 100644 --- a/cmake/Installing.cmake +++ b/cmake/Installing.cmake @@ -1,8 +1,31 @@ +list ( + APPEND + TESTCPP_PUBLIC_HEADERS + include/TestCPP.h +) + +list ( + APPEND + TESTCPP_PRIVATE_HEADERS + include/internal/TestCPPAssertions.h + include/internal/TestCPPCommon.h + include/internal/TestCPPExceptions.h + include/internal/TestCPPTestCase.h + include/internal/TestCPPTestSuite.h + include/internal/TestCPPUtil.h +) + set_target_properties ( ${PROJECT_NAME} PROPERTIES PUBLIC_HEADER - include/TestCPP.h + "${TESTCPP_PUBLIC_HEADERS}" +) +set_target_properties ( + ${PROJECT_NAME} + PROPERTIES + PRIVATE_HEADER + "${TESTCPP_PRIVATE_HEADERS}" ) include (GNUInstallDirs) install ( @@ -10,6 +33,8 @@ install ( EXPORT ${PROJECT_NAME}Targets PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} + PRIVATE_HEADER + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/internal ) export ( EXPORT ${PROJECT_NAME}Targets diff --git a/cmake/Linking.cmake b/cmake/Linking.cmake index f6c6cb0..34d743c 100644 --- a/cmake/Linking.cmake +++ b/cmake/Linking.cmake @@ -1,115 +1,17 @@ -if (${TESTCPP_DEMO_ENABLED}) - if (${TESTCPP_STACKTRACE_ENABLED} AND MSVC) - target_link_libraries ( - ${PROJECT_NAME}_run - ${PROJECT_NAME} - ole32 - dbgeng - ) - - elseif (${TESTCPP_STACKTRACE_ENABLED}) - target_link_libraries ( - ${PROJECT_NAME}_run - ${PROJECT_NAME} - dl - ) +list (APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/link") - else () - target_link_libraries ( - ${PROJECT_NAME}_run - ${PROJECT_NAME} - ) - endif () +if (${TESTCPP_DEMO_ENABLED}) + include (Demo) endif () if (BUILD_TESTING) if (${TESTCPP_COVERAGE_ENABLED}) message (STATUS "Linking in gcov") - target_link_libraries ( - ${PROJECT_NAME} - gcov - ) - - if (${TESTCPP_STACKTRACE_ENABLED} AND MSVC) - target_link_libraries ( - ${PROJECT_NAME}_TestCase_test - ${PROJECT_NAME} - gcov - ole32 - dbgeng - ) - target_link_libraries ( - ${PROJECT_NAME}_TestSuite_test - ${PROJECT_NAME} - gcov - ole32 - dbgeng - ) - - elseif (${TESTCPP_STACKTRACE_ENABLED}) - target_link_libraries ( - ${PROJECT_NAME}_TestCase_test - ${PROJECT_NAME} - gcov - dl - ) - target_link_libraries ( - ${PROJECT_NAME}_TestSuite_test - ${PROJECT_NAME} - gcov - dl - ) - - else () - target_link_libraries ( - ${PROJECT_NAME}_TestCase_test - ${PROJECT_NAME} - gcov - ) - target_link_libraries ( - ${PROJECT_NAME}_TestSuite_test - ${PROJECT_NAME} - gcov - ) - endif () + include (TestCPPWithCoverage) + include (TestsWithCoverage) else () - if (${TESTCPP_STACKTRACE_ENABLED} AND MSVC) - target_link_libraries ( - ${PROJECT_NAME}_TestCase_test - ${PROJECT_NAME} - ole32 - dbgeng - ) - target_link_libraries ( - ${PROJECT_NAME}_TestSuite_test - ${PROJECT_NAME} - ole32 - dbgeng - ) - - elseif (${TESTCPP_STACKTRACE_ENABLED}) - target_link_libraries ( - ${PROJECT_NAME}_TestCase_test - ${PROJECT_NAME} - dl - ) - target_link_libraries ( - ${PROJECT_NAME}_TestSuite_test - ${PROJECT_NAME} - dl - ) - - else () - target_link_libraries ( - ${PROJECT_NAME}_TestCase_test - ${PROJECT_NAME} - ) - target_link_libraries ( - ${PROJECT_NAME}_TestSuite_test - ${PROJECT_NAME} - ) - endif () + include (Tests) endif () endif () diff --git a/cmake/Packing.cmake b/cmake/Packing.cmake index 8de2ae9..32718af 100644 --- a/cmake/Packing.cmake +++ b/cmake/Packing.cmake @@ -24,7 +24,7 @@ set (CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) set (CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) set (CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) -set (CPACK_PACKAGE_CONTACT "el.jonny@gmail.com") +set (CPACK_PACKAGE_CONTACT "jonathan.hyry@outlook.com") set (CPACK_DEBIAN_PACKAGE_MAINTAINER "Jonathan Hyry <${CPACK_PACKAGE_CONTACT}>") set (CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/eljonny/TestCPP") @@ -45,6 +45,13 @@ set (CPACK_DEBIAN_FILE_NAME DEB-DEFAULT) set (CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE) set (CPACK_DEB_COMPONENT_INSTALL YES) +set (CPACK_WIX_UPGRADE_GUID "EDB6D016-633E-4A35-AD5A-7734F6F3E6A5") +set (CPACK_WIX_LICENSE_RTF "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.rtf") +set (CPACK_WIX_PRODUCT_ICON "${CMAKE_CURRENT_SOURCE_DIR}/res/img/testcpp.ico") +set (CPACK_WIX_UI_BANNER "${CMAKE_CURRENT_SOURCE_DIR}/res/img/testcpp-inst-banner.bmp") +set (CPACK_WIX_UI_DIALOG "${CMAKE_CURRENT_SOURCE_DIR}/res/img/testcpp-inst-dialog.bmp") +set (CPACK_WIX_CMAKE_PACKAGE_REGISTRY "${PROJECT_NAME}") + include (CPack) include (CMakePackageConfigHelpers) diff --git a/cmake/Targets.cmake b/cmake/Targets.cmake index 49fa350..cba9f4d 100644 --- a/cmake/Targets.cmake +++ b/cmake/Targets.cmake @@ -1,8 +1,12 @@ add_library ( ${PROJECT_NAME} + src/TestCPPAssertions.cpp + src/TestCPPExceptions.cpp + src/TestCPPTestCase.cpp + src/TestCPPTestSuite.cpp src/TestCPPUtil.cpp - src/TestCPP.cpp ) + add_library ( ${PROJECT_GROUP_NAME}::${PROJECT_NAME} ALIAS @@ -30,4 +34,10 @@ if (BUILD_TESTING) test/src/TestSuite/TestSuiteTests.cpp test/src/TestCPPTestSuiteMain.cpp ) + + add_executable ( + ${PROJECT_NAME}_Assertions_test + test/src/Assertions/AssertionsTests.cpp + test/src/TestCPPAssertionsMain.cpp + ) endif () diff --git a/cmake/Testing.cmake b/cmake/Testing.cmake index e454ef8..b7c4c1b 100644 --- a/cmake/Testing.cmake +++ b/cmake/Testing.cmake @@ -8,4 +8,9 @@ if (BUILD_TESTING) NAME ${PROJECT_NAME}TestSuiteTests COMMAND ${PROJECT_NAME}_TestSuite_test ) + + add_test ( + NAME ${PROJECT_NAME}AssertionsTests + COMMAND ${PROJECT_NAME}_Assertions_test + ) endif () diff --git a/cmake/VarChecks.cmake b/cmake/VarChecks.cmake index 9d448db..c19c896 100644 --- a/cmake/VarChecks.cmake +++ b/cmake/VarChecks.cmake @@ -4,7 +4,7 @@ if (NOT DEFINED TESTCPP_COVERAGE_ENABLED) else () message (STATUS - "Defd TESTCPP_COVERAGE_ENABLED ${TESTCPP_COVERAGE_ENABLED}") + "Defd TESTCPP_COVERAGE_ENABLED ${TESTCPP_COVERAGE_ENABLED}") endif () if (NOT DEFINED TESTCPP_DEMO_ENABLED) @@ -12,7 +12,8 @@ if (NOT DEFINED TESTCPP_DEMO_ENABLED) set (TESTCPP_DEMO_ENABLED 0) else () - message (STATUS "Defd TESTCPP_DEMO_ENABLED ${TESTCPP_DEMO_ENABLED}") + message (STATUS + "Defd TESTCPP_DEMO_ENABLED ${TESTCPP_DEMO_ENABLED}") endif () if (NOT DEFINED TESTCPP_TEST_ENABLED) @@ -20,7 +21,8 @@ if (NOT DEFINED TESTCPP_TEST_ENABLED) set (TESTCPP_TEST_ENABLED 0) else () - message (STATUS "Defd TESTCPP_TEST_ENABLED ${TESTCPP_TEST_ENABLED}") + message (STATUS + "Defd TESTCPP_TEST_ENABLED ${TESTCPP_TEST_ENABLED}") endif () if (NOT DEFINED TESTCPP_ANALYSIS_ENABLED) @@ -29,7 +31,7 @@ if (NOT DEFINED TESTCPP_ANALYSIS_ENABLED) else () message (STATUS - "Defd TESTCPP_ANALYSIS_ENABLED ${TESTCPP_ANALYSIS_ENABLED}") + "Defd TESTCPP_ANALYSIS_ENABLED ${TESTCPP_ANALYSIS_ENABLED}") endif () if (NOT DEFINED TESTCPP_STACKTRACE_ENABLED) @@ -38,7 +40,7 @@ if (NOT DEFINED TESTCPP_STACKTRACE_ENABLED) else () message (STATUS - "Defd TESTCPP_STACKTRACE_ENABLED ${TESTCPP_STACKTRACE_ENABLED}") + "Defd TESTCPP_STACKTRACE_ENABLED ${TESTCPP_STACKTRACE_ENABLED}") endif () if (${TESTCPP_TEST_ENABLED}) diff --git a/cmake/build/CompileDefs.cmake b/cmake/build/CompileDefs.cmake new file mode 100644 index 0000000..acf4d94 --- /dev/null +++ b/cmake/build/CompileDefs.cmake @@ -0,0 +1,7 @@ +if (${TESTCPP_STACKTRACE_ENABLED}) + target_compile_definitions ( + ${PROJECT_NAME} + PUBLIC + TESTCPP_STACKTRACE_ENABLED + ) +endif () diff --git a/cmake/build/DebugCompileDefs.cmake b/cmake/build/DebugCompileDefs.cmake new file mode 100644 index 0000000..024799d --- /dev/null +++ b/cmake/build/DebugCompileDefs.cmake @@ -0,0 +1,31 @@ +target_compile_definitions ( + ${PROJECT_NAME} + PUBLIC + DEBUG_LOG +) + +if (${TESTCPP_DEMO_ENABLED}) + target_compile_definitions ( + ${PROJECT_NAME}_run + PUBLIC + DEBUG_LOG + ) +endif () + +if (BUILD_TESTING) + target_compile_definitions ( + ${PROJECT_NAME}_TestCase_test + PUBLIC + DEBUG_LOG + ) + target_compile_definitions ( + ${PROJECT_NAME}_TestSuite_test + PUBLIC + DEBUG_LOG + ) + target_compile_definitions ( + ${PROJECT_NAME}_Assertions_test + PUBLIC + DEBUG_LOG + ) +endif () diff --git a/cmake/build/GCCClangDebug.cmake b/cmake/build/GCCClangDebug.cmake new file mode 100644 index 0000000..27ce4c3 --- /dev/null +++ b/cmake/build/GCCClangDebug.cmake @@ -0,0 +1,107 @@ +list ( + APPEND + GCC_CLANG_DEBUG_BUILD_OPTS + -g # Enable all debugging information. + -Og # Ensure the compiler doesn't use + # optimizations that would harm + # debuggability of the resulting code. + -Wall # Enable most warnings. + -Wextra # Enable even more warnings. + -Wpedantic # Enable most of the rest of the + # warnings. + -Werror # Treat all warnings as errors. + -Wno-c++98-compat # We're compiling for C++11, so it's not + # necessary to maintain compatibility + # with C++98. + -Wno-c++98-compat-pedantic # We're compiling for C++11, so, again, + # it's not necessary to maintain + # compatibility with C++98. + -Wno-covered-switch-default # -Wswitch-default is more important. + -Wno-unused-lambda-capture # Avoid MSVC error C3493 - There is + # implementation divergence here and + # since we're not using >=C++14 there + # is no workaround other than to ignore + # this warning (the MSVC issue is an + # error). + # A workaround for >=C++14 is to use an + # explicit capture - if ever I change + # the library to use >=C++14 I can + # remove this and use an explicit + # capture. +) + +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR + "${CMAKE_VS_PLATFORM_TOOLSET}" STREQUAL "ClangCL") + + # GCC doesn't support -Wno-global-constructors yet, so only add it + # if we are building with Clang, either with MSVC as a frontend + # or straight-up LLVM. + # Ref https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71482 + + list ( + APPEND + GCC_CLANG_DEBUG_BUILD_OPTS + -Wno-global-constructors # The performance degradation pointed + # out by this warning is non-trivial + # but not enough to work through any + # issues that might be caused by it; + # there were already constructors + # from built-in types that were being + # called (particularly unique_ptr), + # but this points out that the type I + # added (no_destroy) adds custom and + # additional start-up code that can + # change startup performance by + # making it slower. + # How much slower is a matter of + # micro-optimization for this library + # project in particular, but could + # significantly decrease startup + # performance if there were n-many of + # these objects being created. + # At this point there are only 6, so + # this should not cause a noticeable + # hit to startup performance. + ) +endif () + +if (${TESTCPP_DEMO_ENABLED}) + target_compile_options ( + ${PROJECT_NAME}_run + PUBLIC + ${GCC_CLANG_DEBUG_BUILD_OPTS} + ) +endif () + +if (BUILD_TESTING AND ${TESTCPP_COVERAGE_ENABLED}) + message (STATUS "Coverage compilation options enabled") + + include (GCCCoverage) + +else () + target_compile_options ( + ${PROJECT_NAME} + PUBLIC + ${GCC_CLANG_DEBUG_BUILD_OPTS} + ) + + if (BUILD_TESTING) + target_compile_options ( + ${PROJECT_NAME}_TestCase_test + PUBLIC + ${GCC_CLANG_DEBUG_BUILD_OPTS} + ) + + target_compile_options ( + ${PROJECT_NAME}_TestSuite_test + PUBLIC + ${GCC_CLANG_DEBUG_BUILD_OPTS} + ) + + target_compile_options ( + ${PROJECT_NAME}_Assertions_test + PUBLIC + ${GCC_CLANG_DEBUG_BUILD_OPTS} + ) + endif () +endif () diff --git a/cmake/build/GCCClangRelease.cmake b/cmake/build/GCCClangRelease.cmake new file mode 100644 index 0000000..476fce0 --- /dev/null +++ b/cmake/build/GCCClangRelease.cmake @@ -0,0 +1,99 @@ +list ( + APPEND + GCC_CLANG_RELEASE_BUILD_OPTS + -O3 # Optimize the Release build. + -Wall # Enable most warnings. + -Wextra # Enable even more warnings. + -Wpedantic # Enable most of the rest of the + # warnings. + -Werror # Treat all warnings as errors. + -Wno-c++98-compat # We're compiling for C++11, so it's not + # necessary to maintain compatibility + # with C++98. + -Wno-c++98-compat-pedantic # We're compiling for C++11, so, again, + # it's not necessary to maintain + # compatibility with C++98. + -Wno-covered-switch-default # -Wswitch-default is more important. + -Wno-unused-parameter # Unused parameters occur in the Release + # build in debugLog. + -Wno-unused-lambda-capture # Avoid MSVC error C3493 - There is + # implementation divergence here and + # since we're not using >=C++14 there + # is no workaround other than to ignore + # this warning (the MSVC issue is an + # error). + # A workaround for >=C++14 is to use an + # explicit capture - if ever I change + # the library to use >=C++14 I can + # remove this and use an explicit + # capture. +) + +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR + "${CMAKE_VS_PLATFORM_TOOLSET}" STREQUAL "ClangCL") + + # GCC doesn't support -Wno-global-constructors yet, so only add it + # if we are building with Clang, either with MSVC as a frontend + # or straight-up LLVM. + # Ref https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71482 + + list ( + APPEND + GCC_CLANG_RELEASE_BUILD_OPTS + -Wno-global-constructors # The performance degradation pointed + # out by this warning is non-trivial + # but not enough to work through any + # issues that might be caused by it; + # there were already constructors + # from built-in types that were being + # called (particularly unique_ptr), + # but this points out that the type I + # added (no_destroy) adds custom and + # additional start-up code that can + # change startup performance by + # making it slower. + # How much slower is a matter of + # micro-optimization for this library + # project in particular, but could + # significantly decrease startup + # performance if there were n-many of + # these objects being created. + # At this point there are only 6, so + # this should not cause a noticeable + # hit to startup performance. + ) +endif () + +target_compile_options ( + ${PROJECT_NAME} + PUBLIC + ${GCC_CLANG_RELEASE_BUILD_OPTS} +) + +if (${TESTCPP_DEMO_ENABLED}) + target_compile_options ( + ${PROJECT_NAME}_run + PUBLIC + ${GCC_CLANG_RELEASE_BUILD_OPTS} + ) +endif () + +if (BUILD_TESTING) + target_compile_options ( + ${PROJECT_NAME}_TestCase_test + PUBLIC + ${GCC_CLANG_RELEASE_BUILD_OPTS} + ) + + target_compile_options ( + ${PROJECT_NAME}_TestSuite_test + PUBLIC + ${GCC_CLANG_RELEASE_BUILD_OPTS} + ) + + target_compile_options ( + ${PROJECT_NAME}_Assertions_test + PUBLIC + ${GCC_CLANG_RELEASE_BUILD_OPTS} + ) +endif () diff --git a/cmake/build/GCCCoverage.cmake b/cmake/build/GCCCoverage.cmake new file mode 100644 index 0000000..6131b5e --- /dev/null +++ b/cmake/build/GCCCoverage.cmake @@ -0,0 +1,51 @@ +list ( + APPEND + COVERAGE_BUILD_OPTS + -g # Enable all debugging information. + -Og # Ensure the compiler doesn't use + # optimizations that would harm + # debuggability of the resulting code. + -Wall # Enable most warnings. + -Wextra # Enable even more warnings. + -Wpedantic # Enable most of the rest of the + # warnings. + -Werror # Treat all warnings as errors. + -Wno-c++98-compat # We're compiling for C++11, so it's not + # necessary to maintain compatibility + # with C++98. + -Wno-c++98-compat-pedantic # We're compiling for C++11, so, again, + # it's not necessary to maintain + # compatibility with C++98. + -Wno-covered-switch-default # -Wswitch-default is more important. + -fprofile-arcs # Enable profile points that help with + # code coverage. + -ftest-coverage # Enable core code coverage compilation. +) + +# Add -Wno-global-constructors to the GCC build options after it exists +# like in GCCClangRelease.cmake and GCCClangDebug.cmake. +# Ref https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71482 + +target_compile_options ( + ${PROJECT_NAME} + PUBLIC + ${COVERAGE_BUILD_OPTS} +) + +target_compile_options ( + ${PROJECT_NAME}_TestCase_test + PUBLIC + ${COVERAGE_BUILD_OPTS} +) + +target_compile_options ( + ${PROJECT_NAME}_TestSuite_test + PUBLIC + ${COVERAGE_BUILD_OPTS} +) + +target_compile_options ( + ${PROJECT_NAME}_Assertions_test + PUBLIC + ${COVERAGE_BUILD_OPTS} +) diff --git a/cmake/build/MSVCDebug.cmake b/cmake/build/MSVCDebug.cmake new file mode 100644 index 0000000..be646db --- /dev/null +++ b/cmake/build/MSVCDebug.cmake @@ -0,0 +1,95 @@ +list ( + APPEND + MSVC_DEBUG_BUILD_OPTS + /nologo # Suppress the compiler version and + # copyright information. + /Zi # Generate complete debugging information. + /FS # Force synchronous PDB writes. + /EHscr # Enable C++ exceptions, including RTCs for + # noexcept verification at runtime. + /RTC1 # Enable Runtime Checks. + /Wall # Enable all warnings and treat them as + # errors. + /WX # Treat linker warnings as errors also. + /Od # Perform no optimization; the resulting + # code is optimized for debugging and + # debuggability. + /wd4820 # Struct/class data member padding is ok, + # we're concerned about speed, not space, + # and the natural alignment is generally + # fastest. + # Let the compiler add the padding. + /wd5045 # Warns about code that can cause the + # Spectre vulnerability to become + # exploitable, does not go away when + # /Qspectre is specified. + # Applied this warning suppression since + # /Qspectre is specified. + /Qspectre # Instructs the compiler to apply Spectre + # vulnerability mitigations. + /external:anglebrackets # Treat angle-bracket includes as system + # includes. + /external:W0 # Suppress all warnings from external + # headers. +) + +if (${TESTCPP_STACKTRACE_ENABLED}) + list ( + APPEND + MSVC_DEBUG_BUILD_OPTS + "/external:I3rdparty/include" # Include the 3rdparty directory + # as an external include + # directory. + ) +endif () + +if ("${CMAKE_CXX_FLAGS}" MATCHES "/analyze:ruleset") + list ( + APPEND + MSVC_DEBUG_BUILD_OPTS + /analyze:rulesetdirectory "${VS_INSTALLATION_PATH}/Team Tools/Static Analysis Tools/Rule Sets" + ) +endif () + +if (BUILD_TESTING) + list ( + APPEND + MSVC_DEBUG_BUILD_OPTS + /wd4514 # It's ok if the compiler removes unreferenced inline + # functions. + ) +endif () + +target_compile_options ( + ${PROJECT_NAME} + PUBLIC + ${MSVC_DEBUG_BUILD_OPTS} +) + +if (${TESTCPP_DEMO_ENABLED}) + target_compile_options ( + ${PROJECT_NAME}_run + PUBLIC + ${MSVC_DEBUG_BUILD_OPTS} + ) +endif () + +if (BUILD_TESTING) + target_compile_options ( + ${PROJECT_NAME}_TestCase_test + PUBLIC + ${MSVC_DEBUG_BUILD_OPTS} + ) + + target_compile_options ( + ${PROJECT_NAME}_TestSuite_test + PUBLIC + ${MSVC_DEBUG_BUILD_OPTS} + ) + + target_compile_options ( + ${PROJECT_NAME}_Assertions_test + PUBLIC + ${MSVC_DEBUG_BUILD_OPTS} + ) +endif () diff --git a/cmake/build/MSVCRelease.cmake b/cmake/build/MSVCRelease.cmake new file mode 100644 index 0000000..6dc1772 --- /dev/null +++ b/cmake/build/MSVCRelease.cmake @@ -0,0 +1,95 @@ +list ( + APPEND + MSVC_RELEASE_BUILD_OPTS + /nologo # Suppress the compiler version and + # copyright information. + /EHsc # Enable C++ exceptions. + /Wall # Enable all warnings and treat them as + # errors. + /WX # Treat linker warnings as errors also. + /O2 # Do optimizations for the Release build. + /wd4710 # It's ok if functions are not inlined. + /wd4711 # It's also ok if functions are inlined. + /wd4820 # Struct/class data member padding is ok, + # we're concerned about speed, not space, + # and the natural alignment is generally + # fastest. + # Let the compiler add the padding. + /wd4100 # Unused parameters occur in the Release + # build in debugLog. + /wd5045 # Warns about code that can cause the + # Spectre vulnerability to become + # exploitable, does not go away when + # /Qspectre is specified. + # Applied this warning suppression since + # /Qspectre is specified. + /Qspectre # Instructs the compiler to apply Spectre + # vulnerability mitigations. + /external:anglebrackets # Treat angle-bracket includes as system + # includes. + /external:W0 # Suppress all warnings from external + # headers. +) + +if (${TESTCPP_STACKTRACE_ENABLED}) + list ( + APPEND + MSVC_RELEASE_BUILD_OPTS + "/external:I3rdparty/include" # Include the 3rdparty directory + # as an external include + # directory. + ) +endif () + +if (BUILD_TESTING) + list ( + APPEND + MSVC_RELEASE_BUILD_OPTS + /wd4514 # It's ok if the compiler removes unreferenced inline + # functions. + ) +endif () + +target_compile_options ( + ${PROJECT_NAME} + PUBLIC + ${MSVC_RELEASE_BUILD_OPTS} +) + +if (${TESTCPP_DEMO_ENABLED}) + target_compile_options ( + ${PROJECT_NAME}_run + PUBLIC + ${MSVC_RELEASE_BUILD_OPTS} + ) +endif () + +if (BUILD_TESTING) + target_compile_options ( + ${PROJECT_NAME}_TestCase_test + PUBLIC + ${MSVC_RELEASE_BUILD_OPTS} + ) + + target_compile_options ( + ${PROJECT_NAME}_TestSuite_test + PUBLIC + ${MSVC_RELEASE_BUILD_OPTS} + ) + + target_compile_options ( + ${PROJECT_NAME}_Assertions_test + PUBLIC + ${MSVC_RELEASE_BUILD_OPTS} + ) +endif () + +# This section removes the /RTC flags from the CMAKE_CXX_FLAGS_* +# variables, because CMake is automatically adding them to the +# generated VS MSBuild builds without me specifying them, +# specifically because they should not be in a Release build. +foreach (flag_var + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + STRING (REGEX REPLACE "[/\-]RTC[1csu]*" "" ${flag_var} "${${flag_var}}") +endforeach (flag_var) diff --git a/cmake/link/Demo.cmake b/cmake/link/Demo.cmake new file mode 100644 index 0000000..0588e4b --- /dev/null +++ b/cmake/link/Demo.cmake @@ -0,0 +1,21 @@ +if (${TESTCPP_STACKTRACE_ENABLED} AND MSVC) + target_link_libraries ( + ${PROJECT_NAME}_run + ${PROJECT_NAME} + ole32 + dbgeng + ) + +elseif (${TESTCPP_STACKTRACE_ENABLED}) + target_link_libraries ( + ${PROJECT_NAME}_run + ${PROJECT_NAME} + dl + ) + +else () + target_link_libraries ( + ${PROJECT_NAME}_run + ${PROJECT_NAME} + ) +endif () diff --git a/cmake/link/TestCPPWithCoverage.cmake b/cmake/link/TestCPPWithCoverage.cmake new file mode 100644 index 0000000..f2f2058 --- /dev/null +++ b/cmake/link/TestCPPWithCoverage.cmake @@ -0,0 +1,4 @@ +target_link_libraries ( + ${PROJECT_NAME} + gcov +) diff --git a/cmake/link/Tests.cmake b/cmake/link/Tests.cmake new file mode 100644 index 0000000..f823620 --- /dev/null +++ b/cmake/link/Tests.cmake @@ -0,0 +1,51 @@ +if (${TESTCPP_STACKTRACE_ENABLED} AND MSVC) + target_link_libraries ( + ${PROJECT_NAME}_TestCase_test + ${PROJECT_NAME} + ole32 + dbgeng + ) + target_link_libraries ( + ${PROJECT_NAME}_TestSuite_test + ${PROJECT_NAME} + ole32 + dbgeng + ) + target_link_libraries ( + ${PROJECT_NAME}_Assertions_test + ${PROJECT_NAME} + ole32 + dbgeng + ) + +elseif (${TESTCPP_STACKTRACE_ENABLED}) + target_link_libraries ( + ${PROJECT_NAME}_TestCase_test + ${PROJECT_NAME} + dl + ) + target_link_libraries ( + ${PROJECT_NAME}_TestSuite_test + ${PROJECT_NAME} + dl + ) + target_link_libraries ( + ${PROJECT_NAME}_Assertions_test + ${PROJECT_NAME} + dl + ) + +else () + target_link_libraries ( + ${PROJECT_NAME}_TestCase_test + ${PROJECT_NAME} + ) + target_link_libraries ( + ${PROJECT_NAME}_TestSuite_test + ${PROJECT_NAME} + ) + target_link_libraries ( + ${PROJECT_NAME}_Assertions_test + ${PROJECT_NAME} + ) +endif () diff --git a/cmake/link/TestsWithCoverage.cmake b/cmake/link/TestsWithCoverage.cmake new file mode 100644 index 0000000..ae6e6d1 --- /dev/null +++ b/cmake/link/TestsWithCoverage.cmake @@ -0,0 +1,60 @@ +if (${TESTCPP_STACKTRACE_ENABLED} AND MSVC) + target_link_libraries ( + ${PROJECT_NAME}_TestCase_test + ${PROJECT_NAME} + gcov + ole32 + dbgeng + ) + target_link_libraries ( + ${PROJECT_NAME}_TestSuite_test + ${PROJECT_NAME} + gcov + ole32 + dbgeng + ) + target_link_libraries ( + ${PROJECT_NAME}_Assertions_test + ${PROJECT_NAME} + gcov + ole32 + dbgeng + ) + +elseif (${TESTCPP_STACKTRACE_ENABLED}) + target_link_libraries ( + ${PROJECT_NAME}_TestCase_test + ${PROJECT_NAME} + gcov + dl + ) + target_link_libraries ( + ${PROJECT_NAME}_TestSuite_test + ${PROJECT_NAME} + gcov + dl + ) + target_link_libraries ( + ${PROJECT_NAME}_Assertions_test + ${PROJECT_NAME} + gcov + dl + ) + +else () + target_link_libraries ( + ${PROJECT_NAME}_TestCase_test + ${PROJECT_NAME} + gcov + ) + target_link_libraries ( + ${PROJECT_NAME}_TestSuite_test + ${PROJECT_NAME} + gcov + ) + target_link_libraries ( + ${PROJECT_NAME}_Assertions_test + ${PROJECT_NAME} + gcov + ) +endif () diff --git a/cmake/toolchains/VSWhere.cmake b/cmake/toolchains/VSWhere.cmake new file mode 100644 index 0000000..c2ea310 --- /dev/null +++ b/cmake/toolchains/VSWhere.cmake @@ -0,0 +1,132 @@ +#---------------------------------------------------------------------------------------------------------------------- +# MIT License +# +# Copyright (c) 2021 Mark Schofield +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#---------------------------------------------------------------------------------------------------------------------- +include_guard() + +#[[==================================================================================================================== + toolchain_validate_vs_files + --------------------------- + + Note: Not supported for consumption outside of the toolchain files. + + Validates the the specified folder exists and contains the specified files. + + toolchain_validate_vs_files( + > + > + ...> + ) + + If the folder or files are missing, then a FATAL_ERROR is reported. +====================================================================================================================]]# +function(toolchain_validate_vs_files) + set(OPTIONS) + set(ONE_VALUE_KEYWORDS FOLDER DESCRIPTION) + set(MULTI_VALUE_KEYWORDS FILES) + + cmake_parse_arguments(PARSE_ARGV 0 VS "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}") + + if(NOT EXISTS ${VS_FOLDER}) + message(FATAL_ERROR "Folder not present - ${VS_FOLDER} - ensure that the ${VS_DESCRIPTION} are installed with Visual Studio.") + endif() + + foreach(FILE ${VS_FILES}) + if(NOT EXISTS "${VS_FOLDER}/${FILE}") + message(FATAL_ERROR "File not present - ${VS_FOLDER}/${FILE} - ensure that the ${VS_DESCRIPTION} are installed with Visual Studio.") + endif() + endforeach() +endfunction() + +#[[==================================================================================================================== + findVisualStudio + ---------------- + + Finds a Visual Studio instance, and sets CMake variables based on properties of the found instance. + + findVisualStudio( + [VERSION ] + [PRERELEASE ] + [PRODUCTS ] + [REQUIRES ...] + PROPERTIES + < > + ) +====================================================================================================================]]# +function(findVisualStudio) + set(OPTIONS) + set(ONE_VALUE_KEYWORDS VERSION PRERELEASE PRODUCTS) + set(MULTI_VALUE_KEYWORDS REQUIRES PROPERTIES) + + cmake_parse_arguments(PARSE_ARGV 0 FIND_VS "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}") + + find_program(VSWHERE_PATH + NAMES vswhere vswhere.exe + HINTS "$ENV{ProgramFiles\(x86\)}/Microsoft Visual Studio/Installer" + ) + + if(VSWHERE_PATH STREQUAL "VSWHERE_PATH-NOTFOUND") + message(FATAL_ERROR "'vswhere' isn't found.") + endif() + + set(VSWHERE_COMMAND ${VSWHERE_PATH} -latest) + + if(FIND_VS_PRERELEASE) + list(APPEND VSWHERE_COMMAND -prerelease) + endif() + + if(FIND_VS_PRODUCTS) + list(APPEND VSWHERE_COMMAND -products ${FIND_VS_PRODUCTS}) + endif() + + if(FIND_VS_REQUIRES) + list(APPEND VSWHERE_COMMAND -requires ${FIND_VS_REQUIRES}) + endif() + + if(FIND_VS_VERSION) + list(APPEND VSWHERE_COMMAND -version "${FIND_VS_VERSION}") + endif() + + message(VERBOSE "findVisualStudio: VSWHERE_COMMAND = ${VSWHERE_COMMAND}") + + execute_process( + COMMAND ${VSWHERE_COMMAND} + OUTPUT_VARIABLE VSWHERE_OUTPUT + ) + + message(VERBOSE "findVisualStudio: VSWHERE_OUTPUT = ${VSWHERE_OUTPUT}") + + # Matches `VSWHERE_PROPERTY` in the `VSWHERE_OUTPUT` text in the format written by vswhere. + # The matched value is assigned to the variable `VARIABLE_NAME` in the parent scope. + function(getVSWhereProperty VSWHERE_OUTPUT VSWHERE_PROPERTY VARIABLE_NAME) + string(REGEX MATCH "${VSWHERE_PROPERTY}: [^\r\n]*" VSWHERE_VALUE "${VSWHERE_OUTPUT}") + string(REPLACE "${VSWHERE_PROPERTY}: " "" VSWHERE_VALUE "${VSWHERE_VALUE}") + set(${VARIABLE_NAME} "${VSWHERE_VALUE}" PARENT_SCOPE) + endfunction() + + while(FIND_VS_PROPERTIES) + list(POP_FRONT FIND_VS_PROPERTIES VSWHERE_PROPERTY) + list(POP_FRONT FIND_VS_PROPERTIES VSWHERE_CMAKE_VARIABLE) + getVSWhereProperty("${VSWHERE_OUTPUT}" ${VSWHERE_PROPERTY} VSWHERE_VALUE) + set(${VSWHERE_CMAKE_VARIABLE} ${VSWHERE_VALUE} PARENT_SCOPE) + endwhile() +endfunction() diff --git a/cmake/toolchains/Windows.Clang.toolchain.cmake b/cmake/toolchains/Windows.Clang.toolchain.cmake new file mode 100644 index 0000000..82f45d3 --- /dev/null +++ b/cmake/toolchains/Windows.Clang.toolchain.cmake @@ -0,0 +1,271 @@ +#---------------------------------------------------------------------------------------------------------------------- +# MIT License +# +# Copyright (c) 2021 Mark Schofield +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#---------------------------------------------------------------------------------------------------------------------- +# +# This CMake toolchain file configures a CMake, non-'Visual Studio Generator' build to use +# the Clang compilers and tools on Windows. +# +# The following variables can be used to configure the behavior of this toolchain file: +# +# | CMake Variable | Description | +# |---------------------------------------------|-----------------------------------------------------------------------------------------------------------------| +# | CMAKE_SYSTEM_VERSION | The version of the operating system for which CMake is to build. Defaults to '10.0.19041.0'. | +# | CMAKE_SYSTEM_PROCESSOR | The processor to compiler for. One of 'X86', 'AMD64', 'ARM', 'ARM64'. Defaults to ${CMAKE_HOST_SYSTEM_PROCESSOR}. | +# | CMAKE_WINDOWS_KITS_10_DIR | The location of the root of the Windows Kits 10 directory. | +# | CLANG_TIDY_CHECKS | List of rules clang-tidy should check. Defaults not set. | +# | TOOLCHAIN_UPDATE_PROGRAM_PATH | Whether the toolchain should update CMAKE_PROGRAM_PATH. Defaults to 'ON'. | +# | TOOLCHAIN_ADD_VS_NINJA_PATH | Whether the toolchain should add the path to the VS Ninja to the CMAKE_SYSTEM_PROGRAM_PATH. Defaults to 'ON'. | +# +# The toolchain file will set the following variables: +# +# | CMake Variable | Description | +# |---------------------------------------------|-------------------------------------------------------------------------------------------------------| +# | CMAKE_C_COMPILER | The path to the C compiler to use. | +# | CMAKE_CXX_COMPILER | The path to the C++ compiler to use. | +# | CMAKE_MT | The path to the 'mt.exe' tool to use. | +# | CMAKE_RC_COMPILER | The path tp the 'rc.exe' tool to use. | +# | CMAKE_SYSTEM_NAME | "Windows", when cross-compiling | +# | WIN32 | 1 | +# | CMAKE_CXX_CLANG_TIDY | The commandline clang-tidy is used if CLANG_TIDY_CHECKS was set. | +# +# Resources: +# +# +cmake_minimum_required(VERSION 3.20) + +include_guard() + +# If `CMAKE_HOST_SYSTEM_NAME` is not 'Windows', there's nothing to do. +if(NOT (CMAKE_HOST_SYSTEM_NAME STREQUAL Windows)) + return() +endif() + +option(TOOLCHAIN_UPDATE_PROGRAM_PATH "Whether the toolchain should update CMAKE_PROGRAM_PATH." ON) +option(TOOLCHAIN_ADD_VS_NINJA_PATH "Whether the toolchain should add the path to the VS Ninja to the CMAKE_SYSTEM_PROGRAM_PATH." ON) + +set(UNUSED ${CMAKE_TOOLCHAIN_FILE}) # Note: only to prevent cmake unused variable warninig +list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES "CMAKE_SYSTEM_PROCESSOR") +set(WIN32 1) + +# If `CMAKE_SYSTEM_PROCESSOR` isn't set, default to `CMAKE_HOST_SYSTEM_PROCESSOR` +if(NOT CMAKE_SYSTEM_PROCESSOR) + set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_HOST_SYSTEM_PROCESSOR}) +endif() + +# If `CMAKE_SYSTEM_PROCESSOR` is not equal to `CMAKE_HOST_SYSTEM_PROCESSOR`, this is cross-compilation. +# CMake expects `CMAKE_SYSTEM_NAME` to be set to reflect cross-compilation. +if(NOT (CMAKE_SYSTEM_PROCESSOR STREQUAL ${CMAKE_HOST_SYSTEM_PROCESSOR})) + set(CMAKE_SYSTEM_NAME Windows) +endif() + +if(NOT CMAKE_VS_VERSION_RANGE) + set(CMAKE_VS_VERSION_RANGE "[16.0,)") +endif() + +if(NOT CMAKE_VS_VERSION_PRERELEASE) + set(CMAKE_VS_VERSION_PRERELEASE OFF) +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/VSWhere.cmake") + +if(NOT CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE) + if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL ARM64) + set(CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE arm64) + else() + set(CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE x64) + endif() +endif() + +# Find Clang +# +findVisualStudio( + VERSION ${CMAKE_VS_VERSION_RANGE} + PRERELEASE ${CMAKE_VS_VERSION_PRERELEASE} + REQUIRES + Microsoft.VisualStudio.Component.VC.Llvm.Clang + PROPERTIES + installationVersion VS_INSTALLATION_VERSION + installationPath VS_INSTALLATION_PATH +) + +if(NOT VS_INSTALLATION_PATH) + # If there's no Visual Studio with Clang, look for a Visual Studio without Clang so that other Visual Studio + # components can be found. + findVisualStudio( + VERSION ${CMAKE_VS_VERSION_RANGE} + PRERELEASE ${CMAKE_VS_VERSION_PRERELEASE} + PROPERTIES + installationVersion VS_INSTALLATION_VERSION + installationPath VS_INSTALLATION_PATH + ) +endif() + +message(VERBOSE "VS_INSTALLATION_VERSION = ${VS_INSTALLATION_VERSION}") +message(VERBOSE "VS_INSTALLATION_PATH = ${VS_INSTALLATION_PATH}") + +if(NOT VS_INSTALLATION_PATH) + message(FATAL_ERROR "Unable to find Visual Studio") +endif() + +cmake_path(NORMAL_PATH VS_INSTALLATION_PATH) + +set(VS_MSVC_PATH "${VS_INSTALLATION_PATH}/VC/Tools/MSVC") + +# Use 'VS_PLATFORM_TOOLSET_VERSION' to resolve 'CMAKE_VS_PLATFORM_TOOLSET_VERSION' +# +if(NOT VS_PLATFORM_TOOLSET_VERSION) + if(VS_TOOLSET_VERSION) + message(WARNING "Old versions of WindowsToolchain incorrectly used 'VS_TOOLSET_VERSION' to specify the VS toolset version. This functionality is being deprecated - please use 'VS_PLATFORM_TOOLSET_VERSION' instead.") + set(VS_PLATFORM_TOOLSET_VERSION ${VS_TOOLSET_VERSION}) + else() + file(GLOB VS_PLATFORM_TOOLSET_VERSIONS RELATIVE ${VS_MSVC_PATH} ${VS_MSVC_PATH}/*) + list(SORT VS_PLATFORM_TOOLSET_VERSIONS COMPARE NATURAL ORDER DESCENDING) + list(POP_FRONT VS_PLATFORM_TOOLSET_VERSIONS VS_PLATFORM_TOOLSET_VERSION) + unset(VS_PLATFORM_TOOLSET_VERSIONS) + endif() +endif() + +set(CMAKE_VS_PLATFORM_TOOLSET_VERSION ${VS_PLATFORM_TOOLSET_VERSION}) +set(VS_TOOLSET_PATH "${VS_INSTALLATION_PATH}/VC/Tools/MSVC/${CMAKE_VS_PLATFORM_TOOLSET_VERSION}") + +# Set the tooling variables, include_directories and link_directories +# + +# Map CMAKE_SYSTEM_PROCESSOR values to CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE that identifies the tools that should +# be used to produce code for the CMAKE_SYSTEM_PROCESSOR. +if(CMAKE_SYSTEM_PROCESSOR STREQUAL AMD64) + set(CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE x64) +elseif((CMAKE_SYSTEM_PROCESSOR STREQUAL ARM) + OR (CMAKE_SYSTEM_PROCESSOR STREQUAL ARM64) + OR (CMAKE_SYSTEM_PROCESSOR STREQUAL X86)) + set(CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) +elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL x64) + message(WARNING "CMAKE_SYSTEM_PROCESSOR should be 'AMD64', not 'x64'. WindowsToolchain will stop recognizing 'x64' in a future release.") + set(CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE x64) +elseif((CMAKE_SYSTEM_PROCESSOR STREQUAL arm) + OR (CMAKE_SYSTEM_PROCESSOR STREQUAL arm64) + OR (CMAKE_SYSTEM_PROCESSOR STREQUAL x86)) + message(WARNING "CMAKE_SYSTEM_PROCESSOR (${CMAKE_SYSTEM_PROCESSOR}) should be upper-case. WindowsToolchain will stop recognizing non-upper-case forms in a future release.") + set(CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) +else() + message(FATAL_ERROR "Unable identify compiler architecture for CMAKE_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}") +endif() + +set(TOOLCHAIN_C_COMPILER_EXE clang.exe) +if(CMAKE_C_COMPILER_FRONTEND_VARIANT STREQUAL MSVC) + set(TOOLCHAIN_C_COMPILER_EXE clang-cl.exe) +endif() + +find_program(CMAKE_C_COMPILER + ${TOOLCHAIN_C_COMPILER_EXE} + HINTS + "${VS_INSTALLATION_PATH}/VC/Tools/Llvm/x64/bin" + "$ENV{ProgramFiles}/LLVM/bin" + REQUIRED +) + +set(TOOLCHAIN_CXX_COMPILER_EXE clang++.exe) +if(CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL MSVC) + set(TOOLCHAIN_CXX_COMPILER_EXE clang-cl.exe) +endif() + +find_program(CMAKE_CXX_COMPILER + ${TOOLCHAIN_CXX_COMPILER_EXE} + HINTS + "${VS_INSTALLATION_PATH}/VC/Tools/Llvm/x64/bin" + "$ENV{ProgramFiles}/LLVM/bin" + REQUIRED +) + +if(CMAKE_SYSTEM_PROCESSOR STREQUAL ARM) + set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} /EHsc") +elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL arm) + message(WARNING "CMAKE_SYSTEM_PROCESSOR (${CMAKE_SYSTEM_PROCESSOR}) should be upper-case. WindowsToolchain will stop recognizing non-upper-case forms in a future release.") + set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} /EHsc") +endif() + +# Compiler +foreach(LANG C CXX RC) + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${VS_TOOLSET_PATH}/ATLMFC/include") + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${VS_TOOLSET_PATH}/include") +endforeach() + +foreach(LANG C CXX) + if(CMAKE_${LANG}_COMPILER_FRONTEND_VARIANT STREQUAL MSVC) + # Add '/X': Do not add %INCLUDE% to include search path + set(CMAKE_${LANG}_FLAGS_INIT "${CMAKE_${LANG}_FLAGS_INIT} /X") + endif() +endforeach() + +if(VS_USE_SPECTRE_MITIGATION_ATLMFC_RUNTIME) + # Ensure that the necessary folder and files are present before adding the 'link_directories' + toolchain_validate_vs_files( + DESCRIPTION "ATLMFC Spectre libraries" + FOLDER "${VS_TOOLSET_PATH}/ATLMFC/lib/spectre/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}" + FILES + atls.lib + ) + link_directories("${VS_TOOLSET_PATH}/ATLMFC/lib/spectre/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}") +else() + link_directories("${VS_TOOLSET_PATH}/ATLMFC/lib/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}") +endif() + +if(VS_USE_SPECTRE_MITIGATION_RUNTIME) + # Ensure that the necessary folder and files are present before adding the 'link_directories' + toolchain_validate_vs_files( + DESCRIPTION "Spectre libraries" + FOLDER "${VS_TOOLSET_PATH}/lib/spectre/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}" + FILES + msvcrt.lib vcruntime.lib vcruntimed.lib + ) + link_directories("${VS_TOOLSET_PATH}/lib/spectre/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}") +else() + link_directories("${VS_TOOLSET_PATH}/lib/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}") +endif() + +link_directories("${VS_TOOLSET_PATH}/lib/x86/store/references") + +if(CLANG_TIDY_CHECKS) + get_filename_component(CLANG_PATH ${CMAKE_CXX_COMPILER} DIRECTORY CACHE) + set(CMAKE_CXX_CLANG_TIDY "${CLANG_PATH}/clang-tidy.exe;-checks=${CLANG_TIDY_CHECKS}") +endif() + +# Windows Kits +include("${CMAKE_CURRENT_LIST_DIR}/Windows.Kits.cmake") + +# If 'TOOLCHAIN_UPDATE_PROGRAM_PATH' is selected, update CMAKE_PROGRAM_PATH. +# +if(TOOLCHAIN_UPDATE_PROGRAM_PATH) + list(APPEND CMAKE_PROGRAM_PATH "${VS_TOOLSET_PATH}/bin/Host${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}") + list(APPEND CMAKE_PROGRAM_PATH "${WINDOWS_KITS_BIN_PATH}/${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}") +endif() + +# If the CMAKE_GENERATOR is Ninja-based, and the path to the Visual Studio-installed Ninja is present, add it to +# the CMAKE_SYSTEM_PROGRAM_PATH. 'find_program' searches CMAKE_SYSTEM_PROGRAM_PATH after the environment path, so +# an installed Ninja would be preferred. +# +if( (CMAKE_GENERATOR MATCHES "^Ninja") AND + (EXISTS "${VS_INSTALLATION_PATH}/Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja") AND + (TOOLCHAIN_ADD_VS_NINJA_PATH)) + list(APPEND CMAKE_SYSTEM_PROGRAM_PATH "${VS_INSTALLATION_PATH}/Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja") +endif() diff --git a/cmake/toolchains/Windows.Kits.cmake b/cmake/toolchains/Windows.Kits.cmake new file mode 100644 index 0000000..fd33214 --- /dev/null +++ b/cmake/toolchains/Windows.Kits.cmake @@ -0,0 +1,153 @@ +#---------------------------------------------------------------------------------------------------------------------- +# MIT License +# +# Copyright (c) 2021 Mark Schofield +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#---------------------------------------------------------------------------------------------------------------------- +# +# | CMake Variable | Description | +# |-----------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------| +# | CMAKE_SYSTEM_VERSION | The version of the operating system for which CMake is to build. Defaults to the host version. | +# | CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE | The architecture of the tooling to use. Defaults to 'arm64' on ARM64 systems, otherwise 'x64'. | +# | CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION | The version of the Windows SDK to use. Defaults to the highest installed, that is no higher than CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM | +# | CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM | The maximum version of the Windows SDK to use, for example '10.0.19041.0'. Defaults to nothing | +# | CMAKE_WINDOWS_KITS_10_DIR | The location of the root of the Windows Kits 10 directory. | +# +# The following variables will be set: +# +# | CMake Variable | Description | +# |---------------------------------------------|-------------------------------------------------------------------------------------------------------| +# | CMAKE_MT | The path to the 'mt' tool. | +# | CMAKE_RC_COMPILER | The path to the 'rc' tool. | +# | CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION | The version of the Windows SDK to be used. | +# | MDMERGE_TOOL | The path to the 'mdmerge' tool. | +# | MIDL_COMPILER | The path to the 'midl' compiler. | +# | WINDOWS_KITS_BIN_PATH | The path to the folder containing the Windows Kits binaries. | +# | WINDOWS_KITS_INCLUDE_PATH | The path to the folder containing the Windows Kits include files. | +# | WINDOWS_KITS_LIB_PATH | The path to the folder containing the Windows Kits library files. | +# | WINDOWS_KITS_REFERENCES_PATH | The path to the folder containing the Windows Kits references. | +# +include_guard() + +if(NOT CMAKE_SYSTEM_VERSION) + set(CMAKE_SYSTEM_VERSION ${CMAKE_HOST_SYSTEM_VERSION}) +endif() + +if(NOT CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE) + if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL ARM64) + set(CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE arm64) + else() + set(CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE x64) + endif() +endif() + +if(NOT CMAKE_WINDOWS_KITS_10_DIR) + get_filename_component(CMAKE_WINDOWS_KITS_10_DIR "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v10.0;InstallationFolder]" ABSOLUTE CACHE) + if ("${CMAKE_WINDOWS_KITS_10_DIR}" STREQUAL "/registry") + unset(CMAKE_WINDOWS_KITS_10_DIR) + endif() +endif() + +if(NOT CMAKE_WINDOWS_KITS_10_DIR) + message(FATAL_ERROR "Unable to find an installed Windows SDK, and one wasn't specified.") +endif() + +# If a CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION wasn't specified, find the highest installed version that is no higher +# than the host version +if(NOT CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) + file(GLOB WINDOWS_KITS_VERSIONS RELATIVE "${CMAKE_WINDOWS_KITS_10_DIR}/lib" "${CMAKE_WINDOWS_KITS_10_DIR}/lib/*") + list(FILTER WINDOWS_KITS_VERSIONS INCLUDE REGEX "10\\.0\\.") + list(SORT WINDOWS_KITS_VERSIONS COMPARE NATURAL ORDER DESCENDING) + while(WINDOWS_KITS_VERSIONS) + list(POP_FRONT WINDOWS_KITS_VERSIONS CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) + if(NOT CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM) + message(VERBOSE "Windows.Kits: Defaulting version: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}") + break() + endif() + + if(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION VERSION_LESS_EQUAL CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM) + message(VERBOSE "Windows.Kits: Choosing version: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}") + break() + endif() + + message(VERBOSE "Windows.Kits: Not suitable: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}") + set(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) + endwhile() +endif() + +if(NOT CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) + message(FATAL_ERROR "A Windows SDK could not be found.") +endif() + +set(WINDOWS_KITS_BIN_PATH "${CMAKE_WINDOWS_KITS_10_DIR}/bin/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}" CACHE PATH "" FORCE) +set(WINDOWS_KITS_INCLUDE_PATH "${CMAKE_WINDOWS_KITS_10_DIR}/include/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}" CACHE PATH "" FORCE) +set(WINDOWS_KITS_LIB_PATH "${CMAKE_WINDOWS_KITS_10_DIR}/lib/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}" CACHE PATH "" FORCE) +set(WINDOWS_KITS_REFERENCES_PATH "${CMAKE_WINDOWS_KITS_10_DIR}/References" CACHE PATH "" FORCE) +set(WINDOWS_KITS_PLATFORM_PATH "${CMAKE_WINDOWS_KITS_10_DIR}/Platforms/UAP/${CMAKE_SYSTEM_VERSION}/Platform.xml" CACHE PATH "" FORCE) + +if(NOT EXISTS ${WINDOWS_KITS_BIN_PATH}) + message(FATAL_ERROR "Windows SDK ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION} cannot be found: Folder '${WINDOWS_KITS_BIN_PATH}' does not exist.") +endif() + +if(NOT EXISTS ${WINDOWS_KITS_INCLUDE_PATH}) + message(FATAL_ERROR "Windows SDK ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION} cannot be found: Folder '${WINDOWS_KITS_INCLUDE_PATH}' does not exist.") +endif() + +if(NOT EXISTS ${WINDOWS_KITS_LIB_PATH}) + message(FATAL_ERROR "Windows SDK ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION} cannot be found: Folder '${WINDOWS_KITS_LIB_PATH}' does not exist.") +endif() + +set(CMAKE_MT "${WINDOWS_KITS_BIN_PATH}/${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/mt.exe") +set(CMAKE_RC_COMPILER_INIT "${WINDOWS_KITS_BIN_PATH}/${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/rc.exe") +set(CMAKE_RC_FLAGS_INIT "/nologo") + +set(MIDL_COMPILER "${WINDOWS_KITS_BIN_PATH}/${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/midl.exe") +set(MDMERGE_TOOL "${WINDOWS_KITS_BIN_PATH}/${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/mdmerge.exe") + +# Windows SDK +if(CMAKE_SYSTEM_PROCESSOR STREQUAL AMD64) + set(WINDOWS_KITS_TARGET_ARCHITECTURE x64) +elseif((CMAKE_SYSTEM_PROCESSOR STREQUAL ARM) + OR (CMAKE_SYSTEM_PROCESSOR STREQUAL ARM64) + OR (CMAKE_SYSTEM_PROCESSOR STREQUAL X86)) + set(WINDOWS_KITS_TARGET_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) +elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL x64) + message(WARNING "CMAKE_SYSTEM_PROCESSOR should be 'AMD64', not 'x64'. WindowsToolchain will stop recognizing 'x64' in a future release.") + set(WINDOWS_KITS_TARGET_ARCHITECTURE x64) +elseif((CMAKE_SYSTEM_PROCESSOR STREQUAL arm) + OR (CMAKE_SYSTEM_PROCESSOR STREQUAL arm64) + OR (CMAKE_SYSTEM_PROCESSOR STREQUAL x86)) + message(WARNING "CMAKE_SYSTEM_PROCESSOR (${CMAKE_SYSTEM_PROCESSOR}) should be upper-case. WindowsToolchain will stop recognizing non-upper-case forms in a future release.") + set(WINDOWS_KITS_TARGET_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) +else() + message(FATAL_ERROR "Unable identify Windows Kits architecture for CMAKE_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}") +endif() + +foreach(LANG C CXX RC ASM_MASM) + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${WINDOWS_KITS_INCLUDE_PATH}/ucrt") + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${WINDOWS_KITS_INCLUDE_PATH}/shared") + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${WINDOWS_KITS_INCLUDE_PATH}/um") + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${WINDOWS_KITS_INCLUDE_PATH}/winrt") + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${WINDOWS_KITS_INCLUDE_PATH}/cppwinrt") +endforeach() + +link_directories("${WINDOWS_KITS_LIB_PATH}/ucrt/${WINDOWS_KITS_TARGET_ARCHITECTURE}") +link_directories("${WINDOWS_KITS_LIB_PATH}/um/${WINDOWS_KITS_TARGET_ARCHITECTURE}") +link_directories("${WINDOWS_KITS_REFERENCES_PATH}/${WINDOWS_KITS_TARGET_ARCHITECTURE}") diff --git a/cmake/toolchains/Windows.MSVC.toolchain.cmake b/cmake/toolchains/Windows.MSVC.toolchain.cmake new file mode 100644 index 0000000..7edab8e --- /dev/null +++ b/cmake/toolchains/Windows.MSVC.toolchain.cmake @@ -0,0 +1,279 @@ +#---------------------------------------------------------------------------------------------------------------------- +# MIT License +# +# Copyright (c) 2021 Mark Schofield +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#---------------------------------------------------------------------------------------------------------------------- +# +# This CMake toolchain file configures a CMake, non-'Visual Studio Generator' build to use +# the MSVC compilers and tools. +# +# The following variables can be used to configure the behavior of this toolchain file: +# +# | CMake Variable | Description | +# |---------------------------------------------|--------------------------------------------------------------------------------------------------------------------------| +# | CMAKE_SYSTEM_PROCESSOR | The processor to compiler for. One of 'X86', 'AMD64', 'ARM', 'ARM64'. Defaults to ${CMAKE_HOST_SYSTEM_PROCESSOR}. | +# | CMAKE_SYSTEM_VERSION | The version of the operating system for which CMake is to build. Defaults to the host version. | +# | CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE | The architecture of the tooling to use. Defaults to 'arm64' on ARM64 systems, otherwise 'x64'. | +# | CMAKE_VS_PRODUCTS | One or more Visual Studio Product IDs to consider. Defaults to '*' | +# | CMAKE_VS_VERSION_PRERELEASE | Whether 'prerelease' versions of Visual Studio should be considered. Defaults to 'OFF' | +# | CMAKE_VS_VERSION_RANGE | A verson range for VS instances to find. For example, '[16.0,17.0)' will find versions '16.*'. Defaults to '[16.0,17.0)' | +# | CMAKE_WINDOWS_KITS_10_DIR | The location of the root of the Windows Kits 10 directory. | +# | TOOLCHAIN_UPDATE_PROGRAM_PATH | Whether the toolchain should update CMAKE_PROGRAM_PATH. Defaults to 'ON'. | +# | TOOLCHAIN_ADD_VS_NINJA_PATH | Whether the toolchain should add the path to the VS Ninja to the CMAKE_SYSTEM_PROGRAM_PATH. Defaults to 'ON'. | +# | VS_EXPERIMENTAL_MODULE | Whether experimental module support should be enabled. | +# | VS_INSTALLATION_PATH | The location of the root of the Visual Studio installation. If not specified VSWhere will be used to search for one. | +# | VS_PLATFORM_TOOLSET_VERSION | The version of the MSVC toolset to use. For example, 14.29.30133. Defaults to the highest available. | +# | VS_USE_SPECTRE_MITIGATION_ATLMFC_RUNTIME | Whether the compiler should link with the ATLMFC runtime that uses 'Spectre' mitigations. Defaults to 'OFF'. | +# | VS_USE_SPECTRE_MITIGATION_RUNTIME | Whether the compiler should link with a runtime that uses 'Spectre' mitigations. Defaults to 'OFF'. | +# +# The toolchain file will set the following variables: +# +# | CMake Variable | Description | +# |---------------------------------------------|-------------------------------------------------------------------------------------------------------| +# | CMAKE_C_COMPILER | The path to the C compiler to use. | +# | CMAKE_CXX_COMPILER | The path to the C++ compiler to use. | +# | CMAKE_MT | The path to the 'mt.exe' tool to use. | +# | CMAKE_RC_COMPILER | The path tp the 'rc.exe' tool to use. | +# | CMAKE_SYSTEM_NAME | "Windows", when cross-compiling | +# | CMAKE_VS_PLATFORM_TOOLSET_VERSION | The version of the MSVC toolset being used - e.g. 14.29.30133. | +# | WIN32 | 1 | +# | MSVC | 1 | +# | MSVC_VERSION | The '' version of the C++ compiler being used. For example, '1929' | +# +# Other configuration: +# +# * If the 'CMAKE_CUDA_COMPILER' is set, and 'CMAKE_CUDA_HOST_COMPILER' is not set, and ENV{CUDAHOSTCXX} not defined +# then 'CMAKE_CUDA_HOST_COMPILER' is set to the value of 'CMAKE_CXX_COMPILER'. +# +# Resources: +# +# +cmake_minimum_required(VERSION 3.20) + +include_guard() + +# If `CMAKE_HOST_SYSTEM_NAME` is not 'Windows', there's nothing to do. +if(NOT (CMAKE_HOST_SYSTEM_NAME STREQUAL Windows)) + return() +endif() + +option(TOOLCHAIN_UPDATE_PROGRAM_PATH "Whether the toolchain should update CMAKE_PROGRAM_PATH." ON) +option(TOOLCHAIN_ADD_VS_NINJA_PATH "Whether the toolchain should add the path to the VS Ninja to the CMAKE_SYSTEM_PROGRAM_PATH." ON) + +set(UNUSED ${CMAKE_TOOLCHAIN_FILE}) # Note: only to prevent cmake unused variable warninig +list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES + CMAKE_SYSTEM_PROCESSOR + CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE + CMAKE_VS_PRODUCTS + CMAKE_VS_VERSION_PRERELEASE + CMAKE_VS_VERSION_RANGE + VS_INSTALLATION_PATH + VS_INSTALLATION_VERSION + VS_PLATFORM_TOOLSET_VERSION +) +set(WIN32 1) +set(MSVC 1) + +include("${CMAKE_CURRENT_LIST_DIR}/VSWhere.cmake") + +# If `CMAKE_SYSTEM_PROCESSOR` isn't set, default to `CMAKE_HOST_SYSTEM_PROCESSOR` +if(NOT CMAKE_SYSTEM_PROCESSOR) + set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_HOST_SYSTEM_PROCESSOR}) +endif() + +# If `CMAKE_SYSTEM_PROCESSOR` is not equal to `CMAKE_HOST_SYSTEM_PROCESSOR`, this is cross-compilation. +# CMake expects `CMAKE_SYSTEM_NAME` to be set to reflect cross-compilation. +if(NOT (CMAKE_SYSTEM_PROCESSOR STREQUAL ${CMAKE_HOST_SYSTEM_PROCESSOR})) + set(CMAKE_SYSTEM_NAME Windows) +endif() + +if(NOT CMAKE_VS_VERSION_RANGE) + set(CMAKE_VS_VERSION_RANGE "[16.0,)") +endif() + +if(NOT CMAKE_VS_VERSION_PRERELEASE) + set(CMAKE_VS_VERSION_PRERELEASE OFF) +endif() + +if(NOT CMAKE_VS_PRODUCTS) + set(CMAKE_VS_PRODUCTS "*") +endif() + +if(NOT CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE) + if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL ARM64) + set(CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE arm64) + else() + set(CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE x64) + endif() +endif() + +if(NOT VS_USE_SPECTRE_MITIGATION_RUNTIME) + set(VS_USE_SPECTRE_MITIGATION_RUNTIME OFF) +endif() + +# Find Visual Studio +# +if(NOT VS_INSTALLATION_PATH) + findVisualStudio( + VERSION ${CMAKE_VS_VERSION_RANGE} + PRERELEASE ${CMAKE_VS_VERSION_PRERELEASE} + PRODUCTS ${CMAKE_VS_PRODUCTS} + PROPERTIES + installationVersion VS_INSTALLATION_VERSION + installationPath VS_INSTALLATION_PATH + ) +endif() + +message(VERBOSE "VS_INSTALLATION_VERSION = ${VS_INSTALLATION_VERSION}") +message(VERBOSE "VS_INSTALLATION_PATH = ${VS_INSTALLATION_PATH}") + +if(NOT VS_INSTALLATION_PATH) + message(FATAL_ERROR "Unable to find Visual Studio") +endif() + +cmake_path(NORMAL_PATH VS_INSTALLATION_PATH) + +set(VS_MSVC_PATH "${VS_INSTALLATION_PATH}/VC/Tools/MSVC") + +# Use 'VS_PLATFORM_TOOLSET_VERSION' to resolve 'CMAKE_VS_PLATFORM_TOOLSET_VERSION' +# +if(NOT VS_PLATFORM_TOOLSET_VERSION) + if(VS_TOOLSET_VERSION) + message(WARNING "Old versions of WindowsToolchain incorrectly used 'VS_TOOLSET_VERSION' to specify the VS toolset version. This functionality is being deprecated - please use 'VS_PLATFORM_TOOLSET_VERSION' instead.") + set(VS_PLATFORM_TOOLSET_VERSION ${VS_TOOLSET_VERSION}) + else() + file(GLOB VS_PLATFORM_TOOLSET_VERSIONS RELATIVE ${VS_MSVC_PATH} ${VS_MSVC_PATH}/*) + list(SORT VS_PLATFORM_TOOLSET_VERSIONS COMPARE NATURAL ORDER DESCENDING) + list(POP_FRONT VS_PLATFORM_TOOLSET_VERSIONS VS_PLATFORM_TOOLSET_VERSION) + unset(VS_PLATFORM_TOOLSET_VERSIONS) + endif() +endif() + +set(CMAKE_VS_PLATFORM_TOOLSET_VERSION ${VS_PLATFORM_TOOLSET_VERSION}) +set(VS_TOOLSET_PATH "${VS_INSTALLATION_PATH}/VC/Tools/MSVC/${CMAKE_VS_PLATFORM_TOOLSET_VERSION}") + +# Set the tooling variables, include_directories and link_directories +# + +# Map CMAKE_SYSTEM_PROCESSOR values to CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE that identifies the tools that should +# be used to produce code for the CMAKE_SYSTEM_PROCESSOR. +if(CMAKE_SYSTEM_PROCESSOR STREQUAL AMD64) + set(CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE x64) +elseif((CMAKE_SYSTEM_PROCESSOR STREQUAL ARM) + OR (CMAKE_SYSTEM_PROCESSOR STREQUAL ARM64) + OR (CMAKE_SYSTEM_PROCESSOR STREQUAL X86)) + set(CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) +elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL x64) + message(WARNING "CMAKE_SYSTEM_PROCESSOR should be 'AMD64', not 'x64'. WindowsToolchain will stop recognizing 'x64' in a future release.") + set(CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE x64) +elseif((CMAKE_SYSTEM_PROCESSOR STREQUAL arm) + OR (CMAKE_SYSTEM_PROCESSOR STREQUAL arm64) + OR (CMAKE_SYSTEM_PROCESSOR STREQUAL x86)) + message(WARNING "CMAKE_SYSTEM_PROCESSOR (${CMAKE_SYSTEM_PROCESSOR}) should be upper-case. WindowsToolchain will stop recognizing non-upper-case forms in a future release.") + set(CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) +else() + message(FATAL_ERROR "Unable identify compiler architecture for CMAKE_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}") +endif() + +set(CMAKE_CXX_COMPILER "${VS_TOOLSET_PATH}/bin/Host${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}/cl.exe") +set(CMAKE_C_COMPILER "${VS_TOOLSET_PATH}/bin/Host${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}/cl.exe") + +if(CMAKE_SYSTEM_PROCESSOR STREQUAL ARM) + set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} /EHsc") +elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL arm) + message(WARNING "CMAKE_SYSTEM_PROCESSOR (${CMAKE_SYSTEM_PROCESSOR}) should be upper-case. WindowsToolchain will stop recognizing non-upper-case forms in a future release.") + set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} /EHsc") +endif() + +# Compiler +foreach(LANG C CXX RC) + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${VS_TOOLSET_PATH}/ATLMFC/include") + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${VS_TOOLSET_PATH}/include") +endforeach() + +foreach(LANG C CXX) + # Add '/X': Do not add %INCLUDE% to include search path + set(CMAKE_${LANG}_FLAGS_INIT "${CMAKE_${LANG}_FLAGS_INIT} /X") +endforeach() + +if(VS_USE_SPECTRE_MITIGATION_ATLMFC_RUNTIME) + # Ensure that the necessary folder and files are present before adding the 'link_directories' + toolchain_validate_vs_files( + DESCRIPTION "ATLMFC Spectre libraries" + FOLDER "${VS_TOOLSET_PATH}/ATLMFC/lib/spectre/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}" + FILES + atls.lib + ) + link_directories("${VS_TOOLSET_PATH}/ATLMFC/lib/spectre/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}") +else() + link_directories("${VS_TOOLSET_PATH}/ATLMFC/lib/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}") +endif() + +if(VS_USE_SPECTRE_MITIGATION_RUNTIME) + # Ensure that the necessary folder and files are present before adding the 'link_directories' + toolchain_validate_vs_files( + DESCRIPTION "Spectre libraries" + FOLDER "${VS_TOOLSET_PATH}/lib/spectre/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}" + FILES + msvcrt.lib vcruntime.lib vcruntimed.lib + ) + link_directories("${VS_TOOLSET_PATH}/lib/spectre/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}") +else() + link_directories("${VS_TOOLSET_PATH}/lib/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}") +endif() + +link_directories("${VS_TOOLSET_PATH}/lib/x86/store/references") + +# Module support +if(VS_EXPERIMENTAL_MODULE) + set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} /experimental:module") + set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} /stdIfcDir \"${VS_TOOLSET_PATH}/ifc/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}\"") +endif() + +# Windows Kits +include("${CMAKE_CURRENT_LIST_DIR}/Windows.Kits.cmake") + +# CUDA support +# +# If a CUDA compiler is specified, and a host compiler wasn't specified, set 'CMAKE_CXX_COMPILER' +# as the host compiler. +if(CMAKE_CUDA_COMPILER) + if((NOT CMAKE_CUDA_HOST_COMPILER) AND (NOT DEFINED ENV{CUDAHOSTCXX})) + set(CMAKE_CUDA_HOST_COMPILER "${CMAKE_CXX_COMPILER}") + endif() +endif() + +# If 'TOOLCHAIN_UPDATE_PROGRAM_PATH' is selected, update CMAKE_PROGRAM_PATH. +# +if(TOOLCHAIN_UPDATE_PROGRAM_PATH) + list(APPEND CMAKE_PROGRAM_PATH "${VS_TOOLSET_PATH}/bin/Host${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}") + list(APPEND CMAKE_PROGRAM_PATH "${WINDOWS_KITS_BIN_PATH}/${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}") +endif() + +# If the CMAKE_GENERATOR is Ninja-based, and the path to the Visual Studio-installed Ninja is present, add it to +# the CMAKE_SYSTEM_PROGRAM_PATH. 'find_program' searches CMAKE_SYSTEM_PROGRAM_PATH after the environment path, so +# an installed Ninja would be preferred. +# +if( (CMAKE_GENERATOR MATCHES "^Ninja") AND + (EXISTS "${VS_INSTALLATION_PATH}/Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja") AND + (TOOLCHAIN_ADD_VS_NINJA_PATH)) + list(APPEND CMAKE_SYSTEM_PROGRAM_PATH "${VS_INSTALLATION_PATH}/Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja") +endif() diff --git a/codecov.yml b/codecov.yml index 715130c..c65bd15 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,4 +1,4 @@ ignore: - - "include/tests.hpp" - - "src/main.cpp" - - "test/**" \ No newline at end of file + - "demo/** + - "test/**" + - "3rdparty/** diff --git a/demo/src/main.cpp b/demo/src/main.cpp index eb65b50..dd0cd66 100644 --- a/demo/src/main.cpp +++ b/demo/src/main.cpp @@ -1,5 +1,4 @@ #include "TestCPP.h" -#include "TestCPPUtil.h" using TestCPP::TestSuite; using std::string; @@ -12,7 +11,7 @@ int main(void) { try { TestSuite suite( - string("Demo Test Suite"), + "Demo Test Suite", make_tuple( "simpleTest", diff --git a/demo/src/tests.cpp b/demo/src/tests.cpp index 7660d8a..77f6042 100644 --- a/demo/src/tests.cpp +++ b/demo/src/tests.cpp @@ -6,13 +6,16 @@ namespace TestCPP { void simpleTest () { int lower = 5; int higher = 9; - TestSuite::assertTrue(higher > lower, "Something is seriously wrong."); + Assertions::assertTrue( + higher > lower, + "Something is seriously wrong." + ); } void otherSimpleTest () { string s1 = string("A string"); string s2 = string("another string"); - TestSuite::assertNotEquals(s1, s2); + Assertions::assertNotEquals(s1, s2); } } } diff --git a/include/TestCPP.h b/include/TestCPP.h index 0567063..74f6ad2 100644 --- a/include/TestCPP.h +++ b/include/TestCPP.h @@ -25,313 +25,14 @@ OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to */ -//Original author: Jonathan Hyry CSU-Fullerton SECS 6896-02 Fall 2014/15 - -#ifndef TESTCPP_CLASSES_ -#define TESTCPP_CLASSES_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using std::atomic_int; -using std::chrono::nanoseconds; -using std::chrono::system_clock; -using std::chrono::duration_cast; -using std::enable_if; -using std::endl; -using std::forward; -using std::function; -using std::move; -using std::runtime_error; -using std::string; -using std::streambuf; -using std::stringstream; -using std::unique_ptr; -using std::vector; - -namespace TestCPP { - class TestCPPException : public runtime_error { - public: - TestCPPException (const char * msg); - TestCPPException (string&& msg); - }; - - class TestFailedException : public TestCPPException { - public: - TestFailedException (const char * msg); - TestFailedException (string&& msg); - }; - - class TestCaseName { - public: - TestCaseName () = default; - TestCaseName (const char* name); - - const string& getTestName (); - - friend std::ostream& operator<< ( - std::ostream& s, - TestCaseName& tcName - ) - { - s << tcName.getTestName(); - return s; - } - - private: - string testCaseName; - - }; - - class TestCase { - - public: - enum TestCaseOutCompareOptions { - CONTAINS, - EXACT - }; - - TestCase ( - TestCaseName&& testName, - function test, - bool testPassedMessage = true, - bool captureOut = false, - bool captureLog = false, - bool captureErr = false, - TestCaseOutCompareOptions opt = CONTAINS - ); - - TestCase (TestCase& o); - TestCase (TestCase&& o); - - TestCase& operator= (TestCase& rhs); - TestCase& operator= (TestCase&& rhs); - - ~TestCase (); - - void setNotifyPassed (bool); - void outCompareOption (TestCaseOutCompareOptions opt); - void clearStdoutCapture (); - void clearLogCapture (); - void clearStderrCapture (); - bool checkStdout (string against); - bool checkLog (string against); - bool checkStderr (string against); - bool go (); - long long getLastRuntime (); - - private: - bool notifyTestPassed; - bool pass; - long long lastRunTime; - - TestCaseName testName; - function test; - - TestCaseOutCompareOptions option; - - void captureStdout (); - void captureClog (); - void captureStdErr (); - void logTestFailure (string); - void runTest (); - bool checkOutput (TestCaseOutCompareOptions opt, string source, - string against); - - static atomic_int stdoutCaptureCasesConstructed; - static atomic_int logCaptureCasesConstructed; - static atomic_int stderrCaptureCasesConstructed; - static atomic_int stdoutCaptureCasesDestroyed; - static atomic_int logCaptureCasesDestroyed; - static atomic_int stderrCaptureCasesDestroyed; - - static unique_ptr stdoutBuffer; - static unique_ptr clogBuffer; - static unique_ptr stderrBuffer; - static unique_ptr stdoutOriginal; - static unique_ptr clogOriginal; - static unique_ptr stderrOriginal; - - template - static nanoseconds duration (F func, Args&&... args) - { - auto start = system_clock::now(); - func(forward(args)...); - return duration_cast( - system_clock::now() - start - ); - } - }; - - class TestSuite { - - public: - template - TestSuite (string suiteName, - typename enable_if::type) - { - this->testPassedMessage = true; - this->setSuiteName(suiteName); - this->tests = vector(); - } - - template - TestSuite (string suiteName, TestType ...tests) { - this->testPassedMessage = true; - this->setSuiteName(suiteName); - this->tests = vector(); - - this->addTests(tests...); - } - - template - void addTest (T&& test) { - this->tests.emplace_back( - std::get<0>(test), - std::get<1>(test), - this->testPassedMessage - ); - } - - template - typename enable_if::type - inline addTests () { } - - template - void addTests (Test test, OtherTests ...tests) { - addTest(move(test)); - addTests(tests...); - } - - template - static T getTestObject (ConstructionArgs ...args) { - return T(args...); - } - - void setSuiteName (string testSuiteName); - - template - static void assertEquals ( - T1 expected, T2 actual, - string failureMessage = "Arguments are not equivalent!" - ) - { - if (expected != actual) { - stringstream err; - - err << "Equivalence assertion failed!" << endl; - err << failureMessage << endl; - err << "Expected: <" << expected << ">" << endl; - err << "Actual: <" << actual << ">" << endl; - - throw TestFailedException(err.str()); - } - } - - template - static void assertNotEquals ( - T1 expected, T2 actual, - string failureMessage = "Arguments are equivalent!" - ) - { - if (expected == actual) { - stringstream err; - - err << "Non-Equivalence assertion failed!" << endl; - err << failureMessage << endl; - err << "Expected: <" << expected << ">" << endl; - err << "Actual: <" << actual << ">" << endl; - - throw TestFailedException(err.str()); - } - } - - template - static void assertNull ( - T ptr, - string failureMessage = "Object is not null!" - ) - { - bool null = ptr == nullptr; - - if (!null) { - stringstream err; - - err << "Null assertion failed!" << endl; - err << failureMessage << endl; - - throw TestFailedException(err.str()); - } - } - - template - static void assertNotNull ( - T ptr, - string failureMessage = "Object is null!" - ) - { - bool notNull = ptr != nullptr; - - if (!notNull) { - stringstream err; - - err << "Not Null assertion failed!" << endl; - err << failureMessage << endl; - - throw TestFailedException(err.str()); - } - } - - static void assertThrows ( - function shouldThrow, - string failureMessage = - "Should have thrown something!" - ); - - static void assertNoThrows ( - function shouldNotThrow, - string failureMessage = - "Should not have thrown anything!" - ); - - static void assertTrue ( - bool condition, - string failureMessage = "Condition is false!" - ); - - static void assertFalse ( - bool condition, - string failureMessage = "Condition is true!" - ); - - static void fail ( - string failureMessage = "Forced test failure!" - ); - - void enableTestPassedMessage (); - void disableTestPassedMessage (); - - unsigned getLastRunFailCount (); - - void run (); - - private: - bool testPassedMessage; - bool lastRunSucceeded; - unsigned lastRunSuccessCount; - unsigned lastRunFailCount; - unsigned long long totalRuntime; - - string suiteName; - vector tests; - }; -} +#ifndef TESTCPP_AGGREGATE_ +#define TESTCPP_AGGREGATE_ + +#include "internal/TestCPPAssertions.h" +#include "internal/TestCPPCommon.h" +#include "internal/TestCPPExceptions.h" +#include "internal/TestCPPTestCase.h" +#include "internal/TestCPPTestSuite.h" +#include "internal/TestCPPUtil.h" #endif diff --git a/include/internal/TestCPPAssertions.h b/include/internal/TestCPPAssertions.h new file mode 100644 index 0000000..83ff2e9 --- /dev/null +++ b/include/internal/TestCPPAssertions.h @@ -0,0 +1,260 @@ +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + */ + +#ifndef TESTCPP_ASSERTIONS_ +#define TESTCPP_ASSERTIONS_ + +#include +#include +#include +#include + +#include "TestCPPExceptions.h" + +using std::endl; +using std::function; +using std::string; +using std::stringstream; + +/** + * The base namespace for all TestCPP library code. + */ +namespace TestCPP { + + /** + * @class Assertions + * @author Jonathan Hyry + * @date 11/05/24 + * @file TestCPPAssertions.h + * @brief Contains the TestCPP library's assertions. + * + * Defines some assertions here, where they are templated. + * Declares the rest. + * + * This is the place where the TestCPP Assertions API is defined. + */ + class Assertions { + public: + /** + * @brief Check that something equals something else using the + * built-in operator== for each type. + * @param expected The value that the actual value should be + * equivalent to. + * @param actual The actual value that will be checked against + * the expected value. + * @param failureMessage Failure message that should be logged + * if the assertion fails. This + * defaults to a generic failure + * message related to the assertion + * type. + */ + template + static void assertEquals ( + T1 expected, T2 actual, + string failureMessage = "Arguments are not equivalent!" + ) + { + if (expected != actual) { + stringstream err; + + err << "Equivalence assertion failed!" << endl; + err << failureMessage << endl; + err << "Expected: <" << expected << ">" << endl; + err << "Actual: <" << actual << ">" << endl; + + throw TestFailedException(err.str()); + } + } + + /** + * @brief Check that something is not equivalent to something + * else using the built-in operator== for each type. + * @param expected The value that the actual value should not be + * equivalent to. + * @param actual The actual value that will be checked against + * the expected value. + * @param failureMessage Failure message that should be logged + * if the assertion fails. This + * defaults to a generic failure + * message related to the assertion + * type. + */ + template + static void assertNotEquals ( + T1 expected, T2 actual, + string failureMessage = "Arguments are equivalent!" + ) + { + if (expected == actual) { + stringstream err; + + err << "Non-Equivalence assertion failed!" << endl; + err << failureMessage << endl; + err << "Expected: <" << expected << ">" << endl; + err << "Actual: <" << actual << ">" << endl; + + throw TestFailedException(err.str()); + } + } + + /** + * @brief Check that a pointer is null. + * @param ptr The pointer to check. + * @param failureMessage Failure message that should be logged + * if the assertion fails. This + * defaults to a generic failure + * message related to the assertion + * type. + */ + template + static void assertNull ( + T ptr, + string failureMessage = "Object is not null!" + ) + { + bool null = ptr == nullptr; + + if (!null) { + stringstream err; + + err << "Null assertion failed!" << endl; + err << failureMessage << endl; + + throw TestFailedException(err.str()); + } + } + + /** + * @brief Check that a pointer is non-null. + * @param ptr The pointer to check. + * @param failureMessage Failure message that should be logged + * if the assertion fails. This + * defaults to a generic failure + * message related to the assertion + * type. + */ + template + static void assertNotNull ( + T ptr, + string failureMessage = "Object is null!" + ) + { + bool notNull = ptr != nullptr; + + if (!notNull) { + stringstream err; + + err << "Not Null assertion failed!" << endl; + err << failureMessage << endl; + + throw TestFailedException(err.str()); + } + } + + /** + * @brief Verify that a function throws something. + * @param shouldThrow The function to check, to ensure that it + * throws something. + * @param failureMessage Failure message that should be logged + * if the assertion fails. This + * defaults to a generic failure + * message related to the assertion + * type. + */ + static void assertThrows ( + function shouldThrow, + string failureMessage = + "Should have thrown something!" + ); + + /** + * @brief Verify that a function does not throw something. + * @param shouldNotThrow The function to check, to ensure that it + * does not throw something. + * @param failureMessage Failure message that should be logged + * if the assertion fails. This + * defaults to a generic failure + * message related to the assertion + * type. + */ + static void assertNoThrows ( + function shouldNotThrow, + string failureMessage = + "Should not have thrown anything!" + ); + + /** + * @brief Verify that a logical condition is true. + * @param condition The result of the logical condition to + * check. + * @param failureMessage Failure message that should be logged + * if the assertion fails. This + * defaults to a generic failure + * message related to the assertion + * type. + */ + static void assertTrue ( + bool condition, + string failureMessage = "Condition is false!" + ); + + /** + * @brief Verify that a logical condition is not true. + * @param condition The result of the logical condition to + * check. + * @param failureMessage Failure message that should be logged + * if the assertion fails. This + * defaults to a generic failure + * message related to the assertion + * type. + */ + static void assertFalse ( + bool condition, + string failureMessage = "Condition is true!" + ); + + /** + * @brief Force a test to fail. + * @param failureMessage Failure message that should be logged + * if the assertion fails. This + * defaults to a generic failure + * message related to the assertion + * type. + * + * Useful in certain situations when: + * - Generating code for test cases, to ensure that skeletons + * fail by default. + * - Forcing test failure in certain circumstances where there + * is nothing to assert but a certain code path is taken. + */ + static void fail [[noreturn]] ( + string failureMessage = "Forced test failure!" + ); + }; +} + +#endif diff --git a/include/internal/TestCPPCommon.h b/include/internal/TestCPPCommon.h new file mode 100644 index 0000000..5e0e726 --- /dev/null +++ b/include/internal/TestCPPCommon.h @@ -0,0 +1,106 @@ +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + */ + +#ifndef TESTCPP_COMMON_ +#define TESTCPP_COMMON_ + +/** + * The base namespace for all TestCPP library code. + */ +namespace TestCPP { + + /** + * @class TestCPPCommon + * @author Jonathan Hyry + * @date 03/05/24 + * @file TestCPP.h + * @brief Categories of common objects that are used. + */ + class TestCPPCommon { + public: + /** + * @class Nums + * @author Jonathan Hyry + * @date 03/05/24 + * @file TestCPP.h + * @brief Common magic numbers used by the library. + */ + struct Nums { + static constexpr const int TIME_PRECISION = 4; + static constexpr const double NANOS_IN_SEC = 1000000000.0; + }; + + /** + * @class Strings + * @author Jonathan Hyry + * @date 03/05/24 + * @file TestCPP.h + * @brief Common literal strings used by the library. + * + * As struct member name prefixes and postfixes, underscores + * denote leading and trailing spaces, respectively. + */ + struct Strings { + static constexpr const char * ALL = "All "; + static constexpr const char * APOS = "'"; + static constexpr const char * FAIL = " failed! "; + static constexpr const char * FINISHED_SUITE = + "Finished running test suite "; + static constexpr const char * FWSL = "/"; + static constexpr const char * IN_ABOUT = " in about "; + static constexpr const char * NCONTAIN = + " does not contain "; + static constexpr const char * NEQUIV = + " is not equivalent to "; + static constexpr const char * NTR = "No tests to run!"; + static constexpr const char * NVTN = + "Not a valid test name!"; + static constexpr const char * PARENL = "("; + static constexpr const char * PARENR = ")"; + static constexpr const char * PASS = " passed! "; + static constexpr const char * PASSED = " passed"; + static constexpr const char * REASON_ = "Reason: "; + static constexpr const char * SEC = "s"; + static constexpr const char * SP = " "; + static constexpr const char * START_RUN = + "Starting run of test "; + static constexpr const char * SUITE = "Suite "; + static constexpr const char * SUITE_TESTS_PASSED = + " suite tests passed!"; + static constexpr const char * TEST = "Test "; + static constexpr const char * TEST_EXC = + "Exception occurred during test run: "; + static constexpr const char * UNK_CMP_OPT = + "Unknown comparison option! "; + static constexpr const char * UNK_EXC = + "Unknown error occurred in test!"; + static constexpr const char * UNK_OPT = "Unknown option "; + }; + }; +} + +#endif diff --git a/include/internal/TestCPPExceptions.h b/include/internal/TestCPPExceptions.h new file mode 100644 index 0000000..4337f25 --- /dev/null +++ b/include/internal/TestCPPExceptions.h @@ -0,0 +1,102 @@ +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + */ + +#ifndef TESTCPP_EXCEPTIONS_ +#define TESTCPP_EXCEPTIONS_ + +#include +#include + +using std::runtime_error; +using std::string; + +/** + * The base namespace for all TestCPP library code. + */ +namespace TestCPP { + + /** + * @class TestCPPException + * @author Jonathan Hyry + * @date 03/05/24 + * @file TestCPP.h + * @brief Provides a custom base exception for failure conditions. + * + * There are two types of failures in the library: + * - Errors caused by bugs, system problems, etc. + * - Test failures + * + * This type, when used directly, is for representing the first type + * of failure: errors in the library caused by bugs or other + * problems. + */ + class TestCPPException : public runtime_error { + public: + /** + * Construct an exception of this type with a string literal for + * its failure message. + */ + explicit TestCPPException (const char * msg); + + /** + * Construct an exception of this type with a string object for + * its failure message. + */ + explicit TestCPPException (string&& msg); + }; + + /** + * @class TestFailedException + * @author Jonathan Hyry + * @date 03/05/24 + * @file TestCPP.h + * @brief Provides an exception type specifically for test failures. + * + * There are two types of failures in the library: + * - Errors caused by bugs, system problems, etc. + * - Test failures + * + * This type is for representing the second type of failure: test + * failures. + */ + class TestFailedException : public TestCPPException { + public: + /** + * Construct an exception of this type with a string literal for + * its failure message. + */ + explicit TestFailedException (const char * msg); + + /** + * Construct an exception of this type with a string literal for + * its failure message. + */ + explicit TestFailedException (string&& msg); + }; +} + +#endif diff --git a/include/internal/TestCPPTestCase.h b/include/internal/TestCPPTestCase.h new file mode 100644 index 0000000..dc2e5f7 --- /dev/null +++ b/include/internal/TestCPPTestCase.h @@ -0,0 +1,356 @@ +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + */ + +//Original author: Jonathan Hyry CSU-Fullerton SECS 6896-02 Fall 2014/15 + +#ifndef TESTCPP_TESTCASE_TYPE_ +#define TESTCPP_TESTCASE_TYPE_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TestCPPUtil.h" + +using std::atomic_int; +using std::chrono::nanoseconds; +using std::chrono::system_clock; +using std::chrono::duration_cast; +using std::enable_if; +using std::endl; +using std::forward; +using std::function; +using std::move; +using std::ostream; +using std::runtime_error; +using std::string; +using std::streambuf; +using std::stringstream; +using std::unique_ptr; +using std::vector; + +/** + * The base namespace for all TestCPP library code. + */ +namespace TestCPP { + + /** + * @class TestCase + * @author Jonathan Hyry + * @date 03/05/24 + * @file TestCPP.h + * @brief Provides an interface for implementing tests. + * + * TestCase is one of three core types that define the TestCPP API. + * TestCase provides faculties for defining and controlling tests + * and the stdout/stderr/clog environment. + * TestCase objects are defined by the following elements: + * - their name + * - the code that will be run that defines what the test does + * and how it does it + * - whether to log test passage + * - output capturing flags + * - how captured output is analyzed (exact or contains) + */ + class TestCase { + + public: + /** + * Provides a specification for how to analyze captured stdout, + * stderr, and clog data. + */ + enum TestCaseOutCompareOptions { + CONTAINS, + EXACT + }; + + /** + * @brief Construct a test case with possibility to use all + * options. + * + * @param testName The name of the test as a TestObjName. + * @param test The implementation or pointer to the test. + * @param testPassedMessage Whether to omit a message indicating + * test passage. + * @param captureOut Whether to capture stdout output for + * analysis after the test run. + * @param captureLog Whether to capture clog output for + * analysis after the test run. + * @param captureErr Whether to capture stderr output for + * analysis after the test run. + * @param opt Technique for comparing actual output with + * expected output after the test run. + * + * Instantiate and define a test case. + * All parameters are optional other than the test name and the + * test function. + * Optional parameters have the following default values: + * - testPassedMessage defaults to true, so by default test + * cases will emit a message when the pass. + * - captureOut defaults to false, the test case by default + * does not capture stdout data. + * - captureLog defaults to false, the test case by default + * does not capture data streamed to clog. + * - captureErr defaults to false, the test case by default + * does not capture stderr data. + * - opt defaults to CONTAINS, so when output is captured and + * checked for something, the default technique is to check + * that the output of a given stream contains a given + * string. + */ + TestCase ( + TestObjName&& testName, + function test, + bool testPassedMessage = true, + bool captureOut = false, + bool captureLog = false, + bool captureErr = false, + TestCaseOutCompareOptions opt = CONTAINS + ); + + /** + * @brief Construct a TestCase by copying it from another + * TestCase. + * @param o The test case from which to make a copy. + */ + TestCase (TestCase& o); + + /** + * @brief Construct a TestCase by moving all data from another + * TestCase. + * @param o Move everything from this TestCase into the new one. + */ + TestCase (TestCase&& o) noexcept; + + /** + * @brief Copy a TestCase into another TestCase. + * @param rhs The test case to copy from. + * @return A reference to the new TestCase copy. + */ + TestCase& operator= (TestCase& rhs); + + /** + * @brief Move a TestCase into another TestCase. + * @param rhs Move everything from this TestCase into the new + * one. + * @return A reference to the TestCase that everything from the + * old TestCase was moved into. + */ + TestCase& operator= (TestCase&& rhs) noexcept; + + /** + * @brief Destroy a TestCase object. + */ + ~TestCase (); + + /** + * @brief Set whether to notify in std::clog when a test passes. + * @param shouldNotify Will notify on test passed if true. + */ + void setNotifyPassed (bool shouldNotify); + + /** + * @brief Set the output comparison mode. + * @param opt Accepts specified mode from the referenced enum. + * + * If this is called with an option specified that is different + * from what is set for this test case already, subsequent + * calls to any of the output check functions will use the + * updated mode that is specified in the call to this function. + */ + void outCompareOption (TestCaseOutCompareOptions opt); + + /** + * @brief Clears the captured output from stdout. + * + * This can be used for checking sections of output based on + * test configuration. + */ + void clearStdoutCapture (); + + /** + * @brief Clears the captured output from std::clog. + * + * This can be used for checking sections of output based on + * test configuration. + */ + void clearLogCapture (); + + /** + * @brief Clears the captured output from stderr. + * + * This can be used for checking sections of output based on + * test configuration. + */ + void clearStderrCapture (); + + /** + * @brief Check the argument against what is captured from + * stdout using the configured comparison mode. + * @param against The value to check the captured output against + * @return True if the argument results in a successful check + * using the configured comparison mode. + */ + bool checkStdout (string against); + + /** + * @brief Check the argument against what is captured from + * std::clog using the configured comparison mode. + * @param against The value to check the captured output against + * @return True if the argument results in a successful check + * using the configured comparison mode. + */ + bool checkLog (string against); + + /** + * @brief Check the argument against what is captured from + * stderr using the configured comparison mode. + * @param against The value to check the captured output against + * @return True if the argument results in a successful check + * using the configured comparison mode, false + * otherwise. + */ + bool checkStderr (string against); + + /** + * @brief Run the test case. + * @return True if the test ran successfully, false otherwise. + */ + bool go (); + + /** + * @brief Returns the duration of the last run in nanoseconds. + * @return The duration of the last run of this TestCase in + * nanoseconds. + */ + long long getLastRuntime () const; + + private: + bool notifyTestPassed = false; + bool pass = false; + bool stdoutCaptured = false; + bool clogCaptured = false; + bool stderrCaptured = false; + long long lastRunTime = -1; + + TestObjName testName; + function test; + + TestCaseOutCompareOptions option = + TestCaseOutCompareOptions::CONTAINS; + + /** + * @brief If instructed, capture stdout while this test is + * active. + */ + void captureStdout (); + /** + * @brief If instructed, capture clog while this test is + * active. + */ + void captureClog (); + /** + * @brief If instructed, capture stderr while this test is + * active. + */ + void captureStdErr (); + /** + * @brief Write a test failure reason to the specified stream. + * @param out The stream to write the test failure reason to. + * @param reason The test failure reason to write. + */ + void logFailure (ostream& out, string& reason); + /** + * @brief If a test encounters an error while running, this + * function will be called to log the test error. + * @param failureMessage The error message from the test that + * should be logged. + */ + void logTestFailure (string failureMessage); + /** + * @brief Internal test run controller. + */ + void runTest (); + /** + * @brief Handles the internal logic for calls to checkStdout, + * checkLog, and checkStderr based on the selected + * comparison technique for this test. + * @param source The actual output + * @param against The expected output, or a portion of the + * expected output. + * @return True if the argument results in a successful check + * using the configured comparison mode, false + * otherwise. + */ + bool checkOutput (string source, string against); + + static atomic_int stdoutCaptureCasesConstructed; + static atomic_int logCaptureCasesConstructed; + static atomic_int stderrCaptureCasesConstructed; + static atomic_int stdoutCaptureCasesDestroyed; + static atomic_int logCaptureCasesDestroyed; + static atomic_int stderrCaptureCasesDestroyed; + + static Util::no_destroy> + stdoutBuffer; + static Util::no_destroy> + clogBuffer; + static Util::no_destroy> + stderrBuffer; + static Util::no_destroy> + stdoutOriginal; + static Util::no_destroy> + clogOriginal; + static Util::no_destroy> + stderrOriginal; + + /** + * @brief Measure the duration of a function when run. + * @param func Measure the duration of this function when run. + * @param args A template pack of arguments to apply to the + * given function of which to measure the duration. + * @return The duration of the function run, in nanoseconds. + */ + template + static nanoseconds duration (F func, Args&&... args) + { + auto start = system_clock::now(); + func(forward(args)...); + return duration_cast( + system_clock::now() - start + ); + } + }; +} + +#endif diff --git a/include/internal/TestCPPTestSuite.h b/include/internal/TestCPPTestSuite.h new file mode 100644 index 0000000..545a193 --- /dev/null +++ b/include/internal/TestCPPTestSuite.h @@ -0,0 +1,172 @@ +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + */ + +//Original author: Jonathan Hyry CSU-Fullerton SECS 6896-02 Fall 2014/15 + +#ifndef TESTCPP_TESTSUITE_TYPE_ +#define TESTCPP_TESTSUITE_TYPE_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using std::atomic_int; +using std::chrono::nanoseconds; +using std::chrono::system_clock; +using std::chrono::duration_cast; +using std::enable_if; +using std::endl; +using std::forward; +using std::function; +using std::runtime_error; +using std::string; +using std::streambuf; +using std::stringstream; +using std::unique_ptr; +using std::vector; + +/** + * The base namespace for all TestCPP library code. + */ +namespace TestCPP { + + /** + * @class TestSuite + * @author Jonathan Hyry + * @date 05/05/24 + * @file TestCPP.h + * @brief Defines a container for a collection of TestCase objects. + * + * + */ + class TestSuite { + + public: + /** + * @brief Specialization for constructing a test suite with no + * tests. + */ + template + TestSuite (TestObjName&& suiteName, + typename enable_if::type) + { + this->testSuitePassedMessage = true; + this->setSuiteName(std::move(suiteName)); + this->tests = vector(); + } + + /** + * @brief The general case for constructing a test suite with + * tests. + */ + template + TestSuite (TestObjName&& suiteName, TestType ...tests) { + this->testSuitePassedMessage = true; + this->setSuiteName(std::move(suiteName)); + this->tests = vector(); + + this->addTests(tests...); + } + + /** + * @brief Add a test to this test suite. + * + * Appropriate specializations defined in the source file. + */ + template + void addTest (T&& test); + + /** + * @brief Specialization to handle when someone tries to call + * the template function with no tests. + * + * This is a noop. + */ + template + typename enable_if::type + inline addTests () { } + + /** + * @brief Add one or more tests at once to the test suite. + * @param test The first test to add. + * @param tests The rest of the tests to add. + */ + template + void addTests (Test test, OtherTests ...tests) { + addTest(std::move(test)); + addTests(tests...); + } + + /** + * @brief Sets the name of this test suite. + */ + void setSuiteName (TestObjName&& testSuiteName); + + /** + * @brief After called, all tests in the suite will emit a + * message if they pass. + */ + void enableTestPassedMessage (); + + /** + * @brief After called, all tests in the suite will not emit a + * message if they pass. + */ + void disableTestPassedMessage (); + + /** + * @brief Calculate the total number of tests in the suite that + * failed after the last suite run. + * @return The total number of tests that failed in the last + * suite run. + */ + unsigned getLastRunFailCount (); + + /** + * @brief Run all tests in the test suite. + */ + void run (); + + private: + bool testSuitePassedMessage; + bool lastRunSucceeded; + unsigned lastRunSuccessCount; + unsigned lastRunFailCount; + long long totalRuntime; + + TestObjName suiteName; + vector tests; + }; +} + +#endif diff --git a/include/internal/TestCPPUtil.h b/include/internal/TestCPPUtil.h new file mode 100644 index 0000000..256c450 --- /dev/null +++ b/include/internal/TestCPPUtil.h @@ -0,0 +1,148 @@ +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + */ + +#ifndef TESTCPP_UTIL_ +#define TESTCPP_UTIL_ + +#include + +using std::string; + +/** + * The base namespace for all TestCPP library code. + */ +namespace TestCPP { + + /** + * @class TestObjName + * @author Jonathan Hyry + * @date 03/05/24 + * @file TestCPP.h + * @brief Provides a null-safe name for test objects. + * + * Both TestCase and TestSuite objects use this for their name to + * protect against nullptr/NULL being used to create a std::string + * and name the test with that non-null-char-*-based std::string. + */ + class TestObjName { + public: + /** + * @brief Construct an empty Test Object Name object. + * @return The empty TestObjName. + * + * This should never be called by user code but is required for + * the code to compile. + */ + TestObjName () = default; + + /** + * @brief Construct a Test Object Name object with a string + * literal or existing const char *. + * Results in the TestObjName, where it is verified that the + * name used to construct it was not null. + */ + // + // This is intended to be used for implicit conversions and copy + // initialization. + // + // cppcheck-suppress noExplicitConstructor + TestObjName (const char* name); + + /** + * @brief Get the encapsulated name for the TestCPP object that + * holds this object. + * @return The name of the TestCPP object that this object + * names. + */ + const string& getName (); + + /** + * @brief Output the test object name to the specified stream. + * @param s The stream to output to. + * @param tcName The test object name object. + * @return The stream for chaining. + */ + friend std::ostream& operator<< ( + std::ostream& s, + TestObjName& tcName + ); + + private: + string testCaseName; + }; + + /** + * The namespace for general TestCPP utility code. + */ + namespace Util { + + /** + * This type is for suppressing exit-time destructors for statics. + */ + template class no_destroy { + alignas(T) unsigned char data[sizeof(T)]; + public: + template no_destroy(Ts&&... ts) { new (data) T(std::forward(ts)...); } + T& get() { return *reinterpret_cast(data); } + }; + + /** + * @brief Log a message that will only be output when debug + * logging is enabled. + * + * @param message The debug message to log. + * @param omitNewline Whether or not to end the log with a + * newline character. + * Defaults to false. + */ + void debugLog (const string& message, bool omitNewline = false); + + /** + * @brief Check if a std::string is contained within another + * std::string; this exists since we're not using C++26 + * here. + * + * @param source The base std::string to check within. + * @param contains Checks the base std::string to see if it + * contains this std::string. + * + * @return True if source includes contains in whole, false + * otherwise. + */ + bool stringContains (const string& source, + const string& contains); + + /** + * @brief Safely converts unsigned integer values to signed. + * @param toCast The unsigned value to convert. + * @return The signed value equivalent to the unsigned value. + */ + int unsignedToSigned(unsigned toCast); + } +} + +#endif diff --git a/res/img/testcpp-inst-banner.bmp b/res/img/testcpp-inst-banner.bmp new file mode 100644 index 0000000..518de64 Binary files /dev/null and b/res/img/testcpp-inst-banner.bmp differ diff --git a/res/img/testcpp-inst-dialog.bmp b/res/img/testcpp-inst-dialog.bmp new file mode 100644 index 0000000..604d679 Binary files /dev/null and b/res/img/testcpp-inst-dialog.bmp differ diff --git a/res/img/testcpp.ico b/res/img/testcpp.ico new file mode 100644 index 0000000..a0a8773 Binary files /dev/null and b/res/img/testcpp.ico differ diff --git a/res/img/testcpp.png b/res/img/testcpp.png new file mode 100644 index 0000000..68361dc Binary files /dev/null and b/res/img/testcpp.png differ diff --git a/src/TestCPP.cpp b/src/TestCPP.cpp deleted file mode 100644 index aa287e5..0000000 --- a/src/TestCPP.cpp +++ /dev/null @@ -1,612 +0,0 @@ -/* -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or -distribute this software, either in source code form or as a compiled -binary, for any purpose, commercial or non-commercial, and by any -means. - -In jurisdictions that recognize copyright laws, the author or authors -of this software dedicate any and all copyright interest in the -software to the public domain. We make this dedication for the benefit -of the public at large and to the detriment of our heirs and -successors. We intend this dedication to be an overt act of -relinquishment in perpetuity of all present and future rights to this -software under copyright law. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -For more information, please refer to - */ - -//Original author: Jonathan Hyry CSU-Fullerton SECS 6896-02 Fall 2014/15 - -#include "TestCPP.h" - -#ifdef TESTCPP_STACKTRACE_ENABLED -#include -#endif - -#include - -#include "TestCPPUtil.h" - -using TestCPP::Util::debugLog; -using std::cerr; -using std::clog; -using std::cout; -using std::current_exception; -using std::endl; -using std::exception; -using std::exception_ptr; -using std::fixed; -using std::function; -using std::invalid_argument; -using std::move; -using std::ostream; -using std::rethrow_exception; -using std::runtime_error; -using std::setprecision; -using std::string; -using std::tuple; - -namespace TestCPP { - TestCPPException::TestCPPException (const char * msg) : - runtime_error(msg) - { -#ifdef TESTCPP_STACKTRACE_ENABLED - clog << boost::stacktrace::stacktrace(); -#endif - } - TestCPPException::TestCPPException (string&& msg) : - runtime_error(move(msg)) - { -#ifdef TESTCPP_STACKTRACE_ENABLED - clog << boost::stacktrace::stacktrace(); -#endif - } - - TestFailedException::TestFailedException (const char * msg) : - TestCPPException(msg) - { -#ifdef TESTCPP_STACKTRACE_ENABLED - clog << boost::stacktrace::stacktrace(); -#endif - } - - TestFailedException::TestFailedException (string&& msg) : - TestCPPException(move(msg)) - { -#ifdef TESTCPP_STACKTRACE_ENABLED - clog << boost::stacktrace::stacktrace(); -#endif - } - - TestCaseName::TestCaseName (const char* name) { - if (name) { - this->testCaseName = name; - } - else { - throw TestCPPException("Not a valid test name!"); - } - } - - const string& TestCaseName::getTestName () { - return this->testCaseName; - } - - atomic_int TestCase::stdoutCaptureCasesConstructed; - atomic_int TestCase::logCaptureCasesConstructed; - atomic_int TestCase::stderrCaptureCasesConstructed; - atomic_int TestCase::stdoutCaptureCasesDestroyed; - atomic_int TestCase::logCaptureCasesDestroyed; - atomic_int TestCase::stderrCaptureCasesDestroyed; - - unique_ptr TestCase::stdoutBuffer = nullptr; - unique_ptr TestCase::clogBuffer = nullptr; - unique_ptr TestCase::stderrBuffer = nullptr; - unique_ptr TestCase::stdoutOriginal = nullptr; - unique_ptr TestCase::clogOriginal = nullptr; - unique_ptr TestCase::stderrOriginal = nullptr; - - TestCase::TestCase (TestCaseName&& name, - function test, - bool msg, - bool captureOut, bool captureLog, - bool captureErr, - TestCase::TestCaseOutCompareOptions opt) - { - this->notifyTestPassed = msg; - this->test = test; - - this->testName = name; - - if (captureOut) { - captureStdout(); - } - if (captureLog) { - captureClog(); - } - if (captureErr) { - captureStdErr(); - } - - this->option = opt; - } - - TestCase::TestCase (TestCase& o) { - this->outCompareOption(o.option); - this->setNotifyPassed(o.notifyTestPassed); - - this->pass = o.pass; - this->lastRunTime = o.lastRunTime; - - this->testName = o.testName; - this->test = o.test; - } - - TestCase::TestCase (TestCase&& o) { - this->outCompareOption(move(o.option)); - this->setNotifyPassed(move(o.notifyTestPassed)); - - this->pass = move(o.pass); - this->lastRunTime = move(o.lastRunTime); - - this->testName = move(o.testName); - this->test = move(o.test); - } - - TestCase::~TestCase () { - if (TestCase::stdoutBuffer != nullptr) - { - if (TestCase::stdoutCaptureCasesDestroyed == - TestCase::stdoutCaptureCasesConstructed - 1) - { - cout.rdbuf(TestCase::stdoutOriginal.release()); - TestCase::stdoutBuffer = nullptr; - } - - TestCase::stdoutCaptureCasesDestroyed += 1; - } - if (TestCase::clogBuffer != nullptr) - { - if (TestCase::logCaptureCasesDestroyed == - TestCase::logCaptureCasesConstructed - 1) - { - clog.rdbuf(TestCase::clogOriginal.release()); - TestCase::clogBuffer = nullptr; - } - - TestCase::logCaptureCasesDestroyed += 1; - } - if (TestCase::stderrBuffer != nullptr) - { - if (TestCase::stderrCaptureCasesDestroyed == - TestCase::stderrCaptureCasesConstructed - 1) - { - cerr.rdbuf(TestCase::stderrOriginal.release()); - TestCase::stderrBuffer = nullptr; - } - - TestCase::stderrCaptureCasesDestroyed += 1; - } - } - - TestCase& TestCase::operator= (TestCase& rhs) { - this->outCompareOption(rhs.option); - this->setNotifyPassed(rhs.notifyTestPassed); - - this->pass = rhs.pass; - this->lastRunTime = rhs.lastRunTime; - - this->testName = rhs.testName; - this->test = rhs.test; - - return *this; - } - - TestCase& TestCase::operator= (TestCase&& rhs) { - this->outCompareOption(move(rhs.option)); - this->setNotifyPassed(move(rhs.notifyTestPassed)); - - this->pass = move(rhs.pass); - this->lastRunTime = move(rhs.lastRunTime); - - this->testName = move(rhs.testName); - this->test = move(rhs.test); - - return *this; - } - - long long TestCase::getLastRuntime () { - return this->lastRunTime; - } - - void TestCase::logTestFailure (string reason) { - clog << fixed; - clog << setprecision(4); - clog << "Test " << this->testName << " failed! (" - << static_cast(this->lastRunTime) - /1000000000.0 << "s)" << endl; - clog << "Reason: " << reason << endl; - } - - void TestCase::runTest () { - clog << "Starting run of test " << this->testName << endl; - this->lastRunTime = duration(this->test).count(); - if (this->notifyTestPassed) { - clog << fixed; - clog << setprecision(4); - clog << "Test " << this->testName << " passed! (" - << static_cast(this->lastRunTime) - /1000000000.0 << "s)" << endl; - } - this->pass = true; - } - - bool TestCase::go () { - try { - runTest(); - return true; - } - catch(const char * errorMessage) { - this->pass = false; - logTestFailure(errorMessage); - } - catch(string errorMessage) { - this->pass = false; - logTestFailure(errorMessage); - } - catch (exception& e) { - this->pass = false; - logTestFailure(e.what()); - } - catch (...) { - this->pass = false; - logTestFailure("Unknown error occurred in test!"); - } - - return false; - } - - void TestCase::setNotifyPassed (bool notify) { - this->notifyTestPassed = notify; - } - - void TestCase::captureStdout () { - if (TestCase::stdoutCaptureCasesConstructed == - TestCase::stdoutCaptureCasesDestroyed) - { - TestCase::stdoutCaptureCasesConstructed += 1; - TestCase::stdoutBuffer = unique_ptr( - new stringstream() - ); - TestCase::stdoutOriginal = unique_ptr( - cout.rdbuf() - ); - cout.rdbuf(TestCase::stdoutBuffer->rdbuf()); - } - else { - TestCase::stdoutCaptureCasesConstructed += 1; - } - } - - void TestCase::captureClog () { - if (TestCase::logCaptureCasesConstructed == - TestCase::logCaptureCasesDestroyed) - { - TestCase::logCaptureCasesConstructed += 1; - TestCase::clogBuffer = unique_ptr( - new stringstream() - ); - TestCase::clogOriginal = unique_ptr( - clog.rdbuf() - ); - clog.rdbuf(TestCase::clogBuffer->rdbuf()); - } - else { - TestCase::logCaptureCasesConstructed += 1; - } - } - - void TestCase::captureStdErr () { - if (TestCase::stderrCaptureCasesConstructed == - TestCase::stderrCaptureCasesDestroyed) - { - TestCase::stderrCaptureCasesConstructed += 1; - TestCase::stderrBuffer = unique_ptr( - new stringstream() - ); - TestCase::stderrOriginal = unique_ptr( - cerr.rdbuf() - ); - cerr.rdbuf(TestCase::stderrBuffer->rdbuf()); - } - else { - TestCase::stderrCaptureCasesConstructed += 1; - } - } - - void TestCase::outCompareOption (TestCaseOutCompareOptions opt) { - switch (opt) { - case EXACT: - case CONTAINS: - this->option = opt; - break; - - default: - stringstream error; - error << "Unknown option " << opt; - throw TestCPPException(error.str()); - } - } - - void TestCase::clearStdoutCapture () { - if (TestCase::stdoutBuffer) { - TestCase::stdoutBuffer->str(string()); - } - } - - void TestCase::clearLogCapture () { - if (TestCase::clogBuffer) { - TestCase::clogBuffer->str(string()); - } - } - - void TestCase::clearStderrCapture () { - if (TestCase::stderrBuffer) { - TestCase::stderrBuffer->str(string()); - } - } - - bool TestCase::checkStdout (string against) { - return checkOutput(this->option, TestCase::stdoutBuffer->str(), - against); - } - - bool TestCase::checkLog (string against) { - return checkOutput(this->option, TestCase::clogBuffer->str(), - against); - } - - bool TestCase::checkStderr (string against) { - return checkOutput(this->option, TestCase::stderrBuffer->str(), - against); - } - - bool TestCase::checkOutput (TestCase::TestCaseOutCompareOptions opt, - string source, string against) - { - switch (opt) { - case EXACT: - if (source == against) { - return true; - } - else { - stringstream nomatch; - nomatch << "'" << source << "' is not equivalent to '"; - nomatch << against << "'"; - - if (this->clogOriginal != nullptr) { - ostream tmp(this->clogOriginal.get()); - tmp << nomatch.str() << endl; - tmp.flush(); - } - else { - clog << nomatch.str() << endl; - } - - return false; - } - - case CONTAINS: - if (Util::stringContains(source, against)) { - return true; - } - else { - stringstream nomatch; - nomatch << "'" << source << "' does not contain '"; - nomatch << against << "'"; - - if (this->clogOriginal != nullptr) { - ostream tmp(this->clogOriginal.get()); - tmp << nomatch.str() << endl; - tmp.flush(); - } - else { - clog << nomatch.str() << endl; - } - - return false; - } - - default: - stringstream re; - re << "Unknown comparison option! " << opt; - throw TestCPPException(re.str()); - } - } - - void TestSuite::enableTestPassedMessage () { - this->testPassedMessage = true; - for (TestCase test : this->tests) { - test.setNotifyPassed(true); - } - } - void TestSuite::disableTestPassedMessage () { - this->testPassedMessage = false; - for (TestCase test : this->tests) { - test.setNotifyPassed(false); - } - } - - void TestSuite::setSuiteName (string testSuiteName) { - if (testSuiteName.data()) { - this->suiteName = testSuiteName; - } - else { - stringstream e; - e << "An invalid string was passed as the Test Suite Name!"; - throw TestCPPException(e.str()); - } - } - - unsigned TestSuite::getLastRunFailCount () { - return this->lastRunFailCount; - } - - void TestSuite::run () { - if (this->tests.size() == 0) { - clog << "No tests to run!" << endl; - return; - } - - this->lastRunSucceeded = true; - this->lastRunFailCount = 0; - this->lastRunSuccessCount = 0; - this->totalRuntime = 0; - - clog << endl - << "Starting to run test suite '" << this->suiteName << "'" - << endl << endl; - - for (TestCase test : this->tests) { - bool testPassed = false; - try { - testPassed = test.go(); - } - catch (exception& e) { - clog << "Exception occurred during test run: " - << e.what() << endl; - } - catch (...) { - cerr << "An unknown error occurred during test run." - << endl; - } - - if (!testPassed && this->lastRunSucceeded) { - this->lastRunFailCount++; - this->lastRunSucceeded = false; - } - else if (!testPassed) { - this->lastRunFailCount++; - } - else { - this->lastRunSuccessCount++; - } - - this->totalRuntime += test.getLastRuntime(); - } - - clog << endl; - - if (this->testPassedMessage && - this->lastRunFailCount == 0) { - clog << "All '" << this->suiteName - << "' suite tests passed!" << endl; - } - - double suiteRuntimeElapsed = static_cast( - this->totalRuntime)/1000000000.0; - - clog << fixed; - clog << setprecision(0); - clog << "Finished running suite '" << this->suiteName << "' in " - << suiteRuntimeElapsed << "s ("<< this->lastRunSuccessCount - << "/" << this->tests.size() << " passed)" << endl; - clog << endl; - } - - template<> - void TestSuite::addTest (TestCase&& test) { - this->tests.emplace_back(test); - } - - void TestSuite::assertThrows ( - function shouldThrow, - string failureMessage - ) - { - try { - shouldThrow(); - } - catch (...) { - exception_ptr eptr = current_exception(); - - if (eptr) { - try { - rethrow_exception(eptr); - } - catch (const exception& e) { - clog << "assertThrows caught exception: " - << TestFailedException(e.what()).what() - << endl; - } - } - else { - clog << "Something was thrown, not sure what." << endl - << "This satisfies the assertion, so no failure is" - << " present. " - << TestFailedException("Unknown thrown object"). - what(); - } - - return; - } - - throw TestFailedException(move(failureMessage)); - } - - void TestSuite::assertNoThrows ( - function shouldNotThrow, - string failureMessage - ) - { - try { - shouldNotThrow(); - } - catch (...) { - throw TestFailedException(move(failureMessage)); - } - } - - void TestSuite::assertTrue ( - bool condition, - string failureMessage - ) - { - if (!condition) { - stringstream err; - - err << "Boolean Truth assertion failed!" << std::endl; - err << failureMessage << std::endl; - - throw TestFailedException(err.str()); - } - } - - void TestSuite::assertFalse ( - bool condition, - string failureMessage - ) - { - if (condition) { - stringstream err; - - err << "Boolean False assertion failed!" << std::endl; - err << failureMessage << std::endl; - - throw TestFailedException(err.str()); - } - } - - void TestSuite::fail(string failureMessage) { - throw TestFailedException(move(failureMessage)); - } -} diff --git a/src/TestCPPAssertions.cpp b/src/TestCPPAssertions.cpp new file mode 100644 index 0000000..6680cc2 --- /dev/null +++ b/src/TestCPPAssertions.cpp @@ -0,0 +1,123 @@ +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + */ + +#include "internal/TestCPPAssertions.h" + +using std::clog; +using std::current_exception; +using std::endl; +using std::exception; +using std::exception_ptr; +using std::function; +using std::rethrow_exception; +using std::string; +using std::stringstream; + +namespace TestCPP { + + void Assertions::assertThrows ( + function shouldThrow, + string failureMessage + ) + { + try { + shouldThrow(); + } + catch (...) { + exception_ptr eptr = current_exception(); + + if (eptr) { + try { + rethrow_exception(eptr); + } + catch (const exception& e) { + clog << "assertThrows caught exception: " + << TestFailedException(e.what()).what() + << endl; + } + } + else { + clog << "Something was thrown, not sure what." << endl + << "This satisfies the assertion, so no failure is" + << " present. " + << TestFailedException("Unknown thrown object"). + what(); + } + + return; + } + + throw TestFailedException(std::move(failureMessage)); + } + + void Assertions::assertNoThrows ( + function shouldNotThrow, + string failureMessage + ) + { + try { + shouldNotThrow(); + } + catch (...) { + throw TestFailedException(std::move(failureMessage)); + } + } + + void Assertions::assertTrue ( + bool condition, + string failureMessage + ) + { + if (!condition) { + stringstream err; + + err << "Boolean Truth assertion failed!" << endl; + err << failureMessage << endl; + + throw TestFailedException(err.str()); + } + } + + void Assertions::assertFalse ( + bool condition, + string failureMessage + ) + { + if (condition) { + stringstream err; + + err << "Boolean False assertion failed!" << endl; + err << failureMessage << endl; + + throw TestFailedException(err.str()); + } + } + + [[noreturn]] void Assertions::fail(string failureMessage) { + throw TestFailedException(std::move(failureMessage)); + } +} diff --git a/include/TestCPPUtil.h b/src/TestCPPExceptions.cpp similarity index 57% rename from include/TestCPPUtil.h rename to src/TestCPPExceptions.cpp index cae25cf..dc939c6 100644 --- a/include/TestCPPUtil.h +++ b/src/TestCPPExceptions.cpp @@ -25,20 +25,48 @@ OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to */ -#ifndef TESTCPP_UTIL_ -#define TESTCPP_UTIL_ +#include -#include +#include "internal/TestCPPExceptions.h" +#ifdef TESTCPP_STACKTRACE_ENABLED +#include +#endif + +using std::clog; using std::string; +using std::runtime_error; namespace TestCPP { - namespace Util { - void debugLog (const string& message, bool omitNewline = false); - bool stringContains (const string& source, - const string& contains); - int unsignedToSigned(unsigned toCast); + + TestCPPException::TestCPPException (const char * msg) : + runtime_error(msg) + { +#ifdef TESTCPP_STACKTRACE_ENABLED + clog << boost::stacktrace::stacktrace(); +#endif + } + TestCPPException::TestCPPException (string&& msg) : + runtime_error(std::move(msg)) + { +#ifdef TESTCPP_STACKTRACE_ENABLED + clog << boost::stacktrace::stacktrace(); +#endif } -} + TestFailedException::TestFailedException (const char * msg) : + TestCPPException(msg) + { +#ifdef TESTCPP_STACKTRACE_ENABLED + clog << boost::stacktrace::stacktrace(); #endif + } + + TestFailedException::TestFailedException (string&& msg) : + TestCPPException(std::move(msg)) + { +#ifdef TESTCPP_STACKTRACE_ENABLED + clog << boost::stacktrace::stacktrace(); +#endif + } +} diff --git a/src/TestCPPTestCase.cpp b/src/TestCPPTestCase.cpp new file mode 100644 index 0000000..1c7dc6c --- /dev/null +++ b/src/TestCPPTestCase.cpp @@ -0,0 +1,547 @@ +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + */ + +//Original author: Jonathan Hyry CSU-Fullerton SECS 6896-02 Fall 2014/15 + +#include "internal/TestCPPCommon.h" +#include "internal/TestCPPExceptions.h" +#include "internal/TestCPPTestCase.h" + +#ifdef TESTCPP_STACKTRACE_ENABLED +#include +#endif + +#include + +#include "internal/TestCPPUtil.h" + +using TestCPP::Util::debugLog; +using TestCPP::Util::no_destroy; + +using std::cerr; +using std::clog; +using std::cout; +using std::endl; +using std::exception; +using std::fixed; +using std::function; +using std::invalid_argument; +using std::rethrow_exception; +using std::runtime_error; +using std::setprecision; +using std::string; +using std::tuple; + +using TCPPNum = TestCPP::TestCPPCommon::Nums; +using TCPPStr = TestCPP::TestCPPCommon::Strings; + +namespace TestCPP { + + atomic_int TestCase::stdoutCaptureCasesConstructed; + atomic_int TestCase::logCaptureCasesConstructed; + atomic_int TestCase::stderrCaptureCasesConstructed; + atomic_int TestCase::stdoutCaptureCasesDestroyed; + atomic_int TestCase::logCaptureCasesDestroyed; + atomic_int TestCase::stderrCaptureCasesDestroyed; + + no_destroy> + TestCase::stdoutBuffer = + no_destroy>( + unique_ptr( + nullptr, [](stringstream*){} + ) + ); + no_destroy> + TestCase::clogBuffer = + no_destroy>( + unique_ptr( + nullptr, [](stringstream*){} + ) + ); + no_destroy> + TestCase::stderrBuffer = + no_destroy>( + unique_ptr( + nullptr, [](stringstream*){} + ) + ); + no_destroy> + TestCase::stdoutOriginal = + no_destroy>( + unique_ptr( + nullptr, [](streambuf*){} + ) + ); + no_destroy> + TestCase::clogOriginal = + no_destroy>( + unique_ptr( + nullptr, [](streambuf*){} + ) + ); + no_destroy> + TestCase::stderrOriginal = + no_destroy>( + unique_ptr( + nullptr, [](streambuf*){} + ) + ); + + TestCase::TestCase (TestObjName&& name, + function testFn, + bool msg, + bool captureOut, bool captureLog, + bool captureErr, + TestCase::TestCaseOutCompareOptions opt) + { + this->notifyTestPassed = msg; + this->test = testFn; + + this->testName = name; + + if (captureOut) { + captureStdout(); + } + if (captureLog) { + debugLog("CaptureLog windows segfault check - clog cap"); + captureClog(); + debugLog("CaptureLog windows segfault check - clog end"); + } + if (captureErr) { + captureStdErr(); + } + + this->stdoutCaptured = captureOut; + this->clogCaptured = captureLog; + this->stderrCaptured = captureErr; + + this->option = opt; + } + + TestCase::TestCase (TestCase& o) { + this->outCompareOption(o.option); + this->setNotifyPassed(o.notifyTestPassed); + + this->pass = o.pass; + this->lastRunTime = o.lastRunTime; + + this->stdoutCaptured = o.stdoutCaptured; + this->clogCaptured = o.clogCaptured; + this->stderrCaptured = o.stderrCaptured; + + if (this->stdoutCaptured) { + captureStdout(); + } + if (this->clogCaptured) { + captureClog(); + } + if (this->stderrCaptured) { + captureStdErr(); + } + + this->testName = o.testName; + this->test = o.test; + } + + TestCase::TestCase (TestCase&& o) noexcept { + this->outCompareOption(std::move(o.option)); + this->setNotifyPassed(std::move(o.notifyTestPassed)); + + this->pass = std::move(o.pass); + this->lastRunTime = std::move(o.lastRunTime); + + this->stdoutCaptured = std::move(o.stdoutCaptured); + this->clogCaptured = std::move(o.clogCaptured); + this->stderrCaptured = std::move(o.stderrCaptured); + + if (this->stdoutCaptured) { + captureStdout(); + } + if (this->clogCaptured) { + captureClog(); + } + if (this->stderrCaptured) { + captureStdErr(); + } + + this->testName = std::move(o.testName); + this->test = std::move(o.test); + } + + TestCase::~TestCase () { + if (TestCase::stdoutBuffer.get() != nullptr) + { + if (TestCase::stdoutCaptureCasesDestroyed == + TestCase::stdoutCaptureCasesConstructed - 1) + { + cout.rdbuf(TestCase::stdoutOriginal.get().release()); + delete TestCase::stdoutBuffer.get().get(); + TestCase::stdoutBuffer.get() = nullptr; + } + + TestCase::stdoutCaptureCasesDestroyed += 1; + } + if (TestCase::clogBuffer.get() != nullptr) + { + if (TestCase::logCaptureCasesDestroyed == + TestCase::logCaptureCasesConstructed - 1) + { + clog.rdbuf(TestCase::clogOriginal.get().release()); + delete TestCase::clogBuffer.get().get(); + TestCase::clogBuffer.get() = nullptr; + } + + TestCase::logCaptureCasesDestroyed += 1; + } + if (TestCase::stderrBuffer.get() != nullptr) + { + if (TestCase::stderrCaptureCasesDestroyed == + TestCase::stderrCaptureCasesConstructed - 1) + { + cerr.rdbuf(TestCase::stderrOriginal.get().release()); + delete TestCase::stderrBuffer.get().get(); + TestCase::stderrBuffer.get() = nullptr; + } + + TestCase::stderrCaptureCasesDestroyed += 1; + } + } + + TestCase& TestCase::operator= (TestCase& rhs) { + this->outCompareOption(rhs.option); + this->setNotifyPassed(rhs.notifyTestPassed); + + this->pass = rhs.pass; + this->lastRunTime = rhs.lastRunTime; + + this->stdoutCaptured = rhs.stdoutCaptured; + this->clogCaptured = rhs.clogCaptured; + this->stderrCaptured = rhs.stderrCaptured; + + if (this->stdoutCaptured) { + captureStdout(); + } + if (this->clogCaptured) { + captureClog(); + } + if (this->stderrCaptured) { + captureStdErr(); + } + + this->testName = rhs.testName; + this->test = rhs.test; + + return *this; + } + + TestCase& TestCase::operator= (TestCase&& rhs) noexcept { + this->outCompareOption(std::move(rhs.option)); + this->setNotifyPassed(std::move(rhs.notifyTestPassed)); + + this->pass = std::move(rhs.pass); + this->lastRunTime = std::move(rhs.lastRunTime); + + this->stdoutCaptured = std::move(rhs.stdoutCaptured); + this->clogCaptured = std::move(rhs.clogCaptured); + this->stderrCaptured = std::move(rhs.stderrCaptured); + + if (this->stdoutCaptured) { + captureStdout(); + } + if (this->clogCaptured) { + captureClog(); + } + if (this->stderrCaptured) { + captureStdErr(); + } + + this->testName = std::move(rhs.testName); + this->test = std::move(rhs.test); + + return *this; + } + + long long TestCase::getLastRuntime () const { + return this->lastRunTime; + } + + void TestCase::logFailure(ostream& out, string& reason) { + out << fixed; + out << setprecision(TCPPNum::TIME_PRECISION); + out << TCPPStr::TEST << this->testName << TCPPStr::FAIL + << TCPPStr::PARENL + << static_cast(this->lastRunTime)/ + TCPPNum::NANOS_IN_SEC + << TCPPStr::SEC << TCPPStr::PARENR + << endl; + out << TCPPStr::REASON_ << reason << endl; + } + + void TestCase::logTestFailure (string reason) { + unique_ptr logStream = nullptr; + + if (this->clogOriginal.get() != nullptr) { + logStream = unique_ptr( + new ostream(this->clogOriginal.get().get()) + ); + } + else { + logStream = unique_ptr(&clog); + } + + logFailure(*logStream, reason); + + if (this->clogOriginal.get() != nullptr) { + logStream->flush(); + + // If someone is looking for something in the message, + // and it's captured, make sure it's there. + logFailure(clog, reason); + } + + logStream.release(); + logStream.reset(); + } + + void TestCase::runTest () { + clog << TCPPStr::START_RUN << this->testName << endl; + this->lastRunTime = duration(this->test).count(); + if (this->notifyTestPassed) { + clog << fixed; + clog << setprecision(TCPPNum::TIME_PRECISION); + clog << TCPPStr::TEST << this->testName << TCPPStr::PASS + << TCPPStr::PARENL + << static_cast(this->lastRunTime)/ + TCPPNum::NANOS_IN_SEC + << TCPPStr::SEC << TCPPStr::PARENR + << endl; + } + this->pass = true; + } + + bool TestCase::go () { + try { + runTest(); + return true; + } + catch(const char * errorMessage) { + this->pass = false; + logTestFailure(errorMessage); + } + catch(string& errorMessage) { + this->pass = false; + logTestFailure(errorMessage); + } + catch (exception& e) { + this->pass = false; + logTestFailure(e.what()); + } + catch (...) { + this->pass = false; + logTestFailure(TCPPStr::UNK_EXC); + } + + return false; + } + + void TestCase::setNotifyPassed (bool notify) { + this->notifyTestPassed = notify; + } + + void TestCase::captureStdout () { + if (TestCase::stdoutCaptureCasesConstructed == + TestCase::stdoutCaptureCasesDestroyed) + { + TestCase::stdoutCaptureCasesConstructed += 1; + TestCase::stdoutBuffer = + no_destroy>( + unique_ptr( + new stringstream(), [](stringstream *) {} + ) + ); + TestCase::stdoutOriginal = + no_destroy>( + unique_ptr( + cout.rdbuf(), [](streambuf *) {} + ) + ); + cout.rdbuf(TestCase::stdoutBuffer.get()->rdbuf()); + } + else { + TestCase::stdoutCaptureCasesConstructed += 1; + } + } + + void TestCase::captureClog () { + if (TestCase::logCaptureCasesConstructed == + TestCase::logCaptureCasesDestroyed) + { + TestCase::logCaptureCasesConstructed += 1; + TestCase::clogBuffer = + no_destroy>( + unique_ptr( + new stringstream(), [](stringstream *) {} + ) + ); + TestCase::clogOriginal = + no_destroy>( + unique_ptr( + clog.rdbuf(), [](streambuf *) {} + ) + ); + clog.rdbuf(TestCase::clogBuffer.get()->rdbuf()); + } + else { + TestCase::logCaptureCasesConstructed += 1; + } + } + + void TestCase::captureStdErr () { + if (TestCase::stderrCaptureCasesConstructed == + TestCase::stderrCaptureCasesDestroyed) + { + TestCase::stderrCaptureCasesConstructed += 1; + TestCase::stderrBuffer = + no_destroy>( + unique_ptr( + new stringstream(), [](stringstream *) {} + ) + ); + TestCase::stderrOriginal = + no_destroy>( + unique_ptr( + cerr.rdbuf(), [](streambuf *) {} + ) + ); + cerr.rdbuf(TestCase::stderrBuffer.get()->rdbuf()); + } + else { + TestCase::stderrCaptureCasesConstructed += 1; + } + } + + void TestCase::outCompareOption (TestCaseOutCompareOptions opt) { + switch (opt) { + case EXACT: + case CONTAINS: + this->option = opt; + break; + + default: + stringstream error; + error << TCPPStr::UNK_OPT << opt; + throw TestCPPException(error.str()); + } + } + + void TestCase::clearStdoutCapture () { + if (TestCase::stdoutBuffer.get()) { + TestCase::stdoutBuffer.get()->str(string()); + } + } + + void TestCase::clearLogCapture () { + if (TestCase::clogBuffer.get()) { + TestCase::clogBuffer.get()->str(string()); + } + } + + void TestCase::clearStderrCapture () { + if (TestCase::stderrBuffer.get()) { + TestCase::stderrBuffer.get()->str(string()); + } + } + + bool TestCase::checkStdout (string against) { + return checkOutput(TestCase::stdoutBuffer.get()->str(), + against); + } + + bool TestCase::checkLog (string against) { + return checkOutput(TestCase::clogBuffer.get()->str(), + against); + } + + bool TestCase::checkStderr (string against) { + return checkOutput(TestCase::stderrBuffer.get()->str(), + against); + } + + bool TestCase::checkOutput (string source, string against) + { + switch (this->option) { + case EXACT: + if (source == against) { + return true; + } + else { + stringstream nomatch; + nomatch << TCPPStr::APOS << source << TCPPStr::APOS; + nomatch << TCPPStr::NEQUIV << TCPPStr::APOS; + nomatch << against << TCPPStr::APOS; + + if (this->clogOriginal.get() != nullptr) { + ostream tmp(this->clogOriginal.get().get()); + tmp << nomatch.str() << endl; + tmp.flush(); + } + else { + clog << nomatch.str() << endl; + } + + return false; + } + + case CONTAINS: + if (Util::stringContains(source, against)) { + return true; + } + else { + stringstream nomatch; + nomatch << TCPPStr::APOS << source << TCPPStr::APOS; + nomatch << TCPPStr::NCONTAIN << TCPPStr::APOS; + nomatch << against << TCPPStr::APOS; + + if (this->clogOriginal.get() != nullptr) { + ostream tmp(this->clogOriginal.get().get()); + tmp << nomatch.str() << endl; + tmp.flush(); + } + else { + clog << nomatch.str() << endl; + } + + return false; + } + + default: + stringstream re; + re << TCPPStr::UNK_CMP_OPT << this->option; + throw TestCPPException(re.str()); + } + } +} diff --git a/src/TestCPPTestSuite.cpp b/src/TestCPPTestSuite.cpp new file mode 100644 index 0000000..ecaf3d6 --- /dev/null +++ b/src/TestCPPTestSuite.cpp @@ -0,0 +1,171 @@ +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + */ + +//Original author: Jonathan Hyry CSU-Fullerton SECS 6896-02 Fall 2014/15 + +#include + +#include "internal/TestCPPCommon.h" +#include "internal/TestCPPTestCase.h" +#include "internal/TestCPPTestSuite.h" + +using std::cerr; +using std::clog; +using std::cout; +using std::endl; +using std::exception; +using std::fixed; +using std::function; +using std::invalid_argument; +using std::ostream; +using std::rethrow_exception; +using std::runtime_error; +using std::setprecision; +using std::string; +using std::tuple; + +using TCPPNum = TestCPP::TestCPPCommon::Nums; +using TCPPStr = TestCPP::TestCPPCommon::Strings; + +namespace TestCPP { + + void TestSuite::enableTestPassedMessage () { + this->testSuitePassedMessage = true; + for (unsigned i = 0; i < this->tests.size(); i += 1) { + this->tests[i].setNotifyPassed(true); + } + } + void TestSuite::disableTestPassedMessage () { + this->testSuitePassedMessage = false; + for (unsigned i = 0; i < this->tests.size(); i += 1) { + this->tests[i].setNotifyPassed(false); + } + } + + void TestSuite::setSuiteName (TestObjName&& testSuiteName) { + this->suiteName = std::move(testSuiteName); + } + + unsigned TestSuite::getLastRunFailCount () { + return this->lastRunFailCount; + } + + void TestSuite::run () { + if (this->tests.size() == 0) { + clog << TCPPStr::NTR << endl; + return; + } + + this->lastRunSucceeded = true; + this->lastRunFailCount = 0; + this->lastRunSuccessCount = 0; + this->totalRuntime = 0; + + clog << endl + << TCPPStr::START_RUN << TCPPStr::SUITE + << TCPPStr::APOS << this->suiteName << TCPPStr::APOS + << endl + << endl; + + for (TestCase test : this->tests) { + bool testPassed = false; + try { + testPassed = test.go(); + } + catch (exception& e) { + clog << TCPPStr::TEST_EXC << e.what() + << endl; + } + catch (...) { + cerr << TCPPStr::UNK_EXC + << endl; + } + + if (!testPassed) { + this->lastRunFailCount++; + + if (this->lastRunSucceeded) { + this->lastRunSucceeded = false; + } + } + else { + this->lastRunSuccessCount++; + } + + this->totalRuntime += test.getLastRuntime(); + } + + clog << endl; + + if (this->testSuitePassedMessage && + this->lastRunFailCount == 0) { + clog << TCPPStr::ALL << TCPPStr::APOS << this->suiteName + << TCPPStr::APOS << TCPPStr::SUITE_TESTS_PASSED + << endl; + } + + double suiteRuntimeElapsed = static_cast( + this->totalRuntime)/TCPPNum::NANOS_IN_SEC; + + clog << fixed; + clog << setprecision(0); + clog << TCPPStr::FINISHED_SUITE << TCPPStr::APOS + << this->suiteName << TCPPStr::APOS << TCPPStr::IN_ABOUT + << suiteRuntimeElapsed << TCPPStr::SEC << TCPPStr::SP + << TCPPStr::PARENL << this->lastRunSuccessCount + << TCPPStr::FWSL << this->tests.size() << TCPPStr::PASSED + << TCPPStr::PARENR + << endl; + } + + /** + * @brief Add a test to this test suite. + * + * The test should be a TestCase object. + */ + template<> + void TestSuite::addTest (TestCase&& test) { + this->tests.emplace_back(test); + } + + /** + * @brief Add a test to this test suite. + * + * The test should be defined as a tuple with 2 elements to use + * this. The first element is the test name, and the second + * element is the test function that defines the test. + */ + template<> + void TestSuite::addTest (tuple>&& test) { + this->tests.emplace_back( + std::get<0>(test), + std::get<1>(test), + this->testSuitePassedMessage + ); + } +} diff --git a/src/TestCPPUtil.cpp b/src/TestCPPUtil.cpp index b48693e..7d13eb1 100644 --- a/src/TestCPPUtil.cpp +++ b/src/TestCPPUtil.cpp @@ -28,10 +28,14 @@ For more information, please refer to #include #include -#include "TestCPPUtil.h" +#include "internal/TestCPPCommon.h" +#include "internal/TestCPPExceptions.h" +#include "internal/TestCPPUtil.h" #ifdef DEBUG_LOG #include +#else +#include #endif #ifdef DEBUG_LOG @@ -39,8 +43,34 @@ using std::clog; using std::endl; #endif +using TCPPStr = TestCPP::TestCPPCommon::Strings; + namespace TestCPP { + + TestObjName::TestObjName (const char* name) { + if (name) { + this->testCaseName = name; + } + else { + throw TestCPPException(TCPPStr::NVTN); + } + } + + const string& TestObjName::getName () { + return this->testCaseName; + } + + std::ostream& operator<< ( + std::ostream& s, + TestObjName& tcName + ) + { + s << tcName.getName(); + return s; + } + namespace Util { + void debugLog(const string& message, bool omitNewline) { #ifdef DEBUG_LOG clog << message; @@ -62,7 +92,7 @@ namespace TestCPP { } if (toCast >= static_cast(INT_MIN)) { - return static_cast(toCast - INT_MIN) + INT_MIN; + return static_cast(toCast - static_cast(INT_MIN)) + INT_MIN; } throw std::runtime_error( @@ -70,4 +100,4 @@ namespace TestCPP { ); } } -} \ No newline at end of file +} diff --git a/test/include/Assertions/AssertionsSuite.h b/test/include/Assertions/AssertionsSuite.h new file mode 100644 index 0000000..68596b1 --- /dev/null +++ b/test/include/Assertions/AssertionsSuite.h @@ -0,0 +1,33 @@ +#ifndef TESTCPP_ASSERTIONS_SUITE_ +#define TESTCPP_ASSERTIONS_SUITE_ + +#include "AssertionsTests.h" + +namespace TestCPP { + namespace Testing { + namespace AssertionsSuite { + TestSuite suite( + "TestCPP Assertions Tests", + + make_tuple( + "assertNull Test", + function(AssertionsTests::TestAssertNull) + ), + make_tuple( + "assertNotNull Test", + function(AssertionsTests::TestAssertNotNull) + ), + make_tuple( + "assertTrue Test", + function(AssertionsTests::TestAssertTrue) + ), + make_tuple( + "assertFalse Test", + function(AssertionsTests::TestAssertFalse) + ) + ); + } + } +} + +#endif diff --git a/test/include/Assertions/AssertionsTests.h b/test/include/Assertions/AssertionsTests.h new file mode 100644 index 0000000..9a69fb7 --- /dev/null +++ b/test/include/Assertions/AssertionsTests.h @@ -0,0 +1,15 @@ +#ifndef TESTCPP_ASSERTIONS_TESTS_ +#define TESTCPP_ASSERTIONS_TESTS_ + +namespace TestCPP { + namespace Testing { + namespace AssertionsTests { + void TestAssertTrue (); + void TestAssertFalse (); + void TestAssertNull (); + void TestAssertNotNull (); + } + } +} + +#endif diff --git a/test/include/TestCase/TestCaseSuite.h b/test/include/TestCase/TestCaseSuite.h index ba3115b..68b7bc2 100644 --- a/test/include/TestCase/TestCaseSuite.h +++ b/test/include/TestCase/TestCaseSuite.h @@ -17,6 +17,18 @@ namespace TestCPP { "Case runner Test", function(TestCaseTests::TestTestCaseGo) ), + make_tuple( + "Case runner Test - string thrown", + function(TestCaseTests::TestTestCaseGoThrowStr) + ), + make_tuple( + "Case runner Test - char thrown", + function(TestCaseTests::TestTestCaseGoThrowChr) + ), + make_tuple( + "Case runner Test - test catchall", + function(TestCaseTests::TestTestCaseGoThrowInt) + ), make_tuple( "Case setNotifyPassed Test", function( diff --git a/test/include/TestCase/TestCaseTests.h b/test/include/TestCase/TestCaseTests.h index d1569f8..90b526c 100644 --- a/test/include/TestCase/TestCaseTests.h +++ b/test/include/TestCase/TestCaseTests.h @@ -6,6 +6,9 @@ namespace TestCPP { namespace TestCaseTests { void TestConstructCase (); void TestTestCaseGo (); + void TestTestCaseGoThrowStr (); + void TestTestCaseGoThrowChr (); + void TestTestCaseGoThrowInt (); void TestTestCaseSetNotifyPassed (); } } diff --git a/test/include/TestSuite/TestSuiteSuite.h b/test/include/TestSuite/TestSuiteSuite.h index ca195ec..68cef2c 100644 --- a/test/include/TestSuite/TestSuiteSuite.h +++ b/test/include/TestSuite/TestSuiteSuite.h @@ -10,24 +10,64 @@ namespace TestCPP { "TestCPP TestSuite Tests", make_tuple( - "Suite construction Test", - function(TestSuiteTests::TestConstructSuite) + "Suite construction Test - no tests", + function( + TestSuiteTests::TestConstructSuiteBare + ) ), make_tuple( - "assertNull Test", - function(TestSuiteTests::TestAssertNull) + "Suite construction Test - TestCases", + function( + TestSuiteTests::TestConstructSuiteTestCases + ) ), make_tuple( - "assertNotNull Test", - function(TestSuiteTests::TestAssertNotNull) + "Suite construction Test - tuples", + function( + TestSuiteTests::TestConstructSuiteTuples + ) ), make_tuple( - "assertTrue Test", - function(TestSuiteTests::TestAssertTrue) + "Suite construction Test - mixed", + function( + TestSuiteTests::TestConstructSuiteMixed + ) ), make_tuple( - "assertFalse Test", - function(TestSuiteTests::TestAssertFalse) + "Suite enable test passed message - no tests", + function( + TestSuiteTests::TestEnableTestPassedMessageNoTests + ) + ), + make_tuple( + "Suite enable test passed message - one test", + function( + TestSuiteTests::TestEnableTestPassedMessageOneTest + ) + ), + make_tuple( + "Suite enable test passed message - many tests", + function( + TestSuiteTests::TestEnableTestPassedMessageManyTests + ) + ), + make_tuple( + "Suite disable test passed message - no tests", + function( + TestSuiteTests::TestDisableTestPassedMessageNoTests + ) + ), + make_tuple( + "Suite disable test passed message - one test", + function( + TestSuiteTests::TestDisableTestPassedMessageOneTest + ) + ), + make_tuple( + "Suite disable test passed message - many tests", + function( + TestSuiteTests::TestDisableTestPassedMessageManyTests + ) ) ); } diff --git a/test/include/TestSuite/TestSuiteTests.h b/test/include/TestSuite/TestSuiteTests.h index 22aeb86..878c6a6 100644 --- a/test/include/TestSuite/TestSuiteTests.h +++ b/test/include/TestSuite/TestSuiteTests.h @@ -4,11 +4,16 @@ namespace TestCPP { namespace Testing { namespace TestSuiteTests { - void TestConstructSuite (); - void TestAssertTrue (); - void TestAssertFalse (); - void TestAssertNull (); - void TestAssertNotNull (); + void TestConstructSuiteBare (); + void TestConstructSuiteTestCases (); + void TestConstructSuiteTuples (); + void TestConstructSuiteMixed (); + void TestEnableTestPassedMessageNoTests (); + void TestEnableTestPassedMessageOneTest (); + void TestEnableTestPassedMessageManyTests (); + void TestDisableTestPassedMessageNoTests (); + void TestDisableTestPassedMessageOneTest (); + void TestDisableTestPassedMessageManyTests (); } } } diff --git a/test/src/Assertions/AssertionsTests.cpp b/test/src/Assertions/AssertionsTests.cpp new file mode 100644 index 0000000..10f4634 --- /dev/null +++ b/test/src/Assertions/AssertionsTests.cpp @@ -0,0 +1,48 @@ +#include "TestCPP.h" +#include "Assertions/AssertionsTests.h" + +namespace TestCPP { + namespace Testing { + namespace AssertionsTests { + void TestAssertTrue () { + int lower = 5; + int higher = 9; + + Assertions::assertTrue(higher > lower, + "Negated condtion!"); + } + + void TestAssertFalse () { + int lower = 5; + int higher = 9; + + Assertions::assertFalse(higher < lower, + "Negated condtion!"); + } + + void TestAssertNull () { + string * nullString = nullptr; + + Assertions::assertNull(nullString, "nullptr is null!"); + } + + void TestAssertNotNull () { + string notNull("non-null"); + + Assertions::assertNotNull( + ¬Null, + "A constructed std::string is not null!" + ); + Assertions::assertNotNull( + "non-null", + "A const char * is not null!" + ); + + int testInt = 5; + + Assertions::assertNotNull(&testInt, + "An int pointer is not null!"); + } + } + } +} \ No newline at end of file diff --git a/test/src/TestCPPAssertionsMain.cpp b/test/src/TestCPPAssertionsMain.cpp new file mode 100644 index 0000000..b0e5374 --- /dev/null +++ b/test/src/TestCPPAssertionsMain.cpp @@ -0,0 +1,25 @@ +#include "TestCPP.h" + +using TestCPP::TestCase; +using TestCPP::TestSuite; +using std::string; +using std::make_tuple; +using std::function; + +#include "Assertions/AssertionsSuite.h" + +int main(void) +{ + try { + TestCPP::Testing::AssertionsSuite::suite.run(); + return TestCPP::Util::unsignedToSigned( + TestCPP::Testing::AssertionsSuite::suite. + getLastRunFailCount() + ); + } + catch (std::exception& e) { + std::cerr << "Test suite run failed with an exception: " + << e.what() << std::endl; + return -1; + } +} diff --git a/test/src/TestCPPTestCaseMain.cpp b/test/src/TestCPPTestCaseMain.cpp index 89287f2..3db3ee6 100644 --- a/test/src/TestCPPTestCaseMain.cpp +++ b/test/src/TestCPPTestCaseMain.cpp @@ -1,5 +1,4 @@ #include "TestCPP.h" -#include "TestCPPUtil.h" using TestCPP::TestCase; using TestCPP::TestSuite; diff --git a/test/src/TestCPPTestSuiteMain.cpp b/test/src/TestCPPTestSuiteMain.cpp index 61f8968..8cb0f8a 100644 --- a/test/src/TestCPPTestSuiteMain.cpp +++ b/test/src/TestCPPTestSuiteMain.cpp @@ -1,5 +1,4 @@ #include "TestCPP.h" -#include "TestCPPUtil.h" using TestCPP::TestCase; using TestCPP::TestSuite; diff --git a/test/src/TestCase/TestCaseTestChunks.cpp b/test/src/TestCase/TestCaseTestChunks.cpp index 7d6d4fe..a022227 100644 --- a/test/src/TestCase/TestCaseTestChunks.cpp +++ b/test/src/TestCase/TestCaseTestChunks.cpp @@ -1,7 +1,6 @@ #include #include "TestCPP.h" -#include "TestCPPUtil.h" #include "TestCase/TestCaseTestChunks.h" using TestCPP::TestCase; @@ -83,7 +82,6 @@ namespace TestCPP { function([](){}), false, false, true )); - test = unique_ptr(new TestCase( "ConstructCase Test - w/NF,COT,CLF", function([](){}), diff --git a/test/src/TestCase/TestCaseTests.cpp b/test/src/TestCase/TestCaseTests.cpp index 530a68d..8fef9ec 100644 --- a/test/src/TestCase/TestCaseTests.cpp +++ b/test/src/TestCase/TestCaseTests.cpp @@ -1,9 +1,10 @@ #include "TestCPP.h" -#include "TestCPPUtil.h" #include "TestCase/TestCaseTestChunks.h" using TestCPP::Util::debugLog; +using TCPPStr = TestCPP::TestCPPCommon::Strings; + namespace TestCPP { namespace Testing { namespace TestCaseTests { @@ -15,7 +16,7 @@ namespace TestCPP { )); debugLog("Construct with nullptr string"); - TestSuite::assertThrows( + Assertions::assertThrows( []() { debugLog("Construct with nullptr string", true); debugLog(" - assertThrows lambda"); @@ -41,12 +42,82 @@ namespace TestCPP { true )); - TestSuite::assertTrue( + Assertions::assertTrue( test->go(), "Should have succeeded basic no-op test!" ); } + void TestTestCaseGoThrowStr () { + const string throwStr = "Test throw string!"; + + auto test = unique_ptr(new TestCase( + "SUB-TEST TestCaseGo case Test - throws str", + function([&throwStr](){ + throw throwStr; + }), + true, false, true, false, + TestCase::TestCaseOutCompareOptions::CONTAINS + )); + + Assertions::assertFalse( + test->go(), + "Should have succeeded str throws test!" + ); + + Assertions::assertTrue( + test->checkLog(throwStr), + "Something is off, expected output does not exist!" + ); + } + + void TestTestCaseGoThrowChr () { + constexpr const char * throwChr = + "Test throw const char *!"; + + auto test = unique_ptr(new TestCase( + "SUB-TEST TestCaseGo case Test - throws chr", + function([&throwChr](){ + throw throwChr; + }), + true, false, true, false, + TestCase::TestCaseOutCompareOptions::CONTAINS + )); + + Assertions::assertFalse( + test->go(), + "Should have succeeded chr throws test!" + ); + + string tcLog(throwChr); + + Assertions::assertTrue( + test->checkLog(tcLog), + "Something is off, expected output does not exist!" + ); + } + + void TestTestCaseGoThrowInt () { + auto test = unique_ptr(new TestCase( + "SUB-TEST TestCaseGo case Test - throws int", + function([](){ + throw -1; + }), + true, false, true, false, + TestCase::TestCaseOutCompareOptions::CONTAINS + )); + + Assertions::assertFalse( + test->go(), + "Should have succeeded catchall throws test!" + ); + + Assertions::assertTrue( + test->checkLog(TCPPStr::UNK_EXC), + "Something is off, expected output does not exist!" + ); + } + void TestTestCaseSetNotifyPassed () { auto test = unique_ptr(new TestCase( "TestCaseSetNotifyPassed case Test", @@ -55,7 +126,7 @@ namespace TestCPP { TestCase::TestCaseOutCompareOptions::CONTAINS )); - TestSuite::assertTrue( + Assertions::assertTrue( test->go(), "TestSetNotifyPassed go() 1" ); @@ -64,26 +135,26 @@ namespace TestCPP { tcLog << "Test "; tcLog << "TestCaseSetNotifyPassed case Test passed! ("; - TestSuite::assertTrue( + Assertions::assertTrue( !test->checkLog(tcLog.str()), "TestSetNotifyPassed checkLog() 1" ); - TestSuite::assertTrue( + Assertions::assertTrue( !test->checkLog(string("s)")), "TestSetNotifyPassed checkLog() 2" ); test->setNotifyPassed(true); - TestSuite::assertTrue( + Assertions::assertTrue( test->go(), "TestSetNotifyPassed go() 2" ); - TestSuite::assertTrue( + Assertions::assertTrue( test->checkLog(tcLog.str()), "TestSetNotifyPassed checkLog() 3" ); - TestSuite::assertTrue( + Assertions::assertTrue( test->checkLog(string("s)")), "TestSetNotifyPassed checkLog() 4" ); @@ -91,16 +162,16 @@ namespace TestCPP { test->clearLogCapture(); test->setNotifyPassed(false); - TestSuite::assertTrue( + Assertions::assertTrue( test->go(), "TestSetNotifyPassed go() 3" ); - TestSuite::assertTrue( + Assertions::assertTrue( !test->checkLog(tcLog.str()), "TestSetNotifyPassed checkLog() 5" ); - TestSuite::assertTrue( + Assertions::assertTrue( !test->checkLog(string("s)")), "TestSetNotifyPassed checkLog() 6" ); diff --git a/test/src/TestSuite/TestSuiteTests.cpp b/test/src/TestSuite/TestSuiteTests.cpp index 6ee58d7..52189a1 100644 --- a/test/src/TestSuite/TestSuiteTests.cpp +++ b/test/src/TestSuite/TestSuiteTests.cpp @@ -4,50 +4,538 @@ namespace TestCPP { namespace Testing { namespace TestSuiteTests { - void TestConstructSuite () { - auto test = unique_ptr(new TestSuite( - "Suite construction" + void TestConstructSuiteBare () { + auto testSuite = unique_ptr(new TestSuite( + "Suite construction - bare" )); } + void TestConstructSuiteTestCases () { + TestCase test1("dummy 1", [](){}), + test2("dummy 2", [](){}); - void TestAssertTrue () { - int lower = 5; - int higher = 9; + auto testSuite = unique_ptr(new TestSuite( + "Suite construction - TestCases", + test1 + )); + testSuite = unique_ptr(new TestSuite( + "Suite construction - TestCases", + test1, test2 + )); + } + void TestConstructSuiteTuples () { + auto testSuite = unique_ptr(new TestSuite( + "Suite construction - tuples", + make_tuple("dummy 1", function([](){})) + )); + testSuite = unique_ptr(new TestSuite( + "Suite construction - tuples", + make_tuple("dummy 1", function([](){})), + make_tuple("dummy 2", function([](){})) + )); + } + void TestConstructSuiteMixed () { + TestCase test1("dummy 1", [](){}), + test2("dummy 2", [](){}); + auto testSuite = unique_ptr(new TestSuite( + "Suite construction - mixed", + test1, + make_tuple("dummy 1", function([](){})) + )); + testSuite = unique_ptr(new TestSuite( + "Suite construction - mixed", + test1, test2, + make_tuple("dummy 1", function([](){})) + )); + testSuite = unique_ptr(new TestSuite( + "Suite construction - mixed", + test1, + make_tuple("dummy 1", function([](){})), + make_tuple("dummy 2", function([](){})) + )); + testSuite = unique_ptr(new TestSuite( + "Suite construction - mixed", + test1, test2, + make_tuple("dummy 1", function([](){})), + make_tuple("dummy 2", function([](){})) + )); + testSuite = unique_ptr(new TestSuite( + "Suite construction - mixed", + test1, + make_tuple("dummy 1", function([](){})), + make_tuple("dummy 2", function([](){})), + test2 + )); + testSuite = unique_ptr(new TestSuite( + "Suite construction - mixed", + make_tuple("dummy 1", function([](){})), + test1, test2, + make_tuple("dummy 2", function([](){})) + )); + testSuite = unique_ptr(new TestSuite( + "Suite construction - mixed", + test1, + make_tuple("dummy 1", function([](){})), + test2, + make_tuple("dummy 2", function([](){})) + )); + testSuite = unique_ptr(new TestSuite( + "Suite construction - mixed", + make_tuple("dummy 1", function([](){})), + test2, + make_tuple("dummy 2", function([](){})), + test1 + )); + } - TestSuite::assertTrue(higher > lower, - "Negated condtion!"); + void TestEnableTestPassedMessageNoTests() { + auto testSuite = unique_ptr(new TestSuite( + "SUBSUITE - Test Passed Message Enabled - no tests" + )); + testSuite->enableTestPassedMessage(); + testSuite->run(); } + void TestEnableTestPassedMessageOneTest() { + const char * suiteName = + "SUBSUITE - Test Passed Message Enabled - one test"; + const char * testName = + "SUBTEST - enable test passed message dummy 1"; + + TestCase test( + testName, + [](){}, + false, + false, + true, + false + ); + + auto testSuite = unique_ptr(new TestSuite( + suiteName, test + )); + + testSuite->enableTestPassedMessage(); + testSuite->run(); + + stringstream tsLog; + tsLog << "All '"; + tsLog << suiteName; + tsLog << "' suite tests passed!"; + + stringstream tcLog; + tcLog << "Test "; + tcLog << testName; + tcLog << " passed! ("; + + string secondsParen = "s)"; + + Assertions::assertTrue( + test.checkLog(tcLog.str()), + "Should have notified on test passage! 1" + ); + Assertions::assertTrue( + test.checkLog(secondsParen), + "Should have notified on test passage! 2" + ); + Assertions::assertTrue( + test.checkLog(tsLog.str()), + "Should have notified on suite passage! 1" + ); + + test.clearLogCapture(); - void TestAssertFalse () { - int lower = 5; - int higher = 9; + test = TestCase( + testName, + [](){}, + true, + false, + true, + false + ); + + testSuite = unique_ptr(new TestSuite( + suiteName, test + )); + + testSuite->enableTestPassedMessage(); + testSuite->run(); - TestSuite::assertFalse(higher < lower, - "Negated condtion!"); + Assertions::assertTrue( + test.checkLog(tcLog.str()), + "Should have notified on test passage! 3" + ); + Assertions::assertTrue( + test.checkLog(secondsParen), + "Should have notified on test passage! 4" + ); + Assertions::assertTrue( + test.checkLog(tsLog.str()), + "Should have notified on suite passage! 2" + ); } + void TestEnableTestPassedMessageManyTests() { + const char * suiteName = + "SUBSUITE - Test Passed Message Enabled - many tests"; + const char * test1Name = + "SUBTEST - enable test passed message dummy 1"; + const char * test2Name = + "SUBTEST - enable test passed message dummy 2"; + const char * test3Name = + "SUBTEST - enable test passed message dummy 3"; + + TestCase test1( + test1Name, + [](){}, + false, + false, + true, + false + ); + TestCase test2( + test2Name, + [](){}, + false, + false, + true, + false + ); + TestCase test3( + test3Name, + [](){}, + false, + false, + true, + false + ); + + auto testSuite = unique_ptr(new TestSuite( + suiteName, test1, test2, test3 + )); + testSuite->enableTestPassedMessage(); + testSuite->run(); + + stringstream tsLog; + tsLog << "All '"; + tsLog << suiteName; + tsLog << "' suite tests passed!"; + + stringstream tc1Log; + tc1Log << "Test "; + tc1Log << test1Name; + tc1Log << " passed! ("; + stringstream tc2Log; + tc2Log << "Test "; + tc2Log << test2Name; + tc2Log << " passed! ("; + stringstream tc3Log; + tc3Log << "Test "; + tc3Log << test3Name; + tc3Log << " passed! ("; + + string secondsParen = "s)"; + + Assertions::assertTrue( + test1.checkLog(tc1Log.str()), + "Should have notified on test passage! 1" + ); + Assertions::assertTrue( + test1.checkLog(tc2Log.str()), + "Should have notified on test passage! 2" + ); + Assertions::assertTrue( + test1.checkLog(tc3Log.str()), + "Should have notified on test passage! 3" + ); + Assertions::assertTrue( + test1.checkLog(secondsParen), + "Should have notified on test passage! 4" + ); + Assertions::assertTrue( + test1.checkLog(tsLog.str()), + "Should have notified on suite passage! 1" + ); + + test1.clearLogCapture(); + + test1 = TestCase( + test1Name, + [](){}, + true, + false, + true, + false + ); + test2 = TestCase( + test2Name, + [](){}, + true, + false, + true, + false + ); + test3 = TestCase( + test3Name, + [](){}, + true, + false, + true, + false + ); + + testSuite = unique_ptr(new TestSuite( + suiteName, test1, test2, test3 + )); - void TestAssertNull () { - string * nullString = nullptr; + testSuite->enableTestPassedMessage(); + testSuite->run(); - TestSuite::assertNull(nullString, "nullptr is null!"); + Assertions::assertTrue( + test1.checkLog(tc1Log.str()), + "Should have notified on test passage! 5" + ); + Assertions::assertTrue( + test1.checkLog(tc2Log.str()), + "Should have notified on test passage! 6" + ); + Assertions::assertTrue( + test1.checkLog(tc3Log.str()), + "Should have notified on test passage! 7" + ); + Assertions::assertTrue( + test1.checkLog(secondsParen), + "Should have notified on test passage! 8" + ); + Assertions::assertTrue( + test1.checkLog(tsLog.str()), + "Should have notified on suite passage! 2" + ); } - void TestAssertNotNull () { - string notNull("non-null"); + void TestDisableTestPassedMessageNoTests() { + auto testSuite = unique_ptr(new TestSuite( + "Test Passed Message Disabled - no tests" + )); + testSuite->disableTestPassedMessage(); + testSuite->run(); + } + void TestDisableTestPassedMessageOneTest() { + const char * suiteName = + "SUBSUITE - Test Passed Message Disabled - one test"; + const char * testName = + "SUBTEST - disable test passed message dummy 1"; - TestSuite::assertNotNull( - ¬Null, - "A constructed std::string is not null!" + TestCase test( + testName, + [](){}, + false, + false, + true, + false ); - TestSuite::assertNotNull( - "non-null", - "A const char * is not null!" + + auto testSuite = unique_ptr(new TestSuite( + suiteName, test + )); + + test.clearLogCapture(); + + testSuite->disableTestPassedMessage(); + testSuite->run(); + + stringstream tsLog; + tsLog << "All '"; + tsLog << suiteName; + tsLog << "' suite tests passed!"; + + stringstream tcLog; + tcLog << "Test "; + tcLog << testName; + tcLog << " passed! ("; + + string secondsParen = "s)"; + + Assertions::assertFalse( + test.checkLog(tcLog.str()), + "Should not have notified on test passage! 1" ); + Assertions::assertFalse( + test.checkLog(secondsParen), + "Should not have notified on test passage! 2" + ); + Assertions::assertFalse( + test.checkLog(tsLog.str()), + "Should not have notified on suite passage! 1" + ); + + test = TestCase( + testName, + [](){}, + true, + false, + true, + false + ); + + testSuite = unique_ptr(new TestSuite( + suiteName, test + )); + + test.clearLogCapture(); - int testInt = 5; + testSuite->disableTestPassedMessage(); + testSuite->run(); - TestSuite::assertNotNull(&testInt, - "An int pointer is not null!"); + Assertions::assertFalse( + test.checkLog(tcLog.str()), + "Should not have notified on test passage! 3" + ); + Assertions::assertFalse( + test.checkLog(secondsParen), + "Should not have notified on test passage! 4" + ); + Assertions::assertFalse( + test.checkLog(tsLog.str()), + "Should not have notified on suite passage! 2" + ); + } + void TestDisableTestPassedMessageManyTests() { + const char * suiteName = + "SUBSUITE - Test Passed Message Disabled - many tests"; + const char * test1Name = + "SUBTEST - disable test passed message dummy 1"; + const char * test2Name = + "SUBTEST - disable test passed message dummy 2"; + const char * test3Name = + "SUBTEST - disable test passed message dummy 3"; + + TestCase test1( + test1Name, + [](){}, + false, + false, + true, + false + ); + TestCase test2( + test2Name, + [](){}, + false, + false, + true, + false + ); + TestCase test3( + test3Name, + [](){}, + false, + false, + true, + false + ); + + auto testSuite = unique_ptr(new TestSuite( + suiteName, test1, test2, test3 + )); + + test1.clearLogCapture(); + + testSuite->disableTestPassedMessage(); + testSuite->run(); + + stringstream tsLog; + tsLog << "All '"; + tsLog << suiteName; + tsLog << "' suite tests passed!"; + + stringstream tc1Log; + tc1Log << "Test "; + tc1Log << test1Name; + tc1Log << " passed! ("; + stringstream tc2Log; + tc2Log << "Test "; + tc2Log << test2Name; + tc2Log << " passed! ("; + stringstream tc3Log; + tc3Log << "Test "; + tc3Log << test3Name; + tc3Log << " passed! ("; + + string secondsParen = "s)"; + + Assertions::assertFalse( + test1.checkLog(tc1Log.str()), + "Should not have notified on test passage! 1" + ); + Assertions::assertFalse( + test1.checkLog(tc2Log.str()), + "Should not have notified on test passage! 2" + ); + Assertions::assertFalse( + test1.checkLog(tc3Log.str()), + "Should not have notified on test passage! 3" + ); + Assertions::assertFalse( + test1.checkLog(secondsParen), + "Should not have notified on test passage! 4" + ); + Assertions::assertFalse( + test1.checkLog(tsLog.str()), + "Should not have notified on suite passage! 1" + ); + + test1 = TestCase( + test1Name, + [](){}, + true, + false, + true, + false + ); + test2 = TestCase( + test2Name, + [](){}, + true, + false, + true, + false + ); + test3 = TestCase( + test3Name, + [](){}, + true, + false, + true, + false + ); + + testSuite = unique_ptr(new TestSuite( + suiteName, test1, test2, test3 + )); + + test1.clearLogCapture(); + + testSuite->disableTestPassedMessage(); + testSuite->run(); + + Assertions::assertFalse( + test1.checkLog(tc1Log.str()), + "Should not have notified on test passage! 5" + ); + Assertions::assertFalse( + test1.checkLog(tc2Log.str()), + "Should not have notified on test passage! 6" + ); + Assertions::assertFalse( + test1.checkLog(tc3Log.str()), + "Should not have notified on test passage! 7" + ); + Assertions::assertFalse( + test1.checkLog(secondsParen), + "Should not have notified on test passage! 8" + ); + Assertions::assertFalse( + test1.checkLog(tsLog.str()), + "Should not have notified on suite passage! 2" + ); } } }