├── .clang-format ├── .clang-tidy ├── .gitattributes ├── .github └── workflows │ ├── doc.yml │ └── test.yml ├── CHANGELOG.md ├── CMakeLists.txt ├── CODE-OF-CONDUCT.md ├── Makefile ├── README.md ├── UNLICENSE ├── cmake └── Config.cmake.in ├── doc ├── book.toml ├── css │ └── custom.css ├── js │ └── custom.js └── src │ ├── SUMMARY.md │ ├── compile-time-configuration.md │ ├── design │ ├── auto-registration.md │ ├── index.md │ └── options.md │ ├── getting-started.md │ ├── gotchas.md │ ├── guides.md │ ├── index.md │ └── reference │ ├── assertions.md │ ├── building-blocks.md │ ├── constants.md │ ├── framework.md │ ├── index.md │ └── runner.md ├── img ├── failed.png └── passed.png ├── include └── rexo.h ├── tests ├── assertion-coverage.c ├── assertion-failure-messages.c ├── c89 │ ├── assertion-coverage.c │ ├── assertion-failure-messages.c │ ├── config-inherit.c │ ├── fixture-void.c │ ├── fixture.c │ └── minimal.c ├── config-inherit.c ├── config-skip.c ├── cpp98 │ ├── assertion-coverage.cpp │ ├── assertion-failure-messages.cpp │ ├── config-inherit.cpp │ ├── fixture-void.cpp │ ├── fixture.cpp │ └── minimal.cpp ├── empty.c ├── explicit.c ├── fixture-data-only.c ├── fixture-void.c ├── fixture.c ├── minimal.c ├── no-discovery.c └── semi-explicit.c └── tools └── generate-assertion-macros.py /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # Minimum version required for clang-format: 5.0 3 | Language: Cpp 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Right 9 | AlignOperands: true 10 | AlignTrailingComments: false 11 | AllowAllParametersOfDeclarationOnNextLine: false 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: None 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: All 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: true 21 | BinPackArguments: false 22 | BinPackParameters: false 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: true 28 | AfterNamespace: true 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | BeforeCatch: true 33 | BeforeElse: false 34 | IndentBraces: false 35 | BreakAfterJavaFieldAnnotations: false 36 | BreakBeforeBinaryOperators: All 37 | BreakBeforeBraces: Custom 38 | BreakBeforeInheritanceComma: true 39 | BreakBeforeTernaryOperators: true 40 | BreakConstructorInitializers: BeforeComma 41 | BreakStringLiterals: true 42 | ColumnLimit: 80 43 | CommentPragmas: '^ IWYU pragma:' 44 | CompactNamespaces: false 45 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 46 | ConstructorInitializerIndentWidth: 4 47 | ContinuationIndentWidth: 4 48 | Cpp11BracedListStyle: true 49 | DerivePointerAlignment: false 50 | DisableFormat: false 51 | FixNamespaceComments: true 52 | ForEachMacros: [] 53 | IncludeCategories: 54 | - Regex: '.*' 55 | Priority: 1 56 | IncludeIsMainRegex: '$' 57 | IndentCaseLabels: true 58 | IndentWidth: 4 59 | IndentWrappedFunctionNames: false 60 | JavaScriptQuotes: Leave 61 | JavaScriptWrapImports: true 62 | KeepEmptyLinesAtTheStartOfBlocks: false 63 | MacroBlockBegin: '' 64 | MacroBlockEnd: '' 65 | MaxEmptyLinesToKeep: 1 66 | NamespaceIndentation: None 67 | ObjCBlockIndentWidth: 4 68 | ObjCSpaceAfterProperty: true 69 | ObjCSpaceBeforeProtocolList: true 70 | PenaltyBreakAssignment: 10 71 | PenaltyBreakBeforeFirstCallParameter: 1 72 | PenaltyBreakComment: 10 73 | PenaltyBreakFirstLessLess: 10 74 | PenaltyBreakString: 20 75 | PenaltyExcessCharacter: 1000 76 | PenaltyReturnTypeOnItsOwnLine: 0 77 | PointerAlignment: Right 78 | ReflowComments: true 79 | SortIncludes: false 80 | SortUsingDeclarations: true 81 | SpaceAfterCStyleCast: false 82 | SpaceAfterTemplateKeyword: false 83 | SpaceBeforeAssignmentOperators: true 84 | SpaceBeforeParens: ControlStatements 85 | SpaceInEmptyParentheses: false 86 | SpacesBeforeTrailingComments: 1 87 | SpacesInAngles: false 88 | SpacesInCStyleCastParentheses: false 89 | SpacesInContainerLiterals: true 90 | SpacesInParentheses: false 91 | SpacesInSquareBrackets: false 92 | Standard: Cpp11 93 | TabWidth: 4 94 | UseTab: Never 95 | ... 96 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | # Minimum version required for clang-tidy: 5.0 3 | Checks: '*,-clang-analyzer-valist.Uninitialized,-llvm-header-guard,-llvm-include-order' 4 | WarningsAsErrors: '' 5 | HeaderFilterRegex: '' 6 | AnalyzeTemporaryDtors: false 7 | FormatStyle: file 8 | ... 9 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.h linguist-language=C 2 | -------------------------------------------------------------------------------- /.github/workflows/doc.yml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | main: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Check out 13 | uses: actions/checkout@v2 14 | 15 | - name: Set up 16 | uses: peaceiris/actions-mdbook@v1 17 | with: 18 | mdbook-version: 'latest' 19 | 20 | - name: Build 21 | run: mdbook build ./doc 22 | 23 | - name: Deploy 24 | uses: peaceiris/actions-gh-pages@v3 25 | with: 26 | github_token: ${{ secrets.GITHUB_TOKEN }} 27 | publish_dir: ./doc/book 28 | force_orphan: true 29 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [ push ] 4 | 5 | jobs: 6 | main: 7 | strategy: 8 | matrix: 9 | name: [ 10 | Ubuntu | Clang, 11 | Ubuntu | GCC, 12 | macOS | Clang, 13 | macOS | GCC, 14 | Windows Server, 15 | ] 16 | include: 17 | - name: Ubuntu | Clang 18 | os: ubuntu-latest 19 | cc: clang 20 | cxx: clang++ 21 | 22 | - name: Ubuntu | GCC 23 | os: ubuntu-latest 24 | cc: gcc 25 | cxx: g++ 26 | 27 | - name: macOS | Clang 28 | os: macos-latest 29 | cc: clang 30 | cxx: clang++ 31 | 32 | - name: macOS | GCC 33 | os: macos-latest 34 | cc: gcc 35 | cxx: g++ 36 | 37 | - name: Windows Server 38 | os: windows-latest 39 | cc: cl 40 | cxx: cl 41 | runs-on: ${{ matrix.os }} 42 | steps: 43 | - name: Check out 44 | uses: actions/checkout@v2 45 | 46 | - name: Create the build directory 47 | run: cmake -E make_directory ${{ runner.workspace }}/build 48 | 49 | - name: Configure CMake 50 | shell: bash 51 | working-directory: ${{ runner.workspace }}/build 52 | run: | 53 | cmake \ 54 | -D CMAKE_C_COMPILER=${{ matrix.cc }} \ 55 | -D CMAKE_CXX_COMPILER=${{ matrix.cxx }} \ 56 | $GITHUB_WORKSPACE 57 | 58 | - name: Build (debug) 59 | working-directory: ${{ runner.workspace }}/build 60 | shell: bash 61 | run: cmake --build . --config Debug 62 | 63 | - name: Test (debug) 64 | working-directory: ${{ runner.workspace }}/build 65 | shell: bash 66 | run: ctest -C Debug --output-on-failure 67 | 68 | - name: Build (release) 69 | working-directory: ${{ runner.workspace }}/build 70 | shell: bash 71 | run: cmake --build . --config Release 72 | 73 | - name: Test (release) 74 | working-directory: ${{ runner.workspace }}/build 75 | shell: bash 76 | run: ctest -C Release --output-on-failure 77 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | Version numbers comply with the [Sementic Versioning Specification (SemVer)]. 5 | 6 | 7 | ## [v0.2.3] (2021-10-15) 8 | 9 | ### Added 10 | 11 | * Macro `RX_DISABLE_TEST_DISCOVERY` to disable the automatic discovery of tests. 12 | * Support for MinGW. 13 | 14 | 15 | ### Changed 16 | 17 | * Disable address sanitizer for custom data sections. 18 | * Disable test discovery for unsupported compilers. 19 | * Bump Contributor Covenant's code of conduct to version 2.1. 20 | 21 | 22 | ### Fixed 23 | 24 | * Compilation with MSVC in C++ mode failing. 25 | 26 | 27 | ## [v0.2.2] (2021-08-04) 28 | 29 | ### Changed 30 | 31 | * Append a slash character as required by ‘vcpkg’. 32 | 33 | 34 | ## [v0.2.1] (2021-07-22) 35 | 36 | ### Added 37 | 38 | * Checks for some printf-style functions. 39 | 40 | 41 | ## [v0.2.0] (2021-07-13) 42 | 43 | ### Added 44 | 45 | * Support for automatic registration of tests. 46 | * Test suites to group test cases. 47 | * Configuration of test suites, test cases, and test fixtures. 48 | * Designated initializer-like syntax. 49 | * GitHub documentation and test workflows. 50 | * Tests covering most code paths and the supported language standards. 51 | * Version constant. 52 | * Comprehensive documentation. 53 | * Code of conduct file. 54 | * Readme file. 55 | 56 | 57 | ### Changed 58 | 59 | * Code style to snake case. 60 | * Strip the intermediate ‘rexo’ include folder. 61 | * Switch the license from MIT to Unlicense. 62 | 63 | 64 | ## v0.1.0 (2018-06-11) 65 | 66 | * Initial release. 67 | 68 | 69 | [Sementic Versioning Specification (SemVer)]: https://semver.org 70 | [v0.2.3]: https://github.com/christophercrouzet/rexo/compare/v0.2.2...v0.2.3 71 | [v0.2.2]: https://github.com/christophercrouzet/rexo/compare/v0.2.1...v0.2.2 72 | [v0.2.1]: https://github.com/christophercrouzet/rexo/compare/v0.2.0...v0.2.1 73 | [v0.2.0]: https://github.com/christophercrouzet/rexo/compare/v0.1.0...v0.2.0 74 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | project(Rexo 4 | VERSION 0.2.3 5 | LANGUAGES C CXX) 6 | 7 | # ------------------------------------------------------------------------------ 8 | 9 | include(CMakePackageConfigHelpers) 10 | include(GNUInstallDirs) 11 | 12 | # ------------------------------------------------------------------------------ 13 | 14 | set(CMAKE_C_STANDARD 99) 15 | set(CMAKE_C_STANDARD_REQUIRED ON) 16 | set(CMAKE_C_EXTENSIONS OFF) 17 | set(CMAKE_CXX_STANDARD 11) 18 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 19 | set(CMAKE_CXX_EXTENSIONS OFF) 20 | 21 | if(MSVC) 22 | add_compile_options(/W4) 23 | else() 24 | add_compile_options( 25 | -Wpedantic -Wall -Wextra -Waggregate-return -Wcast-align -Wcast-qual 26 | -Wconversion -Wfloat-equal -Wpointer-arith -Wshadow -Wstrict-overflow=5 27 | -Wswitch -Wswitch-default -Wundef -Wunreachable-code -Wwrite-strings 28 | -Wformat-nonliteral -fsanitize=undefined,address) 29 | add_link_options( 30 | -fsanitize=undefined,address 31 | ) 32 | endif() 33 | 34 | if(UNIX) 35 | add_definitions(-D_POSIX_C_SOURCE=199309L) 36 | endif() 37 | 38 | # ------------------------------------------------------------------------------ 39 | 40 | add_library(rexo INTERFACE) 41 | target_include_directories(rexo 42 | INTERFACE 43 | "$" 44 | "$") 45 | install( 46 | TARGETS rexo 47 | EXPORT ${PROJECT_NAME}Targets) 48 | add_library(${PROJECT_NAME}::rexo ALIAS rexo) 49 | 50 | if(APPLE) 51 | target_compile_options(rexo INTERFACE -Wno-implicit-function-declaration) 52 | endif() 53 | 54 | if(UNIX) 55 | target_link_libraries(rexo INTERFACE m) 56 | endif() 57 | 58 | # ------------------------------------------------------------------------------ 59 | 60 | option(REXO_BUILD_TESTS "Build the test targets for Rexo" ON) 61 | 62 | if(REXO_BUILD_TESTS) 63 | set(RX_TEST_TARGETS) 64 | 65 | macro(rx_add_test) 66 | set(RX_ADD_TEST_OPTIONS) 67 | set(RX_ADD_TEST_SINGLE_VALUE_ARGS NAME) 68 | set(RX_ADD_TEST_MULTI_VALUE_ARGS FILES DEPENDS PROPERTIES) 69 | cmake_parse_arguments( 70 | RX_ADD_TEST 71 | "${RX_ADD_TEST_OPTIONS}" 72 | "${RX_ADD_TEST_SINGLE_VALUE_ARGS}" 73 | "${RX_ADD_TEST_MULTI_VALUE_ARGS}" 74 | ${ARGN}) 75 | 76 | add_executable(test-${RX_ADD_TEST_NAME} ${RX_ADD_TEST_FILES}) 77 | set_target_properties(test-${RX_ADD_TEST_NAME} 78 | PROPERTIES 79 | RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/tests 80 | OUTPUT_NAME ${RX_ADD_TEST_NAME} 81 | ${RX_ADD_TEST_PROPERTIES}) 82 | target_link_libraries(test-${RX_ADD_TEST_NAME} 83 | PRIVATE ${RX_ADD_TEST_DEPENDS}) 84 | add_test( 85 | NAME ${RX_ADD_TEST_NAME} 86 | COMMAND test-${RX_ADD_TEST_NAME}) 87 | list(APPEND RX_TEST_TARGETS test-${RX_ADD_TEST_NAME}) 88 | endmacro() 89 | 90 | enable_testing() 91 | 92 | if(NOT MSVC) 93 | rx_add_test( 94 | NAME c89-assertion-coverage 95 | FILES tests/c89/assertion-coverage.c 96 | DEPENDS rexo 97 | PROPERTIES 98 | C_STANDARD 90) 99 | 100 | rx_add_test( 101 | NAME c89-assertion-failure-messages 102 | FILES tests/c89/assertion-failure-messages.c 103 | DEPENDS rexo 104 | PROPERTIES 105 | C_STANDARD 90) 106 | 107 | rx_add_test( 108 | NAME c89-config-inherit 109 | FILES tests/c89/config-inherit.c 110 | DEPENDS rexo 111 | PROPERTIES 112 | C_STANDARD 90) 113 | 114 | rx_add_test( 115 | NAME c89-fixture 116 | FILES tests/c89/fixture.c 117 | DEPENDS rexo 118 | PROPERTIES 119 | C_STANDARD 90) 120 | 121 | rx_add_test( 122 | NAME c89-fixture-void 123 | FILES tests/c89/fixture-void.c 124 | DEPENDS rexo 125 | PROPERTIES 126 | C_STANDARD 90) 127 | 128 | rx_add_test( 129 | NAME c89-minimal 130 | FILES tests/c89/minimal.c 131 | DEPENDS rexo 132 | PROPERTIES 133 | C_STANDARD 90) 134 | endif() 135 | 136 | rx_add_test( 137 | NAME cpp98-assertion-coverage 138 | FILES tests/cpp98/assertion-coverage.cpp 139 | DEPENDS rexo 140 | PROPERTIES 141 | CXX_STANDARD 98) 142 | 143 | rx_add_test( 144 | NAME cpp98-assertion-failure-messages 145 | FILES tests/cpp98/assertion-failure-messages.cpp 146 | DEPENDS rexo 147 | PROPERTIES 148 | CXX_STANDARD 98) 149 | 150 | rx_add_test( 151 | NAME cpp98-config-inherit 152 | FILES tests/cpp98/config-inherit.cpp 153 | DEPENDS rexo 154 | PROPERTIES 155 | CXX_STANDARD 98) 156 | 157 | rx_add_test( 158 | NAME cpp98-fixture 159 | FILES tests/cpp98/fixture.cpp 160 | DEPENDS rexo 161 | PROPERTIES 162 | CXX_STANDARD 98) 163 | 164 | rx_add_test( 165 | NAME cpp98-fixture-void 166 | FILES tests/cpp98/fixture-void.cpp 167 | DEPENDS rexo 168 | PROPERTIES 169 | CXX_STANDARD 98) 170 | 171 | rx_add_test( 172 | NAME cpp98-minimal 173 | FILES tests/cpp98/minimal.cpp 174 | DEPENDS rexo 175 | PROPERTIES 176 | CXX_STANDARD 98) 177 | 178 | rx_add_test( 179 | NAME assertion-coverage 180 | FILES tests/assertion-coverage.c 181 | DEPENDS rexo) 182 | 183 | rx_add_test( 184 | NAME assertion-failure-messages 185 | FILES tests/assertion-failure-messages.c 186 | DEPENDS rexo) 187 | 188 | rx_add_test( 189 | NAME config-inherit 190 | FILES tests/config-inherit.c 191 | DEPENDS rexo) 192 | 193 | rx_add_test( 194 | NAME config-skip 195 | FILES tests/config-skip.c 196 | DEPENDS rexo) 197 | 198 | rx_add_test( 199 | NAME empty 200 | FILES tests/empty.c 201 | DEPENDS rexo) 202 | 203 | rx_add_test( 204 | NAME explicit 205 | FILES tests/explicit.c 206 | DEPENDS rexo) 207 | 208 | rx_add_test( 209 | NAME fixture 210 | FILES tests/fixture.c 211 | DEPENDS rexo) 212 | 213 | rx_add_test( 214 | NAME fixture-data-only 215 | FILES tests/fixture-data-only.c 216 | DEPENDS rexo) 217 | 218 | rx_add_test( 219 | NAME fixture-void 220 | FILES tests/fixture-void.c 221 | DEPENDS rexo) 222 | 223 | rx_add_test( 224 | NAME minimal 225 | FILES tests/minimal.c 226 | DEPENDS rexo) 227 | 228 | rx_add_test( 229 | NAME no-discovery 230 | FILES tests/no-discovery.c 231 | DEPENDS rexo) 232 | 233 | rx_add_test( 234 | NAME semi-explicit 235 | FILES tests/semi-explicit.c 236 | DEPENDS rexo) 237 | 238 | add_custom_target(tests DEPENDS ${RX_TEST_TARGETS}) 239 | endif() 240 | 241 | # ------------------------------------------------------------------------------ 242 | 243 | install( 244 | DIRECTORY include/ 245 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 246 | 247 | # ------------------------------------------------------------------------------ 248 | 249 | set(RX_CMAKE_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) 250 | 251 | configure_package_config_file( 252 | cmake/Config.cmake.in 253 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake 254 | INSTALL_DESTINATION ${RX_CMAKE_INSTALL_DIR}) 255 | 256 | write_basic_package_version_file( 257 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake 258 | VERSION ${PROJECT_VERSION} 259 | COMPATIBILITY AnyNewerVersion) 260 | 261 | install( 262 | FILES 263 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake 264 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake 265 | DESTINATION ${RX_CMAKE_INSTALL_DIR}) 266 | 267 | install( 268 | EXPORT ${PROJECT_NAME}Targets 269 | FILE ${PROJECT_NAME}Targets.cmake 270 | NAMESPACE ${PROJECT_NAME}:: 271 | DESTINATION ${RX_CMAKE_INSTALL_DIR}) 272 | -------------------------------------------------------------------------------- /CODE-OF-CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, caste, color, religion, or sexual 10 | identity and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or advances of 31 | any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email address, 35 | without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | [christopher@crouzet.pm](mailto:christopher@crouzet.pm). 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series of 86 | actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or permanent 93 | ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within the 113 | community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.1, available at 119 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 120 | 121 | Community Impact Guidelines were inspired by 122 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 123 | 124 | For answers to common questions about this code of conduct, see the FAQ at 125 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 126 | [https://www.contributor-covenant.org/translations][translations]. 127 | 128 | [homepage]: https://www.contributor-covenant.org 129 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 130 | [Mozilla CoC]: https://github.com/mozilla/diversity 131 | [FAQ]: https://www.contributor-covenant.org/faq 132 | [translations]: https://www.contributor-covenant.org/translations 133 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROJECT_DIR := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST))))) 2 | 3 | # ------------------------------------------------------------------------------ 4 | 5 | ifndef outdir 6 | OUT_DIR := build 7 | else 8 | OUT_DIR := $(outdir) 9 | endif 10 | 11 | ifndef config 12 | CONFIG := debug 13 | else ifneq "$(filter-out debug release all,$(config))" "" 14 | $(error the 'config' option is not valid) 15 | else ifneq "$(filter all,$(config))" "" 16 | CONFIG := debug release 17 | else 18 | CONFIG := $(config) 19 | endif 20 | 21 | # ------------------------------------------------------------------------------ 22 | 23 | FILES := 24 | 25 | # ------------------------------------------------------------------------------ 26 | 27 | # $(1): build directory. 28 | # $(2): rule. 29 | define rx_forward_rule_impl = 30 | $(MAKE) -C $(1) -s $(2) 31 | endef 32 | 33 | # Forward a rule to the generated Makefiles. 34 | # $(1): rule. 35 | define rx_forward_rule = 36 | $(foreach _x,$(BUILD_DIRS), $(call \ 37 | rx_forward_rule_impl,$(_x),$(1))) 38 | endef 39 | 40 | # ------------------------------------------------------------------------------ 41 | 42 | # Create a Makefile rule. 43 | # $(1): configuration. 44 | define rx_create_makefile = 45 | $(OUT_DIR)/$(1)/Makefile: 46 | @ mkdir -p $(OUT_DIR)/$(1) 47 | @ cd $(OUT_DIR)/$(1) && cmake \ 48 | -DCMAKE_BUILD_TYPE=$(1) \ 49 | -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ 50 | $(PROJECT_DIR) 51 | 52 | BUILD_DIRS += $(OUT_DIR)/$(1) 53 | MAKE_FILES += $(OUT_DIR)/$(1)/Makefile 54 | endef 55 | 56 | $(foreach _config,$(CONFIG),$(eval $(call \ 57 | rx_create_makefile,$(_config)))) 58 | 59 | # ------------------------------------------------------------------------------ 60 | 61 | FILES += $(wildcard include/*.h) 62 | 63 | # ------------------------------------------------------------------------------ 64 | 65 | # Create the rule to build a test. 66 | # $(1): path. 67 | define rx_create_test_rules = 68 | _rule := test-$(subst /,-,$(patsubst \ 69 | tests/%.c,%,$(patsubst tests/%.cpp,%,$(1)))) 70 | 71 | 72 | $(_rule): $(MAKE_FILES) 73 | $(call rx_forward_rule,$(_rule)) 74 | 75 | FILES += $(1) 76 | 77 | .PHONY: $(_rule) 78 | endef 79 | 80 | # $(1): parent directory. 81 | define rx_find_tests_impl = 82 | $(foreach _x,$(wildcard $(1:=/*)),$(call \ 83 | rx_find_tests_impl,$(_x)) $(filter %.c %.cpp,$(_x))) 84 | endef 85 | 86 | # Recursively find all the tests. 87 | define rx_find_tests = 88 | $(call rx_find_tests_impl,tests) 89 | endef 90 | 91 | # Create the rules for each test. 92 | $(foreach _x,$(rx_find_tests),$(eval $(call \ 93 | rx_create_test_rules,$(_x)))) 94 | 95 | # ------------------------------------------------------------------------------ 96 | 97 | tests: $(MAKE_FILES) 98 | @ $(call rx_forward_rule,tests) 99 | 100 | test: tests 101 | @ $(call rx_forward_rule,test) 102 | 103 | .PHONY: tests test 104 | 105 | # ------------------------------------------------------------------------------ 106 | 107 | CLANG_VERSION := $(shell \ 108 | clang --version \ 109 | | grep version \ 110 | | sed 's/^.*version \([0-9]*\.[0-9]*\.[0-9]*\).*$$/\1/') 111 | CLANG_DIR := $(shell dirname $(shell which clang)) 112 | CLANG_INCLUDE_DIR := $(CLANG_DIR)/../lib/clang/$(CLANG_VERSION)/include 113 | 114 | # Run the formatter on a file. 115 | # $(1): file. 116 | define rx_format = 117 | clang-format -style=file $(1) | diff --color -u $(1) -; 118 | endef 119 | 120 | format: 121 | @ $(foreach _file,$(FILES),$(call \ 122 | rx_format,$(_file))) 123 | 124 | tidy: $(MAKE_FILES) 125 | @ clang-tidy $(FILES) \ 126 | -p $(firstword $(BUILD_DIRS))/compile_commands.json \ 127 | -- -I$(CLANG_INCLUDE_DIR) -Iinclude 128 | 129 | .PHONY: format tidy 130 | 131 | # ------------------------------------------------------------------------------ 132 | 133 | coverity: clean 134 | @ cov-build --dir cov-int make tests -j 4 135 | @ tar czvf rexo.tgz cov-int 136 | 137 | .PHONY: coverity 138 | 139 | # ------------------------------------------------------------------------------ 140 | 141 | install: $(MAKE_FILES) 142 | @ $(call rx_forward_rule,install) 143 | 144 | .PHONY: install 145 | 146 | # ------------------------------------------------------------------------------ 147 | 148 | clean: 149 | @ rm -rf $(OUT_DIR) cov-int rexo.tgz 150 | 151 | .PHONY: clean 152 | 153 | # ------------------------------------------------------------------------------ 154 | 155 | all: 156 | 157 | .PHONY: all 158 | 159 | .DEFAULT_GOAL := all 160 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Test][badge-test] 2 | 3 | Rexo 4 | ==== 5 | 6 | Rexo is a neat single-file cross-platform unit testing framework for C/C++. 7 | 8 | It offers the same [xUnit][xunit]-like structure than most other unit testing 9 | frameworks but aims at providing a _truly_ polished API. 10 | 11 | 12 | ## Features 13 | 14 | * **sleek**: polished API with great attention to details. 15 | * **easy**: no learning curve, it's yet another framework based on xUnit 16 | with test suites, test cases, and fixtures. 17 | * **convenient**: automatic registration of tests. 18 | * **granular**: high level or low level API? You choose. 19 | * **portable**: compatible with C89 (ANSI C) and C++. 20 | * **cross-platform**: tested on Linux, macOS, and Windows. 21 | * **simple**: straightforward implementation—KISS all the things! 22 | * **cascading configuration**: configure a whole test suite at once and/or tweak 23 | specific options for each test case. 24 | * **painless**: deployment couldn't be easier—it all fits into a single 25 | header file and has no external dependencies. 26 | 27 | 28 | But also... 29 | 30 | * fully standard compliant minus the optional automatic registration of tests 31 | that relies on a widespread compiler-specific feature. 32 | * designated initializer-like syntax to all C and C++ versions. 33 | 34 | 35 | ## Roadmap 36 | 37 | * implement a command-line option parser (e.g.: for filtering test cases). 38 | * allow choosing the output format of the summary (e.g.: jUnit XML). 39 | * support more assertion macros (e.g.: array comparison, signal handling). 40 | * improve failure messages to be more visual (e.g.: an arrow pointing 41 | where strings differ). 42 | 43 | 44 | ## Usage 45 | 46 | ### Minimal 47 | 48 | ```c 49 | #include 50 | 51 | RX_TEST_CASE(foo, bar) 52 | { 53 | RX_INT_REQUIRE_EQUAL(2 * 3 * 7, 42); 54 | } 55 | 56 | int 57 | main(int argc, const char **argv) 58 | { 59 | return rx_main(0, NULL, argc, argv) == RX_SUCCESS ? 0 : 1; 60 | } 61 | ``` 62 | 63 | ![passed](./img/passed.png) 64 | 65 | 66 | ### Fixture 67 | 68 | ```c 69 | #include 70 | 71 | struct foo_data { 72 | const char *value; 73 | }; 74 | 75 | RX_SET_UP(foo_set_up) 76 | { 77 | struct foo_data *data; 78 | 79 | data = (struct foo_data *)RX_DATA; 80 | data->value = "world!"; 81 | return RX_SUCCESS; 82 | } 83 | 84 | RX_FIXTURE(foo_fixture, struct foo_data, .set_up = foo_set_up); 85 | 86 | RX_TEST_CASE(foo, bar, .fixture = foo_fixture) 87 | { 88 | struct foo_data *data; 89 | 90 | data = (struct foo_data *)RX_DATA; 91 | RX_STR_REQUIRE_EQUAL("Hello", data->value); 92 | } 93 | 94 | int 95 | main(int argc, const char **argv) 96 | { 97 | return rx_main(0, NULL, argc, argv) == RX_SUCCESS ? 0 : 1; 98 | } 99 | ``` 100 | 101 | ![failed](img/failed.png) 102 | 103 | 104 | ## Documentation 105 | 106 | 107 | 108 | 109 | ## Repository 110 | 111 | 112 | 113 | 114 | ## License 115 | 116 | [Unlicense][unlicense]. 117 | 118 | 119 | [badge-test]: https://github.com/christophercrouzet/rexo/workflows/Test/badge.svg 120 | [unlicense]: https://unlicense.org 121 | [xunit]: https://en.wikipedia.org/wiki/XUnit 122 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /cmake/Config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include(${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake) 4 | -------------------------------------------------------------------------------- /doc/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "Rexo" 3 | authors = ["Christopher Crouzet"] 4 | description = "Rexo is a neat unit testing framework for C and C++." 5 | language = "en" 6 | multilingual = false 7 | src = "src" 8 | 9 | [output.html] 10 | additional-css = ["css/custom.css"] 11 | additional-js = ["js/custom.js"] 12 | git-repository-url = "https://github.com/christophercrouzet/rexo" 13 | -------------------------------------------------------------------------------- /doc/css/custom.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --sidebar-width: 277px; 3 | } 4 | 5 | h1 a.header:target::before, 6 | h2 a.header:target::before, 7 | h3 a.header:target::before, 8 | h4 a.header:target::before { 9 | display: none; 10 | } 11 | 12 | h1 a.header:hover::after, 13 | h2 a.header:hover::after, 14 | h3 a.header:hover::after, 15 | h4 a.header:hover::after { 16 | content: '\f0c1'; 17 | display: inline-block; 18 | font-family: 'FontAwesome'; 19 | font-size: 0.75em; 20 | font-weight: normal; 21 | margin-left: 1em; 22 | } 23 | 24 | h1 a.rx-api::before, 25 | h2 a.rx-api::before, 26 | h3 a.rx-api::before, 27 | h4 a.rx-api::before { 28 | content: '»'; 29 | display: inline-block; 30 | margin-right: 0.5em; 31 | } 32 | -------------------------------------------------------------------------------- /doc/js/custom.js: -------------------------------------------------------------------------------- 1 | (function(window, document) { 2 | var elements = document.querySelectorAll('a.header > code'); 3 | elements.forEach(function(x) { 4 | x.parentElement.className += ' rx-api'; 5 | }); 6 | })(window, window.document); 7 | -------------------------------------------------------------------------------- /doc/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | Summary 2 | ======= 3 | 4 | * [Rexo](./index.md) 5 | * [Getting Started](./getting-started.md) 6 | * [Guides](./guides.md) 7 | * [Design Rationale](./design/index.md) 8 | * [Automatic Registration](./design/auto-registration.md) 9 | * [Options Definition](./design/options.md) 10 | * [API Reference](./reference/index.md) 11 | * [Framework](./reference/framework.md) 12 | * [Assertion Macros](./reference/assertions.md) 13 | * [Runner](./reference/runner.md) 14 | * [Building Blocks](./reference/building-blocks.md) 15 | * [Constants](./reference/constants.md) 16 | * [Compile-Time Configuration](./compile-time-configuration.md) 17 | * [Gotchas](./gotchas.md) 18 | -------------------------------------------------------------------------------- /doc/src/compile-time-configuration.md: -------------------------------------------------------------------------------- 1 | Compile-Time Configuration 2 | ========================== 3 | 4 | One strength of single-file header libraries is that they allow to easily 5 | change some behaviour at compile-time by allowing to redefine some macros 6 | before including `rexo.h`. 7 | 8 | This page lists all such macros that can be overridden if desired. 9 | 10 | > **Note:** See the [guide][guide-compile-time-config] for a detailed example. 11 | 12 | 13 | ## Flag Macros 14 | 15 | ### `RX_ENABLE_EXTERNAL_LINKING` 16 | 17 | Sets the storage class qualifier of the public functions to `extern`. 18 | 19 | ```c 20 | #define RX_ENABLE_EXTERNAL_LINKING 21 | ``` 22 | 23 | If not set, the public functions are defined with the `static` qualifier 24 | instead. 25 | 26 | 27 | ### `RX_ENABLE_NPRINTF` 28 | 29 | Enables the usage of the standard `*nprintf` functions. 30 | 31 | ```c 32 | #define RX_ENABLE_NPRINTF 33 | ``` 34 | 35 | If neither the `RX_ENABLE_NPRINTF` nor the `RX_DISABLE_NPRINTF` 36 | macros are explicitly defined, the standard `*nprintf` functions are used 37 | depending on the language (C or C++) and its version. 38 | 39 | 40 | ### `RX_DISABLE_NPRINTF` 41 | 42 | Disables the usage of the standard `*nprintf` functions. 43 | 44 | ```c 45 | #define RX_DISABLE_NPRINTF 46 | ``` 47 | 48 | If neither the `RX_ENABLE_NPRINTF` nor the `RX_DISABLE_NPRINTF` 49 | macros are explicitly defined, the standard `*nprintf` functions are used 50 | depending on the language (C or C++) and its version. 51 | 52 | This macro takes precedence over 53 | the [`RX_ENABLE_NPRINTF`][macro-rx_enable-nprintf] macro. 54 | 55 | 56 | ### `RX_ENABLE_VARIADIC_MACROS` 57 | 58 | Enables the usage of variadic macros. 59 | 60 | ```c 61 | #define RX_ENABLE_VARIADIC_MACROS 62 | ``` 63 | 64 | If neither the `RX_ENABLE_VARIADIC_MACROS` nor the `RX_DISABLE_VARIADIC_MACROS` 65 | macros are explicitly defined, variadic macros are used depending on 66 | the language (C or C++) and its version. 67 | 68 | See the [variadic macros][gotcha-variadic-macros] gotcha. 69 | 70 | 71 | ### `RX_DISABLE_VARIADIC_MACROS` 72 | 73 | Disables the usage of variadic macros. 74 | 75 | ```c 76 | #define RX_DISABLE_VARIADIC_MACROS 77 | ``` 78 | 79 | If neither the `RX_ENABLE_VARIADIC_MACROS` nor the `RX_DISABLE_VARIADIC_MACROS` 80 | macros are explicitly defined, variadic macros are used depending on 81 | the language (C or C++) and its version. 82 | 83 | This macro takes precedence over 84 | the [`RX_ENABLE_VARIADIC_MACROS`][macro-rx_enable-variadic-macros] macro. 85 | 86 | See the [variadic macros][gotcha-variadic-macros] gotcha. 87 | 88 | 89 | ### `RX_ENABLE_DEBUGGING` 90 | 91 | Enables the debugging mode. 92 | 93 | ```c 94 | #define RX_ENABLE_DEBUGGING 95 | ``` 96 | 97 | If neither the `RX_ENABLE_DEBUGGING` nor the `RX_DISABLE_DEBUGGING` macros are 98 | explicitly defined, the debugging mode is enabled depending on the values of 99 | the `DEBUG` and `NDEBUG` macros. 100 | 101 | This takes precedence over 102 | the [`RX_DISABLE_DEBUGGING`][macro-rx_disable_debugging] macro. 103 | 104 | 105 | ### `RX_DISABLE_DEBUGGING` 106 | 107 | Disables the debugging mode. 108 | 109 | ```c 110 | #define RX_DISABLE_DEBUGGING 111 | ``` 112 | 113 | If neither the `RX_ENABLE_DEBUGGING` nor the `RX_DISABLE_DEBUGGING` macros are 114 | explicitly defined, the debugging mode is enabled depending on the values of 115 | the `DEBUG` and `NDEBUG` macros. 116 | 117 | 118 | ### `RX_DISABLE_LOGGING` 119 | 120 | Disables logging. 121 | 122 | ```c 123 | #define RX_DISABLE_LOGGING 124 | ``` 125 | 126 | It suppresses any log that is intended to be displayed in the shell. 127 | 128 | 129 | ### `RX_SET_LOGGING_LEVEL` 130 | 131 | Defines the logging level. 132 | 133 | ```c 134 | #define RX_SET_LOGGING_LEVEL_NONE 135 | #define RX_SET_LOGGING_LEVEL_FATAL 136 | #define RX_SET_LOGGING_LEVEL_ERROR 137 | #define RX_SET_LOGGING_LEVEL_WARNING 138 | #define RX_SET_LOGGING_LEVEL_INFO 139 | #define RX_SET_LOGGING_LEVEL_DEBUG 140 | #define RX_SET_LOGGING_LEVEL_ALL 141 | ``` 142 | 143 | The logging level can be set to only output logs of a level greater or equal to 144 | the given one, e.g.: if the macro `RX_SET_LOGGING_LEVEL_INFO` is set, 145 | then logs of the info, warning, and error levels are printed out. 146 | 147 | See the [`enum rx_log_level`][enum-rx_log_level] enumerator for a description 148 | of each level. 149 | 150 | 151 | ### `RX_DISABLE_LOG_STYLING` 152 | 153 | Disables the styling of logs in the shell. 154 | 155 | ```c 156 | #define RX_DISABLE_LOG_STYLING 157 | ``` 158 | 159 | If the [`RX_LOG`][macro-rx_log] macro hasn't been redefined, its default 160 | implementation adds colours to the logs outputted to Unix shells in order to 161 | visually highlight some bits of information and to help with readability. 162 | 163 | The `RX_DISABLE_LOG_STYLING` macro ensures that no styling is ever applied to 164 | the output logs. 165 | 166 | 167 | ### `RX_DISABLE_TEST_DISCOVERY` 168 | 169 | Disables the automatic discovery of tests. 170 | 171 | ```c 172 | #define RX_DISABLE_TEST_DISCOVERY 173 | ``` 174 | 175 | 176 | ## Type Macros 177 | 178 | ### `RX_UINT32_TYPE` 179 | 180 | Override the unsigned integer 32-bit type. 181 | 182 | ```c 183 | #define RX_UINT32_TYPE 184 | ``` 185 | 186 | See the [`rx_uint32`][type-rx_uint32] type for more info. 187 | 188 | 189 | ### `RX_UINT64_TYPE` 190 | 191 | Override the unsigned integer 64-bit type. 192 | 193 | ```c 194 | #define RX_UINT64_TYPE 195 | ``` 196 | 197 | See the [`rx_uint64`][type-rx_uint64] type for more info. 198 | 199 | 200 | ### `RX_SIZE_TYPE` 201 | 202 | Override the `size_t` type. 203 | 204 | ```c 205 | #define RX_SIZE_TYPE 206 | ``` 207 | 208 | See the [`rx_size`][type-rx_size] type for more info. 209 | 210 | 211 | ## Function-Like Macros 212 | 213 | ### `RX_ASSERT` 214 | 215 | Assertion macro. 216 | 217 | ```c 218 | #define RX_ASSERT(condition) 219 | ``` 220 | 221 | Its purpose is to output an error when the argument `condition` evaluates 222 | to zero. The default implementation runs this check only when the `NDEBUG` macro 223 | is set, otherwise it does nothing. 224 | 225 | If not redefined, the standard header file `assert.h` is included. 226 | 227 | 228 | ### `RX_MALLOC` 229 | 230 | Allocation macro. 231 | 232 | ```c 233 | #define RX_MALLOC(size) 234 | ``` 235 | 236 | Allocates the given `size` in bytes of uninitialized storage. 237 | 238 | It returns a pointer to the allocated block of memory, or `NULL` 239 | if the operation failed. 240 | 241 | If not redefined, the standard header file `stdlib.h` is included. 242 | 243 | 244 | ### `RX_REALLOC` 245 | 246 | Reallocation macro. 247 | 248 | ```c 249 | #define RX_REALLOC(ptr, size) 250 | ``` 251 | 252 | Reallocates the given block of memory pointed by `ptr`. It is being done either 253 | by expanding/shrinking the existing block of memory, if possible, or allocating 254 | a new block of memory otherwise, before copying the data over and freeing 255 | the previous block. 256 | 257 | It returns a pointer to the reallocated block of memory. 258 | 259 | If the operation failed, `NULL` is returned and the original block of memory 260 | isn't freed. 261 | 262 | If not redefined, the standard header file `stdlib.h` is included. 263 | 264 | 265 | ### `RX_FREE` 266 | 267 | Deallocation macro. 268 | 269 | ```c 270 | #define RX_FREE(ptr) 271 | ``` 272 | 273 | Deallocates the given block of memory pointed by `ptr`, that was previously 274 | allocated through [`RX_MALLOC`][macro-rx_malloc] 275 | or [`RX_REALLOC`][macro-rx_realloc]. 276 | 277 | If not redefined, the standard header file `stdlib.h` is included. 278 | 279 | 280 | ### `RX_LOG` 281 | 282 | Logging macro. 283 | 284 | ```c 285 | #define RX_LOG(level, format, ...) 286 | ``` 287 | 288 | Prints a message to `stderr` with a level defined through 289 | [`enum rx_log_level`][enum-rx_log_level]. 290 | 291 | The content of the message is defined through a combination of the `format` 292 | argument and the variadic arguments, in a similar fashion to the standard 293 | `printf()` function. 294 | 295 | 296 | [gotcha-variadic-macros]: ./gotchas.md#variadic-macros 297 | [guide-compile-time-config]: ./guides.md#compile-time-configuration 298 | 299 | [enum-rx_log_level]: ./reference/building-blocks.md#rx_log_level 300 | [macro-rx_disable_debugging]: #rx_disable_debugging 301 | [macro-rx_enable_debugging]: #rx_enable_debugging 302 | [macro-rx_enable_nprintf]: #rx_enable_nprintf 303 | [macro-rx_enable_variadic_macros]: #rx_enable_variadic_macros 304 | [macro-rx_log]: #rx_log 305 | [macro-rx_malloc]: #rx_malloc 306 | [macro-rx_realloc]: #rx_realloc 307 | [macro-rx_set_logging_level]: #rx_set_logging_level 308 | [type-rx_size]: ./reference/building-blocks#rx_size 309 | [type-rx_uint32]: ./reference/building-blocks.md#rx_uint32 310 | [type-rx_uint64]: ./reference/building-blocks.md#rx_uint64 311 | -------------------------------------------------------------------------------- /doc/src/design/auto-registration.md: -------------------------------------------------------------------------------- 1 | Automatic Registration 2 | ====================== 3 | 4 | Rexo aims at closing the gap between the C and C++ unit testing frameworks by 5 | implementing one feature that is almost a given in C++ frameworks but that is 6 | _rarely_ seen in their C counterparts: automatic registration of test suites, 7 | test cases, and fixtures. 8 | 9 | 10 | ## Common Implementation 11 | 12 | The automatic registration of such C++ frameworks is commonly achieved by 13 | defining a function to run for each test case and by having these functions 14 | added to a global array that can then be iterated over at runtime, 15 | as demonstrated in this snippet: 16 | 17 | ```c 18 | /* C++ only! */ 19 | #include 20 | 21 | struct test_case { 22 | const char *name; 23 | void (*run_fn)(); 24 | }; 25 | 26 | struct test_case *test_cases[128]; 27 | static int next_test_case_idx = 0; 28 | 29 | static int 30 | register_test_case(struct test_case *test_case) 31 | { 32 | test_cases[next_test_case_idx++] = test_case; 33 | return 0; 34 | } 35 | 36 | #define TEST_CASE(name) \ 37 | static void name(); \ 38 | struct test_case test_case_##name = {#name, name}; \ 39 | int dummy_##name = register_test_case(&test_case_##name); \ 40 | static void name() 41 | 42 | TEST_CASE(foo) 43 | { 44 | printf("Hello, world, I'm foo!\n"); 45 | } 46 | 47 | TEST_CASE(bar) 48 | { 49 | printf("Hello, world, I'm bar!\n"); 50 | } 51 | 52 | int 53 | main(void) 54 | { 55 | int i; 56 | 57 | for (i = 0; i < next_test_case_idx; ++i) { 58 | printf("running `%s`\n", test_cases[i]->name); 59 | test_cases[i]->run_fn(); 60 | } 61 | 62 | return 0; 63 | } 64 | ``` 65 | 66 | By the time that this program iterates over the test cases in `main`, 67 | the two test cases _foo_ and _bar_ have already been added to the array through 68 | a call to the `register_test_case` function. 69 | 70 | 71 | ## Enter C Land 72 | 73 | Running the previous snippet is possible in C++ because it is able to call any 74 | function _before_ executing `main`. 75 | 76 | Alas, this doesn't work in C due to additional constraints to the language: 77 | 78 | ```c 79 | #include 80 | 81 | static int 82 | get_foo() 83 | { 84 | return 123; 85 | } 86 | 87 | static int foo = get_foo(); /* only allowed in C++ :( */ 88 | 89 | int 90 | main(void) 91 | { 92 | printf("%d\n", foo); 93 | return 0; 94 | } 95 | ``` 96 | 97 | 98 | Without any standard way of providing an automatic registration framework in C, 99 | most minimalist samples from conventional frameworks look like something along 100 | these lines: 101 | 102 | 103 | ```c 104 | #include 105 | #include 106 | 107 | struct my_string { 108 | int length; 109 | const char *value; 110 | }; 111 | 112 | static int 113 | set_up(void **data) 114 | { 115 | struct my_string *s; 116 | 117 | s = (struct my_string *)malloc(sizeof *s); 118 | if (s == NULL) { 119 | return 1; 120 | } 121 | 122 | s->length = 13; 123 | s->value = "hello, world!"; 124 | 125 | *data = (void *)s; 126 | return 0; 127 | } 128 | 129 | static void 130 | tear_down(void *data) 131 | { 132 | free(data); 133 | } 134 | 135 | static void 136 | test_foo(void *data) 137 | { 138 | struct my_string *s; 139 | 140 | s = (struct my_string *)data; 141 | ASSERT_INT_EQUAL(s->length, 13); 142 | } 143 | 144 | static void 145 | test_bar(void *data) 146 | { 147 | struct my_string *s; 148 | 149 | s = (struct my_string *)data; 150 | ASSERT_STR_EQUAL(s, "hello, world!"); 151 | } 152 | 153 | static const struct test_cases cases[] = { 154 | {"foo", test_foo}, 155 | {"bar", test_bar}, 156 | }; 157 | 158 | static const struct test_suite suites[]= { 159 | {"suite", sizeof cases / sizeof cases[0], cases, set_up, tear_down}, 160 | }; 161 | 162 | int 163 | main(void) 164 | { 165 | return run(sizeof suites / sizeof suites[0], suites); 166 | } 167 | ``` 168 | 169 | Not only is this a lot of **boilerplate** but it is also fairly **error-prone** 170 | since it is easy to add new tests while forgetting to register them further down 171 | the line. 172 | 173 | 174 | ## Making It Work 175 | 176 | One solution for C is to use a compiler extension that is expected to be 177 | available for all compilers in a form or another and that provides **custom 178 | memory sections**. 179 | 180 | Custom memory sections allow grouping related objects in the same memory space 181 | and to then iterate over them at runtime. 182 | 183 | As an example, this is the gist of how such an implementation might look like 184 | for the GNU-compliant compilers: 185 | 186 | ```c 187 | #include 188 | 189 | struct test_case { 190 | const char *name; 191 | }; 192 | 193 | const struct test_case test_case_foo = {"foo"}; 194 | const struct test_case test_case_bar = {"bar"}; 195 | 196 | const struct test_case *__start_rxcases; 197 | const struct test_case *__stop_rxcases; 198 | 199 | __attribute__((section("rxcases"))) 200 | const struct test_case *test_case_foo_ptr = &test_case_foo; 201 | 202 | __attribute__((section("rxcases"))) 203 | const struct test_case *test_case_bar_ptr = &test_case_bar; 204 | 205 | int 206 | main(void) 207 | { 208 | const struct test_case **it; 209 | 210 | for (it = &__start_rxcases; it < &__stop_rxcases; ++it) { 211 | if (*it != NULL) { 212 | printf("found `%s`\n", (*it)->name); 213 | } 214 | } 215 | 216 | return 0; 217 | } 218 | ``` 219 | 220 | 221 | And here's the equivalent implementation, adapted to MSVC: 222 | 223 | ```c 224 | #include 225 | 226 | struct test_case { 227 | const char *name; 228 | }; 229 | 230 | const struct test_case test_case_foo = {"foo"}; 231 | const struct test_case test_case_bar = {"bar"}; 232 | 233 | __pragma(section("rxcases$a", read)) 234 | __pragma(section("rxcases$b", read)) 235 | __pragma(section("rxcases$c", read)) 236 | 237 | __declspec(allocate("rxcases$a")) 238 | const struct test_case *section_begin = NULL; 239 | 240 | __declspec(allocate("rxcases$c")) 241 | const struct test_case *section_end = NULL; 242 | 243 | __declspec(allocate("rxcases$b")) 244 | const struct test_case *test_case_foo_ptr = &test_case_foo; 245 | 246 | __declspec(allocate("rxcases$b")) 247 | const struct test_case *test_case_bar_ptr = &test_case_bar; 248 | 249 | int 250 | main(void) 251 | { 252 | const struct test_case **it; 253 | 254 | for (it = §ion_begin + 1; it < §ion_end; ++it) { 255 | if (*it != NULL) { 256 | printf("found `%s`\n", (*it)->name); 257 | } 258 | } 259 | 260 | return 0; 261 | } 262 | ``` 263 | -------------------------------------------------------------------------------- /doc/src/design/index.md: -------------------------------------------------------------------------------- 1 | Design Rationale 2 | ================ 3 | 4 | With dozens of well-established unit testing frameworks out there, the question 5 | _why yet another one?_ is unavoidable. 6 | 7 | The answer is fairly simple—the goal has been to try to further **polish** 8 | the user experience from existing frameworks while maintaining **compatibility** 9 | with both C89 and C++. 10 | 11 | Also, it sounded like a great exercise! 😊 12 | 13 | It is important to note that nothing is truly novel in Rexo—the main concepts 14 | are inspired by existing frameworks like [Criterion][criterion], 15 | [novaprova][novaprova], and others, although their form and implementation 16 | have been entirely revised. 17 | 18 | 19 | ## Automatic Registration 20 | 21 | These two frameworks in particular proved that it was possible to make 22 | automatic registration of tests also possible in C, where most C frameworks 23 | require a _lot_ of boilerplate to register these. 24 | 25 | The advantages that Rexo has in this regard are that: 26 | 27 | * it only relies on a **widespread** compiler-specific feature to get 28 | the automatic registration going. 29 | * it works on both **Unix** and **Windows** platforms. 30 | * it is implemented in a fairly **simple** fashion. 31 | 32 | 33 | Automatic features are always nice to have _unless_ they cannot be overridden. 34 | Rexo is designed in such a way that it is possible for the users to skip either 35 | parts or the whole automatic registration framework by allowing [explicit 36 | registration of tests][explicit-registration-guide], like more conventional C 37 | frameworks offer. 38 | 39 | See the [automatic registration][auto-registration] design page for 40 | more details. 41 | 42 | 43 | ## Options Definition 44 | 45 | [Criterion][criterion] also cleverly demonstrated that it was possible to abuse 46 | the languages to make a **named argument syntax** available for configuring 47 | objects. 48 | 49 | Rexo took this a step further by allowing test cases not only to **inherit** 50 | options from their parent test suites, but also to **tweak only specific 51 | fields** for each test case. 52 | 53 | Since **consistency** is king in design, the approach was also extended to work 54 | with fixtures, thus providing a **unified** interface to configure _all 55 | the things_. 56 | 57 | See the [options definition][options] design page for more details. 58 | 59 | 60 | [criterion]: https://github.com/Snaipe/Criterion 61 | [novaprova]: https://github.com/novaprova/novaprova 62 | 63 | [auto-registration]: ./auto-registration.md 64 | [explicit-registration-guide]: ../guides.md#explicit-registration 65 | [options]: ./options.md 66 | -------------------------------------------------------------------------------- /doc/src/design/options.md: -------------------------------------------------------------------------------- 1 | Options Definition 2 | ================== 3 | 4 | To build an expressive and intuitive framework, it was important to put 5 | a system in place that makes the experience of configuring the different objects 6 | as frictionless as possible. 7 | 8 | 9 | ## Named Arguments Syntax 10 | 11 | When it comes to setting a bunch of options at once, the most user-friendly 12 | approach is usually to define a struct where `0` (or `NULL`) corresponds to 13 | the default value for each field, and to let the users initialize instances 14 | of that struct by only setting the fields that need to have their value changed 15 | from the defaults through. 16 | 17 | This is exactly what the designated initializer syntax introduced with C99 18 | offers: 19 | 20 | ```c 21 | struct config { 22 | const char *foo; 23 | int bar; 24 | double baz; 25 | }; 26 | 27 | /* Only setting the bar field leaves foo and baz to 0. */ 28 | struct config my_config = {.bar = 123}; 29 | ``` 30 | 31 | 32 | Despite Rexo's intention of supporting C89 and C++ when both languages are 33 | not compatible with the designated initializer syntax, it was still a strong 34 | design goal to provide a similar approach for setting options. 35 | 36 | This is done by using variadic macros where the optional arguments start with 37 | a dot character: 38 | 39 | ```c 40 | MY_MACRO(arg_1, arg_2, .option_1 = 123, .option_2 = "abc"); 41 | ``` 42 | 43 | 44 | ## Options Sharing 45 | 46 | This designated initialized syntax can be used to set-up individual test cases 47 | but, to avoid redundancy, it is possible to group test cases with a similar 48 | configuration into a same test suite, configure only that test suite, and have 49 | all of its test cases automatically inherit the configuration. 50 | 51 | Not only that but it is still possible to override only specific options for 52 | individual test cases: 53 | 54 | ```c 55 | RX_TEST_SUITE(my_test_suite, .foo = 123, .bar = "abc"); 56 | 57 | RX_TEST_CASE(my_test_suite, my_test_case, .bar = "def", .baz = 1.23) 58 | { 59 | /* 60 | foo is 123. 61 | bar is "def". 62 | baz is 1.23 63 | */ 64 | } 65 | ``` 66 | 67 | 68 | ## Variadic Macros Fallback 69 | 70 | Since C89 and C++98 don't support variadic macros, an alternative set of macros 71 | is provided, each suffixed with a digit representing the number of options 72 | to pass. 73 | 74 | For example, the equivalent of the previous examples become: 75 | 76 | ```c 77 | MY_MACRO(arg_1, arg_2) 78 | MY_MACRO_1(arg_1, arg_2, .option_2 = "abc"); 79 | MY_MACRO_2(arg_1, arg_2, .option_1 = 123, .option_2 = "abc"); 80 | 81 | RX_TEST_SUITE_2(my_test_suite, .foo = 123, .bar = "abc"); 82 | 83 | RX_TEST_CASE_2(my_test_suite, my_test_case, .bar = "def", .baz = 1.23) 84 | { 85 | /* ... */ 86 | } 87 | ``` 88 | 89 | 90 | [macro-rx_fixture]: ../reference/framework.md#rx_fixture 91 | [macro-rx_void_fixture]: ../reference/framework.md#rx_void_fixture 92 | [macro-rx_test_case]: ../reference/framework.md#rx_test_case 93 | [macro-rx_test_case_fixture]: ../reference/framework.md#rx_test_case_fixture 94 | [macro-rx_test_suite]: ../reference/framework.md#rx_test_suite 95 | -------------------------------------------------------------------------------- /doc/src/getting-started.md: -------------------------------------------------------------------------------- 1 | Getting Started 2 | =============== 3 | 4 | ## Prerequisites 5 | 6 | * a C89 (ANSI C) or a C++ compiler. 7 | 8 | 9 | > **Note:** The automatic registration of tests requires a widespread 10 | > compiler-specific feature that places data in a given memory section. 11 | > The compilers currently supported are the GNU compilers ([clang][clang], 12 | > [gcc][gcc], [icc][icc]) and [MSVC][msvc]. 13 | 14 | 15 | ## Installation 16 | 17 | 18 | ### Just the One File 19 | 20 | The simplest option to be good to go is to copy the file [`rexo.h`][rexo.h] into 21 | your source directory. 22 | 23 | The folder containing the file `rexo.h` needs to be recognized as an include 24 | directory by your compiler, which will make Rexo available by using 25 | `#include ` in your code. 26 | 27 | 28 | ### The Whole Repository 29 | 30 | Instead of copying solely the `rexo.h` file, you could also grab the whole 31 | code repository, which brings in some additional features such as being able 32 | to link against the library using [CMake][cmake]. 33 | 34 | Additionally, [Git][git] can be used to add Rexo's repository as a submodule of 35 | your project, thus allowing to conveniently pull updates at any time. 36 | This can be done by running the `git submodule` command from within 37 | the directory where Rexo should be added to: 38 | 39 | ```sh 40 | git submodule add git@github.com:christophercrouzet/rexo.git 41 | ``` 42 | 43 | 44 | > **Note:** Linking against the Rexo library using CMake is really simple. 45 | > If your project _Foo_ has a structure on disk similar to this one: 46 | > ``` 47 | > foo/ 48 | > ├── deps/ 49 | > │ └── rexo/ 50 | > │ └── ... 51 | > ├── src/ 52 | > ├── tests/ 53 | > └── CMakeLists.txt 54 | > ``` 55 | > 56 | > Then you'd only have to add two lines to your own `CMakeFiles.txt` file: 57 | > ``` 58 | > add_subdirectory(deps/rexo) 59 | > target_link_libraries(foo PRIVATE rexo) 60 | > ``` 61 | 62 | 63 | ## Writing a First Test 64 | 65 | To verify that everything is correctly set-up, create a new file containing 66 | a simple test like the [minimal][minimal-guide] one from the guides section, 67 | compile it, and run it! 68 | 69 | 70 | [minimal-guide]: ./guides.md#minimal 71 | 72 | [cmake]: https://cmake.org 73 | [clang]: https://clang.llvm.org 74 | [gcc]: https://gcc.gnu.org 75 | [git]: https://git-scm.com 76 | [icc]: https://software.intel.com/en-us/c-compilers 77 | [msvc]: https://visualstudio.microsoft.com/vs/features/cplusplus 78 | [rexo.h]: https://github.com/christophercrouzet/rexo/blob/master/include/rexo.h 79 | -------------------------------------------------------------------------------- /doc/src/gotchas.md: -------------------------------------------------------------------------------- 1 | Gotchas 2 | ======= 3 | 4 | ## Variadic Macros 5 | 6 | Since variadic macros are not available as part of the C89 and C++98 7 | specifications, having 8 | the [variadic macros flag][macro-rx_enable_variadic_macros] disabled assumes 9 | no support for variadic macros and defines an alternative set of macros 10 | for each macro that would otherwise accept variadic arguments. 11 | 12 | In other words, a macro documented as `RX_DO_DOMETHING(fixed_arg, ...)` 13 | translates in fact to set of macros suffixed with the number of variadic 14 | arguments such as: 15 | 16 | * `RX_DO_DOMETHING(fixed_arg)`. 17 | * `RX_DO_DOMETHING_1(fixed_arg, var_arg_1)`. 18 | * `RX_DO_DOMETHING_2(fixed_arg, var_arg_1, var_arg_2)`. 19 | * ... 20 | * `RX_DO_DOMETHING_N(fixed_arg, var_arg_1, var_arg_2, ..., var_arg_n)`. 21 | 22 | > **Note:** The set of macros suffixed with a number is available whether the 23 | > [variadic macros flag][macro-rx_enable_variadic_macros] is enabled or not 24 | > in order to provide a compatibility layer between the two modes if needed. 25 | > The only difference comes from the base macro `RX_DO_DOMETHING` being 26 | > defined with variadic arguments or only fixed arguments otherwise. 27 | 28 | 29 | Examples of such macros can be found in the [framework][framework] and as part 30 | of the [assertion macros][assertion-macros]. 31 | 32 | 33 | [assertion-macros]: ./reference/assertions.md 34 | [framework]: ./reference/framework.md 35 | 36 | [macro-rx_enable_variadic_macros]: ./compile-time-configuration.md#rx_enable_variadic_macros 37 | -------------------------------------------------------------------------------- /doc/src/guides.md: -------------------------------------------------------------------------------- 1 | Guides 2 | ====== 3 | 4 | These guides take you over some concrete examples and how-tos covering most 5 | of Rexo's features. 6 | 7 | 8 | ## Minimal 9 | 10 | The easiest way to run a single test is to rely on the [framework][framework] 11 | by defining a test case with the [`RX_TEST_CASE`][macro-rx_test_case] macro, 12 | writing a test in there using one of the [assertion macros][assertion-macros] 13 | available, and finally running that test case by calling 14 | the [`rx_main`][fn-rx_main] function: 15 | 16 | ```c 17 | #include 18 | 19 | /* 20 | Define a new test case 'bar' that is part of an implicit test suite 'foo'. 21 | 22 | The main purpose of the `RX_TEST_CASE` macro is to declare the test case's 23 | function. 24 | */ 25 | RX_TEST_CASE(foo, bar) 26 | { 27 | /* Run a single test that compares two integer values for equality. */ 28 | RX_INT_REQUIRE_EQUAL(2 * 3 * 7, 42); 29 | } 30 | 31 | int 32 | main(int argc, const char **argv) 33 | { 34 | /* Execute the main function that runs the test cases found. */ 35 | return rx_main(0, NULL, argc, argv) == RX_SUCCESS ? 0 : 1; 36 | } 37 | ``` 38 | 39 | 40 | > **Note:** Setting the `test_cases` argument from the [`rx_main`][fn-rx_main] 41 | > function to `NULL` means that the function is responsible for finding all 42 | > the test cases that were defined using the [framework][framework]'s automatic 43 | > registration feature. 44 | 45 | > **Note:** In the example above, the [`RX_TEST_CASE`][macro-rx_test_case] macro 46 | > implicitly creates a test suite named _foo_. 47 | 48 | 49 | ## Runtime Configuration 50 | 51 | When using the [framework][framework] and its automatic registration feature, 52 | configuring test cases is done by passing optional arguments to the 53 | [`RX_TEST_CASE`][macro-rx_test_case] and/or 54 | [`RX_TEST_SUITE`][macro-rx_test_suite] macros. 55 | 56 | ```c 57 | #include 58 | 59 | #include 60 | 61 | RX_TEST_SUITE(foo, .skip = 1); 62 | 63 | /* Inherit the skip option from the test suite 'foo'. */ 64 | RX_TEST_CASE(foo, bar) 65 | { 66 | printf("this does NOT print\n"); 67 | } 68 | 69 | /* Override the skip option to run this specific test case. */ 70 | RX_TEST_CASE(foo, baz, .skip = 0) 71 | { 72 | printf("this does print!\n"); 73 | } 74 | 75 | int 76 | main(int argc, const char **argv) 77 | { 78 | return rx_main(0, NULL, argc, argv) == RX_SUCCESS ? 0 : 1; 79 | } 80 | ``` 81 | 82 | This example explicitly creates a test suite named _foo_ with the `skip` option 83 | enabled. 84 | 85 | Any option defined on a test suite is to be inherited by all the test cases of 86 | that test suite, unless overidden by a specific test case like here with _baz_. 87 | 88 | > **Note:** For a list of all the runtime options available, 89 | > see the [`rx_test_case_config`][struct-rx_test_case_config] struct. 90 | 91 | 92 | ## Fixtures 93 | 94 | Often times a dataset is required to be prepared in advance, before test cases 95 | should be run. 96 | 97 | Like with every other xUnit-like frameworks, Rexo allows this do be done using 98 | fixtures. 99 | 100 | You can define a new fixture through the [`RX_FIXTURE`][macro-rx_fixture] macro 101 | with optional set up and tear down functions, then reference this fixture by 102 | setting the `fixture` option on the [`RX_TEST_CASE`][macro-rx_test_case] macro. 103 | 104 | 105 | ```c 106 | #include 107 | 108 | /* Data structure to use at the core of our fixture. */ 109 | struct foo_data { 110 | const char *value; 111 | }; 112 | 113 | /* Initialize the data structure. Its allocation is handled by Rexo. */ 114 | RX_SET_UP(foo_set_up) 115 | { 116 | struct foo_data *data; 117 | 118 | /* 119 | The macro `RX_DATA` references our data as a pointer to `void` that 120 | needs to be cast to the correct type before being used. 121 | */ 122 | data = (struct foo_data *)RX_DATA; 123 | 124 | /* Initialize it! */ 125 | data->value = "world!"; 126 | 127 | /* Let Rexo know that everything went well. */ 128 | return RX_SUCCESS; 129 | } 130 | 131 | /* Define the fixture. */ 132 | RX_FIXTURE(foo_fixture, struct foo_data, .set_up = foo_set_up); 133 | 134 | RX_TEST_CASE(foo, bar, .fixture = foo_fixture) 135 | { 136 | struct foo_data *data; 137 | 138 | /* Here again, casting needs to be node before operating on the data. */ 139 | data = (struct foo_data *)RX_DATA; 140 | 141 | /* 142 | Run a string equality test that will fail since 'Hello' isn't equal 143 | to the value 'world!' that we initialized earlier. 144 | */ 145 | RX_STR_REQUIRE_EQUAL("Hello", data->value); 146 | } 147 | 148 | int 149 | main(int argc, const char **argv) 150 | { 151 | return rx_main(0, NULL, argc, argv) == RX_SUCCESS ? 0 : 1; 152 | } 153 | ``` 154 | 155 | 156 | Fixture data can be accessed at any time using the [`RX_DATA`][macro-rx_data] 157 | macro. 158 | 159 | 160 | ## Compile-Time Configuration 161 | 162 | Configurations that applies globally are sometimes best done once and for all 163 | during the compilation step. 164 | 165 | Rexo offers a set of macros that, for example, allows redefining the `malloc` 166 | and `assert` functions to use, or to set global flags: 167 | 168 | ```c 169 | #include 170 | #include 171 | #include 172 | 173 | /* Define a custom `malloc` function. */ 174 | void * 175 | my_malloc(size_t size) 176 | { 177 | printf("something worth %ld bytes is being allocated\n", size); 178 | return malloc(size); 179 | } 180 | 181 | /* Override the default `malloc` function used by Rexo with ours. */ 182 | #define RX_MALLOC my_malloc 183 | 184 | /* Override the default `assert` macro used by Rexo. */ 185 | #define RX_ASSERT(x) \ 186 | (void)( \ 187 | (x) \ 188 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 189 | || (abort(), 0)) 190 | 191 | /* Force Rexo's compatibility with C89. */ 192 | #define RX_ENABLE_C89_COMPAT 193 | 194 | /* Proceed with including Rexo and writing tests as usual. */ 195 | 196 | #include 197 | 198 | RX_TEST_CASE(foo, bar) 199 | { 200 | RX_INT_REQUIRE_EQUAL(2 * 3 * 7, 42); 201 | } 202 | 203 | int 204 | main(int argc, const char **argv) 205 | { 206 | return rx_main(0, NULL, argc, argv) == RX_SUCCESS ? 0 : 1; 207 | } 208 | ``` 209 | 210 | > **Note:** The use of `#include ` needs to come after the definition 211 | > of any compile-time option. 212 | 213 | > **Note:** For a list of all the compile-time options available, 214 | > see the [Compile-Time Configuration][compile-time-config] page. 215 | 216 | 217 | ## Explicit Registration 218 | 219 | If you would like to not make use of the [framework][framework]'s automatic 220 | registration feature, it is possible to register everything explicitly in 221 | the same way as most C unit testing frameworks offer: 222 | 223 | ```c 224 | #include 225 | 226 | struct foo_data { 227 | int value; 228 | }; 229 | 230 | enum rx_status 231 | foo_set_up(struct rx_context *RX_PARAM_CONTEXT, void *RX_PARAM_DATA) 232 | { 233 | struct foo_data *data; 234 | 235 | /* Suppress the ‘defined but not used’ compiler warning. */ 236 | (void)RX_PARAM_CONTEXT; 237 | 238 | data = (struct foo_data *)RX_DATA; 239 | data->value = 123; 240 | return RX_SUCCESS; 241 | } 242 | 243 | void 244 | foo_bar(struct rx_context *RX_PARAM_CONTEXT, void *RX_PARAM_DATA) 245 | { 246 | struct foo_data *data; 247 | 248 | data = (struct foo_data *)RX_DATA; 249 | RX_INT_REQUIRE_EQUAL(data->value, 123); 250 | } 251 | 252 | static const struct rx_test_case test_cases[] = { 253 | { /* First test case's definition. */ 254 | 255 | "foo", /* Name of the test suite. */ 256 | "bar", /* Name of the test case. */ 257 | foo_bar, /* Function to run. */ 258 | { /* Test case's configuration. */ 259 | 260 | 0, /* Option 'skip'. */ 261 | { /* Option 'fixture'. */ 262 | 263 | sizeof(struct foo_data), /* Size of the data in bytes. */ 264 | { /* Fixture's configuration. */ 265 | 266 | foo_set_up, /* Function to initialize the fixture. */ 267 | NULL /* Function to clean up the fixture. */ 268 | 269 | } 270 | 271 | } 272 | 273 | } 274 | 275 | } 276 | 277 | }; 278 | 279 | /* Retrieve the number of test cases to run. */ 280 | static const size_t test_case_count 281 | = sizeof(test_cases) / sizeof(test_cases[0]); 282 | 283 | int 284 | main(int argc, const char **argv) 285 | { 286 | /* Explicitly pass the test cases to be run. */ 287 | return rx_main(test_case_count, test_cases, argc, argv) == RX_SUCCESS 288 | ? 0 : 1; 289 | } 290 | ``` 291 | 292 | > **Note:** See the [rx_test_case][struct-rx_test_case] struct. 293 | 294 | > **Note:** The function parameters `struct rx_context *` and `void *` 295 | > are required to be respectively named using 296 | > the [`RX_PARAM_CONTEXT`][macro-rx_param_context] and 297 | > [`RX_PARAM_DATA`][macro-rx_param_data] macros. 298 | 299 | > **Note:** Alternatively, the data parameter can be defined with its actual 300 | > type instead of `void *`, thus removing the need to cast the data within 301 | > the functions. Although this approach requires casting function pointers, 302 | > which is not standard compliant and hence not used in the implementation 303 | > of Rexo, in practice the behaviour is well-defined for the common platforms. 304 | 305 | 306 | [assertion-macros]: ./reference/assertions.md 307 | [compile-time-config]: ./compile-time-configuration.md 308 | [framework]: ./reference/framework.md 309 | 310 | [fn-rx_main]: ./reference/runner.md#rx_main 311 | [macro-rx_data]: ./reference/building-blocks.md#rx_data 312 | [macro-rx_param_context]: ./reference/building-blocks.md#rx_param_context 313 | [macro-rx_param_data]: ./reference/building-blocks.md#rx_param_data 314 | [macro-rx_fixture]: ./reference/framework.md#rx_fixture 315 | [macro-rx_test_case]: ./reference/framework.md#rx_test_case 316 | [macro-rx_test_suite]: ./reference/framework.md#rx_test_suite 317 | [struct-rx_test_case_config]: ./reference/building-blocks.md#rx_test_case_config 318 | [struct-rx_test_case]: ./reference/building-blocks.md#rx_test_case 319 | -------------------------------------------------------------------------------- /doc/src/index.md: -------------------------------------------------------------------------------- 1 | Rexo 2 | ==== 3 | 4 | Rexo is a neat single-file cross-platform unit testing framework for C/C++. 5 | 6 | It offers the same [xUnit][xunit]-like structure than most other unit testing 7 | frameworks but aims at providing a _truly_ polished API. 8 | 9 | 10 | ## Features 11 | 12 | * **sleek**: polished API with great attention to details. 13 | * **easy**: no learning curve, it's yet another framework based on xUnit 14 | with test suites, test cases, and fixtures. 15 | * **convenient**: automatic registration of tests. 16 | * **granular**: high level or low level API? You choose. 17 | * **portable**: compatible with C89 (ANSI C) and C++. 18 | * **cross-platform**: tested on Linux, macOS, and Windows. 19 | * **simple**: straightforward implementation—KISS all the things! 20 | * **cascading configuration**: configure a whole test suite at once and/or tweak 21 | specific options for each test case. 22 | * **painless**: deployment couldn't be easier—it all fits into a single 23 | header file and has no external dependencies. 24 | 25 | 26 | But also... 27 | 28 | * fully standard compliant minus the optional automatic registration of tests 29 | that relies on a widespread compiler-specific feature. 30 | * designated initializer-like syntax to all C and C++ versions. 31 | 32 | 33 | ## Roadmap 34 | 35 | * implement a command-line option parser (e.g.: for filtering test cases). 36 | * allow choosing the output format of the summary (e.g.: jUnit XML). 37 | * support more assertion macros (e.g.: array comparison, signal handling). 38 | * improve failure messages to be more visual (e.g.: an arrow pointing 39 | where strings differ). 40 | 41 | 42 | ## Usage 43 | 44 | ### Minimal 45 | 46 | ```c 47 | #include 48 | 49 | RX_TEST_CASE(foo, bar) 50 | { 51 | RX_INT_REQUIRE_EQUAL(2 * 3 * 7, 42); 52 | } 53 | 54 | int 55 | main(int argc, const char **argv) 56 | { 57 | return rx_main(0, NULL, argc, argv) == RX_SUCCESS ? 0 : 1; 58 | } 59 | ``` 60 | 61 | 62 | ### Fixture 63 | 64 | ```c 65 | #include 66 | 67 | struct foo_data { 68 | const char *value; 69 | }; 70 | 71 | RX_SET_UP(foo_set_up) 72 | { 73 | struct foo_data *data; 74 | 75 | data = (struct foo_data *)RX_DATA; 76 | data->value = "world!"; 77 | return RX_SUCCESS; 78 | } 79 | 80 | RX_FIXTURE(foo_fixture, struct foo_data, .set_up = foo_set_up); 81 | 82 | RX_TEST_CASE(foo, bar, .fixture = foo_fixture) 83 | { 84 | struct foo_data *data; 85 | 86 | data = (struct foo_data *)RX_DATA; 87 | RX_STR_REQUIRE_EQUAL("Hello", data->value); 88 | } 89 | 90 | int 91 | main(int argc, const char **argv) 92 | { 93 | return rx_main(0, NULL, argc, argv) == RX_SUCCESS ? 0 : 1; 94 | } 95 | ``` 96 | 97 | 98 | ## Documentation 99 | 100 | 101 | 102 | 103 | ## Repository 104 | 105 | 106 | 107 | 108 | ## License 109 | 110 | [Unlicense][unlicense]. 111 | 112 | 113 | [unlicense]: https://unlicense.org 114 | [xunit]: https://en.wikipedia.org/wiki/XUnit 115 | -------------------------------------------------------------------------------- /doc/src/reference/assertions.md: -------------------------------------------------------------------------------- 1 | Assertion Macros 2 | ================ 3 | 4 | Each assertion macro comes in two variants that define a different behaviour 5 | when an assertion fails: 6 | 7 | * `REQUIRE`: reports a fatal failure and aborts the current test case. 8 | * `CHECK`: reports a nonfatal failure and keeps running other tests found within 9 | the same test case. 10 | 11 | > **Note:** When in [C89 compatibility mode][macro-rx_enable_c89_compat], 12 | > variadic macro arguments are not available as part of the language. 13 | > See the associated [gotcha][gotcha-variadic-macros]. 14 | 15 | 16 | ## Generic Assertions 17 | 18 | ```c 19 | #define RX_REQUIRE(condition) 20 | #define RX_REQUIRE_MSG(condition, msg, ...) 21 | #define RX_CHECK(condition) 22 | #define RX_CHECK_MSG(condition, msg, ...) 23 | ``` 24 | 25 | Ultimately, these two assertion macros could be enough to express all possible 26 | tests but it is recommended to use the more specialized assertions available. 27 | 28 | 29 | ## Boolean Assertions 30 | 31 | ```c 32 | #define RX_BOOL_REQUIRE_TRUE(condition) 33 | #define RX_BOOL_REQUIRE_TRUE_MSG(condition, msg, ...) 34 | #define RX_BOOL_CHECK_TRUE(condition) 35 | #define RX_BOOL_CHECK_TRUE_MSG(condition, msg, ...) 36 | 37 | #define RX_BOOL_REQUIRE_FALSE(condition) 38 | #define RX_BOOL_REQUIRE_FALSE_MSG(condition, msg, ...) 39 | #define RX_BOOL_CHECK_FALSE(condition) 40 | #define RX_BOOL_CHECK_FALSE_MSG(condition, msg, ...) 41 | ``` 42 | 43 | 44 | ## Integer Assertions 45 | 46 | ```c 47 | #define RX_INT_REQUIRE_EQUAL(x1, x2) 48 | #define RX_INT_REQUIRE_EQUAL_MSG(x1, x2, msg, ...) 49 | #define RX_INT_CHECK_EQUAL(x1, x2) 50 | #define RX_INT_CHECK_EQUAL_MSG(x1, x2, msg, ...) 51 | 52 | #define RX_INT_REQUIRE_NOT_EQUAL(x1, x2) 53 | #define RX_INT_REQUIRE_NOT_EQUAL_MSG(x1, x2, msg, ...) 54 | #define RX_INT_CHECK_NOT_EQUAL(x1, x2) 55 | #define RX_INT_CHECK_NOT_EQUAL_MSG(x1, x2, msg, ...) 56 | 57 | #define RX_INT_REQUIRE_GREATER(x1, x2) 58 | #define RX_INT_REQUIRE_GREATER_MSG(x1, x2, msg, ...) 59 | #define RX_INT_CHECK_GREATER(x1, x2) 60 | #define RX_INT_CHECK_GREATER_MSG(x1, x2, msg, ...) 61 | 62 | #define RX_INT_REQUIRE_LESSER(x1, x2) 63 | #define RX_INT_REQUIRE_LESSER_MSG(x1, x2, msg, ...) 64 | #define RX_INT_CHECK_LESSER(x1, x2) 65 | #define RX_INT_CHECK_LESSER_MSG(x1, x2, msg, ...) 66 | 67 | #define RX_INT_REQUIRE_GREATER_OR_EQUAL(x1, x2) 68 | #define RX_INT_REQUIRE_GREATER_OR_EQUAL_MSG(x1, x2, msg, ...) 69 | #define RX_INT_CHECK_GREATER_OR_EQUAL(x1, x2) 70 | #define RX_INT_CHECK_GREATER_OR_EQUAL_MSG(x1, x2, msg, ...) 71 | 72 | #define RX_INT_REQUIRE_LESSER_OR_EQUAL(x1, x2) 73 | #define RX_INT_REQUIRE_LESSER_OR_EQUAL_MSG(x1, x2, msg, ...) 74 | #define RX_INT_CHECK_LESSER_OR_EQUAL(x1, x2) 75 | #define RX_INT_CHECK_LESSER_OR_EQUAL_MSG(x1, x2, msg, ...) 76 | ``` 77 | 78 | 79 | ## Unsigned Integer Assertions 80 | 81 | ```c 82 | #define RX_UINT_REQUIRE_EQUAL(x1, x2) 83 | #define RX_UINT_REQUIRE_EQUAL_MSG(x1, x2, msg, ...) 84 | #define RX_UINT_CHECK_EQUAL(x1, x2) 85 | #define RX_UINT_CHECK_EQUAL_MSG(x1, x2, msg, ...) 86 | 87 | #define RX_UINT_REQUIRE_NOT_EQUAL(x1, x2) 88 | #define RX_UINT_REQUIRE_NOT_EQUAL_MSG(x1, x2, msg, ...) 89 | #define RX_UINT_CHECK_NOT_EQUAL(x1, x2) 90 | #define RX_UINT_CHECK_NOT_EQUAL_MSG(x1, x2, msg, ...) 91 | 92 | #define RX_UINT_REQUIRE_GREATER(x1, x2) 93 | #define RX_UINT_REQUIRE_GREATER_MSG(x1, x2, msg, ...) 94 | #define RX_UINT_CHECK_GREATER(x1, x2) 95 | #define RX_UINT_CHECK_GREATER_MSG(x1, x2, msg, ...) 96 | 97 | #define RX_UINT_REQUIRE_LESSER(x1, x2) 98 | #define RX_UINT_REQUIRE_LESSER_MSG(x1, x2, msg, ...) 99 | #define RX_UINT_CHECK_LESSER(x1, x2) 100 | #define RX_UINT_CHECK_LESSER_MSG(x1, x2, msg, ...) 101 | 102 | #define RX_UINT_REQUIRE_GREATER_OR_EQUAL(x1, x2) 103 | #define RX_UINT_REQUIRE_GREATER_OR_EQUAL_MSG(x1, x2, msg, ...) 104 | #define RX_UINT_CHECK_GREATER_OR_EQUAL(x1, x2) 105 | #define RX_UINT_CHECK_GREATER_OR_EQUAL_MSG(x1, x2, msg, ...) 106 | 107 | #define RX_UINT_REQUIRE_LESSER_OR_EQUAL(x1, x2) 108 | #define RX_UINT_REQUIRE_LESSER_OR_EQUAL_MSG(x1, x2, msg, ...) 109 | #define RX_UINT_CHECK_LESSER_OR_EQUAL(x1, x2) 110 | #define RX_UINT_CHECK_LESSER_OR_EQUAL_MSG(x1, x2, msg, ...) 111 | ``` 112 | 113 | 114 | ## Floating-Point Assertions 115 | 116 | ```c 117 | #define RX_REAL_REQUIRE_EQUAL(x1, x2) 118 | #define RX_REAL_REQUIRE_EQUAL_MSG(x1, x2, msg, ...) 119 | #define RX_REAL_CHECK_EQUAL(x1, x2) 120 | #define RX_REAL_CHECK_EQUAL_MSG(x1, x2, msg, ...) 121 | 122 | #define RX_REAL_REQUIRE_NOT_EQUAL(x1, x2) 123 | #define RX_REAL_REQUIRE_NOT_EQUAL_MSG(x1, x2, msg, ...) 124 | #define RX_REAL_CHECK_NOT_EQUAL(x1, x2) 125 | #define RX_REAL_CHECK_NOT_EQUAL_MSG(x1, x2, msg, ...) 126 | 127 | #define RX_REAL_REQUIRE_GREATER(x1, x2) 128 | #define RX_REAL_REQUIRE_GREATER_MSG(x1, x2, msg, ...) 129 | #define RX_REAL_CHECK_GREATER(x1, x2) 130 | #define RX_REAL_CHECK_GREATER_MSG(x1, x2, msg, ...) 131 | 132 | #define RX_REAL_REQUIRE_LESSER(x1, x2) 133 | #define RX_REAL_REQUIRE_LESSER_MSG(x1, x2, msg, ...) 134 | #define RX_REAL_CHECK_LESSER(x1, x2) 135 | #define RX_REAL_CHECK_LESSER_MSG(x1, x2, msg, ...) 136 | 137 | #define RX_REAL_REQUIRE_GREATER_OR_EQUAL(x1, x2) 138 | #define RX_REAL_REQUIRE_GREATER_OR_EQUAL_MSG(x1, x2, msg, ...) 139 | #define RX_REAL_CHECK_GREATER_OR_EQUAL(x1, x2) 140 | #define RX_REAL_CHECK_GREATER_OR_EQUAL_MSG(x1, x2, msg, ...) 141 | 142 | #define RX_REAL_REQUIRE_LESSER_OR_EQUAL(x1, x2) 143 | #define RX_REAL_REQUIRE_LESSER_OR_EQUAL_MSG(x1, x2, msg, ...) 144 | #define RX_REAL_CHECK_LESSER_OR_EQUAL(x1, x2) 145 | #define RX_REAL_CHECK_LESSER_OR_EQUAL_MSG(x1, x2, msg, ...) 146 | 147 | #define RX_REAL_REQUIRE_FUZZY_EQUAL(x1, x2, tol) 148 | #define RX_REAL_REQUIRE_FUZZY_EQUAL_MSG(x1, x2, tol, msg, ...) 149 | #define RX_REAL_CHECK_FUZZY_EQUAL(x1, x2, tol) 150 | #define RX_REAL_CHECK_FUZZY_EQUAL_MSG(x1, x2, tol, msg, ...) 151 | 152 | #define RX_REAL_REQUIRE_FUZZY_NOT_EQUAL(x1, x2, tol) 153 | #define RX_REAL_REQUIRE_FUZZY_NOT_EQUAL_MSG(x1, x2, tol, msg, ...) 154 | #define RX_REAL_CHECK_FUZZY_NOT_EQUAL(x1, x2, tol) 155 | #define RX_REAL_CHECK_FUZZY_NOT_EQUAL_MSG(x1, x2, tol, msg, ...) 156 | ``` 157 | 158 | > **Note:** The `EQUAL` and `NOT_EQUAL` comparisons perform strict equality 159 | > checks, which is usually not what you would want to do in the case of 160 | > floating-point due to precision errors. 161 | > 162 | > Therefore, each of these two assertions come with a corresponding `FUZZY` 163 | > alternative that takes a tolerance parameter and performs a relative 164 | > comparison followed by an absolute one, similar to what is described in 165 | > Bruce Dawson's article 166 | > [Comparing Floating Point Numbers, 2012 Edition][comparing-fp]. 167 | > 168 | > The fuzzy comparison test is described with the following logic: 169 | > ```c 170 | > int 171 | > are_equal_fuzzy(float a, float b, float tol) 172 | > { 173 | > float diff; 174 | > 175 | > diff = fabs(a - b); 176 | > if (diff <= tol) { 177 | > return 1; 178 | > } 179 | > 180 | > a = fabs(a); 181 | > b = fabs(b); 182 | > return diff <= (a > b ? a : b) * tol; 183 | > } 184 | > ``` 185 | 186 | 187 | ## String Assertions 188 | 189 | ```c 190 | #define RX_STR_REQUIRE_EQUAL(s1, s2) 191 | #define RX_STR_REQUIRE_EQUAL_MSG(s1, s2, msg, ...) 192 | #define RX_STR_CHECK_EQUAL(s1, s2) 193 | #define RX_STR_CHECK_EQUAL_MSG(s1, s2, msg, ...) 194 | 195 | #define RX_STR_REQUIRE_NOT_EQUAL(s1, s2) 196 | #define RX_STR_REQUIRE_NOT_EQUAL_MSG(s1, s2, msg, ...) 197 | #define RX_STR_CHECK_NOT_EQUAL(s1, s2) 198 | #define RX_STR_CHECK_NOT_EQUAL_MSG(s1, s2, msg, ...) 199 | 200 | #define RX_STR_REQUIRE_EQUAL_NO_CASE(s1, s2) 201 | #define RX_STR_REQUIRE_EQUAL_NO_CASE_MSG(s1, s2, msg, ...) 202 | #define RX_STR_CHECK_EQUAL_NO_CASE(s1, s2) 203 | #define RX_STR_CHECK_EQUAL_NO_CASE_MSG(s1, s2, msg, ...) 204 | 205 | #define RX_STR_REQUIRE_NOT_EQUAL_NO_CASE(s1, s2) 206 | #define RX_STR_REQUIRE_NOT_EQUAL_NO_CASE_MSG(s1, s2, msg, ...) 207 | #define RX_STR_CHECK_NOT_EQUAL_NO_CASE(s1, s2) 208 | #define RX_STR_CHECK_NOT_EQUAL_NO_CASE_MSG(s1, s2, msg, ...) 209 | ``` 210 | 211 | 212 | ## Pointer Assertions 213 | 214 | ```c 215 | #define RX_PTR_REQUIRE_EQUAL(x1, x2) 216 | #define RX_PTR_REQUIRE_EQUAL_MSG(x1, x2, msg, ...) 217 | #define RX_PTR_CHECK_EQUAL(x1, x2) 218 | #define RX_PTR_CHECK_EQUAL_MSG(x1, x2, msg, ...) 219 | 220 | #define RX_PTR_REQUIRE_NOT_EQUAL(x1, x2) 221 | #define RX_PTR_REQUIRE_NOT_EQUAL_MSG(x1, x2, msg, ...) 222 | #define RX_PTR_CHECK_NOT_EQUAL(x1, x2) 223 | #define RX_PTR_CHECK_NOT_EQUAL_MSG(x1, x2, msg, ...) 224 | 225 | #define RX_PTR_REQUIRE_ALIGNED(x, alignment) 226 | #define RX_PTR_REQUIRE_ALIGNED_MSG(x, alignment, msg, ...) 227 | ``` 228 | 229 | 230 | [gotcha-variadic-macros]: ../gotchas.md#variadic_macros_in_c89_compatibility_mode 231 | [macro-rx_enable_c89_compat]: ../compile-time-configuration.md#rx_enable_c89_compat 232 | 233 | [comparing-fp]: https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition 234 | -------------------------------------------------------------------------------- /doc/src/reference/building-blocks.md: -------------------------------------------------------------------------------- 1 | Building Blocks 2 | =============== 3 | 4 | Core of Rexo—data structures, required macros, and lower level API calls. 5 | 6 | Most of these are abstracted away when using the [runner][runner], 7 | the [framework][framework]'s automatic registration feature, 8 | and the [assertion macros][assertion-macros]. 9 | 10 | 11 | ## Enumerators 12 | 13 | ### `rx_status` 14 | 15 | Return codes. 16 | 17 | ```c 18 | enum rx_status { 19 | RX_SUCCESS = 0, 20 | RX_ERROR = -1, 21 | RX_ERROR_ALLOCATION = -2, 22 | RX_ERROR_MAX_SIZE_EXCEEDED = -3 23 | } 24 | ``` 25 | 26 | Error codes come in different categories that all evaluate to negative numbers. 27 | 28 | 29 | ### `rx_severity` 30 | 31 | Severity levels for test failures. 32 | 33 | ```c 34 | enum rx_severity { RX_NONFATAL = 0, RX_FATAL = 1 } 35 | ``` 36 | 37 | Nonfatal failures arise from `RX_CHECK*` assertions while `RX_REQUIRE*` trigger 38 | fatal failures. 39 | 40 | See also the [assertion macros][assertion-macros]. 41 | 42 | 43 | ### `rx_log_level` 44 | 45 | Logging level. 46 | 47 | ```c 48 | enum rx_log_level { 49 | RX_LOG_LEVEL_NONE = 0, 50 | RX_LOG_LEVEL_FATAL = 1, 51 | RX_LOG_LEVEL_ERROR = 3, 52 | RX_LOG_LEVEL_WARNING = 4, 53 | RX_LOG_LEVEL_INFO = 5, 54 | RX_LOG_LEVEL_DEBUG = 6, 55 | RX_LOG_LEVEL_ALL = RX_LOG_LEVEL_DEBUG 56 | }; 57 | ``` 58 | 59 | The purpose of each level is defined as follows: 60 | 61 | | level | description | example | 62 | | :---: | ----------- | ------- | 63 | | **fatal** | critical failure that causes premature termination of the application | lack of disk space, data corruption | 64 | | **error** | failure that doesn't require the application to be prematurely terminated | unable to open a file, allocation failure | 65 | | **warning** | situation that is not ideal but that is not an actual failure per se | use of a deprecated API, poor use of an API | 66 | | **info** | event of general interest corresponding to normal application behaviour | addition of a database entry, statistics | 67 | | **debug** | detailed information to help maintainers troubleshooting problems | allocation size, state of data | 68 | | **trace** | control flow of the application to help maintainers pinpointing problems | entry/exit of functions, dump of argument values | 69 | 70 | 71 | ## Function Pointers 72 | 73 | ### `rx_set_up_fn` 74 | 75 | Function part of the fixture feature, to be called before the function defining 76 | the tests is run. 77 | 78 | ```c 79 | typedef enum rx_status (*rx_set_up_fn)(struct rx_context *, void *) 80 | ``` 81 | 82 | The `struct rx_context *` parameter is reserved for the implementation and 83 | shouldn't be directly accessed by the users. 84 | 85 | The `void *` parameter is used to output any data initialized within 86 | this function. The pointer can be accessed within the function's definition 87 | using the [`RX_DATA`][macro-rx_data] macro. 88 | 89 | When [explicitly registering tests][explicit-registration-guide], the macros 90 | [`RX_PARAM_CONTEXT`][macro-rx_param_context] 91 | and [`RX_PARAM_DATA`][macro-rx_param_data] need to be used to define 92 | the parameter names. 93 | 94 | The function is expected to return an error code if something went wrong, 95 | or `RX_SUCCESS` otherwise. 96 | 97 | 98 | ### `rx_tear_down_fn` 99 | 100 | Function part of the fixture feature, to be called after the function defining 101 | the tests is run. 102 | 103 | ```c 104 | typedef void (*rx_tear_down_fn)(struct rx_context *, void *) 105 | ``` 106 | 107 | The `struct rx_context *` parameter is reserved for the implementation and 108 | shouldn't be directly accessed by the users. 109 | 110 | The `void *` parameter is used to output any data initialized within 111 | this function. The pointer can be accessed within the function's definition 112 | using the [`RX_DATA`][macro-rx_data] macro. 113 | 114 | When [explicitly registering tests][explicit-registration-guide], the macros 115 | [`RX_PARAM_CONTEXT`][macro-rx_param_context] 116 | and [`RX_PARAM_DATA`][macro-rx_param_data] need to be used to define 117 | the parameter names. 118 | 119 | 120 | ### `rx_run_fn` 121 | 122 | Function defining the tests to run. 123 | 124 | ```c 125 | typedef void (*rx_run_fn)(struct rx_context *, void *) 126 | ``` 127 | 128 | The `struct rx_context *` parameter is reserved for the implementation and 129 | shouldn't be directly accessed by the users. 130 | 131 | The `void *` parameter is used to output any data initialized within 132 | this function. The pointer can be accessed within the function's definition 133 | using the [`RX_DATA`][macro-rx_data] macro. 134 | 135 | When [explicitly registering tests][explicit-registration-guide], the macros 136 | [`RX_PARAM_CONTEXT`][macro-rx_param_context] 137 | and [`RX_PARAM_DATA`][macro-rx_param_data] need to be used to define 138 | the parameter names. 139 | 140 | 141 | ## Parameters 142 | 143 | ### `RX_PARAM_CONTEXT` 144 | 145 | Refers to the name expected for the context parameter. 146 | 147 | ```c 148 | #define RX_PARAM_CONTEXT 149 | ``` 150 | 151 | The context parameter is used for the functions 152 | [rx_set_up_fn][fnptr-rx_set_up_fn], [rx_tear_down_fn][fnptr-rx_tear_down_fn], 153 | and [rx_run_fn][fnptr-rx_run_fn]. 154 | 155 | It is not to be directly used unless when 156 | [explicitly registering tests][explicit-registration-guide]. In this case, 157 | the macro `RX_PARAM_CONTEXT` is required to name 158 | the `struct rx_context *` parameter. 159 | 160 | 161 | ### `RX_PARAM_DATA` 162 | 163 | Refers to the name expected for the data parameter. 164 | 165 | ```c 166 | #define RX_PARAM_DATA 167 | ``` 168 | 169 | The context parameter is used for the functions 170 | [rx_set_up_fn][fnptr-rx_set_up_fn], [rx_tear_down_fn][fnptr-rx_tear_down_fn], 171 | and [rx_run_fn][fnptr-rx_run_fn]. 172 | 173 | It is not to be directly used unless when 174 | [explicitly registering tests][explicit-registration-guide]. In this case, 175 | the macro `RX_PARAM_DATA` is required to name the `void *` parameter. 176 | 177 | 178 | ## Data Accessors 179 | 180 | ### `RX_DATA` 181 | 182 | Access the data's pointer. 183 | 184 | ```c 185 | #define RX_DATA 186 | ``` 187 | 188 | This macro can be used within the definitions of the functions 189 | [rx_set_up_fn][fnptr-rx_set_up_fn], [rx_tear_down_fn][fnptr-rx_tear_down_fn], 190 | and [rx_run_fn][fnptr-rx_run_fn]. 191 | 192 | 193 | ## Types 194 | 195 | ### `rx_uint32` 196 | 197 | Type for 32-bit unsigned integers. 198 | 199 | ```c 200 | typedef TYPE rx_uint32; 201 | ``` 202 | 203 | The type is determined by the value of 204 | the [`RX_UINT32_TYPE`][macro-rx_uint32_type] macro. If the macro isn't set, 205 | `unsigned int` is used, which fits the common data models, that is ILP32 206 | (most recent 32-bit systems), LP64 (Unix-like systems), and LLP64 (Windows). 207 | 208 | 209 | ### `rx_uint64` 210 | 211 | Type for 64-bit unsigned integers. 212 | 213 | ```c 214 | typedef TYPE rx_uint64; 215 | ``` 216 | 217 | The type is determined by the value of 218 | the [`RX_UINT64_TYPE`][macro-rx_uint64_type] macro. If the macro isn't set, 219 | `unsigned long long` is used, which fits the common data models, that is ILP32 220 | (most recent 32-bit systems), LP64 (Unix-like systems), and LLP64 (Windows). 221 | 222 | 223 | ### `rx_size` 224 | 225 | Type to use in place of `size_t`. 226 | 227 | ```c 228 | typedef TYPE rx_size; 229 | ``` 230 | 231 | The type is determined by the value of the [`RX_SIZE_TYPE`][macro-rx_size_type] 232 | macro. If the macro isn't set, either [`rx_uint32`][type-rx_uint32] 233 | or [`rx_uint64`][type-rx_uint64] is used, depending on whether the environment 234 | is running on a 32-bit or 64-bit platform. 235 | 236 | 237 | ## Structures 238 | 239 | ### `rx_test_case_config` 240 | 241 | Configuration object to apply to a test case. 242 | 243 | ```c 244 | struct rx_test_case_config { 245 | int skip; 246 | struct rx_fixture fixture; 247 | } 248 | ``` 249 | 250 | In the event where a test case should be skipped by the runner, 251 | the `skip` option can be used. 252 | 253 | Fixtures are defined through the `fixture` option, see 254 | the [`rx_fixture`][struct-rx_fixture] struct. 255 | 256 | Filling the struct with the value `0` sets all the members to 257 | their default values. 258 | 259 | 260 | ### `rx_fixture_config` 261 | 262 | Configuration object to apply to a fixture. 263 | 264 | ```c 265 | struct rx_fixture_config { 266 | rx_set_up_fn set_up; 267 | rx_tear_down_fn tear_down; 268 | } 269 | ``` 270 | 271 | The `set_up` and `tear_down` options respectively define 272 | the [rx_set_up_fn][fnptr-rx_set_up_fn] and 273 | the [rx_tear_down_fn][fnptr-rx_tear_down_fn] functions. 274 | 275 | Filling the struct with the value `0` sets all the members to 276 | their default values. 277 | 278 | 279 | ### `rx_fixture` 280 | 281 | Fixture defining data type size and function pointers to run before and after 282 | the test is run. 283 | 284 | ```c 285 | struct rx_fixture { 286 | rx_size size; 287 | struct rx_fixture_config config; 288 | }; 289 | ``` 290 | 291 | Any configuration can be set through the `config` option. See 292 | the [`rx_fixture_config`][struct-rx_fixture_config] struct. 293 | 294 | 295 | ### `rx_test_case` 296 | 297 | Definition of a single test case. 298 | 299 | ```c 300 | struct rx_test_case { 301 | const char *suite_name; 302 | const char *name; 303 | rx_run_fn run; 304 | struct rx_test_case_config config; 305 | }; 306 | ``` 307 | 308 | The `run` function pointer needs to point to the function that contains 309 | the tests for the test case. See the [`rx_run_fn`][fnptr-rx_run_fn] function. 310 | 311 | Any configuration can be set through the `config` option. See 312 | the [`rx_test_case_config`][struct-rx_test_case_config] struct. 313 | 314 | 315 | ### `rx_failure` 316 | 317 | Information related to a test that failed. 318 | 319 | ```c 320 | struct rx_failure { 321 | const char *file; 322 | int line; 323 | enum rx_severity severity; 324 | const char *msg; 325 | const char *diagnostic_msg; 326 | } 327 | ``` 328 | 329 | 330 | ### `rx_summary` 331 | 332 | Report from running a test case. 333 | 334 | ```c 335 | struct rx_summary { 336 | const struct rx_test_case *test_case; 337 | int skipped; 338 | const char *error; 339 | rx_size assessed_count; 340 | rx_size failure_count; 341 | struct rx_failure *failures; 342 | rx_uint64 elapsed; 343 | } 344 | ``` 345 | 346 | 347 | ### `rx_context` 348 | 349 | Opaque data required internally by Rexo. 350 | 351 | ```c 352 | struct rx_context 353 | ``` 354 | 355 | 356 | ## Functions 357 | 358 | ### `rx_abort` 359 | 360 | Aborts the execution of the test case being currently run. 361 | 362 | ```c 363 | void 364 | rx_abort(struct rx_context *context) 365 | ``` 366 | 367 | 368 | ### `rx_handle_test_result` 369 | 370 | Handles the result of a single test. 371 | 372 | ```c 373 | enum rx_status 374 | rx_handle_test_result(struct rx_context *context, 375 | int result, 376 | const char *file, 377 | int line, 378 | enum rx_severity severity, 379 | const char *failure_msg, 380 | const char *diagnostic_msg) 381 | ``` 382 | 383 | This records the result and any error or diagnostic messages related to it. 384 | 385 | 386 | ### `rx_summary_initialize` 387 | 388 | Initializes a summary. 389 | 390 | ```c 391 | enum rx_status 392 | rx_summary_initialize(struct rx_summary *summary, 393 | const struct rx_test_case *test_case) 394 | ``` 395 | 396 | The struct [`rx_summary`][struct-rx_summary] must be already manually 397 | allocated beforehand. 398 | 399 | 400 | ### `rx_summary_terminate` 401 | 402 | Terminates a summary. 403 | 404 | ```c 405 | void 406 | rx_summary_terminate(struct rx_summary *summary) 407 | ``` 408 | 409 | The struct [`rx_summary`][struct-rx_summary] must be manually freed 410 | afterwards, if needed. 411 | 412 | 413 | ### `rx_summary_print` 414 | 415 | Prints a summary covering the result of running a single test case. 416 | 417 | ```c 418 | void 419 | rx_summary_print(const struct rx_summary *summary) 420 | ``` 421 | 422 | The summary is printed out to `stderr` as it is not intended for further 423 | processing, but to only show the progress of each test case as the results 424 | come in. 425 | 426 | 427 | ### `rx_test_case_run` 428 | 429 | Runs a single test case. 430 | 431 | ```c 432 | enum rx_status 433 | rx_test_case_run(struct rx_summary *summary, 434 | const struct rx_test_case *test_case) 435 | ``` 436 | 437 | The `run` function set for the given test case is being executed with 438 | the results are being stored in the `summary` argument. 439 | 440 | 441 | ### `rx_enumerate_test_cases` 442 | 443 | Enumerates the test cases automatically registered. 444 | 445 | ```c 446 | void 447 | rx_enumerate_test_cases(size_t *test_case_count, 448 | struct rx_test_case *test_cases) 449 | ``` 450 | 451 | If `test_cases` is `NULL`, then the number of test cases available is returned 452 | in `test_case_count`. Otherwise, `test_case_count` must point to a variable set 453 | by the user to the number of elements in the `test_cases` array, and on return 454 | the variable is overwritten with the number of objects actually written to 455 | `test_cases`. 456 | 457 | If `test_case_count` is less than the number of test cases available, 458 | at most `test_case_count` objects will be written. 459 | 460 | 461 | [assertion-macros]: ./assertions.md 462 | [explicit-registration-guide]: ../guides.md#explicit-registration 463 | [framework]: ./framework.md 464 | [runner]: ./runner.md 465 | 466 | [fnptr-rx_run_fn]: #rx_run_fn 467 | [fnptr-rx_set_up_fn]: #rx_set_up_fn 468 | [fnptr-rx_tear_down_fn]: #rx_tear_down_fn 469 | [macro-rx_data]: #rx_data 470 | [macro-rx_param_context]: #rx_param_context 471 | [macro-rx_param_data]: #rx_param_data 472 | [macro-rx_size_type]: ../compile-time-configuration.md#rx_size_type 473 | [macro-rx_uint32_type]: ../compile-time-configuration.md#rx_uint32_type 474 | [macro-rx_uint64_type]: ../compile-time-configuration.md#rx_uint64_type 475 | [type-rx_uint32]: #rx_uint32 476 | [type-rx_uint64]: #rx_uint64 477 | [struct-rx_fixture]: #rx_fixture 478 | [struct-rx_fixture_config]: #rx_fixture_config 479 | [struct-rx_summary]: #rx_summary 480 | [struct-rx_test_case_config]: #rx_test_case_config 481 | -------------------------------------------------------------------------------- /doc/src/reference/constants.md: -------------------------------------------------------------------------------- 1 | Constants 2 | ========= 3 | 4 | ## Versioning 5 | 6 | ### `RX_MAJOR_VERSION` 7 | 8 | Major version of Rexo. 9 | 10 | ```c 11 | #define RX_MAJOR_VERSION 12 | ``` 13 | 14 | 15 | ### `RX_MINOR_VERSION` 16 | 17 | Minor version of Rexo. 18 | 19 | ```c 20 | #define RX_MINOR_VERSION 21 | ``` 22 | 23 | 24 | ### `RX_PATCH_VERSION` 25 | 26 | Patch version of Rexo. 27 | 28 | ```c 29 | #define RX_PATCH_VERSION 30 | ``` 31 | 32 | 33 | ### `RX_VERSION` 34 | 35 | Version of Rexo as a single integer. 36 | 37 | ```c 38 | #define RX_VERSION 39 | ``` 40 | 41 | The major, minor, and patch versions are combined into `RX_VERSION` as a single 42 | integer value that can be used for comparison purposes. 43 | -------------------------------------------------------------------------------- /doc/src/reference/framework.md: -------------------------------------------------------------------------------- 1 | Framework 2 | ========= 3 | 4 | The framework defines the go-to approach when wanting to use the mechanism 5 | that automatically registers test suites, test cases, and fixtures. 6 | 7 | 8 | ### `RX_SET_UP` 9 | 10 | Defines the initialization function of a fixture. 11 | 12 | ```c 13 | #define RX_SET_UP(id) 14 | ``` 15 | 16 | The name for this function needs to be passed to the `id` parameter and can then 17 | referenced through the `set_up` option available as part of 18 | the [`RX_FIXTURE`][macro-rx_fixture] macro. 19 | 20 | 21 | ### `RX_TEAR_DOWN` 22 | 23 | Defines the clean-up function of a fixture. 24 | 25 | ```c 26 | #define RX_TEAR_DOWN(id) 27 | ``` 28 | 29 | The name for this function needs to be passed to the `id` parameter and can then 30 | be referenced through the `tear_down` option available as part of 31 | the [`RX_FIXTURE`][macro-rx_fixture] macro. 32 | 33 | 34 | ### `RX_FIXTURE` 35 | 36 | Defines a fixture. 37 | 38 | ```c 39 | #define RX_FIXTURE(id, type, ...) 40 | ``` 41 | 42 | The name for this fixture needs to be passed to the `id` parameter and can then 43 | be referenced through the configuration of 44 | the [`RX_TEST_SUITE`][macro-rx_test_suite] and 45 | the [`RX_TEST_CASE`][macro-rx_test_case] macros. 46 | 47 | For a list of all the options available through the variadic parameter, see 48 | the [`rx_fixture_config`][struct-rx_fixture_config] struct. 49 | 50 | If the fixture defines some data to be used by its test cases, its type needs 51 | to be passed to the `type` parameter. Otherwise, the alternative 52 | macro [`RX_VOID_FIXTURE`][macro-rx_void_fixture] should be used instead. 53 | 54 | 55 | ### `RX_VOID_FIXTURE` 56 | 57 | Defines a fixture without any user data. 58 | 59 | ```c 60 | #define RX_VOID_FIXTURE(id, ...) 61 | ``` 62 | 63 | The name for this fixture needs to be passed to the `id` parameter and can then 64 | be referenced through the configuration of 65 | the [`RX_TEST_SUITE`][macro-rx_test_suite] and 66 | the [`RX_TEST_CASE`][macro-rx_test_case] macros. 67 | 68 | 69 | ### `RX_TEST_SUITE` 70 | 71 | Defines a test suite. 72 | 73 | ```c 74 | #define RX_TEST_SUITE(id, ...) 75 | ``` 76 | 77 | The name for this suite needs to be passed to the `id` parameter and can then be 78 | referenced by the [`RX_TEST_CASE`][macro-rx_test_case] macro. 79 | 80 | For a list of all the options available through the variadic parameter, see 81 | the [`rx_test_case_config`][struct-rx_test_case_config] struct. 82 | 83 | 84 | ### `RX_TEST_CASE` 85 | 86 | Defines a test case function. 87 | 88 | ```c 89 | #define RX_TEST_CASE(suite_id, id, ...) 90 | ``` 91 | 92 | For a list of all the options available through the variadic parameter, see 93 | the [`rx_test_case_config`][struct-rx_test_case_config] struct. 94 | 95 | 96 | [macro-rx_fixture]: #rx_fixture 97 | [macro-rx_test_case]: #rx_test_case 98 | [macro-rx_test_suite]: #rx_test_suite 99 | [macro-rx_void_fixture]: #rx_void_fixture 100 | [struct-rx_fixture_config]: ./building-blocks.md#rx_fixture_config 101 | [struct-rx_test_case_config]: ./building-blocks.md#rx_test_case_config 102 | -------------------------------------------------------------------------------- /doc/src/reference/index.md: -------------------------------------------------------------------------------- 1 | API Reference 2 | ============= 3 | 4 | As a single-header file library, all of Rexo's public API and implementation is 5 | available from within a single include: 6 | 7 | ```c 8 | #include 9 | ``` 10 | -------------------------------------------------------------------------------- /doc/src/reference/runner.md: -------------------------------------------------------------------------------- 1 | Runner 2 | ====== 3 | 4 | High-level API to run all the unit tests. 5 | 6 | 7 | ### `rx_main` 8 | 9 | Runs the given tests. 10 | 11 | ```c 12 | enum rx_status 13 | rx_main(size_t test_case_count, 14 | const struct rx_test_case *test_cases, 15 | int argc, 16 | const char * const *argv) 17 | ``` 18 | 19 | The `rx_main` function can be seen as composing all the orthogonal calls into 20 | a single higher level API for ease of use and is likely to be the only one 21 | function needing to ever be called by most users. 22 | 23 | If the `test_cases` argument is `NULL`, then the tests found through 24 | the [framework][framework]'s automatic registration feature are used. 25 | 26 | This function is implemented using the lower level API described in 27 | [building blocks][building-blocks]. Use these directly instead of `rx_main` 28 | if you'd like to further customize the process. 29 | 30 | 31 | [building-blocks]: ./building-blocks.md 32 | [framework]: ./framework.md 33 | -------------------------------------------------------------------------------- /img/failed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/christophercrouzet/rexo/35d9503eaa888902ee2114359f0711bebb1ac073/img/failed.png -------------------------------------------------------------------------------- /img/passed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/christophercrouzet/rexo/35d9503eaa888902ee2114359f0711bebb1ac073/img/passed.png -------------------------------------------------------------------------------- /tests/assertion-coverage.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #define ASSERT(x) \ 7 | (void)( \ 8 | (x) \ 9 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 10 | || (abort(), 0)) 11 | 12 | RX_TEST_CASE(my_test_suite, all_check_successes) 13 | { 14 | RX_CHECK(123 == 123); 15 | RX_BOOL_CHECK_TRUE(123 == 123); 16 | RX_BOOL_CHECK_FALSE(123 == 456); 17 | RX_INT_CHECK_EQUAL(123, 123); 18 | RX_INT_CHECK_NOT_EQUAL(123, 456); 19 | RX_INT_CHECK_GREATER(456, 123); 20 | RX_INT_CHECK_LESSER(123, 456); 21 | RX_INT_CHECK_GREATER_OR_EQUAL(456, 123); 22 | RX_INT_CHECK_LESSER_OR_EQUAL(123, 456); 23 | RX_UINT_CHECK_EQUAL(123, 123); 24 | RX_UINT_CHECK_NOT_EQUAL(123, 456); 25 | RX_UINT_CHECK_GREATER(456, 123); 26 | RX_UINT_CHECK_LESSER(123, 456); 27 | RX_UINT_CHECK_GREATER_OR_EQUAL(456, 123); 28 | RX_UINT_CHECK_LESSER_OR_EQUAL(123, 456); 29 | RX_REAL_CHECK_EQUAL(1.23, 1.23); 30 | RX_REAL_CHECK_NOT_EQUAL(1.23, 4.56); 31 | RX_REAL_CHECK_GREATER(4.56, 1.23); 32 | RX_REAL_CHECK_LESSER(1.23, 4.56); 33 | RX_REAL_CHECK_GREATER_OR_EQUAL(4.56, 1.23); 34 | RX_REAL_CHECK_LESSER_OR_EQUAL(1.23, 4.56); 35 | RX_REAL_CHECK_FUZZY_EQUAL(1.23, 1.23, 0.001); 36 | RX_REAL_CHECK_FUZZY_NOT_EQUAL(1.23, 4.56, 0.001); 37 | RX_STR_CHECK_EQUAL("hello", "hello"); 38 | RX_STR_CHECK_NOT_EQUAL("hello", "world!"); 39 | RX_STR_CHECK_EQUAL_NO_CASE("hello", "hello"); 40 | RX_STR_CHECK_NOT_EQUAL_NO_CASE("hello", "world!"); 41 | RX_PTR_CHECK_EQUAL((void *)0x123, (void *)0x123); 42 | RX_PTR_CHECK_NOT_EQUAL((void *)0x123, (void *)0x456); 43 | RX_PTR_CHECK_ALIGNED((void *)0x100, 128); 44 | } 45 | 46 | RX_TEST_CASE(my_test_suite, all_require_successes) 47 | { 48 | RX_REQUIRE(123 == 123); 49 | RX_BOOL_REQUIRE_TRUE(123 == 123); 50 | RX_BOOL_REQUIRE_FALSE(123 == 456); 51 | RX_INT_REQUIRE_EQUAL(123, 123); 52 | RX_INT_REQUIRE_NOT_EQUAL(123, 456); 53 | RX_INT_REQUIRE_GREATER(456, 123); 54 | RX_INT_REQUIRE_LESSER(123, 456); 55 | RX_INT_REQUIRE_GREATER_OR_EQUAL(456, 123); 56 | RX_INT_REQUIRE_LESSER_OR_EQUAL(123, 456); 57 | RX_UINT_REQUIRE_EQUAL(123, 123); 58 | RX_UINT_REQUIRE_NOT_EQUAL(123, 456); 59 | RX_UINT_REQUIRE_GREATER(456, 123); 60 | RX_UINT_REQUIRE_LESSER(123, 456); 61 | RX_UINT_REQUIRE_GREATER_OR_EQUAL(456, 123); 62 | RX_UINT_REQUIRE_LESSER_OR_EQUAL(123, 456); 63 | RX_REAL_REQUIRE_EQUAL(1.23, 1.23); 64 | RX_REAL_REQUIRE_NOT_EQUAL(1.23, 4.56); 65 | RX_REAL_REQUIRE_GREATER(4.56, 1.23); 66 | RX_REAL_REQUIRE_LESSER(1.23, 4.56); 67 | RX_REAL_REQUIRE_GREATER_OR_EQUAL(4.56, 1.23); 68 | RX_REAL_REQUIRE_LESSER_OR_EQUAL(1.23, 4.56); 69 | RX_REAL_REQUIRE_FUZZY_EQUAL(1.23, 1.23, 0.001); 70 | RX_REAL_REQUIRE_FUZZY_NOT_EQUAL(1.23, 4.56, 0.001); 71 | RX_STR_REQUIRE_EQUAL("hello", "hello"); 72 | RX_STR_REQUIRE_NOT_EQUAL("hello", "world!"); 73 | RX_STR_REQUIRE_EQUAL_NO_CASE("hello", "hello"); 74 | RX_STR_REQUIRE_NOT_EQUAL_NO_CASE("hello", "world!"); 75 | RX_PTR_REQUIRE_EQUAL((void *)0x123, (void *)0x123); 76 | RX_PTR_REQUIRE_NOT_EQUAL((void *)0x123, (void *)0x456); 77 | RX_PTR_REQUIRE_ALIGNED((void *)0x100, 128); 78 | } 79 | 80 | RX_TEST_CASE(my_test_suite, all_check_failures) 81 | { 82 | RX_CHECK(123 == 456); 83 | RX_BOOL_CHECK_TRUE(123 == 456); 84 | RX_BOOL_CHECK_FALSE(123 == 123); 85 | RX_INT_CHECK_EQUAL(123, 456); 86 | RX_INT_CHECK_NOT_EQUAL(123, 123); 87 | RX_INT_CHECK_GREATER(123, 456); 88 | RX_INT_CHECK_LESSER(456, 123); 89 | RX_INT_CHECK_GREATER_OR_EQUAL(123, 456); 90 | RX_INT_CHECK_LESSER_OR_EQUAL(456, 123); 91 | RX_UINT_CHECK_EQUAL(123, 456); 92 | RX_UINT_CHECK_NOT_EQUAL(123, 123); 93 | RX_UINT_CHECK_GREATER(123, 456); 94 | RX_UINT_CHECK_LESSER(456, 123); 95 | RX_UINT_CHECK_GREATER_OR_EQUAL(123, 456); 96 | RX_UINT_CHECK_LESSER_OR_EQUAL(456, 123); 97 | RX_REAL_CHECK_EQUAL(1.23, 4.56); 98 | RX_REAL_CHECK_NOT_EQUAL(1.23, 1.23); 99 | RX_REAL_CHECK_GREATER(1.23, 4.56); 100 | RX_REAL_CHECK_LESSER(4.56, 1.23); 101 | RX_REAL_CHECK_GREATER_OR_EQUAL(1.23, 4.56); 102 | RX_REAL_CHECK_LESSER_OR_EQUAL(4.56, 1.23); 103 | RX_REAL_CHECK_FUZZY_EQUAL(1.23, 4.56, 0.001); 104 | RX_REAL_CHECK_FUZZY_NOT_EQUAL(1.23, 1.23, 0.001); 105 | RX_STR_CHECK_EQUAL("hello", "world!"); 106 | RX_STR_CHECK_NOT_EQUAL("hello", "hello"); 107 | RX_STR_CHECK_EQUAL_NO_CASE("hello", "world!"); 108 | RX_STR_CHECK_NOT_EQUAL_NO_CASE("hello", "hello"); 109 | RX_PTR_CHECK_EQUAL((void *)0x123, (void *)0x456); 110 | RX_PTR_CHECK_NOT_EQUAL((void *)0x123, (void *)0x123); 111 | RX_PTR_CHECK_ALIGNED((void *)0x100, 100); 112 | } 113 | 114 | RX_TEST_CASE(my_test_suite, require_failure) 115 | { 116 | RX_REQUIRE(123 == 456); 117 | } 118 | 119 | RX_TEST_CASE(my_test_suite, bool_require_true_failure) 120 | { 121 | RX_BOOL_REQUIRE_TRUE(123 == 456); 122 | } 123 | 124 | RX_TEST_CASE(my_test_suite, bool_require_false_failure) 125 | { 126 | RX_BOOL_REQUIRE_FALSE(123 == 123); 127 | } 128 | 129 | RX_TEST_CASE(my_test_suite, int_require_equal_failure) 130 | { 131 | RX_INT_REQUIRE_EQUAL(123, 456); 132 | } 133 | 134 | RX_TEST_CASE(my_test_suite, int_require_not_equal_failure) 135 | { 136 | RX_INT_REQUIRE_NOT_EQUAL(123, 123); 137 | } 138 | 139 | RX_TEST_CASE(my_test_suite, int_require_greater_failure) 140 | { 141 | RX_INT_REQUIRE_GREATER(123, 456); 142 | } 143 | 144 | RX_TEST_CASE(my_test_suite, int_require_lesser_failure) 145 | { 146 | RX_INT_REQUIRE_LESSER(456, 123); 147 | } 148 | 149 | RX_TEST_CASE(my_test_suite, int_require_greater_or_equal_failure) 150 | { 151 | RX_INT_REQUIRE_GREATER_OR_EQUAL(123, 456); 152 | } 153 | 154 | RX_TEST_CASE(my_test_suite, int_require_lesser_or_equal_failure) 155 | { 156 | RX_INT_REQUIRE_LESSER_OR_EQUAL(456, 123); 157 | } 158 | 159 | RX_TEST_CASE(my_test_suite, uint_require_equal_failure) 160 | { 161 | RX_UINT_REQUIRE_EQUAL(123, 456); 162 | } 163 | 164 | RX_TEST_CASE(my_test_suite, uint_require_not_equal_failure) 165 | { 166 | RX_UINT_REQUIRE_NOT_EQUAL(123, 123); 167 | } 168 | 169 | RX_TEST_CASE(my_test_suite, uint_require_greater_failure) 170 | { 171 | RX_UINT_REQUIRE_GREATER(123, 456); 172 | } 173 | 174 | RX_TEST_CASE(my_test_suite, uint_require_lesser_failure) 175 | { 176 | RX_UINT_REQUIRE_LESSER(456, 123); 177 | } 178 | 179 | RX_TEST_CASE(my_test_suite, uint_require_greater_or_equal_failure) 180 | { 181 | RX_UINT_REQUIRE_GREATER_OR_EQUAL(123, 456); 182 | } 183 | 184 | RX_TEST_CASE(my_test_suite, uint_require_lesser_or_equal_failure) 185 | { 186 | RX_UINT_REQUIRE_LESSER_OR_EQUAL(456, 123); 187 | } 188 | 189 | RX_TEST_CASE(my_test_suite, real_require_equal_failure) 190 | { 191 | RX_REAL_REQUIRE_EQUAL(1.23, 4.56); 192 | } 193 | 194 | RX_TEST_CASE(my_test_suite, real_require_not_equal_failure) 195 | { 196 | RX_REAL_REQUIRE_NOT_EQUAL(1.23, 1.23); 197 | } 198 | 199 | RX_TEST_CASE(my_test_suite, real_require_greater_failure) 200 | { 201 | RX_REAL_REQUIRE_GREATER(1.23, 4.56); 202 | } 203 | 204 | RX_TEST_CASE(my_test_suite, real_require_lesser_failure) 205 | { 206 | RX_REAL_REQUIRE_LESSER(4.56, 1.23); 207 | } 208 | 209 | RX_TEST_CASE(my_test_suite, real_require_greater_or_equal_failure) 210 | { 211 | RX_REAL_REQUIRE_GREATER_OR_EQUAL(1.23, 4.56); 212 | } 213 | 214 | RX_TEST_CASE(my_test_suite, real_require_lesser_or_equal_failure) 215 | { 216 | RX_REAL_REQUIRE_LESSER_OR_EQUAL(4.56, 1.23); 217 | } 218 | 219 | RX_TEST_CASE(my_test_suite, real_require_fuzzy_equal_failure) 220 | { 221 | RX_REAL_REQUIRE_FUZZY_EQUAL(1.23, 4.56, 0.001); 222 | } 223 | 224 | RX_TEST_CASE(my_test_suite, real_require_fuzzy_not_equal_failure) 225 | { 226 | RX_REAL_REQUIRE_FUZZY_NOT_EQUAL(1.23, 1.23, 0.001); 227 | } 228 | 229 | RX_TEST_CASE(my_test_suite, str_require_equal_failure) 230 | { 231 | RX_STR_REQUIRE_EQUAL("hello", "world!"); 232 | } 233 | 234 | RX_TEST_CASE(my_test_suite, str_require_not_equal_failure) 235 | { 236 | RX_STR_REQUIRE_NOT_EQUAL("hello", "hello"); 237 | } 238 | 239 | RX_TEST_CASE(my_test_suite, str_require_equal_no_case_failure) 240 | { 241 | RX_STR_REQUIRE_EQUAL_NO_CASE("hello", "world!"); 242 | } 243 | 244 | RX_TEST_CASE(my_test_suite, str_require_not_equal_no_case_failure) 245 | { 246 | RX_STR_REQUIRE_NOT_EQUAL_NO_CASE("hello", "hello"); 247 | } 248 | 249 | RX_TEST_CASE(my_test_suite, ptr_require_equal_failure) 250 | { 251 | RX_PTR_REQUIRE_EQUAL((void *)0x123, (void *)0x456); 252 | } 253 | 254 | RX_TEST_CASE(my_test_suite, ptr_require_not_equal_failure) 255 | { 256 | RX_PTR_REQUIRE_NOT_EQUAL((void *)0x123, (void *)0x123); 257 | } 258 | 259 | RX_TEST_CASE(my_test_suite, ptr_require_aligned_failure) 260 | { 261 | RX_PTR_REQUIRE_ALIGNED((void *)0x100, 100); 262 | } 263 | 264 | int 265 | main(int argc, const char **argv) 266 | { 267 | size_t i; 268 | rx_size test_case_count; 269 | struct rx_test_case *test_cases; 270 | size_t assessed_count; 271 | size_t nonfatal_failure_count; 272 | size_t fatal_failure_count; 273 | 274 | (void)argc; 275 | (void)argv; 276 | 277 | assessed_count = 0; 278 | nonfatal_failure_count = 0; 279 | fatal_failure_count = 0; 280 | 281 | rx_enumerate_test_cases(&test_case_count, NULL); 282 | test_cases 283 | = (struct rx_test_case *)malloc(sizeof *test_cases * test_case_count); 284 | if (test_cases == NULL) { 285 | printf("failed to allocate the test cases\n"); 286 | return 1; 287 | } 288 | 289 | rx_enumerate_test_cases(&test_case_count, test_cases); 290 | for (i = 0; i < test_case_count; ++i) { 291 | size_t j; 292 | const struct rx_test_case *test_case; 293 | struct rx_summary summary; 294 | 295 | test_case = &test_cases[i]; 296 | 297 | if (rx_summary_initialize(&summary, test_case) != RX_SUCCESS) { 298 | printf("failed to initialize a summary\n"); 299 | return 1; 300 | } 301 | 302 | if (rx_test_case_run(&summary, test_case) != RX_SUCCESS) { 303 | printf("failed to run a test case\n"); 304 | rx_summary_terminate(&summary); 305 | return 1; 306 | } 307 | 308 | assessed_count += summary.assessed_count; 309 | for (j = 0; j < summary.failure_count; ++j) { 310 | switch (summary.failures[j].severity) { 311 | case RX_NONFATAL: 312 | ++nonfatal_failure_count; 313 | break; 314 | case RX_FATAL: 315 | ++fatal_failure_count; 316 | break; 317 | default: 318 | ASSERT(0); 319 | } 320 | } 321 | 322 | rx_summary_terminate(&summary); 323 | } 324 | 325 | free(test_cases); 326 | 327 | ASSERT(assessed_count == 120); 328 | ASSERT(nonfatal_failure_count == 30); 329 | ASSERT(fatal_failure_count == 30); 330 | return 0; 331 | } 332 | -------------------------------------------------------------------------------- /tests/c89/assertion-coverage.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #define ASSERT(x) \ 7 | (void)( \ 8 | (x) \ 9 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 10 | || (abort(), 0)) 11 | 12 | RX_TEST_CASE(my_test_suite, all_check_successes) 13 | { 14 | RX_CHECK(123 == 123); 15 | RX_BOOL_CHECK_TRUE(123 == 123); 16 | RX_BOOL_CHECK_FALSE(123 == 456); 17 | RX_INT_CHECK_EQUAL(123, 123); 18 | RX_INT_CHECK_NOT_EQUAL(123, 456); 19 | RX_INT_CHECK_GREATER(456, 123); 20 | RX_INT_CHECK_LESSER(123, 456); 21 | RX_INT_CHECK_GREATER_OR_EQUAL(456, 123); 22 | RX_INT_CHECK_LESSER_OR_EQUAL(123, 456); 23 | RX_UINT_CHECK_EQUAL(123, 123); 24 | RX_UINT_CHECK_NOT_EQUAL(123, 456); 25 | RX_UINT_CHECK_GREATER(456, 123); 26 | RX_UINT_CHECK_LESSER(123, 456); 27 | RX_UINT_CHECK_GREATER_OR_EQUAL(456, 123); 28 | RX_UINT_CHECK_LESSER_OR_EQUAL(123, 456); 29 | RX_REAL_CHECK_EQUAL(1.23, 1.23); 30 | RX_REAL_CHECK_NOT_EQUAL(1.23, 4.56); 31 | RX_REAL_CHECK_GREATER(4.56, 1.23); 32 | RX_REAL_CHECK_LESSER(1.23, 4.56); 33 | RX_REAL_CHECK_GREATER_OR_EQUAL(4.56, 1.23); 34 | RX_REAL_CHECK_LESSER_OR_EQUAL(1.23, 4.56); 35 | RX_REAL_CHECK_FUZZY_EQUAL(1.23, 1.23, 0.001); 36 | RX_REAL_CHECK_FUZZY_NOT_EQUAL(1.23, 4.56, 0.001); 37 | RX_STR_CHECK_EQUAL("hello", "hello"); 38 | RX_STR_CHECK_NOT_EQUAL("hello", "world!"); 39 | RX_STR_CHECK_EQUAL_NO_CASE("hello", "hello"); 40 | RX_STR_CHECK_NOT_EQUAL_NO_CASE("hello", "world!"); 41 | RX_PTR_CHECK_EQUAL((void *)0x123, (void *)0x123); 42 | RX_PTR_CHECK_NOT_EQUAL((void *)0x123, (void *)0x456); 43 | RX_PTR_CHECK_ALIGNED((void *)0x100, 128); 44 | } 45 | 46 | RX_TEST_CASE(my_test_suite, all_require_successes) 47 | { 48 | RX_REQUIRE(123 == 123); 49 | RX_BOOL_REQUIRE_TRUE(123 == 123); 50 | RX_BOOL_REQUIRE_FALSE(123 == 456); 51 | RX_INT_REQUIRE_EQUAL(123, 123); 52 | RX_INT_REQUIRE_NOT_EQUAL(123, 456); 53 | RX_INT_REQUIRE_GREATER(456, 123); 54 | RX_INT_REQUIRE_LESSER(123, 456); 55 | RX_INT_REQUIRE_GREATER_OR_EQUAL(456, 123); 56 | RX_INT_REQUIRE_LESSER_OR_EQUAL(123, 456); 57 | RX_UINT_REQUIRE_EQUAL(123, 123); 58 | RX_UINT_REQUIRE_NOT_EQUAL(123, 456); 59 | RX_UINT_REQUIRE_GREATER(456, 123); 60 | RX_UINT_REQUIRE_LESSER(123, 456); 61 | RX_UINT_REQUIRE_GREATER_OR_EQUAL(456, 123); 62 | RX_UINT_REQUIRE_LESSER_OR_EQUAL(123, 456); 63 | RX_REAL_REQUIRE_EQUAL(1.23, 1.23); 64 | RX_REAL_REQUIRE_NOT_EQUAL(1.23, 4.56); 65 | RX_REAL_REQUIRE_GREATER(4.56, 1.23); 66 | RX_REAL_REQUIRE_LESSER(1.23, 4.56); 67 | RX_REAL_REQUIRE_GREATER_OR_EQUAL(4.56, 1.23); 68 | RX_REAL_REQUIRE_LESSER_OR_EQUAL(1.23, 4.56); 69 | RX_REAL_REQUIRE_FUZZY_EQUAL(1.23, 1.23, 0.001); 70 | RX_REAL_REQUIRE_FUZZY_NOT_EQUAL(1.23, 4.56, 0.001); 71 | RX_STR_REQUIRE_EQUAL("hello", "hello"); 72 | RX_STR_REQUIRE_NOT_EQUAL("hello", "world!"); 73 | RX_STR_REQUIRE_EQUAL_NO_CASE("hello", "hello"); 74 | RX_STR_REQUIRE_NOT_EQUAL_NO_CASE("hello", "world!"); 75 | RX_PTR_REQUIRE_EQUAL((void *)0x123, (void *)0x123); 76 | RX_PTR_REQUIRE_NOT_EQUAL((void *)0x123, (void *)0x456); 77 | RX_PTR_REQUIRE_ALIGNED((void *)0x100, 128); 78 | } 79 | 80 | RX_TEST_CASE(my_test_suite, all_check_failures) 81 | { 82 | RX_CHECK(123 == 456); 83 | RX_BOOL_CHECK_TRUE(123 == 456); 84 | RX_BOOL_CHECK_FALSE(123 == 123); 85 | RX_INT_CHECK_EQUAL(123, 456); 86 | RX_INT_CHECK_NOT_EQUAL(123, 123); 87 | RX_INT_CHECK_GREATER(123, 456); 88 | RX_INT_CHECK_LESSER(456, 123); 89 | RX_INT_CHECK_GREATER_OR_EQUAL(123, 456); 90 | RX_INT_CHECK_LESSER_OR_EQUAL(456, 123); 91 | RX_UINT_CHECK_EQUAL(123, 456); 92 | RX_UINT_CHECK_NOT_EQUAL(123, 123); 93 | RX_UINT_CHECK_GREATER(123, 456); 94 | RX_UINT_CHECK_LESSER(456, 123); 95 | RX_UINT_CHECK_GREATER_OR_EQUAL(123, 456); 96 | RX_UINT_CHECK_LESSER_OR_EQUAL(456, 123); 97 | RX_REAL_CHECK_EQUAL(1.23, 4.56); 98 | RX_REAL_CHECK_NOT_EQUAL(1.23, 1.23); 99 | RX_REAL_CHECK_GREATER(1.23, 4.56); 100 | RX_REAL_CHECK_LESSER(4.56, 1.23); 101 | RX_REAL_CHECK_GREATER_OR_EQUAL(1.23, 4.56); 102 | RX_REAL_CHECK_LESSER_OR_EQUAL(4.56, 1.23); 103 | RX_REAL_CHECK_FUZZY_EQUAL(1.23, 4.56, 0.001); 104 | RX_REAL_CHECK_FUZZY_NOT_EQUAL(1.23, 1.23, 0.001); 105 | RX_STR_CHECK_EQUAL("hello", "world!"); 106 | RX_STR_CHECK_NOT_EQUAL("hello", "hello"); 107 | RX_STR_CHECK_EQUAL_NO_CASE("hello", "world!"); 108 | RX_STR_CHECK_NOT_EQUAL_NO_CASE("hello", "hello"); 109 | RX_PTR_CHECK_EQUAL((void *)0x123, (void *)0x456); 110 | RX_PTR_CHECK_NOT_EQUAL((void *)0x123, (void *)0x123); 111 | RX_PTR_CHECK_ALIGNED((void *)0x100, 100); 112 | } 113 | 114 | RX_TEST_CASE(my_test_suite, require_failure) 115 | { 116 | RX_REQUIRE(123 == 456); 117 | } 118 | 119 | RX_TEST_CASE(my_test_suite, bool_require_true_failure) 120 | { 121 | RX_BOOL_REQUIRE_TRUE(123 == 456); 122 | } 123 | 124 | RX_TEST_CASE(my_test_suite, bool_require_false_failure) 125 | { 126 | RX_BOOL_REQUIRE_FALSE(123 == 123); 127 | } 128 | 129 | RX_TEST_CASE(my_test_suite, int_require_equal_failure) 130 | { 131 | RX_INT_REQUIRE_EQUAL(123, 456); 132 | } 133 | 134 | RX_TEST_CASE(my_test_suite, int_require_not_equal_failure) 135 | { 136 | RX_INT_REQUIRE_NOT_EQUAL(123, 123); 137 | } 138 | 139 | RX_TEST_CASE(my_test_suite, int_require_greater_failure) 140 | { 141 | RX_INT_REQUIRE_GREATER(123, 456); 142 | } 143 | 144 | RX_TEST_CASE(my_test_suite, int_require_lesser_failure) 145 | { 146 | RX_INT_REQUIRE_LESSER(456, 123); 147 | } 148 | 149 | RX_TEST_CASE(my_test_suite, int_require_greater_or_equal_failure) 150 | { 151 | RX_INT_REQUIRE_GREATER_OR_EQUAL(123, 456); 152 | } 153 | 154 | RX_TEST_CASE(my_test_suite, int_require_lesser_or_equal_failure) 155 | { 156 | RX_INT_REQUIRE_LESSER_OR_EQUAL(456, 123); 157 | } 158 | 159 | RX_TEST_CASE(my_test_suite, uint_require_equal_failure) 160 | { 161 | RX_UINT_REQUIRE_EQUAL(123, 456); 162 | } 163 | 164 | RX_TEST_CASE(my_test_suite, uint_require_not_equal_failure) 165 | { 166 | RX_UINT_REQUIRE_NOT_EQUAL(123, 123); 167 | } 168 | 169 | RX_TEST_CASE(my_test_suite, uint_require_greater_failure) 170 | { 171 | RX_UINT_REQUIRE_GREATER(123, 456); 172 | } 173 | 174 | RX_TEST_CASE(my_test_suite, uint_require_lesser_failure) 175 | { 176 | RX_UINT_REQUIRE_LESSER(456, 123); 177 | } 178 | 179 | RX_TEST_CASE(my_test_suite, uint_require_greater_or_equal_failure) 180 | { 181 | RX_UINT_REQUIRE_GREATER_OR_EQUAL(123, 456); 182 | } 183 | 184 | RX_TEST_CASE(my_test_suite, uint_require_lesser_or_equal_failure) 185 | { 186 | RX_UINT_REQUIRE_LESSER_OR_EQUAL(456, 123); 187 | } 188 | 189 | RX_TEST_CASE(my_test_suite, real_require_equal_failure) 190 | { 191 | RX_REAL_REQUIRE_EQUAL(1.23, 4.56); 192 | } 193 | 194 | RX_TEST_CASE(my_test_suite, real_require_not_equal_failure) 195 | { 196 | RX_REAL_REQUIRE_NOT_EQUAL(1.23, 1.23); 197 | } 198 | 199 | RX_TEST_CASE(my_test_suite, real_require_greater_failure) 200 | { 201 | RX_REAL_REQUIRE_GREATER(1.23, 4.56); 202 | } 203 | 204 | RX_TEST_CASE(my_test_suite, real_require_lesser_failure) 205 | { 206 | RX_REAL_REQUIRE_LESSER(4.56, 1.23); 207 | } 208 | 209 | RX_TEST_CASE(my_test_suite, real_require_greater_or_equal_failure) 210 | { 211 | RX_REAL_REQUIRE_GREATER_OR_EQUAL(1.23, 4.56); 212 | } 213 | 214 | RX_TEST_CASE(my_test_suite, real_require_lesser_or_equal_failure) 215 | { 216 | RX_REAL_REQUIRE_LESSER_OR_EQUAL(4.56, 1.23); 217 | } 218 | 219 | RX_TEST_CASE(my_test_suite, real_require_fuzzy_equal_failure) 220 | { 221 | RX_REAL_REQUIRE_FUZZY_EQUAL(1.23, 4.56, 0.001); 222 | } 223 | 224 | RX_TEST_CASE(my_test_suite, real_require_fuzzy_not_equal_failure) 225 | { 226 | RX_REAL_REQUIRE_FUZZY_NOT_EQUAL(1.23, 1.23, 0.001); 227 | } 228 | 229 | RX_TEST_CASE(my_test_suite, str_require_equal_failure) 230 | { 231 | RX_STR_REQUIRE_EQUAL("hello", "world!"); 232 | } 233 | 234 | RX_TEST_CASE(my_test_suite, str_require_not_equal_failure) 235 | { 236 | RX_STR_REQUIRE_NOT_EQUAL("hello", "hello"); 237 | } 238 | 239 | RX_TEST_CASE(my_test_suite, str_require_equal_no_case_failure) 240 | { 241 | RX_STR_REQUIRE_EQUAL_NO_CASE("hello", "world!"); 242 | } 243 | 244 | RX_TEST_CASE(my_test_suite, str_require_not_equal_no_case_failure) 245 | { 246 | RX_STR_REQUIRE_NOT_EQUAL_NO_CASE("hello", "hello"); 247 | } 248 | 249 | RX_TEST_CASE(my_test_suite, ptr_require_equal_failure) 250 | { 251 | RX_PTR_REQUIRE_EQUAL((void *)0x123, (void *)0x456); 252 | } 253 | 254 | RX_TEST_CASE(my_test_suite, ptr_require_not_equal_failure) 255 | { 256 | RX_PTR_REQUIRE_NOT_EQUAL((void *)0x123, (void *)0x123); 257 | } 258 | 259 | RX_TEST_CASE(my_test_suite, ptr_require_aligned_failure) 260 | { 261 | RX_PTR_REQUIRE_ALIGNED((void *)0x100, 100); 262 | } 263 | 264 | int 265 | main(int argc, const char **argv) 266 | { 267 | size_t i; 268 | rx_size test_case_count; 269 | struct rx_test_case *test_cases; 270 | size_t assessed_count; 271 | size_t nonfatal_failure_count; 272 | size_t fatal_failure_count; 273 | 274 | (void)argc; 275 | (void)argv; 276 | 277 | assessed_count = 0; 278 | nonfatal_failure_count = 0; 279 | fatal_failure_count = 0; 280 | 281 | rx_enumerate_test_cases(&test_case_count, NULL); 282 | test_cases 283 | = (struct rx_test_case *)malloc(sizeof *test_cases * test_case_count); 284 | if (test_cases == NULL) { 285 | printf("failed to allocate the test cases\n"); 286 | return 1; 287 | } 288 | 289 | rx_enumerate_test_cases(&test_case_count, test_cases); 290 | for (i = 0; i < test_case_count; ++i) { 291 | size_t j; 292 | const struct rx_test_case *test_case; 293 | struct rx_summary summary; 294 | 295 | test_case = &test_cases[i]; 296 | 297 | if (rx_summary_initialize(&summary, test_case) != RX_SUCCESS) { 298 | printf("failed to initialize a summary\n"); 299 | return 1; 300 | } 301 | 302 | if (rx_test_case_run(&summary, test_case) != RX_SUCCESS) { 303 | printf("failed to run a test case\n"); 304 | rx_summary_terminate(&summary); 305 | return 1; 306 | } 307 | 308 | assessed_count += summary.assessed_count; 309 | for (j = 0; j < summary.failure_count; ++j) { 310 | switch (summary.failures[j].severity) { 311 | case RX_NONFATAL: 312 | ++nonfatal_failure_count; 313 | break; 314 | case RX_FATAL: 315 | ++fatal_failure_count; 316 | break; 317 | default: 318 | ASSERT(0); 319 | } 320 | } 321 | 322 | rx_summary_terminate(&summary); 323 | } 324 | 325 | free(test_cases); 326 | 327 | ASSERT(assessed_count == 120); 328 | ASSERT(nonfatal_failure_count == 30); 329 | ASSERT(fatal_failure_count == 30); 330 | return 0; 331 | } 332 | -------------------------------------------------------------------------------- /tests/c89/config-inherit.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define ASSERT(x) \ 6 | (void)( \ 7 | (x) \ 8 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 9 | || (abort(), 0)) 10 | 11 | static int step = 0; 12 | 13 | RX_TEST_SUITE_1(my_test_suite, .skip = 1); 14 | 15 | RX_TEST_CASE(my_test_suite, my_test_case_1) 16 | { 17 | ++step; 18 | ASSERT(0); 19 | 20 | RX_INT_REQUIRE_EQUAL(42, 42); 21 | } 22 | 23 | RX_TEST_CASE_1(my_test_suite, my_test_case_2, .skip = 0) 24 | { 25 | ++step; 26 | ASSERT(step == 2); 27 | 28 | RX_INT_REQUIRE_EQUAL(42, 42); 29 | } 30 | 31 | int 32 | main(int argc, const char **argv) 33 | { 34 | ++step; 35 | ASSERT(step == 1); 36 | 37 | ASSERT(rx_main(0, NULL, argc, argv) == RX_SUCCESS); 38 | 39 | ++step; 40 | ASSERT(step == 3); 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /tests/c89/fixture-void.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define ASSERT(x) \ 6 | (void)( \ 7 | (x) \ 8 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 9 | || (abort(), 0)) 10 | 11 | static int step = 0; 12 | 13 | RX_SET_UP(my_set_up) 14 | { 15 | ++step; 16 | ASSERT(step == 2); 17 | 18 | return RX_SUCCESS; 19 | } 20 | 21 | RX_TEAR_DOWN(my_tear_down) 22 | { 23 | ++step; 24 | ASSERT(step == 4); 25 | } 26 | 27 | RX_VOID_FIXTURE_2(my_fixture, .set_up = my_set_up, .tear_down = my_tear_down); 28 | 29 | RX_TEST_CASE_1(my_test_suite, my_test_case, .fixture = my_fixture) 30 | { 31 | ++step; 32 | ASSERT(step == 3); 33 | 34 | RX_INT_REQUIRE_EQUAL(42, 42); 35 | } 36 | 37 | int 38 | main(int argc, const char **argv) 39 | { 40 | ++step; 41 | ASSERT(step == 1); 42 | 43 | ASSERT(rx_main(0, NULL, argc, argv) == RX_SUCCESS); 44 | 45 | ++step; 46 | ASSERT(step == 5); 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /tests/c89/fixture.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define ASSERT(x) \ 6 | (void)( \ 7 | (x) \ 8 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 9 | || (abort(), 0)) 10 | 11 | static int step = 0; 12 | 13 | struct my_data { 14 | int value; 15 | }; 16 | 17 | RX_SET_UP(my_set_up) 18 | { 19 | struct my_data *data; 20 | 21 | data = (struct my_data *)RX_DATA; 22 | 23 | ++step; 24 | ASSERT(step == 2); 25 | 26 | data->value = 123; 27 | return RX_SUCCESS; 28 | } 29 | 30 | RX_TEAR_DOWN(my_tear_down) 31 | { 32 | struct my_data *data; 33 | 34 | data = (struct my_data *)RX_DATA; 35 | 36 | ++step; 37 | ASSERT(step == 4); 38 | 39 | ASSERT(data->value == 123); 40 | } 41 | 42 | RX_FIXTURE_2(my_fixture, 43 | struct my_data, 44 | .set_up = my_set_up, 45 | .tear_down = my_tear_down); 46 | 47 | RX_TEST_CASE_1(my_test_suite, my_test_case, .fixture = my_fixture) 48 | { 49 | struct my_data *data; 50 | 51 | data = (struct my_data *)RX_DATA; 52 | 53 | ++step; 54 | ASSERT(step == 3); 55 | 56 | ASSERT(data->value == 123); 57 | 58 | RX_INT_REQUIRE_EQUAL(42, 42); 59 | } 60 | 61 | int 62 | main(int argc, const char **argv) 63 | { 64 | ++step; 65 | ASSERT(step == 1); 66 | 67 | ASSERT(rx_main(0, NULL, argc, argv) == RX_SUCCESS); 68 | 69 | ++step; 70 | ASSERT(step == 5); 71 | 72 | return 0; 73 | } 74 | -------------------------------------------------------------------------------- /tests/c89/minimal.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define ASSERT(x) \ 6 | (void)( \ 7 | (x) \ 8 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 9 | || (abort(), 0)) 10 | 11 | static int step = 0; 12 | 13 | RX_TEST_CASE(my_test_suite, my_test_case) 14 | { 15 | ++step; 16 | ASSERT(step == 2); 17 | 18 | RX_INT_REQUIRE_EQUAL(42, 42); 19 | } 20 | 21 | int 22 | main(int argc, const char **argv) 23 | { 24 | ++step; 25 | ASSERT(step == 1); 26 | 27 | ASSERT(rx_main(0, NULL, argc, argv) == RX_SUCCESS); 28 | 29 | ++step; 30 | ASSERT(step == 3); 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /tests/config-inherit.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define ASSERT(x) \ 6 | (void)( \ 7 | (x) \ 8 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 9 | || (abort(), 0)) 10 | 11 | static int step = 0; 12 | 13 | RX_TEST_SUITE(my_test_suite, .skip = 1); 14 | 15 | RX_TEST_CASE(my_test_suite, my_test_case_1) 16 | { 17 | ++step; 18 | ASSERT(0); 19 | 20 | RX_INT_REQUIRE_EQUAL(42, 42); 21 | } 22 | 23 | RX_TEST_CASE(my_test_suite, my_test_case_2, .skip = 0) 24 | { 25 | ++step; 26 | ASSERT(step == 2); 27 | 28 | RX_INT_REQUIRE_EQUAL(42, 42); 29 | } 30 | 31 | int 32 | main(int argc, const char **argv) 33 | { 34 | ++step; 35 | ASSERT(step == 1); 36 | 37 | ASSERT(rx_main(0, NULL, argc, argv) == RX_SUCCESS); 38 | 39 | ++step; 40 | ASSERT(step == 3); 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /tests/config-skip.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define ASSERT(x) \ 6 | (void)( \ 7 | (x) \ 8 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 9 | || (abort(), 0)) 10 | 11 | static int step = 0; 12 | 13 | RX_TEST_CASE(my_test_suite, my_test_case_1) 14 | { 15 | ++step; 16 | ASSERT(step == 2); 17 | 18 | RX_INT_REQUIRE_EQUAL(42, 42); 19 | } 20 | 21 | RX_TEST_CASE(my_test_suite, my_test_case_2, .skip = 1) 22 | { 23 | ++step; 24 | ASSERT(0); 25 | 26 | RX_INT_REQUIRE_EQUAL(42, 42); 27 | } 28 | 29 | int 30 | main(int argc, const char **argv) 31 | { 32 | ++step; 33 | ASSERT(step == 1); 34 | 35 | ASSERT(rx_main(0, NULL, argc, argv) == RX_SUCCESS); 36 | 37 | ++step; 38 | ASSERT(step == 3); 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /tests/cpp98/assertion-coverage.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #define ASSERT(x) \ 7 | (void)( \ 8 | (x) \ 9 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 10 | || (abort(), 0)) 11 | 12 | RX_TEST_CASE(my_test_suite, all_check_successes) 13 | { 14 | RX_CHECK(123 == 123); 15 | RX_BOOL_CHECK_TRUE(123 == 123); 16 | RX_BOOL_CHECK_FALSE(123 == 456); 17 | RX_INT_CHECK_EQUAL(123, 123); 18 | RX_INT_CHECK_NOT_EQUAL(123, 456); 19 | RX_INT_CHECK_GREATER(456, 123); 20 | RX_INT_CHECK_LESSER(123, 456); 21 | RX_INT_CHECK_GREATER_OR_EQUAL(456, 123); 22 | RX_INT_CHECK_LESSER_OR_EQUAL(123, 456); 23 | RX_UINT_CHECK_EQUAL(123, 123); 24 | RX_UINT_CHECK_NOT_EQUAL(123, 456); 25 | RX_UINT_CHECK_GREATER(456, 123); 26 | RX_UINT_CHECK_LESSER(123, 456); 27 | RX_UINT_CHECK_GREATER_OR_EQUAL(456, 123); 28 | RX_UINT_CHECK_LESSER_OR_EQUAL(123, 456); 29 | RX_REAL_CHECK_EQUAL(1.23, 1.23); 30 | RX_REAL_CHECK_NOT_EQUAL(1.23, 4.56); 31 | RX_REAL_CHECK_GREATER(4.56, 1.23); 32 | RX_REAL_CHECK_LESSER(1.23, 4.56); 33 | RX_REAL_CHECK_GREATER_OR_EQUAL(4.56, 1.23); 34 | RX_REAL_CHECK_LESSER_OR_EQUAL(1.23, 4.56); 35 | RX_REAL_CHECK_FUZZY_EQUAL(1.23, 1.23, 0.001); 36 | RX_REAL_CHECK_FUZZY_NOT_EQUAL(1.23, 4.56, 0.001); 37 | RX_STR_CHECK_EQUAL("hello", "hello"); 38 | RX_STR_CHECK_NOT_EQUAL("hello", "world!"); 39 | RX_STR_CHECK_EQUAL_NO_CASE("hello", "hello"); 40 | RX_STR_CHECK_NOT_EQUAL_NO_CASE("hello", "world!"); 41 | RX_PTR_CHECK_EQUAL((void *)0x123, (void *)0x123); 42 | RX_PTR_CHECK_NOT_EQUAL((void *)0x123, (void *)0x456); 43 | RX_PTR_CHECK_ALIGNED((void *)0x100, 128); 44 | } 45 | 46 | RX_TEST_CASE(my_test_suite, all_require_successes) 47 | { 48 | RX_REQUIRE(123 == 123); 49 | RX_BOOL_REQUIRE_TRUE(123 == 123); 50 | RX_BOOL_REQUIRE_FALSE(123 == 456); 51 | RX_INT_REQUIRE_EQUAL(123, 123); 52 | RX_INT_REQUIRE_NOT_EQUAL(123, 456); 53 | RX_INT_REQUIRE_GREATER(456, 123); 54 | RX_INT_REQUIRE_LESSER(123, 456); 55 | RX_INT_REQUIRE_GREATER_OR_EQUAL(456, 123); 56 | RX_INT_REQUIRE_LESSER_OR_EQUAL(123, 456); 57 | RX_UINT_REQUIRE_EQUAL(123, 123); 58 | RX_UINT_REQUIRE_NOT_EQUAL(123, 456); 59 | RX_UINT_REQUIRE_GREATER(456, 123); 60 | RX_UINT_REQUIRE_LESSER(123, 456); 61 | RX_UINT_REQUIRE_GREATER_OR_EQUAL(456, 123); 62 | RX_UINT_REQUIRE_LESSER_OR_EQUAL(123, 456); 63 | RX_REAL_REQUIRE_EQUAL(1.23, 1.23); 64 | RX_REAL_REQUIRE_NOT_EQUAL(1.23, 4.56); 65 | RX_REAL_REQUIRE_GREATER(4.56, 1.23); 66 | RX_REAL_REQUIRE_LESSER(1.23, 4.56); 67 | RX_REAL_REQUIRE_GREATER_OR_EQUAL(4.56, 1.23); 68 | RX_REAL_REQUIRE_LESSER_OR_EQUAL(1.23, 4.56); 69 | RX_REAL_REQUIRE_FUZZY_EQUAL(1.23, 1.23, 0.001); 70 | RX_REAL_REQUIRE_FUZZY_NOT_EQUAL(1.23, 4.56, 0.001); 71 | RX_STR_REQUIRE_EQUAL("hello", "hello"); 72 | RX_STR_REQUIRE_NOT_EQUAL("hello", "world!"); 73 | RX_STR_REQUIRE_EQUAL_NO_CASE("hello", "hello"); 74 | RX_STR_REQUIRE_NOT_EQUAL_NO_CASE("hello", "world!"); 75 | RX_PTR_REQUIRE_EQUAL((void *)0x123, (void *)0x123); 76 | RX_PTR_REQUIRE_NOT_EQUAL((void *)0x123, (void *)0x456); 77 | RX_PTR_REQUIRE_ALIGNED((void *)0x100, 128); 78 | } 79 | 80 | RX_TEST_CASE(my_test_suite, all_check_failures) 81 | { 82 | RX_CHECK(123 == 456); 83 | RX_BOOL_CHECK_TRUE(123 == 456); 84 | RX_BOOL_CHECK_FALSE(123 == 123); 85 | RX_INT_CHECK_EQUAL(123, 456); 86 | RX_INT_CHECK_NOT_EQUAL(123, 123); 87 | RX_INT_CHECK_GREATER(123, 456); 88 | RX_INT_CHECK_LESSER(456, 123); 89 | RX_INT_CHECK_GREATER_OR_EQUAL(123, 456); 90 | RX_INT_CHECK_LESSER_OR_EQUAL(456, 123); 91 | RX_UINT_CHECK_EQUAL(123, 456); 92 | RX_UINT_CHECK_NOT_EQUAL(123, 123); 93 | RX_UINT_CHECK_GREATER(123, 456); 94 | RX_UINT_CHECK_LESSER(456, 123); 95 | RX_UINT_CHECK_GREATER_OR_EQUAL(123, 456); 96 | RX_UINT_CHECK_LESSER_OR_EQUAL(456, 123); 97 | RX_REAL_CHECK_EQUAL(1.23, 4.56); 98 | RX_REAL_CHECK_NOT_EQUAL(1.23, 1.23); 99 | RX_REAL_CHECK_GREATER(1.23, 4.56); 100 | RX_REAL_CHECK_LESSER(4.56, 1.23); 101 | RX_REAL_CHECK_GREATER_OR_EQUAL(1.23, 4.56); 102 | RX_REAL_CHECK_LESSER_OR_EQUAL(4.56, 1.23); 103 | RX_REAL_CHECK_FUZZY_EQUAL(1.23, 4.56, 0.001); 104 | RX_REAL_CHECK_FUZZY_NOT_EQUAL(1.23, 1.23, 0.001); 105 | RX_STR_CHECK_EQUAL("hello", "world!"); 106 | RX_STR_CHECK_NOT_EQUAL("hello", "hello"); 107 | RX_STR_CHECK_EQUAL_NO_CASE("hello", "world!"); 108 | RX_STR_CHECK_NOT_EQUAL_NO_CASE("hello", "hello"); 109 | RX_PTR_CHECK_EQUAL((void *)0x123, (void *)0x456); 110 | RX_PTR_CHECK_NOT_EQUAL((void *)0x123, (void *)0x123); 111 | RX_PTR_CHECK_ALIGNED((void *)0x100, 100); 112 | } 113 | 114 | RX_TEST_CASE(my_test_suite, require_failure) 115 | { 116 | RX_REQUIRE(123 == 456); 117 | } 118 | 119 | RX_TEST_CASE(my_test_suite, bool_require_true_failure) 120 | { 121 | RX_BOOL_REQUIRE_TRUE(123 == 456); 122 | } 123 | 124 | RX_TEST_CASE(my_test_suite, bool_require_false_failure) 125 | { 126 | RX_BOOL_REQUIRE_FALSE(123 == 123); 127 | } 128 | 129 | RX_TEST_CASE(my_test_suite, int_require_equal_failure) 130 | { 131 | RX_INT_REQUIRE_EQUAL(123, 456); 132 | } 133 | 134 | RX_TEST_CASE(my_test_suite, int_require_not_equal_failure) 135 | { 136 | RX_INT_REQUIRE_NOT_EQUAL(123, 123); 137 | } 138 | 139 | RX_TEST_CASE(my_test_suite, int_require_greater_failure) 140 | { 141 | RX_INT_REQUIRE_GREATER(123, 456); 142 | } 143 | 144 | RX_TEST_CASE(my_test_suite, int_require_lesser_failure) 145 | { 146 | RX_INT_REQUIRE_LESSER(456, 123); 147 | } 148 | 149 | RX_TEST_CASE(my_test_suite, int_require_greater_or_equal_failure) 150 | { 151 | RX_INT_REQUIRE_GREATER_OR_EQUAL(123, 456); 152 | } 153 | 154 | RX_TEST_CASE(my_test_suite, int_require_lesser_or_equal_failure) 155 | { 156 | RX_INT_REQUIRE_LESSER_OR_EQUAL(456, 123); 157 | } 158 | 159 | RX_TEST_CASE(my_test_suite, uint_require_equal_failure) 160 | { 161 | RX_UINT_REQUIRE_EQUAL(123, 456); 162 | } 163 | 164 | RX_TEST_CASE(my_test_suite, uint_require_not_equal_failure) 165 | { 166 | RX_UINT_REQUIRE_NOT_EQUAL(123, 123); 167 | } 168 | 169 | RX_TEST_CASE(my_test_suite, uint_require_greater_failure) 170 | { 171 | RX_UINT_REQUIRE_GREATER(123, 456); 172 | } 173 | 174 | RX_TEST_CASE(my_test_suite, uint_require_lesser_failure) 175 | { 176 | RX_UINT_REQUIRE_LESSER(456, 123); 177 | } 178 | 179 | RX_TEST_CASE(my_test_suite, uint_require_greater_or_equal_failure) 180 | { 181 | RX_UINT_REQUIRE_GREATER_OR_EQUAL(123, 456); 182 | } 183 | 184 | RX_TEST_CASE(my_test_suite, uint_require_lesser_or_equal_failure) 185 | { 186 | RX_UINT_REQUIRE_LESSER_OR_EQUAL(456, 123); 187 | } 188 | 189 | RX_TEST_CASE(my_test_suite, real_require_equal_failure) 190 | { 191 | RX_REAL_REQUIRE_EQUAL(1.23, 4.56); 192 | } 193 | 194 | RX_TEST_CASE(my_test_suite, real_require_not_equal_failure) 195 | { 196 | RX_REAL_REQUIRE_NOT_EQUAL(1.23, 1.23); 197 | } 198 | 199 | RX_TEST_CASE(my_test_suite, real_require_greater_failure) 200 | { 201 | RX_REAL_REQUIRE_GREATER(1.23, 4.56); 202 | } 203 | 204 | RX_TEST_CASE(my_test_suite, real_require_lesser_failure) 205 | { 206 | RX_REAL_REQUIRE_LESSER(4.56, 1.23); 207 | } 208 | 209 | RX_TEST_CASE(my_test_suite, real_require_greater_or_equal_failure) 210 | { 211 | RX_REAL_REQUIRE_GREATER_OR_EQUAL(1.23, 4.56); 212 | } 213 | 214 | RX_TEST_CASE(my_test_suite, real_require_lesser_or_equal_failure) 215 | { 216 | RX_REAL_REQUIRE_LESSER_OR_EQUAL(4.56, 1.23); 217 | } 218 | 219 | RX_TEST_CASE(my_test_suite, real_require_fuzzy_equal_failure) 220 | { 221 | RX_REAL_REQUIRE_FUZZY_EQUAL(1.23, 4.56, 0.001); 222 | } 223 | 224 | RX_TEST_CASE(my_test_suite, real_require_fuzzy_not_equal_failure) 225 | { 226 | RX_REAL_REQUIRE_FUZZY_NOT_EQUAL(1.23, 1.23, 0.001); 227 | } 228 | 229 | RX_TEST_CASE(my_test_suite, str_require_equal_failure) 230 | { 231 | RX_STR_REQUIRE_EQUAL("hello", "world!"); 232 | } 233 | 234 | RX_TEST_CASE(my_test_suite, str_require_not_equal_failure) 235 | { 236 | RX_STR_REQUIRE_NOT_EQUAL("hello", "hello"); 237 | } 238 | 239 | RX_TEST_CASE(my_test_suite, str_require_equal_no_case_failure) 240 | { 241 | RX_STR_REQUIRE_EQUAL_NO_CASE("hello", "world!"); 242 | } 243 | 244 | RX_TEST_CASE(my_test_suite, str_require_not_equal_no_case_failure) 245 | { 246 | RX_STR_REQUIRE_NOT_EQUAL_NO_CASE("hello", "hello"); 247 | } 248 | 249 | RX_TEST_CASE(my_test_suite, ptr_require_equal_failure) 250 | { 251 | RX_PTR_REQUIRE_EQUAL((void *)0x123, (void *)0x456); 252 | } 253 | 254 | RX_TEST_CASE(my_test_suite, ptr_require_not_equal_failure) 255 | { 256 | RX_PTR_REQUIRE_NOT_EQUAL((void *)0x123, (void *)0x123); 257 | } 258 | 259 | RX_TEST_CASE(my_test_suite, ptr_require_aligned_failure) 260 | { 261 | RX_PTR_REQUIRE_ALIGNED((void *)0x100, 100); 262 | } 263 | 264 | int 265 | main(int argc, const char **argv) 266 | { 267 | size_t i; 268 | rx_size test_case_count; 269 | struct rx_test_case *test_cases; 270 | size_t assessed_count; 271 | size_t nonfatal_failure_count; 272 | size_t fatal_failure_count; 273 | 274 | (void)argc; 275 | (void)argv; 276 | 277 | assessed_count = 0; 278 | nonfatal_failure_count = 0; 279 | fatal_failure_count = 0; 280 | 281 | rx_enumerate_test_cases(&test_case_count, NULL); 282 | test_cases 283 | = (struct rx_test_case *)malloc(sizeof *test_cases * test_case_count); 284 | if (test_cases == NULL) { 285 | printf("failed to allocate the test cases\n"); 286 | return 1; 287 | } 288 | 289 | rx_enumerate_test_cases(&test_case_count, test_cases); 290 | for (i = 0; i < test_case_count; ++i) { 291 | size_t j; 292 | const struct rx_test_case *test_case; 293 | struct rx_summary summary; 294 | 295 | test_case = &test_cases[i]; 296 | 297 | if (rx_summary_initialize(&summary, test_case) != RX_SUCCESS) { 298 | printf("failed to initialize a summary\n"); 299 | return 1; 300 | } 301 | 302 | if (rx_test_case_run(&summary, test_case) != RX_SUCCESS) { 303 | printf("failed to run a test case\n"); 304 | rx_summary_terminate(&summary); 305 | return 1; 306 | } 307 | 308 | assessed_count += summary.assessed_count; 309 | for (j = 0; j < summary.failure_count; ++j) { 310 | switch (summary.failures[j].severity) { 311 | case RX_NONFATAL: 312 | ++nonfatal_failure_count; 313 | break; 314 | case RX_FATAL: 315 | ++fatal_failure_count; 316 | break; 317 | default: 318 | ASSERT(0); 319 | } 320 | } 321 | 322 | rx_summary_terminate(&summary); 323 | } 324 | 325 | free(test_cases); 326 | 327 | ASSERT(assessed_count == 120); 328 | ASSERT(nonfatal_failure_count == 30); 329 | ASSERT(fatal_failure_count == 30); 330 | return 0; 331 | } 332 | -------------------------------------------------------------------------------- /tests/cpp98/config-inherit.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define ASSERT(x) \ 6 | (void)( \ 7 | (x) \ 8 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 9 | || (abort(), 0)) 10 | 11 | static int step = 0; 12 | 13 | RX_TEST_SUITE_1(my_test_suite, .skip = 1); 14 | 15 | RX_TEST_CASE(my_test_suite, my_test_case_1) 16 | { 17 | ++step; 18 | ASSERT(0); 19 | 20 | RX_INT_REQUIRE_EQUAL(42, 42); 21 | } 22 | 23 | RX_TEST_CASE_1(my_test_suite, my_test_case_2, .skip = 0) 24 | { 25 | ++step; 26 | ASSERT(step == 2); 27 | 28 | RX_INT_REQUIRE_EQUAL(42, 42); 29 | } 30 | 31 | int 32 | main(int argc, const char **argv) 33 | { 34 | ++step; 35 | ASSERT(step == 1); 36 | 37 | ASSERT(rx_main(0, NULL, argc, argv) == RX_SUCCESS); 38 | 39 | ++step; 40 | ASSERT(step == 3); 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /tests/cpp98/fixture-void.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define ASSERT(x) \ 6 | (void)( \ 7 | (x) \ 8 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 9 | || (abort(), 0)) 10 | 11 | static int step = 0; 12 | 13 | RX_SET_UP(my_set_up) 14 | { 15 | ++step; 16 | ASSERT(step == 2); 17 | 18 | return RX_SUCCESS; 19 | } 20 | 21 | RX_TEAR_DOWN(my_tear_down) 22 | { 23 | ++step; 24 | ASSERT(step == 4); 25 | } 26 | 27 | RX_VOID_FIXTURE_2(my_fixture, .set_up = my_set_up, .tear_down = my_tear_down); 28 | 29 | RX_TEST_CASE_1(my_test_suite, my_test_case, .fixture = my_fixture) 30 | { 31 | ++step; 32 | ASSERT(step == 3); 33 | 34 | RX_INT_REQUIRE_EQUAL(42, 42); 35 | } 36 | 37 | int 38 | main(int argc, const char **argv) 39 | { 40 | ++step; 41 | ASSERT(step == 1); 42 | 43 | ASSERT(rx_main(0, NULL, argc, argv) == RX_SUCCESS); 44 | 45 | ++step; 46 | ASSERT(step == 5); 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /tests/cpp98/fixture.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define ASSERT(x) \ 6 | (void)( \ 7 | (x) \ 8 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 9 | || (abort(), 0)) 10 | 11 | static int step = 0; 12 | 13 | struct my_data { 14 | int value; 15 | }; 16 | 17 | RX_SET_UP(my_set_up) 18 | { 19 | struct my_data *data; 20 | 21 | data = (struct my_data *)RX_DATA; 22 | 23 | ++step; 24 | ASSERT(step == 2); 25 | 26 | data->value = 123; 27 | return RX_SUCCESS; 28 | } 29 | 30 | RX_TEAR_DOWN(my_tear_down) 31 | { 32 | struct my_data *data; 33 | 34 | data = (struct my_data *)RX_DATA; 35 | 36 | ++step; 37 | ASSERT(step == 4); 38 | 39 | ASSERT(data->value == 123); 40 | } 41 | 42 | RX_FIXTURE_2(my_fixture, 43 | struct my_data, 44 | .set_up = my_set_up, 45 | .tear_down = my_tear_down); 46 | 47 | RX_TEST_CASE_1(my_test_suite, my_test_case, .fixture = my_fixture) 48 | { 49 | struct my_data *data; 50 | 51 | data = (struct my_data *)RX_DATA; 52 | 53 | ++step; 54 | ASSERT(step == 3); 55 | 56 | ASSERT(data->value == 123); 57 | 58 | RX_INT_REQUIRE_EQUAL(42, 42); 59 | } 60 | 61 | int 62 | main(int argc, const char **argv) 63 | { 64 | ++step; 65 | ASSERT(step == 1); 66 | 67 | ASSERT(rx_main(0, NULL, argc, argv) == RX_SUCCESS); 68 | 69 | ++step; 70 | ASSERT(step == 5); 71 | 72 | return 0; 73 | } 74 | -------------------------------------------------------------------------------- /tests/cpp98/minimal.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define ASSERT(x) \ 6 | (void)( \ 7 | (x) \ 8 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 9 | || (abort(), 0)) 10 | 11 | static int step = 0; 12 | 13 | RX_TEST_CASE(my_test_suite, my_test_case) 14 | { 15 | ++step; 16 | ASSERT(step == 2); 17 | 18 | RX_INT_REQUIRE_EQUAL(42, 42); 19 | } 20 | 21 | int 22 | main(int argc, const char **argv) 23 | { 24 | ++step; 25 | ASSERT(step == 1); 26 | 27 | ASSERT(rx_main(0, NULL, argc, argv) == RX_SUCCESS); 28 | 29 | ++step; 30 | ASSERT(step == 3); 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /tests/empty.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define ASSERT(x) \ 6 | (void)( \ 7 | (x) \ 8 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 9 | || (abort(), 0)) 10 | 11 | static int step = 0; 12 | 13 | int 14 | main(int argc, const char **argv) 15 | { 16 | ++step; 17 | ASSERT(step == 1); 18 | 19 | ASSERT(rx_main(0, NULL, argc, argv) == RX_SUCCESS); 20 | 21 | ++step; 22 | ASSERT(step == 2); 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /tests/explicit.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define ASSERT(x) \ 6 | (void)( \ 7 | (x) \ 8 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 9 | || (abort(), 0)) 10 | 11 | static int step = 0; 12 | 13 | struct my_data { 14 | int value; 15 | }; 16 | 17 | enum rx_status 18 | my_set_up(struct rx_context *RX_PARAM_CONTEXT, void *RX_PARAM_DATA) 19 | { 20 | struct my_data *data; 21 | 22 | (void)RX_PARAM_CONTEXT; 23 | 24 | data = (struct my_data *)RX_DATA; 25 | 26 | ++step; 27 | ASSERT(step == 2); 28 | 29 | data->value = 123; 30 | return RX_SUCCESS; 31 | } 32 | 33 | void 34 | my_tear_down(struct rx_context *RX_PARAM_CONTEXT, void *RX_PARAM_DATA) 35 | { 36 | struct my_data *data; 37 | 38 | (void)RX_PARAM_CONTEXT; 39 | 40 | data = (struct my_data *)RX_DATA; 41 | 42 | ++step; 43 | ASSERT(step == 4); 44 | 45 | ASSERT(data->value == 123); 46 | } 47 | 48 | void 49 | my_test_suite_my_test_case(struct rx_context *RX_PARAM_CONTEXT, 50 | void *RX_PARAM_DATA) 51 | { 52 | struct my_data *data; 53 | 54 | (void)RX_PARAM_CONTEXT; 55 | 56 | data = (struct my_data *)RX_DATA; 57 | 58 | ++step; 59 | ASSERT(step == 3); 60 | 61 | ASSERT(data->value == 123); 62 | 63 | RX_INT_REQUIRE_EQUAL(42, 42); 64 | } 65 | 66 | static const struct rx_test_case my_test_cases[] = { 67 | { 68 | "my_test_suite", 69 | "my_test_case", 70 | my_test_suite_my_test_case, 71 | {0, {sizeof(struct my_data), {my_set_up, my_tear_down}}}, 72 | }, 73 | }; 74 | 75 | int 76 | main(int argc, const char **argv) 77 | { 78 | ++step; 79 | ASSERT(step == 1); 80 | 81 | ASSERT(rx_main(1, my_test_cases, argc, argv) == RX_SUCCESS); 82 | 83 | ++step; 84 | ASSERT(step == 5); 85 | 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /tests/fixture-data-only.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define ASSERT(x) \ 6 | (void)( \ 7 | (x) \ 8 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 9 | || (abort(), 0)) 10 | 11 | static int step = 0; 12 | 13 | struct my_data { 14 | int value; 15 | }; 16 | 17 | RX_FIXTURE(my_fixture, struct my_data); 18 | 19 | RX_TEST_CASE(my_test_suite, my_test_case, .fixture = my_fixture) 20 | { 21 | struct my_data *data; 22 | 23 | data = (struct my_data *)RX_DATA; 24 | 25 | ++step; 26 | ASSERT(step == 2); 27 | 28 | data->value = 123; 29 | } 30 | 31 | int 32 | main(int argc, const char **argv) 33 | { 34 | ++step; 35 | ASSERT(step == 1); 36 | 37 | ASSERT(rx_main(0, NULL, argc, argv) == RX_SUCCESS); 38 | 39 | ++step; 40 | ASSERT(step == 3); 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /tests/fixture-void.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define ASSERT(x) \ 6 | (void)( \ 7 | (x) \ 8 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 9 | || (abort(), 0)) 10 | 11 | static int step = 0; 12 | 13 | RX_SET_UP(my_set_up) 14 | { 15 | ++step; 16 | ASSERT(step == 2); 17 | 18 | return RX_SUCCESS; 19 | } 20 | 21 | RX_TEAR_DOWN(my_tear_down) 22 | { 23 | ++step; 24 | ASSERT(step == 4); 25 | } 26 | 27 | RX_VOID_FIXTURE(my_fixture, .set_up = my_set_up, .tear_down = my_tear_down); 28 | 29 | RX_TEST_CASE(my_test_suite, my_test_case, .fixture = my_fixture) 30 | { 31 | ++step; 32 | ASSERT(step == 3); 33 | 34 | RX_INT_REQUIRE_EQUAL(42, 42); 35 | } 36 | 37 | int 38 | main(int argc, const char **argv) 39 | { 40 | ++step; 41 | ASSERT(step == 1); 42 | 43 | ASSERT(rx_main(0, NULL, argc, argv) == RX_SUCCESS); 44 | 45 | ++step; 46 | ASSERT(step == 5); 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /tests/fixture.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define ASSERT(x) \ 6 | (void)( \ 7 | (x) \ 8 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 9 | || (abort(), 0)) 10 | 11 | static int step = 0; 12 | 13 | struct my_data { 14 | int value; 15 | }; 16 | 17 | RX_SET_UP(my_set_up) 18 | { 19 | struct my_data *data; 20 | 21 | data = (struct my_data *)RX_DATA; 22 | 23 | ++step; 24 | ASSERT(step == 2); 25 | 26 | data->value = 123; 27 | return RX_SUCCESS; 28 | } 29 | 30 | RX_TEAR_DOWN(my_tear_down) 31 | { 32 | struct my_data *data; 33 | 34 | data = (struct my_data *)RX_DATA; 35 | 36 | ++step; 37 | ASSERT(step == 4); 38 | 39 | ASSERT(data->value == 123); 40 | } 41 | 42 | RX_FIXTURE(my_fixture, 43 | struct my_data, 44 | .set_up = my_set_up, 45 | .tear_down = my_tear_down); 46 | 47 | RX_TEST_CASE(my_test_suite, my_test_case, .fixture = my_fixture) 48 | { 49 | struct my_data *data; 50 | 51 | data = (struct my_data *)RX_DATA; 52 | 53 | ++step; 54 | ASSERT(step == 3); 55 | 56 | ASSERT(data->value == 123); 57 | 58 | RX_INT_REQUIRE_EQUAL(42, 42); 59 | } 60 | 61 | int 62 | main(int argc, const char **argv) 63 | { 64 | ++step; 65 | ASSERT(step == 1); 66 | 67 | ASSERT(rx_main(0, NULL, argc, argv) == RX_SUCCESS); 68 | 69 | ++step; 70 | ASSERT(step == 5); 71 | 72 | return 0; 73 | } 74 | -------------------------------------------------------------------------------- /tests/minimal.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define ASSERT(x) \ 6 | (void)( \ 7 | (x) \ 8 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 9 | || (abort(), 0)) 10 | 11 | static int step = 0; 12 | 13 | RX_TEST_CASE(my_test_suite, my_test_case) 14 | { 15 | ++step; 16 | ASSERT(step == 2); 17 | 18 | RX_INT_REQUIRE_EQUAL(42, 42); 19 | } 20 | 21 | int 22 | main(int argc, const char **argv) 23 | { 24 | ++step; 25 | ASSERT(step == 1); 26 | 27 | ASSERT(rx_main(0, NULL, argc, argv) == RX_SUCCESS); 28 | 29 | ++step; 30 | ASSERT(step == 3); 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /tests/no-discovery.c: -------------------------------------------------------------------------------- 1 | #define RX_DISABLE_TEST_DISCOVERY 2 | 3 | #include 4 | 5 | #include 6 | 7 | #define ASSERT(x) \ 8 | (void)( \ 9 | (x) \ 10 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 11 | || (abort(), 0)) 12 | 13 | static int step = 0; 14 | 15 | RX_TEST_CASE(my_test_suite, my_test_case_1) 16 | { 17 | ++step; 18 | } 19 | 20 | RX_TEST_CASE(my_test_suite, my_test_case_2) 21 | { 22 | ++step; 23 | ASSERT(step == 2); 24 | 25 | RX_INT_REQUIRE_EQUAL(42, 42); 26 | } 27 | 28 | static const struct rx_test_case my_test_cases[] = { 29 | { 30 | "my_test_suite", 31 | "my_test_case_2", 32 | my_test_suite_my_test_case_2, 33 | {0, {0, {NULL, NULL}}}, 34 | }, 35 | }; 36 | 37 | int 38 | main(int argc, const char **argv) 39 | { 40 | ++step; 41 | ASSERT(step == 1); 42 | 43 | ASSERT(rx_main(1, my_test_cases, argc, argv) == RX_SUCCESS); 44 | 45 | ++step; 46 | ASSERT(step == 3); 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /tests/semi-explicit.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define ASSERT(x) \ 6 | (void)( \ 7 | (x) \ 8 | || (printf(__FILE__ ":%d: assertion `" #x "` failed\n", __LINE__), 0) \ 9 | || (abort(), 0)) 10 | 11 | static int step = 0; 12 | 13 | struct my_data { 14 | int value; 15 | }; 16 | 17 | RX_SET_UP(my_set_up) 18 | { 19 | struct my_data *data; 20 | 21 | data = (struct my_data *)RX_DATA; 22 | 23 | ++step; 24 | ASSERT(step == 2); 25 | 26 | data->value = 123; 27 | return RX_SUCCESS; 28 | } 29 | 30 | RX_TEAR_DOWN(my_tear_down) 31 | { 32 | struct my_data *data; 33 | 34 | data = (struct my_data *)RX_DATA; 35 | 36 | ++step; 37 | ASSERT(step == 4); 38 | 39 | ASSERT(data->value == 123); 40 | } 41 | 42 | RX_FIXTURE(my_fixture, 43 | struct my_data, 44 | .set_up = my_set_up, 45 | .tear_down = my_tear_down); 46 | 47 | RX_TEST_CASE(my_test_suite, my_test_case, .fixture = my_fixture) 48 | { 49 | struct my_data *data; 50 | 51 | data = (struct my_data *)RX_DATA; 52 | 53 | ++step; 54 | ASSERT(step == 3); 55 | 56 | ASSERT(data->value == 123); 57 | 58 | RX_INT_REQUIRE_EQUAL(42, 42); 59 | } 60 | 61 | static const struct rx_test_case my_test_cases[] = { 62 | { 63 | "my_test_suite", 64 | "my_test_case", 65 | my_test_suite_my_test_case, 66 | {0, {sizeof(struct my_data), {my_set_up, my_tear_down}}}, 67 | }, 68 | }; 69 | 70 | int 71 | main(int argc, const char **argv) 72 | { 73 | ++step; 74 | ASSERT(step == 1); 75 | 76 | ASSERT(rx_main(1, my_test_cases, argc, argv) == RX_SUCCESS); 77 | 78 | ++step; 79 | ASSERT(step == 5); 80 | 81 | return 0; 82 | } 83 | -------------------------------------------------------------------------------- /tools/generate-assertion-macros.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Generate the C code related to the assertion macros.""" 5 | 6 | import os 7 | import re 8 | from typing import ( 9 | Any, 10 | Mapping, 11 | Optional, 12 | Sequence, 13 | ) 14 | 15 | _HERE = os.path.dirname(__file__) 16 | _SRC_FILE_PATH = os.path.abspath( 17 | os.path.join(_HERE, os.pardir, "include", "rexo.h") 18 | ) 19 | 20 | _REPL_EXPR = re.compile( 21 | ( 22 | r"^/\* Assertion Macro Helpers[\S\s]+(?=^#endif /\* REXO_REXO_H \*/$)" 23 | ), 24 | re.MULTILINE 25 | ) 26 | 27 | _MAX_ARG_COUNT = 8 28 | 29 | _SEVERITIES = ( 30 | { 31 | "name": "REQUIRE", 32 | "value": "RX_FATAL", 33 | }, 34 | { 35 | "name": "CHECK", 36 | "value": "RX_NONFATAL", 37 | }, 38 | ) 39 | 40 | _MACRO_DESCS = ( 41 | { 42 | "public_params": ( 43 | "CONDITION", 44 | ), 45 | "helper_params": ( 46 | "CONDITION", 47 | "SEVERITY", 48 | ), 49 | "fn": "rxp_assess_value", 50 | "fn_args": ( 51 | "RX_PARAM_CONTEXT", 52 | "!!(CONDITION)", 53 | "RXP_TRUE", 54 | "#CONDITION", 55 | "__FILE__", 56 | "__LINE__", 57 | "SEVERITY", 58 | ), 59 | }, 60 | { 61 | "type": "BOOL", 62 | "public_params": ( 63 | "CONDITION", 64 | ), 65 | "helper_params": ( 66 | "CONDITION", 67 | "EXPECTED", 68 | "OP", 69 | "SEVERITY", 70 | ), 71 | "fn": "rxp_bool_assess_value", 72 | "fn_args": ( 73 | "RX_PARAM_CONTEXT", 74 | "!!(CONDITION)", 75 | "EXPECTED", 76 | "#CONDITION", 77 | "__FILE__", 78 | "__LINE__", 79 | "SEVERITY", 80 | ), 81 | "ops": ( 82 | { 83 | "name": "TRUE", 84 | "values": { 85 | "EXPECTED": "RXP_TRUE", 86 | } 87 | }, 88 | { 89 | "name": "FALSE", 90 | "values": { 91 | "EXPECTED": "RXP_FALSE", 92 | } 93 | }, 94 | ), 95 | }, 96 | { 97 | "type": "INT", 98 | "public_params": ( 99 | "X1", 100 | "X2", 101 | ), 102 | "helper_variant": "COMPARISON", 103 | "helper_params": ( 104 | "X1", 105 | "X2", 106 | "OP", 107 | "SEVERITY", 108 | ), 109 | "fn": "rxp_int_assess_comparison", 110 | "fn_args": ( 111 | "RX_PARAM_CONTEXT", 112 | "(X1)", 113 | "(X2)", 114 | "OP", 115 | "#X1", 116 | "#X2", 117 | "__FILE__", 118 | "__LINE__", 119 | "SEVERITY", 120 | ), 121 | "ops": ( 122 | { 123 | "name": "EQUAL", 124 | "values": { 125 | "OP": "RXP_OP_EQUAL", 126 | } 127 | }, 128 | { 129 | "name": "NOT_EQUAL", 130 | "values": { 131 | "OP": "RXP_OP_NOT_EQUAL", 132 | } 133 | }, 134 | { 135 | "name": "GREATER", 136 | "values": { 137 | "OP": "RXP_OP_GREATER", 138 | } 139 | }, 140 | { 141 | "name": "LESSER", 142 | "values": { 143 | "OP": "RXP_OP_LESSER", 144 | } 145 | }, 146 | { 147 | "name": "GREATER_OR_EQUAL", 148 | "values": { 149 | "OP": "RXP_OP_GREATER_OR_EQUAL", 150 | } 151 | }, 152 | { 153 | "name": "LESSER_OR_EQUAL", 154 | "values": { 155 | "OP": "RXP_OP_LESSER_OR_EQUAL", 156 | } 157 | }, 158 | ), 159 | }, 160 | { 161 | "type": "UINT", 162 | "public_params": ( 163 | "X1", 164 | "X2", 165 | ), 166 | "helper_variant": "COMPARISON", 167 | "helper_params": ( 168 | "X1", 169 | "X2", 170 | "OP", 171 | "SEVERITY", 172 | ), 173 | "fn": "rxp_uint_assess_comparison", 174 | "fn_args": ( 175 | "RX_PARAM_CONTEXT", 176 | "(X1)", 177 | "(X2)", 178 | "OP", 179 | "#X1", 180 | "#X2", 181 | "__FILE__", 182 | "__LINE__", 183 | "SEVERITY", 184 | ), 185 | "ops": ( 186 | { 187 | "name": "EQUAL", 188 | "values": { 189 | "OP": "RXP_OP_EQUAL", 190 | } 191 | }, 192 | { 193 | "name": "NOT_EQUAL", 194 | "values": { 195 | "OP": "RXP_OP_NOT_EQUAL", 196 | } 197 | }, 198 | { 199 | "name": "GREATER", 200 | "values": { 201 | "OP": "RXP_OP_GREATER", 202 | } 203 | }, 204 | { 205 | "name": "LESSER", 206 | "values": { 207 | "OP": "RXP_OP_LESSER", 208 | } 209 | }, 210 | { 211 | "name": "GREATER_OR_EQUAL", 212 | "values": { 213 | "OP": "RXP_OP_GREATER_OR_EQUAL", 214 | } 215 | }, 216 | { 217 | "name": "LESSER_OR_EQUAL", 218 | "values": { 219 | "OP": "RXP_OP_LESSER_OR_EQUAL", 220 | } 221 | }, 222 | ), 223 | }, 224 | { 225 | "type": "REAL", 226 | "public_params": ( 227 | "X1", 228 | "X2", 229 | ), 230 | "helper_variant": "COMPARISON", 231 | "helper_params": ( 232 | "X1", 233 | "X2", 234 | "OP", 235 | "SEVERITY", 236 | ), 237 | "fn": "rxp_real_assess_comparison", 238 | "fn_args": ( 239 | "RX_PARAM_CONTEXT", 240 | "(X1)", 241 | "(X2)", 242 | "OP", 243 | "#X1", 244 | "#X2", 245 | "__FILE__", 246 | "__LINE__", 247 | "SEVERITY", 248 | ), 249 | "ops": ( 250 | { 251 | "name": "EQUAL", 252 | "values": { 253 | "OP": "RXP_OP_EQUAL", 254 | } 255 | }, 256 | { 257 | "name": "NOT_EQUAL", 258 | "values": { 259 | "OP": "RXP_OP_NOT_EQUAL", 260 | } 261 | }, 262 | { 263 | "name": "GREATER", 264 | "values": { 265 | "OP": "RXP_OP_GREATER", 266 | } 267 | }, 268 | { 269 | "name": "LESSER", 270 | "values": { 271 | "OP": "RXP_OP_LESSER", 272 | } 273 | }, 274 | { 275 | "name": "GREATER_OR_EQUAL", 276 | "values": { 277 | "OP": "RXP_OP_GREATER_OR_EQUAL", 278 | } 279 | }, 280 | { 281 | "name": "LESSER_OR_EQUAL", 282 | "values": { 283 | "OP": "RXP_OP_LESSER_OR_EQUAL", 284 | } 285 | }, 286 | ), 287 | }, 288 | { 289 | "type": "REAL", 290 | "public_params": ( 291 | "X1", 292 | "X2", 293 | "TOL", 294 | ), 295 | "helper_variant": "FUZZY_COMPARISON", 296 | "helper_params": ( 297 | "X1", 298 | "X2", 299 | "TOL", 300 | "OP", 301 | "SEVERITY", 302 | ), 303 | "fn": "rxp_real_assess_fuzzy_comparison", 304 | "fn_args": ( 305 | "RX_PARAM_CONTEXT", 306 | "(X1)", 307 | "(X2)", 308 | "(TOL)", 309 | "OP", 310 | "#X1", 311 | "#X2", 312 | "__FILE__", 313 | "__LINE__", 314 | "SEVERITY", 315 | ), 316 | "ops": ( 317 | { 318 | "name": "FUZZY_EQUAL", 319 | "values": { 320 | "OP": "RXP_OP_EQUAL", 321 | } 322 | }, 323 | { 324 | "name": "FUZZY_NOT_EQUAL", 325 | "values": { 326 | "OP": "RXP_OP_NOT_EQUAL", 327 | } 328 | }, 329 | ), 330 | }, 331 | { 332 | "type": "STR", 333 | "public_params": ( 334 | "S1", 335 | "S2", 336 | ), 337 | "helper_variant": "COMPARISON", 338 | "helper_params": ( 339 | "S1", 340 | "S2", 341 | "STR_CASE", 342 | "OP", 343 | "SEVERITY", 344 | ), 345 | "fn": "rxp_str_assess_comparison", 346 | "fn_args": ( 347 | "RX_PARAM_CONTEXT", 348 | "(S1)", 349 | "(S2)", 350 | "STR_CASE", 351 | "OP", 352 | "#S1", 353 | "#S2", 354 | "__FILE__", 355 | "__LINE__", 356 | "SEVERITY", 357 | ), 358 | "ops": ( 359 | { 360 | "name": "EQUAL", 361 | "values": { 362 | "STR_CASE": "RXP_STR_CASE_OBEY", 363 | "OP": "RXP_OP_EQUAL", 364 | } 365 | }, 366 | { 367 | "name": "NOT_EQUAL", 368 | "values": { 369 | "STR_CASE": "RXP_STR_CASE_OBEY", 370 | "OP": "RXP_OP_NOT_EQUAL", 371 | } 372 | }, 373 | { 374 | "name": "EQUAL_NO_CASE", 375 | "values": { 376 | "STR_CASE": "RXP_STR_CASE_IGNORE", 377 | "OP": "RXP_OP_EQUAL", 378 | } 379 | }, 380 | { 381 | "name": "NOT_EQUAL_NO_CASE", 382 | "values": { 383 | "STR_CASE": "RXP_STR_CASE_IGNORE", 384 | "OP": "RXP_OP_NOT_EQUAL", 385 | } 386 | }, 387 | ), 388 | }, 389 | { 390 | "type": "PTR", 391 | "public_params": ( 392 | "X1", 393 | "X2", 394 | ), 395 | "helper_variant": "COMPARISON", 396 | "helper_params": ( 397 | "X1", 398 | "X2", 399 | "OP", 400 | "SEVERITY", 401 | ), 402 | "fn": "rxp_ptr_assess_comparison", 403 | "fn_args": ( 404 | "RX_PARAM_CONTEXT", 405 | "(X1)", 406 | "(X2)", 407 | "OP", 408 | "#X1", 409 | "#X2", 410 | "__FILE__", 411 | "__LINE__", 412 | "SEVERITY", 413 | ), 414 | "ops": ( 415 | { 416 | "name": "EQUAL", 417 | "values": { 418 | "OP": "RXP_OP_EQUAL", 419 | } 420 | }, 421 | { 422 | "name": "NOT_EQUAL", 423 | "values": { 424 | "OP": "RXP_OP_NOT_EQUAL", 425 | } 426 | }, 427 | ), 428 | }, 429 | { 430 | "type": "PTR", 431 | "public_params": ( 432 | "X", 433 | "ALIGNMENT", 434 | ), 435 | "helper_variant": "ALIGNMENT", 436 | "helper_params": ( 437 | "X", 438 | "ALIGNMENT", 439 | "SEVERITY", 440 | ), 441 | "fn": "rxp_ptr_assess_alignment", 442 | "fn_args": ( 443 | "RX_PARAM_CONTEXT", 444 | "(X)", 445 | "(ALIGNMENT)", 446 | "#X", 447 | "__FILE__", 448 | "__LINE__", 449 | "SEVERITY", 450 | ), 451 | "ops": ( 452 | { 453 | "name": "ALIGNED", 454 | }, 455 | ), 456 | }, 457 | ) 458 | 459 | 460 | def _format_macro( 461 | lines: Sequence[str], 462 | indent: int, 463 | ): 464 | lines = list(lines) 465 | 466 | if indent: 467 | for i, line in enumerate(lines): 468 | lines[i] = "{padding}{line}".format( 469 | padding=" " * indent, 470 | line=line, 471 | ) 472 | 473 | for i, line in enumerate(lines[:-1]): 474 | lines[i] = "{:79s}\\".format(line) 475 | 476 | return "{}\n".format("\n".join(lines)) 477 | 478 | 479 | def _format_helper_macro_name( 480 | desc: Mapping[str, Any], 481 | ): 482 | type = ( 483 | "" if "type" not in desc 484 | else "_{}".format(desc["type"]) 485 | ) 486 | variant = ( 487 | "" if "helper_variant" not in desc 488 | else "_{}".format(desc["helper_variant"]) 489 | ) 490 | 491 | return "RXP{type}_DEFINE{variant}_TEST".format( 492 | type=type, 493 | variant=variant, 494 | ) 495 | 496 | 497 | def _define_section_header( 498 | title: str, 499 | ): 500 | lines = [] 501 | lines.append("{:67s}O-(''Q)".format("/* {title}".format(title=title))) 502 | lines.append(" {} */".format("-" * 74)) 503 | return "{}\n".format("\n".join(lines)) 504 | 505 | 506 | 507 | def _define_helper_macro( 508 | desc: Mapping[str, Any], 509 | variadic: bool = False, 510 | fmt_specifiers: Optional[int] = None, 511 | indent: int = 0, 512 | ): 513 | if variadic or fmt_specifiers is None: 514 | fmt_specifiers_params = ("...",) 515 | fmt_specifiers_args = ("__VA_ARGS__",) 516 | else: 517 | fmt_specifiers_params = ("MSG",) + tuple( 518 | "_{}".format(i) for i in range(fmt_specifiers) 519 | ) 520 | fmt_specifiers_args = fmt_specifiers_params 521 | 522 | 523 | # Signature 524 | 525 | name = _format_helper_macro_name(desc) 526 | 527 | lines = [] 528 | lines.append("#define {}(".format(name)) 529 | lines.append(" {},".format(", ".join(desc["helper_params"]))) 530 | lines.append(" {}".format(", ".join(fmt_specifiers_params))) 531 | lines.append(")") 532 | 533 | 534 | # Contents 535 | 536 | lines.append(" {}(".format(desc["fn"])) 537 | 538 | for i, arg in enumerate(desc["fn_args"]): 539 | lines.append(" {},".format(arg)) 540 | 541 | lines.append(" {}".format(", ".join(fmt_specifiers_args))) 542 | lines.append(" )") 543 | 544 | 545 | return _format_macro(lines, indent) 546 | 547 | 548 | def _define_assertion_macro( 549 | desc: Mapping[str, Any], 550 | severity: str, 551 | op: Optional[str] = None, 552 | variadic: bool = False, 553 | fmt_specifiers: Optional[int] = None, 554 | indent: int = 0, 555 | ): 556 | values = {} if op is None else op.get("values", {}).copy() 557 | values.update({ 558 | "SEVERITY": severity["value"], 559 | }) 560 | 561 | if fmt_specifiers is None: 562 | fmt_specifiers_params = None 563 | fmt_specifiers_args = ("NULL",) * (_MAX_ARG_COUNT + 1) 564 | elif variadic: 565 | fmt_specifiers_params = ("...",) 566 | fmt_specifiers_args = ("__VA_ARGS__",) 567 | else: 568 | fmt_specifiers_params = ("MSG",) + tuple( 569 | "_{}".format(i) for i in range(fmt_specifiers) 570 | ) 571 | fmt_specifiers_args = ( 572 | fmt_specifiers_params 573 | + ("NULL",) * (_MAX_ARG_COUNT - fmt_specifiers) 574 | ) 575 | 576 | 577 | # Signature 578 | 579 | name = ( 580 | "RX{type}{severity}{op}{postfix}".format( 581 | type="" if "type" not in desc else "_{}".format(desc["type"]), 582 | severity="_{}".format(severity["name"]), 583 | op="" if op is None else "_{}".format(op["name"]), 584 | postfix=( 585 | "" if fmt_specifiers is None 586 | else ( 587 | "_MSG" if fmt_specifiers == 0 588 | else "_MSG_{}".format(fmt_specifiers) 589 | ) 590 | ), 591 | ) 592 | ) 593 | 594 | lines = [] 595 | lines.append("#define {}(".format(name)) 596 | lines.append(" {}".format(", ".join(desc["public_params"]))) 597 | 598 | if fmt_specifiers_params is not None: 599 | lines[-1] += "," 600 | lines.append(" {}".format(", ".join(fmt_specifiers_params))) 601 | 602 | lines.append(")") 603 | 604 | 605 | # Contents 606 | 607 | name = _format_helper_macro_name(desc) 608 | args = tuple(values.get(x, x) for x in desc["helper_params"]) 609 | 610 | lines.append(" {}(".format(name)) 611 | lines.append(" {}".format(", ".join(args))) 612 | 613 | if fmt_specifiers_args is not None: 614 | lines[-1] += "," 615 | lines.append(" {}".format(", ".join(fmt_specifiers_args))) 616 | 617 | lines.append(" )") 618 | 619 | return _format_macro(lines, indent) 620 | 621 | 622 | def _define_macros(): 623 | blocks = [] 624 | 625 | blocks.append(_define_section_header("Assertion Macro Helpers")) 626 | blocks.append("\n") 627 | 628 | blocks.append("#if RXP_HAS_VARIADIC_MACROS\n") 629 | 630 | for desc in _MACRO_DESCS: 631 | blocks.append( 632 | _define_helper_macro( 633 | desc, 634 | variadic=True, 635 | fmt_specifiers=None, 636 | indent=4, 637 | ) 638 | ) 639 | blocks.append("\n") 640 | 641 | blocks.pop() 642 | blocks.append("#else /* RXP_HAS_VARIADIC_MACROS */\n") 643 | 644 | for desc in _MACRO_DESCS: 645 | blocks.append( 646 | _define_helper_macro( 647 | desc, 648 | variadic=False, 649 | fmt_specifiers=_MAX_ARG_COUNT, 650 | indent=4, 651 | ) 652 | ) 653 | blocks.append("\n") 654 | 655 | blocks.pop() 656 | blocks.append("#endif /* RXP_HAS_VARIADIC_MACROS */\n") 657 | blocks.append("\n") 658 | 659 | 660 | blocks.append(_define_section_header("Main Assertion Macros")) 661 | blocks.append("\n") 662 | 663 | for desc in _MACRO_DESCS: 664 | for op in desc.get("ops", (None,)): 665 | for severity in _SEVERITIES: 666 | blocks.append( 667 | _define_assertion_macro( 668 | desc, 669 | severity, 670 | op, 671 | ) 672 | ) 673 | blocks.append("\n") 674 | 675 | 676 | blocks.append(_define_section_header("Message Assertion Macros")) 677 | blocks.append("\n") 678 | 679 | blocks.append("#if RXP_HAS_VARIADIC_MACROS\n") 680 | 681 | for desc in _MACRO_DESCS: 682 | for op in desc.get("ops", (None,)): 683 | for severity in _SEVERITIES: 684 | blocks.append( 685 | _define_assertion_macro( 686 | desc, 687 | severity, 688 | op, 689 | variadic=True, 690 | fmt_specifiers=0, 691 | indent=4, 692 | ) 693 | ) 694 | blocks.append("\n") 695 | 696 | blocks.pop() 697 | blocks.append("#else /* RXP_HAS_VARIADIC_MACROS */\n") 698 | 699 | for desc in _MACRO_DESCS: 700 | for op in desc.get("ops", (None,)): 701 | for severity in _SEVERITIES: 702 | blocks.append( 703 | _define_assertion_macro( 704 | desc, 705 | severity, 706 | op, 707 | variadic=False, 708 | fmt_specifiers=0, 709 | indent=4, 710 | ) 711 | ) 712 | blocks.append("\n") 713 | 714 | blocks.pop() 715 | blocks.append("#endif /* RXP_HAS_VARIADIC_MACROS */\n") 716 | blocks.append("\n") 717 | 718 | 719 | blocks.append(_define_section_header("Extended Message Assertion Macros")) 720 | blocks.append("\n") 721 | 722 | for desc in _MACRO_DESCS: 723 | for i in range(1, _MAX_ARG_COUNT + 1): 724 | for op in desc.get("ops", (None,)): 725 | for severity in _SEVERITIES: 726 | blocks.append( 727 | _define_assertion_macro( 728 | desc, 729 | severity, 730 | op, 731 | variadic=False, 732 | fmt_specifiers=i, 733 | ) 734 | ) 735 | blocks.append("\n") 736 | 737 | return "".join(blocks) 738 | 739 | 740 | def run(): 741 | contents = _define_macros() 742 | 743 | with open(_SRC_FILE_PATH, "r") as f: 744 | buf = f.read() 745 | 746 | match = _REPL_EXPR.search(buf) 747 | buf = buf[:match.start()] + contents + buf[match.end():] 748 | 749 | with open(_SRC_FILE_PATH, "w") as f: 750 | f.write(buf) 751 | 752 | 753 | if __name__ == "__main__": 754 | run() 755 | --------------------------------------------------------------------------------