├── .gitignore ├── Doxyfile ├── CMakeLists.txt ├── LICENSE ├── .github └── workflows │ └── parg-ci-workflow.yaml ├── README.md ├── parg.h ├── parg.c └── test ├── test_parg.c └── greatest.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.obj 3 | 4 | *.a 5 | *.lib 6 | 7 | *.so 8 | *.so.* 9 | *.dll 10 | 11 | *.exe 12 | 13 | /build/ 14 | /doc/ 15 | -------------------------------------------------------------------------------- /Doxyfile: -------------------------------------------------------------------------------- 1 | # Doxyfile 1.8.6 2 | PROJECT_NAME = "parg" 3 | OUTPUT_DIRECTORY = doc 4 | JAVADOC_AUTOBRIEF = YES 5 | TAB_SIZE = 8 6 | OPTIMIZE_OUTPUT_FOR_C = YES 7 | EXTRACT_ALL = YES 8 | INPUT = parg.h README.md 9 | GENERATE_LATEX = NO 10 | USE_MDFILE_AS_MAINPAGE = README.md 11 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | project(parg C) 4 | 5 | include(CTest) 6 | 7 | if(MSVC) 8 | add_compile_options(/W3) 9 | elseif(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") 10 | add_compile_options(-Wall -Wextra -pedantic) 11 | endif() 12 | 13 | add_library(parg parg.c parg.h) 14 | target_include_directories(parg PUBLIC $) 15 | 16 | if(BUILD_TESTING) 17 | add_executable(test_parg test/test_parg.c) 18 | target_link_libraries(test_parg PRIVATE parg) 19 | if(MSVC) 20 | target_compile_definitions(test_parg PRIVATE _CRT_SECURE_NO_WARNINGS) 21 | endif() 22 | 23 | add_test(test_parg test_parg) 24 | endif() 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT No Attribution License (MIT-0) 2 | 3 | Copyright 2015-2023 Joergen Ibsen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a 6 | copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | and/or sell copies of the Software, and to permit persons to whom the 10 | Software is furnished to do so. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 15 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 17 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 18 | DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /.github/workflows/parg-ci-workflow.yaml: -------------------------------------------------------------------------------- 1 | name: parg CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | windows: 7 | name: Windows ${{ matrix.config.name }} 8 | runs-on: windows-2022 9 | 10 | strategy: 11 | matrix: 12 | config: 13 | - name: MSVC x64 14 | generator: Visual Studio 17 2022 15 | cmake-flags: -A x64 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - name: Configure 21 | run: cmake -G "${{ matrix.config.generator }}" ${{ matrix.config.cmake-flags }} -B build 22 | 23 | - name: Build 24 | run: cd build && cmake --build . --config Debug 25 | 26 | - name: Test 27 | run: cd build && ctest -V --output-on-failure --interactive-debug-mode 0 -C Debug 28 | 29 | linux: 30 | name: Linux ${{ matrix.config.name }} 31 | runs-on: ubuntu-latest 32 | env: 33 | CC: ${{ matrix.config.cc }} 34 | 35 | strategy: 36 | matrix: 37 | config: 38 | - name: Clang UBSan 39 | cc: clang 40 | cmake-flags: -DCMAKE_C_FLAGS_DEBUG='-g -fsanitize=undefined' 41 | 42 | - name: Clang ASan 43 | cc: clang 44 | cmake-flags: -DCMAKE_C_FLAGS_DEBUG='-O1 -g -fsanitize=address -fno-omit-frame-pointer' 45 | 46 | steps: 47 | - uses: actions/checkout@v4 48 | 49 | - name: Configure 50 | run: cmake ${{ matrix.config.cmake-flags }} -DCMAKE_BUILD_TYPE=Debug -B build 51 | 52 | - name: Build 53 | run: cd build && cmake --build . --verbose 54 | 55 | - name: Test 56 | run: cd build && ctest -V --output-on-failure --interactive-debug-mode 0 57 | 58 | coverage: 59 | name: Linux Coverage 60 | runs-on: ubuntu-latest 61 | 62 | steps: 63 | - uses: actions/checkout@v4 64 | 65 | - name: Configure 66 | run: cmake -DCMAKE_C_FLAGS_DEBUG='-g -O0 --coverage' -DCMAKE_BUILD_TYPE=Debug -B build 67 | 68 | - name: Build 69 | run: cd build && cmake --build . --verbose 70 | 71 | - name: Test 72 | run: cd build && ctest -V --output-on-failure --interactive-debug-mode 0 73 | 74 | - name: Generate coverage 75 | run: cd build && gcov -b CMakeFiles/parg.dir/parg.c.gcno -o CMakeFiles/parg.dir 76 | 77 | - name: Upload coverage to Codecov 78 | uses: codecov/codecov-action@v3 79 | with: 80 | files: ./build/parg.c.gcov 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![parg CI](https://github.com/jibsen/parg/actions/workflows/parg-ci-workflow.yaml/badge.svg)](https://github.com/jibsen/parg/actions) [![codecov](https://codecov.io/gh/jibsen/parg/branch/master/graph/badge.svg)](https://codecov.io/gh/jibsen/parg) 3 | 4 | About 5 | ----- 6 | 7 | Most command-line programs have to parse options, so there are a lot of 8 | different solutions to this problem. Some offer many features, while others 9 | are more basic. 10 | 11 | One of the simpler solutions for C is the [getopt][] function, and its 12 | extension `getopt_long`. They iterate over the options in `argv`, returning 13 | them one at a time on successive calls. 14 | 15 | One nice thing about them is that they are available on most Unix-like 16 | operating systems (and usually accompany GCC elsewhere, like Windows). 17 | Unfortunately, some implementation details vary between platforms. 18 | 19 | A potential question is what license the version you get when you include 20 | them is available under. Some are GPL, others LGPL. There are also ports of 21 | `getopt` that use more liberal licenses. 22 | 23 | `parg` is a parser for `argv` that works similarly to `getopt`, but does not 24 | aim to be a direct replacement. It attempts to make some choices about how to 25 | handle the extensions and idiosyncrasies of other `getopt` implementations, 26 | and document them. 27 | 28 | It consists of a single source and include file, written in portable ANSI C. 29 | It is made available under the [MIT No Attribution License](LICENSE) (MIT-0). 30 | 31 | [getopt]: https://en.wikipedia.org/wiki/Getopt 32 | 33 | 34 | Usage 35 | ----- 36 | 37 | The include file `parg.h` contains documentation in the form of [doxygen][] 38 | comments. A configuration file is included, run `doxygen` to generate 39 | documentation in HTML format. 40 | 41 | You can add the source files `parg.c` and `parg.h` to your own projects. 42 | 43 | For CI, `parg` uses [CMake][] to provide an easy way to build and test across 44 | various platforms and toolsets. To create a build system for the tools on your 45 | platform, and build `parg`, use something along the lines of: 46 | 47 | ~~~sh 48 | mkdir build 49 | cd build 50 | cmake .. 51 | cmake --build . 52 | ~~~ 53 | 54 | [doxygen]: http://www.doxygen.org/ 55 | [CMake]: http://www.cmake.org/ 56 | 57 | 58 | Example 59 | ------- 60 | 61 | Here is an example that parses command-line options using `parg_getopt()`: 62 | 63 | ~~~c 64 | #include 65 | #include 66 | 67 | #include "parg.h" 68 | 69 | int main(int argc, char *argv[]) 70 | { 71 | struct parg_state ps; 72 | int c; 73 | 74 | parg_init(&ps); 75 | 76 | while ((c = parg_getopt(&ps, argc, argv, "hs:v")) != -1) { 77 | switch (c) { 78 | case 1: 79 | printf("nonoption '%s'\n", ps.optarg); 80 | break; 81 | case 'h': 82 | printf("Usage: testparg [-h] [-v] [-s STRING]\n"); 83 | return EXIT_SUCCESS; 84 | break; 85 | case 's': 86 | printf("option -s with argument '%s'\n", ps.optarg); 87 | break; 88 | case 'v': 89 | printf("testparg 1.0.0\n"); 90 | return EXIT_SUCCESS; 91 | break; 92 | case '?': 93 | if (ps.optopt == 's') { 94 | printf("option -s requires an argument\n"); 95 | } 96 | else { 97 | printf("unknown option -%c\n", ps.optopt); 98 | } 99 | return EXIT_FAILURE; 100 | break; 101 | default: 102 | printf("error: unhandled option -%c\n", c); 103 | return EXIT_FAILURE; 104 | break; 105 | } 106 | } 107 | 108 | for (c = ps.optind; c < argc; ++c) { 109 | printf("nonoption '%s'\n", argv[c]); 110 | } 111 | 112 | return EXIT_SUCCESS; 113 | } 114 | ~~~ 115 | 116 | 117 | Comparison to `getopt` 118 | ---------------------- 119 | 120 | ### Use of global variables 121 | 122 | `getopt` uses global variables to store its state between calls. `parg` uses 123 | a struct `parg_state`, which you must pass with each call. 124 | 125 | ### Handling of nonoptions 126 | 127 | POSIX and BSD `getopt` return `-1` on the first nonoption argument. GNU 128 | `getopt` by default reorders `argv` (even though it is passed as const), so 129 | all options come first. 130 | 131 | `parg` does not change `argv`, and returns each nonoption as the option 132 | argument of an option with value `1` (like GNU `getopt`, if `optstring` were 133 | prefixed by '`-`'). 134 | 135 | If you wish to process all options first, and have the nonoptions ordered at 136 | the end of `argv`, you can use `parg_reorder()`: 137 | 138 | ~~~c 139 | optend = parg_reorder(argc, argv, optstring, NULL); 140 | 141 | while ((c = parg_getopt(&ps, optend, argv, optstring)) != -1) { 142 | /* ... */ 143 | } 144 | 145 | /* elements of argv[] from optend to argc are nonoptions */ 146 | ~~~ 147 | 148 | ### Value of `optind` on error 149 | 150 | When there are multiple short options in one argument, `getopt` does not 151 | increment `optind` until the last one is processed. This makes it harder to 152 | tell which argument an unknown option came from (if `a` is an unknown option, 153 | `-a` and `-ab` will return '`?`' with different values in `optind`). 154 | 155 | `parg` always increments the `optind` value in it's state so it points to the 156 | next `argv` element to be processed. So when `parg` returns '`?`' (or '`:`'), 157 | the element that contains the error is `argv[optind - 1]`. 158 | 159 | ### Value of `optopt` on error 160 | 161 | With `getopt_long`, it varies what the values of `optopt` and `longindex` are 162 | when an error is found with option arguments of long options. Sometimes these 163 | values are not documented. 164 | 165 | `parg` sets `optopt` to `val` if `flag` is `NULL`, and `0` otherwise (which 166 | equals the return value on successful match), and `longindex` is set to the 167 | index of the entry in `longopts` that matched. 168 | 169 | ### Return value on option argument error 170 | 171 | When the first character of `optstring` is '`:`', it varies what `getopt` 172 | returns on extraneous option arguments. 173 | 174 | In this case, `parg` returns '`?`' if no option match is found, and '`:`' if 175 | a match is found, but is missing a required argument, or has an extraneous 176 | argument. 177 | 178 | 179 | Alternatives 180 | ------------ 181 | 182 | Some ports of `getopt`: 183 | 184 | - [Free Getopt](http://freegetopt.sourceforge.net/) 185 | - [ya_getopt](http://github.com/kubo/ya_getopt/) 186 | - [getopt_port](http://github.com/kimgr/getopt_port/) 187 | 188 | Other command-line parsing libraries that support C: 189 | 190 | - [Gengetopt](http://www.gnu.org/software/gengetopt/) 191 | - [Argp](http://www.gnu.org/software/libc/manual/html_node/Argp.html) 192 | - [popt](http://en.wikipedia.org/wiki/Popt) 193 | - [argtable](https://www.argtable.org/) 194 | - [optlist](http://michael.dipperstein.com/optlist/) 195 | - [Arg_parser](http://www.nongnu.org/arg-parser/arg_parser.html) 196 | - [Gopt](http://www.purposeful.co.uk/software/gopt/) 197 | - [docopt](http://docopt.org/) 198 | - [optparse](https://github.com/skeeto/optparse) 199 | - [getopt](https://github.com/wc-duck/getopt) 200 | - [argparse](https://github.com/cofyc/argparse) 201 | 202 | A few C++ command-line parsing libraries: 203 | 204 | - [TCLAP](http://tclap.sourceforge.net/) 205 | - [program_options](http://www.boost.org/doc/libs/1_58_0/doc/html/program_options.html) 206 | - [CommandLine](http://llvm.org/docs/CommandLine.html) 207 | - [CLI11](https://github.com/CLIUtils/CLI11) 208 | - [argparse](https://github.com/p-ranav/argparse) 209 | - [clipp](https://github.com/muellan/clipp) 210 | - [argh](https://github.com/adishavit/argh) 211 | -------------------------------------------------------------------------------- /parg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * parg - parse argv 3 | * 4 | * Copyright 2015-2023 Joergen Ibsen 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | * 21 | * SPDX-License-Identifier: MIT-0 22 | */ 23 | 24 | #ifndef PARG_H_INCLUDED 25 | #define PARG_H_INCLUDED 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | #define PARG_VER_MAJOR 1 /**< Major version number */ 32 | #define PARG_VER_MINOR 0 /**< Minor version number */ 33 | #define PARG_VER_PATCH 3 /**< Patch version number */ 34 | #define PARG_VER_STRING "1.0.3" /**< Version number as a string */ 35 | 36 | /** 37 | * Structure containing state between calls to parser. 38 | * 39 | * @see parg_init 40 | */ 41 | struct parg_state { 42 | const char *optarg; /**< Pointer to option argument, if any */ 43 | int optind; /**< Next index in argv to process */ 44 | int optopt; /**< Option value resulting in error, if any */ 45 | const char *nextchar; /**< Next character to process */ 46 | }; 47 | 48 | /** 49 | * Structure for supplying long options to `parg_getopt_long()`. 50 | * 51 | * @see parg_getopt_long 52 | */ 53 | struct parg_option { 54 | const char *name; /**< Name of option */ 55 | int has_arg; /**< Option argument status */ 56 | int *flag; /**< Pointer to flag variable */ 57 | int val; /**< Value of option */ 58 | }; 59 | 60 | /** 61 | * Values for `has_arg` flag in `parg_option`. 62 | * 63 | * @see parg_option 64 | */ 65 | typedef enum { 66 | PARG_NOARG, /**< No argument */ 67 | PARG_REQARG, /**< Required argument */ 68 | PARG_OPTARG /**< Optional argument */ 69 | } parg_arg_num; 70 | 71 | /** 72 | * Initialize `ps`. 73 | * 74 | * Must be called before using state with a parser. 75 | * 76 | * @see parg_state 77 | * 78 | * @param ps pointer to state 79 | */ 80 | void 81 | parg_init(struct parg_state *ps); 82 | 83 | /** 84 | * Parse next short option in `argv`. 85 | * 86 | * Elements in `argv` that contain short options start with a single dash 87 | * followed by one or more option characters, and optionally an option 88 | * argument for the last option character. Examples are '`-d`', '`-ofile`', 89 | * and '`-dofile`'. 90 | * 91 | * Consecutive calls to this function match the command-line arguments in 92 | * `argv` against the short option characters in `optstring`. 93 | * 94 | * If an option character in `optstring` is followed by a colon, '`:`', the 95 | * option requires an argument. If it is followed by two colons, the option 96 | * may take an optional argument. 97 | * 98 | * If a match is found, `optarg` points to the option argument, if any, and 99 | * the value of the option character is returned. 100 | * 101 | * If a match is found, but is missing a required option argument, `optopt` 102 | * is set to the option character. If the first character in `optstring` is 103 | * '`:`', then '`:`' is returned, otherwise '`?`' is returned. 104 | * 105 | * If no option character in `optstring` matches a short option, `optopt` 106 | * is set to the option character, and '`?`' is returned. 107 | * 108 | * If an element of argv does not contain options (a nonoption element), 109 | * `optarg` points to the element, and `1` is returned. 110 | * 111 | * An element consisting of a single dash, '`-`', is returned as a nonoption. 112 | * 113 | * Parsing stops and `-1` is returned, when the end of `argv` is reached, or 114 | * if an element contains '`--`'. 115 | * 116 | * Works similarly to `getopt`, if `optstring` were prefixed by '`-`'. 117 | * 118 | * @param ps pointer to state 119 | * @param argc number of elements in `argv` 120 | * @param argv array of pointers to command-line arguments 121 | * @param optstring string containing option characters 122 | * @return option value on match, `1` on nonoption element, `-1` on end of 123 | * arguments, '`?`' on unmatched option, '`?`' or '`:`' on option argument 124 | * error 125 | */ 126 | int 127 | parg_getopt(struct parg_state *ps, int argc, char *const argv[], 128 | const char *optstring); 129 | 130 | /** 131 | * Parse next long or short option in `argv`. 132 | * 133 | * Elements in `argv` that contain a long option start with two dashes 134 | * followed by a string, and optionally an equal sign and an option argument. 135 | * Examples are '`--help`' and '`--size=5`'. 136 | * 137 | * If no exact match is found, an unambiguous prefix of a long option will 138 | * match. For example, if '`foo`' and '`foobar`' are valid long options, then 139 | * '`--fo`' is ambiguous and will not match, '`--foo`' matches exactly, and 140 | * '`--foob`' is an unambiguous prefix and will match. 141 | * 142 | * If a long option match is found, and `flag` is `NULL`, `val` is returned. 143 | * 144 | * If a long option match is found, and `flag` is not `NULL`, `val` is stored 145 | * in the variable `flag` points to, and `0` is returned. 146 | * 147 | * If a long option match is found, but is missing a required option argument, 148 | * or has an option argument even though it takes none, `optopt` is set to 149 | * `val` if `flag` is `NULL`, and `0` otherwise. If the first character in 150 | * `optstring` is '`:`', then '`:`' is returned, otherwise '`?`' is returned. 151 | * 152 | * If `longindex` is not `NULL`, the index of the entry in `longopts` that 153 | * matched is stored there. 154 | * 155 | * If no long option in `longopts` matches a long option, '`?`' is returned. 156 | * 157 | * Handling of nonoptions and short options is like `parg_getopt()`. 158 | * 159 | * If no short options are required, an empty string, `""`, should be passed 160 | * as `optstring`. 161 | * 162 | * Works similarly to `getopt_long`, if `optstring` were prefixed by '`-`'. 163 | * 164 | * @see parg_getopt 165 | * 166 | * @param ps pointer to state 167 | * @param argc number of elements in `argv` 168 | * @param argv array of pointers to command-line arguments 169 | * @param optstring string containing option characters 170 | * @param longopts array of `parg_option` structures 171 | * @param longindex pointer to variable to store index of matching option in 172 | * @return option value on match, `0` for flag option, `1` on nonoption 173 | * element, `-1` on end of arguments, '`?`' on unmatched or ambiguous option, 174 | * '`?`' or '`:`' on option argument error 175 | */ 176 | int 177 | parg_getopt_long(struct parg_state *ps, int argc, char *const argv[], 178 | const char *optstring, 179 | const struct parg_option *longopts, int *longindex); 180 | 181 | /** 182 | * Reorder elements of `argv` so options appear first. 183 | * 184 | * If there are no long options, `longopts` may be `NULL`. 185 | * 186 | * The return value can be used as `argc` parameter for `parg_getopt()` and 187 | * `parg_getopt_long()`. 188 | * 189 | * @param argc number of elements in `argv` 190 | * @param argv array of pointers to command-line arguments 191 | * @param optstring string containing option characters 192 | * @param longopts array of `parg_option` structures 193 | * @return index of first nonoption in `argv` on success, `-1` on error 194 | */ 195 | int 196 | parg_reorder(int argc, char *argv[], 197 | const char *optstring, 198 | const struct parg_option *longopts); 199 | 200 | #ifdef __cplusplus 201 | } /* extern "C" */ 202 | #endif 203 | 204 | #endif /* PARG_H_INCLUDED */ 205 | -------------------------------------------------------------------------------- /parg.c: -------------------------------------------------------------------------------- 1 | /* 2 | * parg - parse argv 3 | * 4 | * Copyright 2015-2023 Joergen Ibsen 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | * 21 | * SPDX-License-Identifier: MIT-0 22 | */ 23 | 24 | #include "parg.h" 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | /* 31 | * Check if state is at end of argv. 32 | */ 33 | static int 34 | is_argv_end(const struct parg_state *ps, int argc, char *const argv[]) 35 | { 36 | return ps->optind >= argc || argv[ps->optind] == NULL; 37 | } 38 | 39 | /* 40 | * Match nextchar against optstring. 41 | */ 42 | static int 43 | match_short(struct parg_state *ps, int argc, char *const argv[], 44 | const char *optstring) 45 | { 46 | const char *p = strchr(optstring, *ps->nextchar); 47 | 48 | if (p == NULL) { 49 | ps->optopt = *ps->nextchar++; 50 | return '?'; 51 | } 52 | 53 | /* If no option argument, return option */ 54 | if (p[1] != ':') { 55 | return *ps->nextchar++; 56 | } 57 | 58 | /* If more characters, return as option argument */ 59 | if (ps->nextchar[1] != '\0') { 60 | ps->optarg = &ps->nextchar[1]; 61 | ps->nextchar = NULL; 62 | return *p; 63 | } 64 | 65 | /* If option argument is optional, return option */ 66 | if (p[2] == ':') { 67 | return *ps->nextchar++; 68 | } 69 | 70 | /* Option argument required, so return next argv element */ 71 | if (is_argv_end(ps, argc, argv)) { 72 | ps->optopt = *ps->nextchar++; 73 | return optstring[0] == ':' ? ':' : '?'; 74 | } 75 | 76 | ps->optarg = argv[ps->optind++]; 77 | ps->nextchar = NULL; 78 | return *p; 79 | } 80 | 81 | /* 82 | * Match string at nextchar against longopts. 83 | */ 84 | static int 85 | match_long(struct parg_state *ps, int argc, char *const argv[], 86 | const char *optstring, 87 | const struct parg_option *longopts, int *longindex) 88 | { 89 | size_t len; 90 | int num_match = 0; 91 | int match = -1; 92 | int i; 93 | 94 | len = strcspn(ps->nextchar, "="); 95 | 96 | for (i = 0; longopts[i].name; ++i) { 97 | if (strncmp(ps->nextchar, longopts[i].name, len) == 0) { 98 | match = i; 99 | num_match++; 100 | /* Take if exact match */ 101 | if (longopts[i].name[len] == '\0') { 102 | num_match = 1; 103 | break; 104 | } 105 | } 106 | } 107 | 108 | /* Return '?' on no or ambiguous match */ 109 | if (num_match != 1) { 110 | ps->optopt = 0; 111 | ps->nextchar = NULL; 112 | return '?'; 113 | } 114 | 115 | assert(match != -1); 116 | 117 | if (longindex) { 118 | *longindex = match; 119 | } 120 | 121 | if (ps->nextchar[len] == '=') { 122 | /* Option argument present, check if extraneous */ 123 | if (longopts[match].has_arg == PARG_NOARG) { 124 | ps->optopt = longopts[match].flag ? 0 : longopts[match].val; 125 | ps->nextchar = NULL; 126 | return optstring[0] == ':' ? ':' : '?'; 127 | } 128 | else { 129 | ps->optarg = &ps->nextchar[len + 1]; 130 | } 131 | } 132 | else if (longopts[match].has_arg == PARG_REQARG) { 133 | /* Option argument required, so return next argv element */ 134 | if (is_argv_end(ps, argc, argv)) { 135 | ps->optopt = longopts[match].flag ? 0 : longopts[match].val; 136 | ps->nextchar = NULL; 137 | return optstring[0] == ':' ? ':' : '?'; 138 | } 139 | 140 | ps->optarg = argv[ps->optind++]; 141 | } 142 | 143 | ps->nextchar = NULL; 144 | 145 | if (longopts[match].flag != NULL) { 146 | *longopts[match].flag = longopts[match].val; 147 | return 0; 148 | } 149 | 150 | return longopts[match].val; 151 | } 152 | 153 | void 154 | parg_init(struct parg_state *ps) 155 | { 156 | ps->optarg = NULL; 157 | ps->optind = 1; 158 | ps->optopt = '?'; 159 | ps->nextchar = NULL; 160 | } 161 | 162 | int 163 | parg_getopt(struct parg_state *ps, int argc, char *const argv[], 164 | const char *optstring) 165 | { 166 | return parg_getopt_long(ps, argc, argv, optstring, NULL, NULL); 167 | } 168 | 169 | int 170 | parg_getopt_long(struct parg_state *ps, int argc, char *const argv[], 171 | const char *optstring, 172 | const struct parg_option *longopts, int *longindex) 173 | { 174 | assert(ps != NULL); 175 | assert(argv != NULL); 176 | assert(optstring != NULL); 177 | 178 | ps->optarg = NULL; 179 | 180 | if (argc < 2) { 181 | return -1; 182 | } 183 | 184 | /* Advance to next element if needed */ 185 | if (ps->nextchar == NULL || *ps->nextchar == '\0') { 186 | if (is_argv_end(ps, argc, argv)) { 187 | return -1; 188 | } 189 | 190 | ps->nextchar = argv[ps->optind++]; 191 | 192 | /* Check for nonoption element (including '-') */ 193 | if (ps->nextchar[0] != '-' || ps->nextchar[1] == '\0') { 194 | ps->optarg = ps->nextchar; 195 | ps->nextchar = NULL; 196 | return 1; 197 | } 198 | 199 | /* Check for '--' */ 200 | if (ps->nextchar[1] == '-') { 201 | if (ps->nextchar[2] == '\0') { 202 | ps->nextchar = NULL; 203 | return -1; 204 | } 205 | 206 | if (longopts != NULL) { 207 | ps->nextchar += 2; 208 | 209 | return match_long(ps, argc, argv, optstring, 210 | longopts, longindex); 211 | } 212 | } 213 | 214 | ps->nextchar++; 215 | } 216 | 217 | /* Match nextchar */ 218 | return match_short(ps, argc, argv, optstring); 219 | } 220 | 221 | /* 222 | * Reverse elements of `v` from `i` to `j`. 223 | */ 224 | static void 225 | reverse(char *v[], int i, int j) 226 | { 227 | while (j - i > 1) { 228 | char *tmp = v[i]; 229 | v[i] = v[j - 1]; 230 | v[j - 1] = tmp; 231 | ++i; 232 | --j; 233 | } 234 | } 235 | 236 | /* 237 | * Reorder elements of `argv` with no special cases. 238 | * 239 | * This function assumes there is no `--` element, and the last element 240 | * is not an option missing a required argument. 241 | * 242 | * The algorithm is described here: 243 | * http://hardtoc.com/2016/11/07/reordering-arguments.html 244 | */ 245 | static int 246 | parg_reorder_simple(int argc, char *argv[], 247 | const char *optstring, 248 | const struct parg_option *longopts) 249 | { 250 | struct parg_state ps; 251 | int change; 252 | int l = 0; 253 | int m = 0; 254 | int r = 0; 255 | 256 | if (argc < 2) { 257 | return argc; 258 | } 259 | 260 | do { 261 | int nextind; 262 | int c; 263 | 264 | parg_init(&ps); 265 | 266 | nextind = ps.optind; 267 | 268 | /* Parse until end of argument */ 269 | do { 270 | c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL); 271 | } while (ps.nextchar != NULL && *ps.nextchar != '\0'); 272 | 273 | change = 0; 274 | 275 | do { 276 | /* Find next non-option */ 277 | for (l = nextind; c != 1 && c != -1;) { 278 | l = ps.optind; 279 | 280 | do { 281 | c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL); 282 | } while (ps.nextchar != NULL && *ps.nextchar != '\0'); 283 | } 284 | 285 | /* Find next option */ 286 | for (m = l; c == 1;) { 287 | m = ps.optind; 288 | 289 | do { 290 | c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL); 291 | } while (ps.nextchar != NULL && *ps.nextchar != '\0'); 292 | } 293 | 294 | /* Find next non-option */ 295 | for (r = m; c != 1 && c != -1;) { 296 | r = ps.optind; 297 | 298 | do { 299 | c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL); 300 | } while (ps.nextchar != NULL && *ps.nextchar != '\0'); 301 | } 302 | 303 | /* Find next option */ 304 | for (nextind = r; c == 1;) { 305 | nextind = ps.optind; 306 | 307 | do { 308 | c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL); 309 | } while (ps.nextchar != NULL && *ps.nextchar != '\0'); 310 | } 311 | 312 | if (m < r) { 313 | change = 1; 314 | reverse(argv, l, m); 315 | reverse(argv, m, r); 316 | reverse(argv, l, r); 317 | } 318 | } while (c != -1); 319 | } while (change != 0); 320 | 321 | return l + (r - m); 322 | } 323 | 324 | int 325 | parg_reorder(int argc, char *argv[], 326 | const char *optstring, 327 | const struct parg_option *longopts) 328 | { 329 | struct parg_state ps; 330 | int lastind; 331 | int optend; 332 | int c; 333 | 334 | assert(argv != NULL); 335 | assert(optstring != NULL); 336 | 337 | if (argc < 2) { 338 | return argc; 339 | } 340 | 341 | parg_init(&ps); 342 | 343 | /* Find end of normal arguments */ 344 | do { 345 | lastind = ps.optind; 346 | 347 | c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL); 348 | 349 | /* Check for trailing option with error */ 350 | if ((c == '?' || c == ':') && is_argv_end(&ps, argc, argv)) { 351 | lastind = ps.optind - 1; 352 | break; 353 | } 354 | } while (c != -1); 355 | 356 | optend = parg_reorder_simple(lastind, argv, optstring, longopts); 357 | 358 | /* Rotate `--` or trailing option with error into position */ 359 | if (lastind < argc) { 360 | reverse(argv, optend, lastind); 361 | reverse(argv, optend, lastind + 1); 362 | ++optend; 363 | } 364 | 365 | return optend; 366 | } 367 | -------------------------------------------------------------------------------- /test/test_parg.c: -------------------------------------------------------------------------------- 1 | /* 2 | * test_parg - parg unit test 3 | * 4 | * Copyright 2015-2023 Joergen Ibsen 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | * 21 | * SPDX-License-Identifier: MIT-0 22 | */ 23 | 24 | #include "parg.h" 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include "greatest.h" 31 | 32 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 33 | 34 | static const char *os_def = ":no::r:"; 35 | 36 | static const struct parg_option po_def[] = { 37 | { "noarg", PARG_NOARG, NULL, 'n' }, 38 | { "optarg", PARG_OPTARG, NULL, 'o' }, 39 | { "reqarg", PARG_REQARG, NULL, 'r' }, 40 | { "foo", PARG_NOARG, NULL, 'f' }, 41 | { "foobar", PARG_NOARG, NULL, 'b' }, 42 | { 0, 0, 0, 0 } 43 | }; 44 | 45 | /* 46 | * parg_getopt tests 47 | */ 48 | 49 | TEST parg_getopt_app_only(void) 50 | { 51 | struct parg_state ps; 52 | char *argv[] = { "app" }; 53 | 54 | parg_init(&ps); 55 | 56 | ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def)); 57 | ASSERT_EQ(NULL, ps.optarg); 58 | ASSERT_EQ(1, ps.optind); 59 | 60 | PASS(); 61 | } 62 | 63 | TEST parg_getopt_dash(void) 64 | { 65 | struct parg_state ps; 66 | char *argv[] = { "app", "-" }; 67 | 68 | parg_init(&ps); 69 | 70 | ASSERT_EQ(1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def)); 71 | ASSERT_EQ(argv[1], ps.optarg); 72 | ASSERT_EQ(2, ps.optind); 73 | 74 | ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def)); 75 | ASSERT_EQ(NULL, ps.optarg); 76 | ASSERT_EQ(2, ps.optind); 77 | 78 | PASS(); 79 | } 80 | 81 | TEST parg_getopt_double_dash(void) 82 | { 83 | struct parg_state ps; 84 | char *argv[] = { "app", "--" }; 85 | 86 | parg_init(&ps); 87 | 88 | ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def)); 89 | ASSERT_EQ(NULL, ps.optarg); 90 | ASSERT_EQ(2, ps.optind); 91 | 92 | PASS(); 93 | } 94 | 95 | TEST parg_getopt_nonopt(void) 96 | { 97 | struct parg_state ps; 98 | char *argv[] = { "app", "foo" }; 99 | 100 | parg_init(&ps); 101 | 102 | ASSERT_EQ(1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def)); 103 | ASSERT_EQ(argv[1], ps.optarg); 104 | ASSERT_EQ(2, ps.optind); 105 | 106 | ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def)); 107 | ASSERT_EQ(NULL, ps.optarg); 108 | ASSERT_EQ(2, ps.optind); 109 | 110 | PASS(); 111 | } 112 | 113 | TEST parg_getopt_no_match(void) 114 | { 115 | struct parg_state ps; 116 | char *argv[] = { "app", "-u" }; 117 | 118 | parg_init(&ps); 119 | 120 | ASSERT_EQ('?', parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def)); 121 | ASSERT_EQ('u', ps.optopt); 122 | ASSERT_EQ(2, ps.optind); 123 | 124 | ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def)); 125 | ASSERT_EQ(NULL, ps.optarg); 126 | ASSERT_EQ(2, ps.optind); 127 | 128 | PASS(); 129 | } 130 | 131 | TEST parg_getopt_noarg(void) 132 | { 133 | struct parg_state ps; 134 | char *argv[] = { "app", "-n" }; 135 | 136 | parg_init(&ps); 137 | 138 | ASSERT_EQ('n', parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def)); 139 | ASSERT_EQ(NULL, ps.optarg); 140 | ASSERT_EQ(2, ps.optind); 141 | 142 | ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def)); 143 | ASSERT_EQ(NULL, ps.optarg); 144 | ASSERT_EQ(2, ps.optind); 145 | 146 | PASS(); 147 | } 148 | 149 | TEST parg_getopt_optarg_missing(void) 150 | { 151 | struct parg_state ps; 152 | char *argv[] = { "app", "-o" }; 153 | 154 | parg_init(&ps); 155 | 156 | ASSERT_EQ('o', parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def)); 157 | ASSERT_EQ(NULL, ps.optarg); 158 | ASSERT_EQ(2, ps.optind); 159 | 160 | ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def)); 161 | ASSERT_EQ(NULL, ps.optarg); 162 | ASSERT_EQ(2, ps.optind); 163 | 164 | PASS(); 165 | } 166 | 167 | TEST parg_getopt_optarg_inline(void) 168 | { 169 | struct parg_state ps; 170 | char *argv[] = { "app", "-oarg" }; 171 | 172 | parg_init(&ps); 173 | 174 | ASSERT_EQ('o', parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def)); 175 | ASSERT_EQ(&argv[1][2], ps.optarg); 176 | ASSERT_EQ(2, ps.optind); 177 | 178 | ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def)); 179 | ASSERT_EQ(NULL, ps.optarg); 180 | ASSERT_EQ(2, ps.optind); 181 | 182 | PASS(); 183 | } 184 | 185 | TEST parg_getopt_reqarg_missing(void) 186 | { 187 | struct parg_state ps; 188 | char *argv[] = { "app", "-r" }; 189 | 190 | parg_init(&ps); 191 | 192 | ASSERT_EQ(':', parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def)); 193 | ASSERT_EQ(NULL, ps.optarg); 194 | ASSERT_EQ('r', ps.optopt); 195 | ASSERT_EQ(2, ps.optind); 196 | 197 | ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def)); 198 | ASSERT_EQ(NULL, ps.optarg); 199 | ASSERT_EQ(2, ps.optind); 200 | 201 | PASS(); 202 | } 203 | 204 | TEST parg_getopt_reqarg_inline(void) 205 | { 206 | struct parg_state ps; 207 | char *argv[] = { "app", "-rarg" }; 208 | 209 | parg_init(&ps); 210 | 211 | ASSERT_EQ('r', parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def)); 212 | ASSERT_EQ(&argv[1][2], ps.optarg); 213 | ASSERT_EQ(2, ps.optind); 214 | 215 | ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def)); 216 | ASSERT_EQ(NULL, ps.optarg); 217 | ASSERT_EQ(2, ps.optind); 218 | 219 | PASS(); 220 | } 221 | 222 | TEST parg_getopt_reqarg_nextarg(void) 223 | { 224 | struct parg_state ps; 225 | char *argv[] = { "app", "-r", "arg" }; 226 | 227 | parg_init(&ps); 228 | 229 | ASSERT_EQ('r', parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def)); 230 | ASSERT_EQ(argv[2], ps.optarg); 231 | ASSERT_EQ(3, ps.optind); 232 | 233 | ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def)); 234 | ASSERT_EQ(NULL, ps.optarg); 235 | ASSERT_EQ(3, ps.optind); 236 | 237 | PASS(); 238 | } 239 | 240 | /* 241 | * parg_getopt_long tests 242 | */ 243 | 244 | TEST parg_getopt_long_app_only(void) 245 | { 246 | struct parg_state ps; 247 | char *argv[] = { "app" }; 248 | int li = -1; 249 | 250 | parg_init(&ps); 251 | 252 | ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 253 | ASSERT_EQ(NULL, ps.optarg); 254 | ASSERT_EQ(1, ps.optind); 255 | 256 | PASS(); 257 | } 258 | 259 | TEST parg_getopt_long_dash(void) 260 | { 261 | struct parg_state ps; 262 | char *argv[] = { "app", "-" }; 263 | int li = -1; 264 | 265 | parg_init(&ps); 266 | 267 | ASSERT_EQ(1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 268 | ASSERT_EQ(argv[1], ps.optarg); 269 | ASSERT_EQ(2, ps.optind); 270 | 271 | ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 272 | ASSERT_EQ(NULL, ps.optarg); 273 | ASSERT_EQ(2, ps.optind); 274 | 275 | PASS(); 276 | } 277 | 278 | TEST parg_getopt_long_double_dash(void) 279 | { 280 | struct parg_state ps; 281 | char *argv[] = { "app", "--" }; 282 | int li = -1; 283 | 284 | parg_init(&ps); 285 | 286 | ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 287 | ASSERT_EQ(NULL, ps.optarg); 288 | ASSERT_EQ(2, ps.optind); 289 | 290 | PASS(); 291 | } 292 | 293 | TEST parg_getopt_long_nonopt(void) 294 | { 295 | struct parg_state ps; 296 | char *argv[] = { "app", "foo" }; 297 | int li = -1; 298 | 299 | parg_init(&ps); 300 | 301 | ASSERT_EQ(1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 302 | ASSERT_EQ(argv[1], ps.optarg); 303 | ASSERT_EQ(2, ps.optind); 304 | 305 | ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 306 | ASSERT_EQ(NULL, ps.optarg); 307 | ASSERT_EQ(2, ps.optind); 308 | 309 | PASS(); 310 | } 311 | 312 | TEST parg_getopt_long_no_match(void) 313 | { 314 | struct parg_state ps; 315 | char *argv[] = { "app", "--unknown" }; 316 | int li = -1; 317 | 318 | parg_init(&ps); 319 | 320 | ASSERT_EQ('?', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 321 | ASSERT_EQ(2, ps.optind); 322 | 323 | ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 324 | ASSERT_EQ(NULL, ps.optarg); 325 | ASSERT_EQ(2, ps.optind); 326 | 327 | PASS(); 328 | } 329 | 330 | TEST parg_getopt_long_flag(void) 331 | { 332 | struct parg_state ps; 333 | char *argv[] = { "app", "--flag" }; 334 | int li = -1; 335 | int flag = 0; 336 | struct parg_option po_flag[] = { 337 | { "flag", PARG_NOARG, 0, 1 }, 338 | { 0, 0, 0, 0 } 339 | }; 340 | 341 | po_flag[0].flag = &flag; 342 | 343 | parg_init(&ps); 344 | 345 | ASSERT_EQ(0, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_flag, &li)); 346 | ASSERT_EQ(0, li); 347 | ASSERT_EQ(1, flag); 348 | ASSERT_EQ(2, ps.optind); 349 | 350 | ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_flag, &li)); 351 | ASSERT_EQ(NULL, ps.optarg); 352 | ASSERT_EQ(2, ps.optind); 353 | 354 | PASS(); 355 | } 356 | 357 | TEST parg_getopt_long_noarg(void) 358 | { 359 | struct parg_state ps; 360 | char *argv[] = { "app", "--noarg" }; 361 | int li = -1; 362 | 363 | parg_init(&ps); 364 | 365 | ASSERT_EQ('n', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 366 | ASSERT_EQ(0, li); 367 | ASSERT_EQ(NULL, ps.optarg); 368 | ASSERT_EQ(2, ps.optind); 369 | 370 | ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 371 | ASSERT_EQ(NULL, ps.optarg); 372 | ASSERT_EQ(2, ps.optind); 373 | 374 | PASS(); 375 | } 376 | 377 | TEST parg_getopt_long_noarg_inline(void) 378 | { 379 | struct parg_state ps; 380 | char *argv[] = { "app", "--noarg=arg" }; 381 | int li = -1; 382 | 383 | parg_init(&ps); 384 | 385 | ASSERT_EQ(':', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 386 | ASSERT_EQ(0, li); 387 | ASSERT_EQ(NULL, ps.optarg); 388 | ASSERT_EQ('n', ps.optopt); 389 | ASSERT_EQ(2, ps.optind); 390 | 391 | ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 392 | ASSERT_EQ(NULL, ps.optarg); 393 | ASSERT_EQ(2, ps.optind); 394 | 395 | PASS(); 396 | } 397 | 398 | TEST parg_getopt_long_optarg_missing(void) 399 | { 400 | struct parg_state ps; 401 | char *argv[] = { "app", "--optarg" }; 402 | int li = -1; 403 | 404 | parg_init(&ps); 405 | 406 | ASSERT_EQ('o', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 407 | ASSERT_EQ(1, li); 408 | ASSERT_EQ(NULL, ps.optarg); 409 | ASSERT_EQ(2, ps.optind); 410 | 411 | ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 412 | ASSERT_EQ(NULL, ps.optarg); 413 | ASSERT_EQ(2, ps.optind); 414 | 415 | PASS(); 416 | } 417 | 418 | TEST parg_getopt_long_optarg_inline(void) 419 | { 420 | struct parg_state ps; 421 | char *argv[] = { "app", "--optarg=arg" }; 422 | int li = -1; 423 | 424 | parg_init(&ps); 425 | 426 | ASSERT_EQ('o', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 427 | ASSERT_EQ(1, li); 428 | ASSERT_EQ(&argv[1][9], ps.optarg); 429 | ASSERT_EQ(2, ps.optind); 430 | 431 | ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 432 | ASSERT_EQ(NULL, ps.optarg); 433 | ASSERT_EQ(2, ps.optind); 434 | 435 | PASS(); 436 | } 437 | 438 | TEST parg_getopt_long_reqarg_missing(void) 439 | { 440 | struct parg_state ps; 441 | char *argv[] = { "app", "--reqarg" }; 442 | int li = -1; 443 | 444 | parg_init(&ps); 445 | 446 | ASSERT_EQ(':', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 447 | ASSERT_EQ(2, li); 448 | ASSERT_EQ(NULL, ps.optarg); 449 | ASSERT_EQ('r', ps.optopt); 450 | ASSERT_EQ(2, ps.optind); 451 | 452 | ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 453 | ASSERT_EQ(NULL, ps.optarg); 454 | ASSERT_EQ(2, ps.optind); 455 | 456 | PASS(); 457 | } 458 | 459 | TEST parg_getopt_long_reqarg_inline(void) 460 | { 461 | struct parg_state ps; 462 | char *argv[] = { "app", "--reqarg=arg" }; 463 | int li = -1; 464 | 465 | parg_init(&ps); 466 | 467 | ASSERT_EQ('r', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 468 | ASSERT_EQ(2, li); 469 | ASSERT_EQ(&argv[1][9], ps.optarg); 470 | ASSERT_EQ(2, ps.optind); 471 | 472 | ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 473 | ASSERT_EQ(NULL, ps.optarg); 474 | ASSERT_EQ(2, ps.optind); 475 | 476 | PASS(); 477 | } 478 | 479 | TEST parg_getopt_long_reqarg_nextarg(void) 480 | { 481 | struct parg_state ps; 482 | char *argv[] = { "app", "--reqarg", "arg" }; 483 | int li = -1; 484 | 485 | parg_init(&ps); 486 | 487 | ASSERT_EQ('r', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 488 | ASSERT_EQ(2, li); 489 | ASSERT_EQ(argv[2], ps.optarg); 490 | ASSERT_EQ(3, ps.optind); 491 | 492 | ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 493 | ASSERT_EQ(NULL, ps.optarg); 494 | ASSERT_EQ(3, ps.optind); 495 | 496 | PASS(); 497 | } 498 | 499 | TEST parg_getopt_long_prefix_ambiguous(void) 500 | { 501 | struct parg_state ps; 502 | char *argv[] = { "app", "--fo" }; 503 | int li = -1; 504 | 505 | parg_init(&ps); 506 | 507 | ASSERT_EQ('?', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 508 | ASSERT_EQ(NULL, ps.optarg); 509 | ASSERT_EQ(2, ps.optind); 510 | 511 | ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 512 | ASSERT_EQ(NULL, ps.optarg); 513 | ASSERT_EQ(2, ps.optind); 514 | 515 | PASS(); 516 | } 517 | 518 | TEST parg_getopt_long_prefix_exact(void) 519 | { 520 | struct parg_state ps; 521 | char *argv[] = { "app", "--foo" }; 522 | int li = -1; 523 | 524 | parg_init(&ps); 525 | 526 | ASSERT_EQ('f', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 527 | ASSERT_EQ(3, li); 528 | ASSERT_EQ(NULL, ps.optarg); 529 | ASSERT_EQ(2, ps.optind); 530 | 531 | ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 532 | ASSERT_EQ(NULL, ps.optarg); 533 | ASSERT_EQ(2, ps.optind); 534 | 535 | PASS(); 536 | } 537 | 538 | TEST parg_getopt_long_prefix_unambiguous(void) 539 | { 540 | struct parg_state ps; 541 | char *argv[] = { "app", "--foob" }; 542 | int li = -1; 543 | 544 | parg_init(&ps); 545 | 546 | ASSERT_EQ('b', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 547 | ASSERT_EQ(4, li); 548 | ASSERT_EQ(NULL, ps.optarg); 549 | ASSERT_EQ(2, ps.optind); 550 | 551 | ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); 552 | ASSERT_EQ(NULL, ps.optarg); 553 | ASSERT_EQ(2, ps.optind); 554 | 555 | PASS(); 556 | } 557 | 558 | /* 559 | * parg_reorder tests 560 | */ 561 | 562 | TEST parg_reorder_short(void) 563 | { 564 | char *argv[] = { 565 | "app", "first", "-r", "--", "foo", "-u", "bar", "-o", "--", 566 | "-n", "last" 567 | }; 568 | char *argv_expected[] = { 569 | "app", "-r", "--", "-u", "-o", "--", "first", "foo", "bar", 570 | "-n", "last" 571 | }; 572 | size_t i; 573 | 574 | ASSERT_EQ(6, parg_reorder(ARRAY_SIZE(argv), argv, os_def, NULL)); 575 | 576 | for (i = 0; i < ARRAY_SIZE(argv); ++i) { 577 | ASSERT_STR_EQ(argv_expected[i], argv[i]); 578 | } 579 | 580 | PASS(); 581 | } 582 | 583 | TEST parg_reorder_long(void) 584 | { 585 | char *argv[] = { 586 | "app", "first", "--reqarg", "--", "foo", "--unknown", "bar", 587 | "--optarg", "--", "--noarg", "last" 588 | }; 589 | char *argv_expected[] = { 590 | "app", "--reqarg", "--", "--unknown", "--optarg", "--", 591 | "first", "foo", "bar", "--noarg", "last" 592 | }; 593 | size_t i; 594 | 595 | ASSERT_EQ(6, parg_reorder(ARRAY_SIZE(argv), argv, "", po_def)); 596 | 597 | for (i = 0; i < ARRAY_SIZE(argv); ++i) { 598 | ASSERT_STR_EQ(argv_expected[i], argv[i]); 599 | } 600 | 601 | PASS(); 602 | } 603 | 604 | TEST parg_reorder_app_only(void) 605 | { 606 | char *argv[] = { "app" }; 607 | 608 | ASSERT_EQ(1, parg_reorder(ARRAY_SIZE(argv), argv, os_def, NULL)); 609 | 610 | PASS(); 611 | } 612 | 613 | TEST parg_reorder_double_dash_last(void) 614 | { 615 | char *argv[] = { "app", "foo", "-n", "--" }; 616 | char *argv_expected[] = { "app", "-n", "--", "foo" }; 617 | size_t i; 618 | 619 | ASSERT_EQ(3, parg_reorder(ARRAY_SIZE(argv), argv, os_def, NULL)); 620 | 621 | for (i = 0; i < ARRAY_SIZE(argv); ++i) { 622 | ASSERT_STR_EQ(argv_expected[i], argv[i]); 623 | } 624 | 625 | PASS(); 626 | } 627 | 628 | TEST parg_reorder_missing_arg_last(void) 629 | { 630 | char *argv[] = { "app", "foo", "-r" }; 631 | char *argv_expected[] = { "app", "-r", "foo" }; 632 | size_t i; 633 | 634 | ASSERT_EQ(2, parg_reorder(ARRAY_SIZE(argv), argv, os_def, NULL)); 635 | 636 | for (i = 0; i < ARRAY_SIZE(argv); ++i) { 637 | ASSERT_STR_EQ(argv_expected[i], argv[i]); 638 | } 639 | 640 | PASS(); 641 | } 642 | 643 | SUITE(parg_getopt_tests) 644 | { 645 | RUN_TEST(parg_getopt_app_only); 646 | 647 | RUN_TEST(parg_getopt_dash); 648 | 649 | RUN_TEST(parg_getopt_double_dash); 650 | 651 | RUN_TEST(parg_getopt_nonopt); 652 | 653 | RUN_TEST(parg_getopt_no_match); 654 | 655 | RUN_TEST(parg_getopt_noarg); 656 | 657 | RUN_TEST(parg_getopt_optarg_missing); 658 | RUN_TEST(parg_getopt_optarg_inline); 659 | 660 | RUN_TEST(parg_getopt_reqarg_missing); 661 | RUN_TEST(parg_getopt_reqarg_inline); 662 | RUN_TEST(parg_getopt_reqarg_nextarg); 663 | } 664 | 665 | SUITE(parg_getopt_long_tests) 666 | { 667 | RUN_TEST(parg_getopt_long_app_only); 668 | 669 | RUN_TEST(parg_getopt_long_dash); 670 | 671 | RUN_TEST(parg_getopt_long_double_dash); 672 | 673 | RUN_TEST(parg_getopt_long_nonopt); 674 | 675 | RUN_TEST(parg_getopt_long_no_match); 676 | 677 | RUN_TEST(parg_getopt_long_flag); 678 | 679 | RUN_TEST(parg_getopt_long_noarg); 680 | RUN_TEST(parg_getopt_long_noarg_inline); 681 | 682 | RUN_TEST(parg_getopt_long_optarg_missing); 683 | RUN_TEST(parg_getopt_long_optarg_inline); 684 | 685 | RUN_TEST(parg_getopt_long_reqarg_missing); 686 | RUN_TEST(parg_getopt_long_reqarg_inline); 687 | RUN_TEST(parg_getopt_long_reqarg_nextarg); 688 | 689 | RUN_TEST(parg_getopt_long_prefix_ambiguous); 690 | RUN_TEST(parg_getopt_long_prefix_exact); 691 | RUN_TEST(parg_getopt_long_prefix_unambiguous); 692 | } 693 | 694 | SUITE(parg_reorder_tests) 695 | { 696 | RUN_TEST(parg_reorder_short); 697 | RUN_TEST(parg_reorder_long); 698 | 699 | RUN_TEST(parg_reorder_app_only); 700 | 701 | RUN_TEST(parg_reorder_double_dash_last); 702 | 703 | RUN_TEST(parg_reorder_missing_arg_last); 704 | } 705 | 706 | GREATEST_MAIN_DEFS(); 707 | 708 | int main(int argc, char *argv[]) 709 | { 710 | GREATEST_MAIN_BEGIN(); 711 | RUN_SUITE(parg_getopt_tests); 712 | RUN_SUITE(parg_getopt_long_tests); 713 | RUN_SUITE(parg_reorder_tests); 714 | GREATEST_MAIN_END(); 715 | } 716 | -------------------------------------------------------------------------------- /test/greatest.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2021 Scott Vokes 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef GREATEST_H 18 | #define GREATEST_H 19 | 20 | #if defined(__cplusplus) && !defined(GREATEST_NO_EXTERN_CPLUSPLUS) 21 | extern "C" { 22 | #endif 23 | 24 | /* 1.5.0 */ 25 | #define GREATEST_VERSION_MAJOR 1 26 | #define GREATEST_VERSION_MINOR 5 27 | #define GREATEST_VERSION_PATCH 0 28 | 29 | /* A unit testing system for C, contained in 1 file. 30 | * It doesn't use dynamic allocation or depend on anything 31 | * beyond ANSI C89. 32 | * 33 | * An up-to-date version can be found at: 34 | * https://github.com/silentbicycle/greatest/ 35 | */ 36 | 37 | 38 | /********************************************************************* 39 | * Minimal test runner template 40 | *********************************************************************/ 41 | #if 0 42 | 43 | #include "greatest.h" 44 | 45 | TEST foo_should_foo(void) { 46 | PASS(); 47 | } 48 | 49 | static void setup_cb(void *data) { 50 | printf("setup callback for each test case\n"); 51 | } 52 | 53 | static void teardown_cb(void *data) { 54 | printf("teardown callback for each test case\n"); 55 | } 56 | 57 | SUITE(suite) { 58 | /* Optional setup/teardown callbacks which will be run before/after 59 | * every test case. If using a test suite, they will be cleared when 60 | * the suite finishes. */ 61 | SET_SETUP(setup_cb, voidp_to_callback_data); 62 | SET_TEARDOWN(teardown_cb, voidp_to_callback_data); 63 | 64 | RUN_TEST(foo_should_foo); 65 | } 66 | 67 | /* Add definitions that need to be in the test runner's main file. */ 68 | GREATEST_MAIN_DEFS(); 69 | 70 | /* Set up, run suite(s) of tests, report pass/fail/skip stats. */ 71 | int run_tests(void) { 72 | GREATEST_INIT(); /* init. greatest internals */ 73 | /* List of suites to run (if any). */ 74 | RUN_SUITE(suite); 75 | 76 | /* Tests can also be run directly, without using test suites. */ 77 | RUN_TEST(foo_should_foo); 78 | 79 | GREATEST_PRINT_REPORT(); /* display results */ 80 | return greatest_all_passed(); 81 | } 82 | 83 | /* main(), for a standalone command-line test runner. 84 | * This replaces run_tests above, and adds command line option 85 | * handling and exiting with a pass/fail status. */ 86 | int main(int argc, char **argv) { 87 | GREATEST_MAIN_BEGIN(); /* init & parse command-line args */ 88 | RUN_SUITE(suite); 89 | GREATEST_MAIN_END(); /* display results */ 90 | } 91 | 92 | #endif 93 | /*********************************************************************/ 94 | 95 | 96 | #include 97 | #include 98 | #include 99 | #include 100 | 101 | /*********** 102 | * Options * 103 | ***********/ 104 | 105 | /* Default column width for non-verbose output. */ 106 | #ifndef GREATEST_DEFAULT_WIDTH 107 | #define GREATEST_DEFAULT_WIDTH 72 108 | #endif 109 | 110 | /* FILE *, for test logging. */ 111 | #ifndef GREATEST_STDOUT 112 | #define GREATEST_STDOUT stdout 113 | #endif 114 | 115 | /* Remove GREATEST_ prefix from most commonly used symbols? */ 116 | #ifndef GREATEST_USE_ABBREVS 117 | #define GREATEST_USE_ABBREVS 1 118 | #endif 119 | 120 | /* Set to 0 to disable all use of setjmp/longjmp. */ 121 | #ifndef GREATEST_USE_LONGJMP 122 | #define GREATEST_USE_LONGJMP 0 123 | #endif 124 | 125 | /* Make it possible to replace fprintf with another 126 | * function with the same interface. */ 127 | #ifndef GREATEST_FPRINTF 128 | #define GREATEST_FPRINTF fprintf 129 | #endif 130 | 131 | #if GREATEST_USE_LONGJMP 132 | #include 133 | #endif 134 | 135 | /* Set to 0 to disable all use of time.h / clock(). */ 136 | #ifndef GREATEST_USE_TIME 137 | #define GREATEST_USE_TIME 1 138 | #endif 139 | 140 | #if GREATEST_USE_TIME 141 | #include 142 | #endif 143 | 144 | /* Floating point type, for ASSERT_IN_RANGE. */ 145 | #ifndef GREATEST_FLOAT 146 | #define GREATEST_FLOAT double 147 | #define GREATEST_FLOAT_FMT "%g" 148 | #endif 149 | 150 | /* Size of buffer for test name + optional '_' separator and suffix */ 151 | #ifndef GREATEST_TESTNAME_BUF_SIZE 152 | #define GREATEST_TESTNAME_BUF_SIZE 128 153 | #endif 154 | 155 | 156 | /********* 157 | * Types * 158 | *********/ 159 | 160 | /* Info for the current running suite. */ 161 | typedef struct greatest_suite_info { 162 | unsigned int tests_run; 163 | unsigned int passed; 164 | unsigned int failed; 165 | unsigned int skipped; 166 | 167 | #if GREATEST_USE_TIME 168 | /* timers, pre/post running suite and individual tests */ 169 | clock_t pre_suite; 170 | clock_t post_suite; 171 | clock_t pre_test; 172 | clock_t post_test; 173 | #endif 174 | } greatest_suite_info; 175 | 176 | /* Type for a suite function. */ 177 | typedef void greatest_suite_cb(void); 178 | 179 | /* Types for setup/teardown callbacks. If non-NULL, these will be run 180 | * and passed the pointer to their additional data. */ 181 | typedef void greatest_setup_cb(void *udata); 182 | typedef void greatest_teardown_cb(void *udata); 183 | 184 | /* Type for an equality comparison between two pointers of the same type. 185 | * Should return non-0 if equal, otherwise 0. 186 | * UDATA is a closure value, passed through from ASSERT_EQUAL_T[m]. */ 187 | typedef int greatest_equal_cb(const void *expd, const void *got, void *udata); 188 | 189 | /* Type for a callback that prints a value pointed to by T. 190 | * Return value has the same meaning as printf's. 191 | * UDATA is a closure value, passed through from ASSERT_EQUAL_T[m]. */ 192 | typedef int greatest_printf_cb(const void *t, void *udata); 193 | 194 | /* Callbacks for an arbitrary type; needed for type-specific 195 | * comparisons via GREATEST_ASSERT_EQUAL_T[m].*/ 196 | typedef struct greatest_type_info { 197 | greatest_equal_cb *equal; 198 | greatest_printf_cb *print; 199 | } greatest_type_info; 200 | 201 | typedef struct greatest_memory_cmp_env { 202 | const unsigned char *exp; 203 | const unsigned char *got; 204 | size_t size; 205 | } greatest_memory_cmp_env; 206 | 207 | /* Callbacks for string and raw memory types. */ 208 | extern greatest_type_info greatest_type_info_string; 209 | extern greatest_type_info greatest_type_info_memory; 210 | 211 | typedef enum { 212 | GREATEST_FLAG_FIRST_FAIL = 0x01, 213 | GREATEST_FLAG_LIST_ONLY = 0x02, 214 | GREATEST_FLAG_ABORT_ON_FAIL = 0x04 215 | } greatest_flag_t; 216 | 217 | /* Internal state for a PRNG, used to shuffle test order. */ 218 | struct greatest_prng { 219 | unsigned char random_order; /* use random ordering? */ 220 | unsigned char initialized; /* is random ordering initialized? */ 221 | unsigned char pad_0[6]; 222 | unsigned long state; /* PRNG state */ 223 | unsigned long count; /* how many tests, this pass */ 224 | unsigned long count_ceil; /* total number of tests */ 225 | unsigned long count_run; /* total tests run */ 226 | unsigned long a; /* LCG multiplier */ 227 | unsigned long c; /* LCG increment */ 228 | unsigned long m; /* LCG modulus, based on count_ceil */ 229 | }; 230 | 231 | /* Struct containing all test runner state. */ 232 | typedef struct greatest_run_info { 233 | unsigned char flags; 234 | unsigned char verbosity; 235 | unsigned char running_test; /* guard for nested RUN_TEST calls */ 236 | unsigned char exact_name_match; 237 | 238 | unsigned int tests_run; /* total test count */ 239 | 240 | /* currently running test suite */ 241 | greatest_suite_info suite; 242 | 243 | /* overall pass/fail/skip counts */ 244 | unsigned int passed; 245 | unsigned int failed; 246 | unsigned int skipped; 247 | unsigned int assertions; 248 | 249 | /* info to print about the most recent failure */ 250 | unsigned int fail_line; 251 | unsigned int pad_1; 252 | const char *fail_file; 253 | const char *msg; 254 | 255 | /* current setup/teardown hooks and userdata */ 256 | greatest_setup_cb *setup; 257 | void *setup_udata; 258 | greatest_teardown_cb *teardown; 259 | void *teardown_udata; 260 | 261 | /* formatting info for ".....s...F"-style output */ 262 | unsigned int col; 263 | unsigned int width; 264 | 265 | /* only run a specific suite or test */ 266 | const char *suite_filter; 267 | const char *test_filter; 268 | const char *test_exclude; 269 | const char *name_suffix; /* print suffix with test name */ 270 | char name_buf[GREATEST_TESTNAME_BUF_SIZE]; 271 | 272 | struct greatest_prng prng[2]; /* 0: suites, 1: tests */ 273 | 274 | #if GREATEST_USE_TIME 275 | /* overall timers */ 276 | clock_t begin; 277 | clock_t end; 278 | #endif 279 | 280 | #if GREATEST_USE_LONGJMP 281 | int pad_jmp_buf; 282 | unsigned char pad_2[4]; 283 | jmp_buf jump_dest; 284 | #endif 285 | } greatest_run_info; 286 | 287 | struct greatest_report_t { 288 | /* overall pass/fail/skip counts */ 289 | unsigned int passed; 290 | unsigned int failed; 291 | unsigned int skipped; 292 | unsigned int assertions; 293 | }; 294 | 295 | /* Global var for the current testing context. 296 | * Initialized by GREATEST_MAIN_DEFS(). */ 297 | extern greatest_run_info greatest_info; 298 | 299 | /* Type for ASSERT_ENUM_EQ's ENUM_STR argument. */ 300 | typedef const char *greatest_enum_str_fun(int value); 301 | 302 | 303 | /********************** 304 | * Exported functions * 305 | **********************/ 306 | 307 | /* These are used internally by greatest macros. */ 308 | int greatest_test_pre(const char *name); 309 | void greatest_test_post(int res); 310 | int greatest_do_assert_equal_t(const void *expd, const void *got, 311 | greatest_type_info *type_info, void *udata); 312 | void greatest_prng_init_first_pass(int id); 313 | int greatest_prng_init_second_pass(int id, unsigned long seed); 314 | void greatest_prng_step(int id); 315 | 316 | /* These are part of the public greatest API. */ 317 | void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata); 318 | void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, void *udata); 319 | void GREATEST_INIT(void); 320 | void GREATEST_PRINT_REPORT(void); 321 | int greatest_all_passed(void); 322 | void greatest_set_suite_filter(const char *filter); 323 | void greatest_set_test_filter(const char *filter); 324 | void greatest_set_test_exclude(const char *filter); 325 | void greatest_set_exact_name_match(void); 326 | void greatest_stop_at_first_fail(void); 327 | void greatest_abort_on_fail(void); 328 | void greatest_list_only(void); 329 | void greatest_get_report(struct greatest_report_t *report); 330 | unsigned int greatest_get_verbosity(void); 331 | void greatest_set_verbosity(unsigned int verbosity); 332 | void greatest_set_flag(greatest_flag_t flag); 333 | void greatest_set_test_suffix(const char *suffix); 334 | 335 | 336 | /******************** 337 | * Language Support * 338 | ********************/ 339 | 340 | /* If __VA_ARGS__ (C99) is supported, allow parametric testing 341 | * without needing to manually manage the argument struct. */ 342 | #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 19901L) || \ 343 | (defined(_MSC_VER) && _MSC_VER >= 1800) 344 | #define GREATEST_VA_ARGS 345 | #endif 346 | 347 | 348 | /********** 349 | * Macros * 350 | **********/ 351 | 352 | /* Define a suite. (The duplication is intentional -- it eliminates 353 | * a warning from -Wmissing-declarations.) */ 354 | #define GREATEST_SUITE(NAME) void NAME(void); void NAME(void) 355 | 356 | /* Declare a suite, provided by another compilation unit. */ 357 | #define GREATEST_SUITE_EXTERN(NAME) void NAME(void) 358 | 359 | /* Start defining a test function. 360 | * The arguments are not included, to allow parametric testing. */ 361 | #define GREATEST_TEST static enum greatest_test_res 362 | 363 | /* PASS/FAIL/SKIP result from a test. Used internally. */ 364 | typedef enum greatest_test_res { 365 | GREATEST_TEST_RES_PASS = 0, 366 | GREATEST_TEST_RES_FAIL = -1, 367 | GREATEST_TEST_RES_SKIP = 1 368 | } greatest_test_res; 369 | 370 | /* Run a suite. */ 371 | #define GREATEST_RUN_SUITE(S_NAME) greatest_run_suite(S_NAME, #S_NAME) 372 | 373 | /* Run a test in the current suite. */ 374 | #define GREATEST_RUN_TEST(TEST) \ 375 | do { \ 376 | if (greatest_test_pre(#TEST) == 1) { \ 377 | enum greatest_test_res res = GREATEST_SAVE_CONTEXT(); \ 378 | if (res == GREATEST_TEST_RES_PASS) { \ 379 | res = TEST(); \ 380 | } \ 381 | greatest_test_post(res); \ 382 | } \ 383 | } while (0) 384 | 385 | /* Ignore a test, don't warn about it being unused. */ 386 | #define GREATEST_IGNORE_TEST(TEST) (void)TEST 387 | 388 | /* Run a test in the current suite with one void * argument, 389 | * which can be a pointer to a struct with multiple arguments. */ 390 | #define GREATEST_RUN_TEST1(TEST, ENV) \ 391 | do { \ 392 | if (greatest_test_pre(#TEST) == 1) { \ 393 | enum greatest_test_res res = GREATEST_SAVE_CONTEXT(); \ 394 | if (res == GREATEST_TEST_RES_PASS) { \ 395 | res = TEST(ENV); \ 396 | } \ 397 | greatest_test_post(res); \ 398 | } \ 399 | } while (0) 400 | 401 | #ifdef GREATEST_VA_ARGS 402 | #define GREATEST_RUN_TESTp(TEST, ...) \ 403 | do { \ 404 | if (greatest_test_pre(#TEST) == 1) { \ 405 | enum greatest_test_res res = GREATEST_SAVE_CONTEXT(); \ 406 | if (res == GREATEST_TEST_RES_PASS) { \ 407 | res = TEST(__VA_ARGS__); \ 408 | } \ 409 | greatest_test_post(res); \ 410 | } \ 411 | } while (0) 412 | #endif 413 | 414 | 415 | /* Check if the test runner is in verbose mode. */ 416 | #define GREATEST_IS_VERBOSE() ((greatest_info.verbosity) > 0) 417 | #define GREATEST_LIST_ONLY() \ 418 | (greatest_info.flags & GREATEST_FLAG_LIST_ONLY) 419 | #define GREATEST_FIRST_FAIL() \ 420 | (greatest_info.flags & GREATEST_FLAG_FIRST_FAIL) 421 | #define GREATEST_ABORT_ON_FAIL() \ 422 | (greatest_info.flags & GREATEST_FLAG_ABORT_ON_FAIL) 423 | #define GREATEST_FAILURE_ABORT() \ 424 | (GREATEST_FIRST_FAIL() && \ 425 | (greatest_info.suite.failed > 0 || greatest_info.failed > 0)) 426 | 427 | /* Message-less forms of tests defined below. */ 428 | #define GREATEST_PASS() GREATEST_PASSm(NULL) 429 | #define GREATEST_FAIL() GREATEST_FAILm(NULL) 430 | #define GREATEST_SKIP() GREATEST_SKIPm(NULL) 431 | #define GREATEST_ASSERT(COND) \ 432 | GREATEST_ASSERTm(#COND, COND) 433 | #define GREATEST_ASSERT_OR_LONGJMP(COND) \ 434 | GREATEST_ASSERT_OR_LONGJMPm(#COND, COND) 435 | #define GREATEST_ASSERT_FALSE(COND) \ 436 | GREATEST_ASSERT_FALSEm(#COND, COND) 437 | #define GREATEST_ASSERT_EQ(EXP, GOT) \ 438 | GREATEST_ASSERT_EQm(#EXP " != " #GOT, EXP, GOT) 439 | #define GREATEST_ASSERT_NEQ(EXP, GOT) \ 440 | GREATEST_ASSERT_NEQm(#EXP " == " #GOT, EXP, GOT) 441 | #define GREATEST_ASSERT_GT(EXP, GOT) \ 442 | GREATEST_ASSERT_GTm(#EXP " <= " #GOT, EXP, GOT) 443 | #define GREATEST_ASSERT_GTE(EXP, GOT) \ 444 | GREATEST_ASSERT_GTEm(#EXP " < " #GOT, EXP, GOT) 445 | #define GREATEST_ASSERT_LT(EXP, GOT) \ 446 | GREATEST_ASSERT_LTm(#EXP " >= " #GOT, EXP, GOT) 447 | #define GREATEST_ASSERT_LTE(EXP, GOT) \ 448 | GREATEST_ASSERT_LTEm(#EXP " > " #GOT, EXP, GOT) 449 | #define GREATEST_ASSERT_EQ_FMT(EXP, GOT, FMT) \ 450 | GREATEST_ASSERT_EQ_FMTm(#EXP " != " #GOT, EXP, GOT, FMT) 451 | #define GREATEST_ASSERT_IN_RANGE(EXP, GOT, TOL) \ 452 | GREATEST_ASSERT_IN_RANGEm(#EXP " != " #GOT " +/- " #TOL, EXP, GOT, TOL) 453 | #define GREATEST_ASSERT_EQUAL_T(EXP, GOT, TYPE_INFO, UDATA) \ 454 | GREATEST_ASSERT_EQUAL_Tm(#EXP " != " #GOT, EXP, GOT, TYPE_INFO, UDATA) 455 | #define GREATEST_ASSERT_STR_EQ(EXP, GOT) \ 456 | GREATEST_ASSERT_STR_EQm(#EXP " != " #GOT, EXP, GOT) 457 | #define GREATEST_ASSERT_STRN_EQ(EXP, GOT, SIZE) \ 458 | GREATEST_ASSERT_STRN_EQm(#EXP " != " #GOT, EXP, GOT, SIZE) 459 | #define GREATEST_ASSERT_MEM_EQ(EXP, GOT, SIZE) \ 460 | GREATEST_ASSERT_MEM_EQm(#EXP " != " #GOT, EXP, GOT, SIZE) 461 | #define GREATEST_ASSERT_ENUM_EQ(EXP, GOT, ENUM_STR) \ 462 | GREATEST_ASSERT_ENUM_EQm(#EXP " != " #GOT, EXP, GOT, ENUM_STR) 463 | 464 | /* The following forms take an additional message argument first, 465 | * to be displayed by the test runner. */ 466 | 467 | /* Fail if a condition is not true, with message. */ 468 | #define GREATEST_ASSERTm(MSG, COND) \ 469 | do { \ 470 | greatest_info.assertions++; \ 471 | if (!(COND)) { GREATEST_FAILm(MSG); } \ 472 | } while (0) 473 | 474 | /* Fail if a condition is not true, longjmping out of test. */ 475 | #define GREATEST_ASSERT_OR_LONGJMPm(MSG, COND) \ 476 | do { \ 477 | greatest_info.assertions++; \ 478 | if (!(COND)) { GREATEST_FAIL_WITH_LONGJMPm(MSG); } \ 479 | } while (0) 480 | 481 | /* Fail if a condition is not false, with message. */ 482 | #define GREATEST_ASSERT_FALSEm(MSG, COND) \ 483 | do { \ 484 | greatest_info.assertions++; \ 485 | if ((COND)) { GREATEST_FAILm(MSG); } \ 486 | } while (0) 487 | 488 | /* Internal macro for relational assertions */ 489 | #define GREATEST__REL(REL, MSG, EXP, GOT) \ 490 | do { \ 491 | greatest_info.assertions++; \ 492 | if (!((EXP) REL (GOT))) { GREATEST_FAILm(MSG); } \ 493 | } while (0) 494 | 495 | /* Fail if EXP is not ==, !=, >, <, >=, or <= to GOT. */ 496 | #define GREATEST_ASSERT_EQm(MSG,E,G) GREATEST__REL(==, MSG,E,G) 497 | #define GREATEST_ASSERT_NEQm(MSG,E,G) GREATEST__REL(!=, MSG,E,G) 498 | #define GREATEST_ASSERT_GTm(MSG,E,G) GREATEST__REL(>, MSG,E,G) 499 | #define GREATEST_ASSERT_GTEm(MSG,E,G) GREATEST__REL(>=, MSG,E,G) 500 | #define GREATEST_ASSERT_LTm(MSG,E,G) GREATEST__REL(<, MSG,E,G) 501 | #define GREATEST_ASSERT_LTEm(MSG,E,G) GREATEST__REL(<=, MSG,E,G) 502 | 503 | /* Fail if EXP != GOT (equality comparison by ==). 504 | * Warning: FMT, EXP, and GOT will be evaluated more 505 | * than once on failure. */ 506 | #define GREATEST_ASSERT_EQ_FMTm(MSG, EXP, GOT, FMT) \ 507 | do { \ 508 | greatest_info.assertions++; \ 509 | if ((EXP) != (GOT)) { \ 510 | GREATEST_FPRINTF(GREATEST_STDOUT, "\nExpected: "); \ 511 | GREATEST_FPRINTF(GREATEST_STDOUT, FMT, EXP); \ 512 | GREATEST_FPRINTF(GREATEST_STDOUT, "\n Got: "); \ 513 | GREATEST_FPRINTF(GREATEST_STDOUT, FMT, GOT); \ 514 | GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ 515 | GREATEST_FAILm(MSG); \ 516 | } \ 517 | } while (0) 518 | 519 | /* Fail if EXP is not equal to GOT, printing enum IDs. */ 520 | #define GREATEST_ASSERT_ENUM_EQm(MSG, EXP, GOT, ENUM_STR) \ 521 | do { \ 522 | int greatest_EXP = (int)(EXP); \ 523 | int greatest_GOT = (int)(GOT); \ 524 | greatest_enum_str_fun *greatest_ENUM_STR = ENUM_STR; \ 525 | if (greatest_EXP != greatest_GOT) { \ 526 | GREATEST_FPRINTF(GREATEST_STDOUT, "\nExpected: %s", \ 527 | greatest_ENUM_STR(greatest_EXP)); \ 528 | GREATEST_FPRINTF(GREATEST_STDOUT, "\n Got: %s\n", \ 529 | greatest_ENUM_STR(greatest_GOT)); \ 530 | GREATEST_FAILm(MSG); \ 531 | } \ 532 | } while (0) \ 533 | 534 | /* Fail if GOT not in range of EXP +|- TOL. */ 535 | #define GREATEST_ASSERT_IN_RANGEm(MSG, EXP, GOT, TOL) \ 536 | do { \ 537 | GREATEST_FLOAT greatest_EXP = (EXP); \ 538 | GREATEST_FLOAT greatest_GOT = (GOT); \ 539 | GREATEST_FLOAT greatest_TOL = (TOL); \ 540 | greatest_info.assertions++; \ 541 | if ((greatest_EXP > greatest_GOT && \ 542 | greatest_EXP - greatest_GOT > greatest_TOL) || \ 543 | (greatest_EXP < greatest_GOT && \ 544 | greatest_GOT - greatest_EXP > greatest_TOL)) { \ 545 | GREATEST_FPRINTF(GREATEST_STDOUT, \ 546 | "\nExpected: " GREATEST_FLOAT_FMT \ 547 | " +/- " GREATEST_FLOAT_FMT \ 548 | "\n Got: " GREATEST_FLOAT_FMT \ 549 | "\n", \ 550 | greatest_EXP, greatest_TOL, greatest_GOT); \ 551 | GREATEST_FAILm(MSG); \ 552 | } \ 553 | } while (0) 554 | 555 | /* Fail if EXP is not equal to GOT, according to strcmp. */ 556 | #define GREATEST_ASSERT_STR_EQm(MSG, EXP, GOT) \ 557 | do { \ 558 | GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, \ 559 | &greatest_type_info_string, NULL); \ 560 | } while (0) \ 561 | 562 | /* Fail if EXP is not equal to GOT, according to strncmp. */ 563 | #define GREATEST_ASSERT_STRN_EQm(MSG, EXP, GOT, SIZE) \ 564 | do { \ 565 | size_t size = SIZE; \ 566 | GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, \ 567 | &greatest_type_info_string, &size); \ 568 | } while (0) \ 569 | 570 | /* Fail if EXP is not equal to GOT, according to memcmp. */ 571 | #define GREATEST_ASSERT_MEM_EQm(MSG, EXP, GOT, SIZE) \ 572 | do { \ 573 | greatest_memory_cmp_env env; \ 574 | env.exp = (const unsigned char *)EXP; \ 575 | env.got = (const unsigned char *)GOT; \ 576 | env.size = SIZE; \ 577 | GREATEST_ASSERT_EQUAL_Tm(MSG, env.exp, env.got, \ 578 | &greatest_type_info_memory, &env); \ 579 | } while (0) \ 580 | 581 | /* Fail if EXP is not equal to GOT, according to a comparison 582 | * callback in TYPE_INFO. If they are not equal, optionally use a 583 | * print callback in TYPE_INFO to print them. */ 584 | #define GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, TYPE_INFO, UDATA) \ 585 | do { \ 586 | greatest_type_info *type_info = (TYPE_INFO); \ 587 | greatest_info.assertions++; \ 588 | if (!greatest_do_assert_equal_t(EXP, GOT, \ 589 | type_info, UDATA)) { \ 590 | if (type_info == NULL || type_info->equal == NULL) { \ 591 | GREATEST_FAILm("type_info->equal callback missing!"); \ 592 | } else { \ 593 | GREATEST_FAILm(MSG); \ 594 | } \ 595 | } \ 596 | } while (0) \ 597 | 598 | /* Pass. */ 599 | #define GREATEST_PASSm(MSG) \ 600 | do { \ 601 | greatest_info.msg = MSG; \ 602 | return GREATEST_TEST_RES_PASS; \ 603 | } while (0) 604 | 605 | /* Fail. */ 606 | #define GREATEST_FAILm(MSG) \ 607 | do { \ 608 | greatest_info.fail_file = __FILE__; \ 609 | greatest_info.fail_line = __LINE__; \ 610 | greatest_info.msg = MSG; \ 611 | if (GREATEST_ABORT_ON_FAIL()) { abort(); } \ 612 | return GREATEST_TEST_RES_FAIL; \ 613 | } while (0) 614 | 615 | /* Optional GREATEST_FAILm variant that longjmps. */ 616 | #if GREATEST_USE_LONGJMP 617 | #define GREATEST_FAIL_WITH_LONGJMP() GREATEST_FAIL_WITH_LONGJMPm(NULL) 618 | #define GREATEST_FAIL_WITH_LONGJMPm(MSG) \ 619 | do { \ 620 | greatest_info.fail_file = __FILE__; \ 621 | greatest_info.fail_line = __LINE__; \ 622 | greatest_info.msg = MSG; \ 623 | longjmp(greatest_info.jump_dest, GREATEST_TEST_RES_FAIL); \ 624 | } while (0) 625 | #endif 626 | 627 | /* Skip the current test. */ 628 | #define GREATEST_SKIPm(MSG) \ 629 | do { \ 630 | greatest_info.msg = MSG; \ 631 | return GREATEST_TEST_RES_SKIP; \ 632 | } while (0) 633 | 634 | /* Check the result of a subfunction using ASSERT, etc. */ 635 | #define GREATEST_CHECK_CALL(RES) \ 636 | do { \ 637 | enum greatest_test_res greatest_RES = RES; \ 638 | if (greatest_RES != GREATEST_TEST_RES_PASS) { \ 639 | return greatest_RES; \ 640 | } \ 641 | } while (0) \ 642 | 643 | #if GREATEST_USE_TIME 644 | #define GREATEST_SET_TIME(NAME) \ 645 | NAME = clock(); \ 646 | if (NAME == (clock_t) -1) { \ 647 | GREATEST_FPRINTF(GREATEST_STDOUT, \ 648 | "clock error: %s\n", #NAME); \ 649 | exit(EXIT_FAILURE); \ 650 | } 651 | 652 | #define GREATEST_CLOCK_DIFF(C1, C2) \ 653 | GREATEST_FPRINTF(GREATEST_STDOUT, " (%lu ticks, %.3f sec)", \ 654 | (long unsigned int) (C2) - (long unsigned int)(C1), \ 655 | (double)((C2) - (C1)) / (1.0 * (double)CLOCKS_PER_SEC)) 656 | #else 657 | #define GREATEST_SET_TIME(UNUSED) 658 | #define GREATEST_CLOCK_DIFF(UNUSED1, UNUSED2) 659 | #endif 660 | 661 | #if GREATEST_USE_LONGJMP 662 | #define GREATEST_SAVE_CONTEXT() \ 663 | /* setjmp returns 0 (GREATEST_TEST_RES_PASS) on first call * \ 664 | * so the test runs, then RES_FAIL from FAIL_WITH_LONGJMP. */ \ 665 | ((enum greatest_test_res)(setjmp(greatest_info.jump_dest))) 666 | #else 667 | #define GREATEST_SAVE_CONTEXT() \ 668 | /*a no-op, since setjmp/longjmp aren't being used */ \ 669 | GREATEST_TEST_RES_PASS 670 | #endif 671 | 672 | /* Run every suite / test function run within BODY in pseudo-random 673 | * order, seeded by SEED. (The top 3 bits of the seed are ignored.) 674 | * 675 | * This should be called like: 676 | * GREATEST_SHUFFLE_TESTS(seed, { 677 | * GREATEST_RUN_TEST(some_test); 678 | * GREATEST_RUN_TEST(some_other_test); 679 | * GREATEST_RUN_TEST(yet_another_test); 680 | * }); 681 | * 682 | * Note that the body of the second argument will be evaluated 683 | * multiple times. */ 684 | #define GREATEST_SHUFFLE_SUITES(SD, BODY) GREATEST_SHUFFLE(0, SD, BODY) 685 | #define GREATEST_SHUFFLE_TESTS(SD, BODY) GREATEST_SHUFFLE(1, SD, BODY) 686 | #define GREATEST_SHUFFLE(ID, SD, BODY) \ 687 | do { \ 688 | struct greatest_prng *prng = &greatest_info.prng[ID]; \ 689 | greatest_prng_init_first_pass(ID); \ 690 | do { \ 691 | prng->count = 0; \ 692 | if (prng->initialized) { greatest_prng_step(ID); } \ 693 | BODY; \ 694 | if (!prng->initialized) { \ 695 | if (!greatest_prng_init_second_pass(ID, SD)) { break; } \ 696 | } else if (prng->count_run == prng->count_ceil) { \ 697 | break; \ 698 | } \ 699 | } while (!GREATEST_FAILURE_ABORT()); \ 700 | prng->count_run = prng->random_order = prng->initialized = 0; \ 701 | } while(0) 702 | 703 | /* Include several function definitions in the main test file. */ 704 | #define GREATEST_MAIN_DEFS() \ 705 | \ 706 | /* Is FILTER a subset of NAME? */ \ 707 | static int greatest_name_match(const char *name, const char *filter, \ 708 | int res_if_none) { \ 709 | size_t offset = 0; \ 710 | size_t filter_len = filter ? strlen(filter) : 0; \ 711 | if (filter_len == 0) { return res_if_none; } /* no filter */ \ 712 | if (greatest_info.exact_name_match && strlen(name) != filter_len) { \ 713 | return 0; /* ignore substring matches */ \ 714 | } \ 715 | while (name[offset] != '\0') { \ 716 | if (name[offset] == filter[0]) { \ 717 | if (0 == strncmp(&name[offset], filter, filter_len)) { \ 718 | return 1; \ 719 | } \ 720 | } \ 721 | offset++; \ 722 | } \ 723 | \ 724 | return 0; \ 725 | } \ 726 | \ 727 | static void greatest_buffer_test_name(const char *name) { \ 728 | struct greatest_run_info *g = &greatest_info; \ 729 | size_t len = strlen(name), size = sizeof(g->name_buf); \ 730 | memset(g->name_buf, 0x00, size); \ 731 | (void)strncat(g->name_buf, name, size - 1); \ 732 | if (g->name_suffix && (len + 1 < size)) { \ 733 | g->name_buf[len] = '_'; \ 734 | strncat(&g->name_buf[len+1], g->name_suffix, size-(len+2)); \ 735 | } \ 736 | } \ 737 | \ 738 | /* Before running a test, check the name filtering and \ 739 | * test shuffling state, if applicable, and then call setup hooks. */ \ 740 | int greatest_test_pre(const char *name) { \ 741 | struct greatest_run_info *g = &greatest_info; \ 742 | int match; \ 743 | greatest_buffer_test_name(name); \ 744 | match = greatest_name_match(g->name_buf, g->test_filter, 1) && \ 745 | !greatest_name_match(g->name_buf, g->test_exclude, 0); \ 746 | if (GREATEST_LIST_ONLY()) { /* just listing test names */ \ 747 | if (match) { \ 748 | GREATEST_FPRINTF(GREATEST_STDOUT, " %s\n", g->name_buf); \ 749 | } \ 750 | goto clear; \ 751 | } \ 752 | if (match && (!GREATEST_FIRST_FAIL() || g->suite.failed == 0)) { \ 753 | struct greatest_prng *p = &g->prng[1]; \ 754 | if (p->random_order) { \ 755 | p->count++; \ 756 | if (!p->initialized || ((p->count - 1) != p->state)) { \ 757 | goto clear; /* don't run this test yet */ \ 758 | } \ 759 | } \ 760 | if (g->running_test) { \ 761 | fprintf(stderr, "Error: Test run inside another test.\n"); \ 762 | return 0; \ 763 | } \ 764 | GREATEST_SET_TIME(g->suite.pre_test); \ 765 | if (g->setup) { g->setup(g->setup_udata); } \ 766 | p->count_run++; \ 767 | g->running_test = 1; \ 768 | return 1; /* test should be run */ \ 769 | } else { \ 770 | goto clear; /* skipped */ \ 771 | } \ 772 | clear: \ 773 | g->name_suffix = NULL; \ 774 | return 0; \ 775 | } \ 776 | \ 777 | static void greatest_do_pass(void) { \ 778 | struct greatest_run_info *g = &greatest_info; \ 779 | if (GREATEST_IS_VERBOSE()) { \ 780 | GREATEST_FPRINTF(GREATEST_STDOUT, "PASS %s: %s", \ 781 | g->name_buf, g->msg ? g->msg : ""); \ 782 | } else { \ 783 | GREATEST_FPRINTF(GREATEST_STDOUT, "."); \ 784 | } \ 785 | g->suite.passed++; \ 786 | } \ 787 | \ 788 | static void greatest_do_fail(void) { \ 789 | struct greatest_run_info *g = &greatest_info; \ 790 | if (GREATEST_IS_VERBOSE()) { \ 791 | GREATEST_FPRINTF(GREATEST_STDOUT, \ 792 | "FAIL %s: %s (%s:%u)", g->name_buf, \ 793 | g->msg ? g->msg : "", g->fail_file, g->fail_line); \ 794 | } else { \ 795 | GREATEST_FPRINTF(GREATEST_STDOUT, "F"); \ 796 | g->col++; /* add linebreak if in line of '.'s */ \ 797 | if (g->col != 0) { \ 798 | GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ 799 | g->col = 0; \ 800 | } \ 801 | GREATEST_FPRINTF(GREATEST_STDOUT, "FAIL %s: %s (%s:%u)\n", \ 802 | g->name_buf, g->msg ? g->msg : "", \ 803 | g->fail_file, g->fail_line); \ 804 | } \ 805 | g->suite.failed++; \ 806 | } \ 807 | \ 808 | static void greatest_do_skip(void) { \ 809 | struct greatest_run_info *g = &greatest_info; \ 810 | if (GREATEST_IS_VERBOSE()) { \ 811 | GREATEST_FPRINTF(GREATEST_STDOUT, "SKIP %s: %s", \ 812 | g->name_buf, g->msg ? g->msg : ""); \ 813 | } else { \ 814 | GREATEST_FPRINTF(GREATEST_STDOUT, "s"); \ 815 | } \ 816 | g->suite.skipped++; \ 817 | } \ 818 | \ 819 | void greatest_test_post(int res) { \ 820 | GREATEST_SET_TIME(greatest_info.suite.post_test); \ 821 | if (greatest_info.teardown) { \ 822 | void *udata = greatest_info.teardown_udata; \ 823 | greatest_info.teardown(udata); \ 824 | } \ 825 | \ 826 | greatest_info.running_test = 0; \ 827 | if (res <= GREATEST_TEST_RES_FAIL) { \ 828 | greatest_do_fail(); \ 829 | } else if (res >= GREATEST_TEST_RES_SKIP) { \ 830 | greatest_do_skip(); \ 831 | } else if (res == GREATEST_TEST_RES_PASS) { \ 832 | greatest_do_pass(); \ 833 | } \ 834 | greatest_info.name_suffix = NULL; \ 835 | greatest_info.suite.tests_run++; \ 836 | greatest_info.col++; \ 837 | if (GREATEST_IS_VERBOSE()) { \ 838 | GREATEST_CLOCK_DIFF(greatest_info.suite.pre_test, \ 839 | greatest_info.suite.post_test); \ 840 | GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ 841 | } else if (greatest_info.col % greatest_info.width == 0) { \ 842 | GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ 843 | greatest_info.col = 0; \ 844 | } \ 845 | fflush(GREATEST_STDOUT); \ 846 | } \ 847 | \ 848 | static void report_suite(void) { \ 849 | if (greatest_info.suite.tests_run > 0) { \ 850 | GREATEST_FPRINTF(GREATEST_STDOUT, \ 851 | "\n%u test%s - %u passed, %u failed, %u skipped", \ 852 | greatest_info.suite.tests_run, \ 853 | greatest_info.suite.tests_run == 1 ? "" : "s", \ 854 | greatest_info.suite.passed, \ 855 | greatest_info.suite.failed, \ 856 | greatest_info.suite.skipped); \ 857 | GREATEST_CLOCK_DIFF(greatest_info.suite.pre_suite, \ 858 | greatest_info.suite.post_suite); \ 859 | GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ 860 | } \ 861 | } \ 862 | \ 863 | static void update_counts_and_reset_suite(void) { \ 864 | greatest_info.setup = NULL; \ 865 | greatest_info.setup_udata = NULL; \ 866 | greatest_info.teardown = NULL; \ 867 | greatest_info.teardown_udata = NULL; \ 868 | greatest_info.passed += greatest_info.suite.passed; \ 869 | greatest_info.failed += greatest_info.suite.failed; \ 870 | greatest_info.skipped += greatest_info.suite.skipped; \ 871 | greatest_info.tests_run += greatest_info.suite.tests_run; \ 872 | memset(&greatest_info.suite, 0, sizeof(greatest_info.suite)); \ 873 | greatest_info.col = 0; \ 874 | } \ 875 | \ 876 | static int greatest_suite_pre(const char *suite_name) { \ 877 | struct greatest_prng *p = &greatest_info.prng[0]; \ 878 | if (!greatest_name_match(suite_name, greatest_info.suite_filter, 1) \ 879 | || (GREATEST_FAILURE_ABORT())) { return 0; } \ 880 | if (p->random_order) { \ 881 | p->count++; \ 882 | if (!p->initialized || ((p->count - 1) != p->state)) { \ 883 | return 0; /* don't run this suite yet */ \ 884 | } \ 885 | } \ 886 | p->count_run++; \ 887 | update_counts_and_reset_suite(); \ 888 | GREATEST_FPRINTF(GREATEST_STDOUT, "\n* Suite %s:\n", suite_name); \ 889 | GREATEST_SET_TIME(greatest_info.suite.pre_suite); \ 890 | return 1; \ 891 | } \ 892 | \ 893 | static void greatest_suite_post(void) { \ 894 | GREATEST_SET_TIME(greatest_info.suite.post_suite); \ 895 | report_suite(); \ 896 | } \ 897 | \ 898 | static void greatest_run_suite(greatest_suite_cb *suite_cb, \ 899 | const char *suite_name) { \ 900 | if (greatest_suite_pre(suite_name)) { \ 901 | suite_cb(); \ 902 | greatest_suite_post(); \ 903 | } \ 904 | } \ 905 | \ 906 | int greatest_do_assert_equal_t(const void *expd, const void *got, \ 907 | greatest_type_info *type_info, void *udata) { \ 908 | int eq = 0; \ 909 | if (type_info == NULL || type_info->equal == NULL) { return 0; } \ 910 | eq = type_info->equal(expd, got, udata); \ 911 | if (!eq) { \ 912 | if (type_info->print != NULL) { \ 913 | GREATEST_FPRINTF(GREATEST_STDOUT, "\nExpected: "); \ 914 | (void)type_info->print(expd, udata); \ 915 | GREATEST_FPRINTF(GREATEST_STDOUT, "\n Got: "); \ 916 | (void)type_info->print(got, udata); \ 917 | GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ 918 | } \ 919 | } \ 920 | return eq; \ 921 | } \ 922 | \ 923 | static void greatest_usage(const char *name) { \ 924 | GREATEST_FPRINTF(GREATEST_STDOUT, \ 925 | "Usage: %s [-hlfavex] [-s SUITE] [-t TEST] [-x EXCLUDE]\n" \ 926 | " -h, --help print this Help\n" \ 927 | " -l List suites and tests, then exit (dry run)\n" \ 928 | " -f Stop runner after first failure\n" \ 929 | " -a Abort on first failure (implies -f)\n" \ 930 | " -v Verbose output\n" \ 931 | " -s SUITE only run suites containing substring SUITE\n" \ 932 | " -t TEST only run tests containing substring TEST\n" \ 933 | " -e only run exact name match for -s or -t\n" \ 934 | " -x EXCLUDE exclude tests containing substring EXCLUDE\n", \ 935 | name); \ 936 | } \ 937 | \ 938 | static void greatest_parse_options(int argc, char **argv) { \ 939 | int i = 0; \ 940 | for (i = 1; i < argc; i++) { \ 941 | if (argv[i][0] == '-') { \ 942 | char f = argv[i][1]; \ 943 | if ((f == 's' || f == 't' || f == 'x') && argc <= i + 1) { \ 944 | greatest_usage(argv[0]); exit(EXIT_FAILURE); \ 945 | } \ 946 | switch (f) { \ 947 | case 's': /* suite name filter */ \ 948 | greatest_set_suite_filter(argv[i + 1]); i++; break; \ 949 | case 't': /* test name filter */ \ 950 | greatest_set_test_filter(argv[i + 1]); i++; break; \ 951 | case 'x': /* test name exclusion */ \ 952 | greatest_set_test_exclude(argv[i + 1]); i++; break; \ 953 | case 'e': /* exact name match */ \ 954 | greatest_set_exact_name_match(); break; \ 955 | case 'f': /* first fail flag */ \ 956 | greatest_stop_at_first_fail(); break; \ 957 | case 'a': /* abort() on fail flag */ \ 958 | greatest_abort_on_fail(); break; \ 959 | case 'l': /* list only (dry run) */ \ 960 | greatest_list_only(); break; \ 961 | case 'v': /* first fail flag */ \ 962 | greatest_info.verbosity++; break; \ 963 | case 'h': /* help */ \ 964 | greatest_usage(argv[0]); exit(EXIT_SUCCESS); \ 965 | default: \ 966 | case '-': \ 967 | if (0 == strncmp("--help", argv[i], 6)) { \ 968 | greatest_usage(argv[0]); exit(EXIT_SUCCESS); \ 969 | } else if (0 == strcmp("--", argv[i])) { \ 970 | return; /* ignore following arguments */ \ 971 | } \ 972 | GREATEST_FPRINTF(GREATEST_STDOUT, \ 973 | "Unknown argument '%s'\n", argv[i]); \ 974 | greatest_usage(argv[0]); \ 975 | exit(EXIT_FAILURE); \ 976 | } \ 977 | } \ 978 | } \ 979 | } \ 980 | \ 981 | int greatest_all_passed(void) { return (greatest_info.failed == 0); } \ 982 | \ 983 | void greatest_set_test_filter(const char *filter) { \ 984 | greatest_info.test_filter = filter; \ 985 | } \ 986 | \ 987 | void greatest_set_test_exclude(const char *filter) { \ 988 | greatest_info.test_exclude = filter; \ 989 | } \ 990 | \ 991 | void greatest_set_suite_filter(const char *filter) { \ 992 | greatest_info.suite_filter = filter; \ 993 | } \ 994 | \ 995 | void greatest_set_exact_name_match(void) { \ 996 | greatest_info.exact_name_match = 1; \ 997 | } \ 998 | \ 999 | void greatest_stop_at_first_fail(void) { \ 1000 | greatest_set_flag(GREATEST_FLAG_FIRST_FAIL); \ 1001 | } \ 1002 | \ 1003 | void greatest_abort_on_fail(void) { \ 1004 | greatest_set_flag(GREATEST_FLAG_ABORT_ON_FAIL); \ 1005 | } \ 1006 | \ 1007 | void greatest_list_only(void) { \ 1008 | greatest_set_flag(GREATEST_FLAG_LIST_ONLY); \ 1009 | } \ 1010 | \ 1011 | void greatest_get_report(struct greatest_report_t *report) { \ 1012 | if (report) { \ 1013 | report->passed = greatest_info.passed; \ 1014 | report->failed = greatest_info.failed; \ 1015 | report->skipped = greatest_info.skipped; \ 1016 | report->assertions = greatest_info.assertions; \ 1017 | } \ 1018 | } \ 1019 | \ 1020 | unsigned int greatest_get_verbosity(void) { \ 1021 | return greatest_info.verbosity; \ 1022 | } \ 1023 | \ 1024 | void greatest_set_verbosity(unsigned int verbosity) { \ 1025 | greatest_info.verbosity = (unsigned char)verbosity; \ 1026 | } \ 1027 | \ 1028 | void greatest_set_flag(greatest_flag_t flag) { \ 1029 | greatest_info.flags = (unsigned char)(greatest_info.flags | flag); \ 1030 | } \ 1031 | \ 1032 | void greatest_set_test_suffix(const char *suffix) { \ 1033 | greatest_info.name_suffix = suffix; \ 1034 | } \ 1035 | \ 1036 | void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata) { \ 1037 | greatest_info.setup = cb; \ 1038 | greatest_info.setup_udata = udata; \ 1039 | } \ 1040 | \ 1041 | void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, void *udata) { \ 1042 | greatest_info.teardown = cb; \ 1043 | greatest_info.teardown_udata = udata; \ 1044 | } \ 1045 | \ 1046 | static int greatest_string_equal_cb(const void *expd, const void *got, \ 1047 | void *udata) { \ 1048 | size_t *size = (size_t *)udata; \ 1049 | return (size != NULL \ 1050 | ? (0 == strncmp((const char *)expd, (const char *)got, *size)) \ 1051 | : (0 == strcmp((const char *)expd, (const char *)got))); \ 1052 | } \ 1053 | \ 1054 | static int greatest_string_printf_cb(const void *t, void *udata) { \ 1055 | (void)udata; /* note: does not check \0 termination. */ \ 1056 | return GREATEST_FPRINTF(GREATEST_STDOUT, "%s", (const char *)t); \ 1057 | } \ 1058 | \ 1059 | greatest_type_info greatest_type_info_string = { \ 1060 | greatest_string_equal_cb, greatest_string_printf_cb, \ 1061 | }; \ 1062 | \ 1063 | static int greatest_memory_equal_cb(const void *expd, const void *got, \ 1064 | void *udata) { \ 1065 | greatest_memory_cmp_env *env = (greatest_memory_cmp_env *)udata; \ 1066 | return (0 == memcmp(expd, got, env->size)); \ 1067 | } \ 1068 | \ 1069 | /* Hexdump raw memory, with differences highlighted */ \ 1070 | static int greatest_memory_printf_cb(const void *t, void *udata) { \ 1071 | greatest_memory_cmp_env *env = (greatest_memory_cmp_env *)udata; \ 1072 | const unsigned char *buf = (const unsigned char *)t; \ 1073 | unsigned char diff_mark = ' '; \ 1074 | FILE *out = GREATEST_STDOUT; \ 1075 | size_t i, line_i, line_len = 0; \ 1076 | int len = 0; /* format hexdump with differences highlighted */ \ 1077 | for (i = 0; i < env->size; i+= line_len) { \ 1078 | diff_mark = ' '; \ 1079 | line_len = env->size - i; \ 1080 | if (line_len > 16) { line_len = 16; } \ 1081 | for (line_i = i; line_i < i + line_len; line_i++) { \ 1082 | if (env->exp[line_i] != env->got[line_i]) diff_mark = 'X'; \ 1083 | } \ 1084 | len += GREATEST_FPRINTF(out, "\n%04x %c ", \ 1085 | (unsigned int)i, diff_mark); \ 1086 | for (line_i = i; line_i < i + line_len; line_i++) { \ 1087 | int m = env->exp[line_i] == env->got[line_i]; /* match? */ \ 1088 | len += GREATEST_FPRINTF(out, "%02x%c", \ 1089 | buf[line_i], m ? ' ' : '<'); \ 1090 | } \ 1091 | for (line_i = 0; line_i < 16 - line_len; line_i++) { \ 1092 | len += GREATEST_FPRINTF(out, " "); \ 1093 | } \ 1094 | GREATEST_FPRINTF(out, " "); \ 1095 | for (line_i = i; line_i < i + line_len; line_i++) { \ 1096 | unsigned char c = buf[line_i]; \ 1097 | len += GREATEST_FPRINTF(out, "%c", isprint(c) ? c : '.'); \ 1098 | } \ 1099 | } \ 1100 | len += GREATEST_FPRINTF(out, "\n"); \ 1101 | return len; \ 1102 | } \ 1103 | \ 1104 | void greatest_prng_init_first_pass(int id) { \ 1105 | greatest_info.prng[id].random_order = 1; \ 1106 | greatest_info.prng[id].count_run = 0; \ 1107 | } \ 1108 | \ 1109 | int greatest_prng_init_second_pass(int id, unsigned long seed) { \ 1110 | struct greatest_prng *p = &greatest_info.prng[id]; \ 1111 | if (p->count == 0) { return 0; } \ 1112 | p->count_ceil = p->count; \ 1113 | for (p->m = 1; p->m < p->count; p->m <<= 1) {} \ 1114 | p->state = seed & 0x1fffffff; /* only use lower 29 bits */ \ 1115 | p->a = 4LU * p->state; /* to avoid overflow when */ \ 1116 | p->a = (p->a ? p->a : 4) | 1; /* multiplied by 4 */ \ 1117 | p->c = 2147483647; /* and so p->c ((2 ** 31) - 1) is */ \ 1118 | p->initialized = 1; /* always relatively prime to p->a. */ \ 1119 | fprintf(stderr, "init_second_pass: a %lu, c %lu, state %lu\n", \ 1120 | p->a, p->c, p->state); \ 1121 | return 1; \ 1122 | } \ 1123 | \ 1124 | /* Step the pseudorandom number generator until its state reaches \ 1125 | * another test ID between 0 and the test count. \ 1126 | * This use a linear congruential pseudorandom number generator, \ 1127 | * with the power-of-two ceiling of the test count as the modulus, the \ 1128 | * masked seed as the multiplier, and a prime as the increment. For \ 1129 | * each generated value < the test count, run the corresponding test. \ 1130 | * This will visit all IDs 0 <= X < mod once before repeating, \ 1131 | * with a starting position chosen based on the initial seed. \ 1132 | * For details, see: Knuth, The Art of Computer Programming \ 1133 | * Volume. 2, section 3.2.1. */ \ 1134 | void greatest_prng_step(int id) { \ 1135 | struct greatest_prng *p = &greatest_info.prng[id]; \ 1136 | do { \ 1137 | p->state = ((p->a * p->state) + p->c) & (p->m - 1); \ 1138 | } while (p->state >= p->count_ceil); \ 1139 | } \ 1140 | \ 1141 | void GREATEST_INIT(void) { \ 1142 | /* Suppress unused function warning if features aren't used */ \ 1143 | (void)greatest_run_suite; \ 1144 | (void)greatest_parse_options; \ 1145 | (void)greatest_prng_step; \ 1146 | (void)greatest_prng_init_first_pass; \ 1147 | (void)greatest_prng_init_second_pass; \ 1148 | (void)greatest_set_test_suffix; \ 1149 | \ 1150 | memset(&greatest_info, 0, sizeof(greatest_info)); \ 1151 | greatest_info.width = GREATEST_DEFAULT_WIDTH; \ 1152 | GREATEST_SET_TIME(greatest_info.begin); \ 1153 | } \ 1154 | \ 1155 | /* Report passes, failures, skipped tests, the number of \ 1156 | * assertions, and the overall run time. */ \ 1157 | void GREATEST_PRINT_REPORT(void) { \ 1158 | if (!GREATEST_LIST_ONLY()) { \ 1159 | update_counts_and_reset_suite(); \ 1160 | GREATEST_SET_TIME(greatest_info.end); \ 1161 | GREATEST_FPRINTF(GREATEST_STDOUT, \ 1162 | "\nTotal: %u test%s", \ 1163 | greatest_info.tests_run, \ 1164 | greatest_info.tests_run == 1 ? "" : "s"); \ 1165 | GREATEST_CLOCK_DIFF(greatest_info.begin, \ 1166 | greatest_info.end); \ 1167 | GREATEST_FPRINTF(GREATEST_STDOUT, ", %u assertion%s\n", \ 1168 | greatest_info.assertions, \ 1169 | greatest_info.assertions == 1 ? "" : "s"); \ 1170 | GREATEST_FPRINTF(GREATEST_STDOUT, \ 1171 | "Pass: %u, fail: %u, skip: %u.\n", \ 1172 | greatest_info.passed, \ 1173 | greatest_info.failed, greatest_info.skipped); \ 1174 | } \ 1175 | } \ 1176 | \ 1177 | greatest_type_info greatest_type_info_memory = { \ 1178 | greatest_memory_equal_cb, greatest_memory_printf_cb, \ 1179 | }; \ 1180 | \ 1181 | greatest_run_info greatest_info 1182 | 1183 | /* Handle command-line arguments, etc. */ 1184 | #define GREATEST_MAIN_BEGIN() \ 1185 | do { \ 1186 | GREATEST_INIT(); \ 1187 | greatest_parse_options(argc, argv); \ 1188 | } while (0) 1189 | 1190 | /* Report results, exit with exit status based on results. */ 1191 | #define GREATEST_MAIN_END() \ 1192 | do { \ 1193 | GREATEST_PRINT_REPORT(); \ 1194 | return (greatest_all_passed() ? EXIT_SUCCESS : EXIT_FAILURE); \ 1195 | } while (0) 1196 | 1197 | /* Make abbreviations without the GREATEST_ prefix for the 1198 | * most commonly used symbols. */ 1199 | #if GREATEST_USE_ABBREVS 1200 | #define TEST GREATEST_TEST 1201 | #define SUITE GREATEST_SUITE 1202 | #define SUITE_EXTERN GREATEST_SUITE_EXTERN 1203 | #define RUN_TEST GREATEST_RUN_TEST 1204 | #define RUN_TEST1 GREATEST_RUN_TEST1 1205 | #define RUN_SUITE GREATEST_RUN_SUITE 1206 | #define IGNORE_TEST GREATEST_IGNORE_TEST 1207 | #define ASSERT GREATEST_ASSERT 1208 | #define ASSERTm GREATEST_ASSERTm 1209 | #define ASSERT_FALSE GREATEST_ASSERT_FALSE 1210 | #define ASSERT_EQ GREATEST_ASSERT_EQ 1211 | #define ASSERT_NEQ GREATEST_ASSERT_NEQ 1212 | #define ASSERT_GT GREATEST_ASSERT_GT 1213 | #define ASSERT_GTE GREATEST_ASSERT_GTE 1214 | #define ASSERT_LT GREATEST_ASSERT_LT 1215 | #define ASSERT_LTE GREATEST_ASSERT_LTE 1216 | #define ASSERT_EQ_FMT GREATEST_ASSERT_EQ_FMT 1217 | #define ASSERT_IN_RANGE GREATEST_ASSERT_IN_RANGE 1218 | #define ASSERT_EQUAL_T GREATEST_ASSERT_EQUAL_T 1219 | #define ASSERT_STR_EQ GREATEST_ASSERT_STR_EQ 1220 | #define ASSERT_STRN_EQ GREATEST_ASSERT_STRN_EQ 1221 | #define ASSERT_MEM_EQ GREATEST_ASSERT_MEM_EQ 1222 | #define ASSERT_ENUM_EQ GREATEST_ASSERT_ENUM_EQ 1223 | #define ASSERT_FALSEm GREATEST_ASSERT_FALSEm 1224 | #define ASSERT_EQm GREATEST_ASSERT_EQm 1225 | #define ASSERT_NEQm GREATEST_ASSERT_NEQm 1226 | #define ASSERT_GTm GREATEST_ASSERT_GTm 1227 | #define ASSERT_GTEm GREATEST_ASSERT_GTEm 1228 | #define ASSERT_LTm GREATEST_ASSERT_LTm 1229 | #define ASSERT_LTEm GREATEST_ASSERT_LTEm 1230 | #define ASSERT_EQ_FMTm GREATEST_ASSERT_EQ_FMTm 1231 | #define ASSERT_IN_RANGEm GREATEST_ASSERT_IN_RANGEm 1232 | #define ASSERT_EQUAL_Tm GREATEST_ASSERT_EQUAL_Tm 1233 | #define ASSERT_STR_EQm GREATEST_ASSERT_STR_EQm 1234 | #define ASSERT_STRN_EQm GREATEST_ASSERT_STRN_EQm 1235 | #define ASSERT_MEM_EQm GREATEST_ASSERT_MEM_EQm 1236 | #define ASSERT_ENUM_EQm GREATEST_ASSERT_ENUM_EQm 1237 | #define PASS GREATEST_PASS 1238 | #define FAIL GREATEST_FAIL 1239 | #define SKIP GREATEST_SKIP 1240 | #define PASSm GREATEST_PASSm 1241 | #define FAILm GREATEST_FAILm 1242 | #define SKIPm GREATEST_SKIPm 1243 | #define SET_SETUP GREATEST_SET_SETUP_CB 1244 | #define SET_TEARDOWN GREATEST_SET_TEARDOWN_CB 1245 | #define CHECK_CALL GREATEST_CHECK_CALL 1246 | #define SHUFFLE_TESTS GREATEST_SHUFFLE_TESTS 1247 | #define SHUFFLE_SUITES GREATEST_SHUFFLE_SUITES 1248 | 1249 | #ifdef GREATEST_VA_ARGS 1250 | #define RUN_TESTp GREATEST_RUN_TESTp 1251 | #endif 1252 | 1253 | #if GREATEST_USE_LONGJMP 1254 | #define ASSERT_OR_LONGJMP GREATEST_ASSERT_OR_LONGJMP 1255 | #define ASSERT_OR_LONGJMPm GREATEST_ASSERT_OR_LONGJMPm 1256 | #define FAIL_WITH_LONGJMP GREATEST_FAIL_WITH_LONGJMP 1257 | #define FAIL_WITH_LONGJMPm GREATEST_FAIL_WITH_LONGJMPm 1258 | #endif 1259 | 1260 | #endif /* USE_ABBREVS */ 1261 | 1262 | #if defined(__cplusplus) && !defined(GREATEST_NO_EXTERN_CPLUSPLUS) 1263 | } 1264 | #endif 1265 | 1266 | #endif 1267 | --------------------------------------------------------------------------------