├── .gitattributes ├── doc ├── cstring_API.pdf └── cstring_array_API.pdf ├── .editorconfig ├── .gitignore ├── LICENSE ├── .clang-format ├── README.md ├── cstring-unit-tests.c └── utest └── utest.h /.gitattributes: -------------------------------------------------------------------------------- 1 | *.h linguist-language=C 2 | *.sh text eol=lf 3 | -------------------------------------------------------------------------------- /doc/cstring_API.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/german-one/c-string/HEAD/doc/cstring_API.pdf -------------------------------------------------------------------------------- /doc/cstring_array_API.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/german-one/c-string/HEAD/doc/cstring_array_API.pdf -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | 11 | 12 | # Matches multiple files with brace expansion notation 13 | # Set default charset 14 | [*.{c,h}] 15 | charset = utf-8 16 | indent_style = space 17 | indent_size = 4 18 | trim_trailing_whitespace = true 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | *.bat 31 | 32 | # Debug files 33 | *.dSYM/ 34 | 35 | # toptal/gitignore/templates/CMake.gitignore 36 | 37 | CMakeLists.txt.user 38 | CMakeCache.txt 39 | CMakeFiles 40 | CMakeScripts 41 | Testing 42 | Makefile 43 | cmake_install.cmake 44 | install_manifest.txt 45 | compile_commands.json 46 | CTestTestfile.cmake 47 | _deps 48 | 49 | bin 50 | bin_ 51 | build 52 | doxygen 53 | obj 54 | 55 | # toptal/gitignore/templates/VisualStudioCode.gitignore 56 | 57 | .vscode/* 58 | !.vscode/settings.json 59 | !.vscode/tasks.json 60 | !.vscode/launch.json 61 | !.vscode/extensions.json 62 | 63 | !.vscode/*.code-snippets -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2025 Steffen Illhardt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: -4 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveAssignments: true 6 | AlignConsecutiveDeclarations: false 7 | AlignEscapedNewlines: Left 8 | AlignOperands: true 9 | AlignTrailingComments: true 10 | AllowAllParametersOfDeclarationOnNextLine: true 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: InlineOnly 14 | AllowShortIfStatementsOnASingleLine: true 15 | AllowShortLoopsOnASingleLine: false 16 | AlwaysBreakAfterDefinitionReturnType: None 17 | AlwaysBreakAfterReturnType: None 18 | AlwaysBreakBeforeMultilineStrings: false 19 | AlwaysBreakTemplateDeclarations: Yes 20 | BinPackArguments: true 21 | BinPackParameters: true 22 | BraceWrapping: 23 | AfterClass: false 24 | AfterControlStatement: false 25 | AfterEnum: false 26 | AfterFunction: false 27 | AfterNamespace: false 28 | AfterObjCDeclaration: false 29 | AfterStruct: false 30 | AfterUnion: false 31 | AfterExternBlock: false 32 | BeforeCatch: false 33 | BeforeElse: false 34 | IndentBraces: false 35 | SplitEmptyFunction: true 36 | SplitEmptyRecord: true 37 | SplitEmptyNamespace: true 38 | BreakBeforeBinaryOperators: None 39 | BreakBeforeBraces: Attach 40 | BreakBeforeInheritanceComma: false 41 | BreakInheritanceList: BeforeColon 42 | BreakBeforeTernaryOperators: true 43 | BreakConstructorInitializersBeforeComma: false 44 | BreakConstructorInitializers: BeforeColon 45 | BreakAfterJavaFieldAnnotations: false 46 | BreakStringLiterals: true 47 | ColumnLimit: 0 48 | CommentPragmas: '^ IWYU pragma:' 49 | CompactNamespaces: false 50 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 51 | ConstructorInitializerIndentWidth: 4 52 | ContinuationIndentWidth: 4 53 | Cpp11BracedListStyle: true 54 | DerivePointerAlignment: false 55 | DisableFormat: false 56 | ExperimentalAutoDetectBinPacking: false 57 | FixNamespaceComments: false 58 | ForEachMacros: 59 | - foreach 60 | - Q_FOREACH 61 | - BOOST_FOREACH 62 | IncludeBlocks: Preserve 63 | IncludeCategories: 64 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 65 | Priority: 2 66 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 67 | Priority: 3 68 | - Regex: '.*' 69 | Priority: 1 70 | IncludeIsMainRegex: '(Test)?$' 71 | IndentCaseLabels: false 72 | IndentPPDirectives: None 73 | IndentWidth: 4 74 | IndentWrappedFunctionNames: false 75 | JavaScriptQuotes: Leave 76 | JavaScriptWrapImports: true 77 | KeepEmptyLinesAtTheStartOfBlocks: true 78 | MacroBlockBegin: '' 79 | MacroBlockEnd: '' 80 | MaxEmptyLinesToKeep: 1 81 | NamespaceIndentation: None 82 | ObjCBinPackProtocolList: Auto 83 | ObjCBlockIndentWidth: 2 84 | ObjCSpaceAfterProperty: false 85 | ObjCSpaceBeforeProtocolList: true 86 | PenaltyBreakAssignment: 2 87 | PenaltyBreakBeforeFirstCallParameter: 19 88 | PenaltyBreakComment: 300 89 | PenaltyBreakFirstLessLess: 120 90 | PenaltyBreakString: 1000 91 | PenaltyBreakTemplateDeclaration: 10 92 | PenaltyExcessCharacter: 1000000 93 | PenaltyReturnTypeOnItsOwnLine: 60 94 | PointerAlignment: Right 95 | ReflowComments: true 96 | SortIncludes: true 97 | SortUsingDeclarations: true 98 | SpaceAfterCStyleCast: false 99 | SpaceAfterTemplateKeyword: true 100 | SpaceBeforeAssignmentOperators: true 101 | SpaceBeforeCpp11BracedList: false 102 | SpaceBeforeCtorInitializerColon: true 103 | SpaceBeforeInheritanceColon: true 104 | SpaceBeforeParens: ControlStatements 105 | SpaceBeforeRangeBasedForLoopColon: true 106 | SpaceInEmptyParentheses: false 107 | SpacesBeforeTrailingComments: 1 108 | SpacesInAngles: false 109 | SpacesInContainerLiterals: true 110 | SpacesInCStyleCastParentheses: false 111 | SpacesInParentheses: false 112 | SpacesInSquareBrackets: false 113 | Standard: Cpp11 114 | StatementMacros: 115 | - Q_UNUSED 116 | - QT_REQUIRE_VERSION 117 | TabWidth: 4 118 | UseTab: Never 119 | ... 120 | 121 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a type-safe, header-only implementation of a `std::basic_string`-like string in plain C code. 2 | It can be considered an offshoot of the [c-vector](https://github.com/eteran/c-vector) library and is intended to be binary compatible with it. Many sequences of the `c-string` library essentially correspond to the `c-vector` library code. All credits go to [eteran](https://github.com/eteran) and contributors. 3 | While the `c-vector` library implements macros to emulate methods of a `std::vector`, the `c-string` library is specialized for null-terminated strings of characters. Its macros emulate `std::basic_string` methods. A few additional features are implemented that don't have an equivalent for a `std::basic_string`. 4 | The `cstring_array` API extension is for vectors of `cstring`. It supports tokenization (also known as `split`) and concatenation (also known as `join`). 5 | 6 | - Include __cstring.h__ to use the API. No other file of this repository is necessary. 7 | - Besides of containing the code for tests, the __cstring-unit-tests.c__ is also meant to provide some examples of how to use the API. 8 | - PDF prints of the relevant parts of a Doxygen-generated manual can be found in the __doc__ folder. 9 | 10 | Just like the `cvector`, a `cstring` is prefixed with metadata, in the tradition of a length-prefixed string implementation. 11 | The members of the `cstring` metadata are found at the same offset as those of a `cvector`. They count all characters (incl. the terminating null) which makes a `cstring` _interchangeable_ with a `cvector` of the same set of consecutive characters. Unlike the `cvector` macros (and as is usual for strings) the `cstring` macros, which return size and capacity, do not count the string terminator. 12 | 13 | The user is given a pointer to the first character of the actual string. This way a `cstring` can be used with the C string and I/O library functions that don't modify its content, as well as whith those of the platform API. 14 | 15 | Example: 16 | 17 | ```c 18 | #include "cstring.h" 19 | #include 20 | 21 | int main(void) { 22 | // Declaration of a null-string. The initialization with NULL is critical. 23 | cstring_string_type(char) str = NULL; 24 | 25 | // Assign string "abc". 26 | cstring_assign(str, "abc", 3); 27 | printf("1)\nString: %s\nLength: %zu\nCapacity: %zu\n\n", str, cstring_length(str), cstring_capacity(str)); 28 | 29 | // Append string "hi". 30 | cstring_append(str, "hi", 2); 31 | printf("2)\nString: %s\nLength: %zu\nCapacity: %zu\n\n", str, cstring_length(str), cstring_capacity(str)); 32 | 33 | // Insert string "defg" at offset 3 (between 'c' and 'h'). 34 | cstring_insert(str, 3, "defg", 4); 35 | printf("3)\nString: %s\nLength: %zu\nCapacity: %zu\n\n", str, cstring_length(str), cstring_capacity(str)); 36 | 37 | // Erase 4 characters at offset 1. 38 | cstring_erase(str, 1, 4); 39 | printf("4)\nString: %s\nLength: %zu\nCapacity: %zu\n\n", str, cstring_length(str), cstring_capacity(str)); 40 | 41 | // Add an 'x' at the end of the string. 42 | cstring_push_back(str, 'x'); 43 | printf("5)\nString: %s\nLength: %zu\nCapacity: %zu\n\n", str, cstring_length(str), cstring_capacity(str)); 44 | 45 | // Replace two characters at offset 3 with three 'z' characters. 46 | cstring_replace(str, 3, 2, "zzz", 3); 47 | printf("6)\nString: %s\nLength: %zu\nCapacity: %zu\n\n", str, cstring_length(str), cstring_capacity(str)); 48 | 49 | // Copy a 4-character substring from offset 2. 50 | cstring_string_type(char) str2 = NULL; 51 | cstring_substring(str, 2, 4, str2); 52 | printf("7)\nString2: %s\nLength: %zu\nCapacity: %zu\n\n", str2, cstring_length(str2), cstring_capacity(str2)); 53 | 54 | // Free allocated memory. 55 | cstring_free(str2); 56 | cstring_free(str); 57 | 58 | return 0; 59 | } 60 | 61 | ``` 62 | 63 | ---- 64 | 65 | ### cstring API 66 | 67 | | **std::basic_string** | **cstring** | 68 | | --------------------- | ----------- | 69 | | [`std::basic_string str{};`](https://en.cppreference.com/w/cpp/string/basic_string/basic_string) | `cstring_string_type(type) str = NULL;` [^1],
`cstring_literal(name, type, lit)` [^2],
`cstring_init(name, type)` [^3] | 70 | | [Destructor](https://en.cppreference.com/w/cpp/string/basic_string/%7Ebasic_string) | `cstring_free(str)` | 71 | | [`str.assign(s, count)`](https://en.cppreference.com/w/cpp/string/basic_string/assign) | `cstring_assign(str, s, count)` | 72 | | [`str.at(pos)`](https://en.cppreference.com/w/cpp/string/basic_string/at) | `cstring_at(str, pos)` | 73 | | [`str[pos]`](https://en.cppreference.com/w/cpp/string/basic_string/operator_at) | `str[pos]` | 74 | | [`str.front()`](https://en.cppreference.com/w/cpp/string/basic_string/front) | `cstring_front(str)` | 75 | | [`str.back()`](https://en.cppreference.com/w/cpp/string/basic_string/back) | `cstring_back(str)` | 76 | | [`str.data()`](https://en.cppreference.com/w/cpp/string/basic_string/data), [`str.c_str()`](https://en.cppreference.com/w/cpp/string/basic_string/c_str) | `str` | 77 | | [`str.begin()`](https://en.cppreference.com/w/cpp/string/basic_string/begin) | `cstring_begin(str)` | 78 | | [`str.end()`](https://en.cppreference.com/w/cpp/string/basic_string/end) | `cstring_end(str)` | 79 | | [`str.empty()`](https://en.cppreference.com/w/cpp/string/basic_string/empty) | `cstring_empty(str)` | 80 | | [`str.size()`, `str.length()`](https://en.cppreference.com/w/cpp/string/basic_string/size) | `cstring_size(str)`, `cstring_length(str)` | 81 | | [`std::basic_string::max_size()`](https://en.cppreference.com/w/cpp/string/basic_string/max_size) | `cstring_max_size(type)` | 82 | | [`str.reserve(n)`](https://en.cppreference.com/w/cpp/string/basic_string/reserve) | `cstring_reserve(str, n)` | 83 | | [`str.capacity()`](https://en.cppreference.com/w/cpp/string/basic_string/capacity) | `cstring_capacity(str)` | 84 | | [`str.shrink_to_fit()`](https://en.cppreference.com/w/cpp/string/basic_string/shrink_to_fit) | `cstring_shrink_to_fit(str)` | 85 | | N/A | `cstring_unsafe_set_size(str, size)` [^4] | 86 | | [`str.clear()`](https://en.cppreference.com/w/cpp/string/basic_string/clear) | `cstring_clear(str)` | 87 | | [`str.insert(index, s, count)`](https://en.cppreference.com/w/cpp/string/basic_string/insert) | `cstring_insert(str, index, s, count)` | 88 | | [`str.erase(index, count)`](https://en.cppreference.com/w/cpp/string/basic_string/erase) | `cstring_erase(str, index, count)` | 89 | | [`str.push_back(ch)`](https://en.cppreference.com/w/cpp/string/basic_string/push_back) | `cstring_push_back(str, ch)` | 90 | | [`str.pop_back()`](https://en.cppreference.com/w/cpp/string/basic_string/pop_back) | `cstring_pop_back(str)` | 91 | | [`str.append(s, count)`](https://en.cppreference.com/w/cpp/string/basic_string/append) | `cstring_append(str, s, count)` | 92 | | [`str.replace(pos, count, s, count2)`](https://en.cppreference.com/w/cpp/string/basic_string/replace) | `cstring_replace(str, pos, count, s, count2)` | 93 | | [`from.copy(to, npos, 0)`](https://en.cppreference.com/w/cpp/string/basic_string/copy) | `cstring_copy(from, to)` [^5] | 94 | | [`str.resize(count, ch)`](https://en.cppreference.com/w/cpp/string/basic_string/resize) | `cstring_resize(str, count, ch)` | 95 | | [`str.swap(other)`](https://en.cppreference.com/w/cpp/string/basic_string/swap) | `cstring_swap(str, other)` | 96 | | N/A | `cstring_trim(str, value, mode)` [^6] | 97 | | N/A | `cstring_fix(str, length, value, mode)` [^7] | 98 | | N/A | `cstring_reverse(str)` [^8] | 99 | | [`offset = str.find(s, pos, count)`](https://en.cppreference.com/w/cpp/string/basic_string/find) | `cstring_find(str, pos, s, count, offset)` | 100 | | [`offset = str.rfind(s, pos, count)`](https://en.cppreference.com/w/cpp/string/basic_string/rfind) | `cstring_rfind(str, pos, s, count, offset)` | 101 | | [`offset = str.find_first_of(s, pos, count)`](https://en.cppreference.com/w/cpp/string/basic_string/find_first_of) | `cstring_find_first_of(str, pos, s, count, offset)` | 102 | | [`offset = str.find_first_not_of(s, pos, count)`](https://en.cppreference.com/w/cpp/string/basic_string/find_first_not_of) | `cstring_find_first_not_of(str, pos, s, count, offset)` | 103 | | [`offset = str.find_last_of(s, pos, count)`](https://en.cppreference.com/w/cpp/string/basic_string/find_last_of) | `cstring_find_last_of(str, pos, s, count, offset)` | 104 | | [`offset = str.find_last_not_of(s, pos, count)`](https://en.cppreference.com/w/cpp/string/basic_string/find_last_not_of) | `cstring_find_last_not_of(str, pos, s, count, offset)` | 105 | | [`order = str1.compare(str2)`](https://en.cppreference.com/w/cpp/string/basic_string/compare) | `cstring_compare(str1, str2, order)` | 106 | | [`found = str.starts_with(s)`](https://en.cppreference.com/w/cpp/string/basic_string/starts_with) | `cstring_starts_with(str, s, count, found)` | 107 | | [`found = str.ends_with(s)`](https://en.cppreference.com/w/cpp/string/basic_string/ends_with) | `cstring_ends_with(str, s, count, found)` | 108 | | [`found = str.contains(s)`](https://en.cppreference.com/w/cpp/string/basic_string/contains) | `cstring_ends_with(str, s, count, found)` | 109 | | [`to = from.substring(pos, count)`](https://en.cppreference.com/w/cpp/string/basic_string/substring) | `cstring_substring(from, pos, count, to)` | 110 | 111 | ---- 112 | 113 | ### cstring_array API 114 | A `cstring_array` is a NULL pointer-terminated vector of `cstring`. 115 | The concept is similar to the vector of arguments in `int main(int argc, char *argv[])`. While `argv` is guaranteed to be NULL pointer-terminated, this terminator is not counted in `argc`. 116 | Like a `cstring`, a `cstring_array` is a heap-allocated and metadata-prefixed object. 117 | 118 | | **Macro** | **Description** | 119 | | --------- | --------------- | 120 | | `cstring_array_type(type) arr = NULL` | Declare a vector of `cstring` using the specified character type. | 121 | | `cstring_split(str, max_tok, ptr, count, ret_array)` | Tokenize `str` into a vector of `cstring`. | 122 | | `cstring_array_free(arr)` | Recursively free all memory associated with the vector. | 123 | | `cstring_array_at(arr, pos)` | Return the string pointer at position `pos` in the cstring_array. | 124 | | `cstring_array_front(arr)` | Return the string pointer to the first string in the cstring_array. | 125 | | `cstring_array_back(arr)` | Return the string pointer to the last string in the cstring_array. | 126 | | `cstring_array_begin(arr)` | Return an iterator to first string of the vector. | 127 | | `cstring_array_end(arr)` | Return an iterator to one past the last string of the vector. | 128 | | `cstring_array_empty(arr)` | Return non-zero if the vector is empty. | 129 | | `cstring_array_size(arr)` | Get the current length of the vector. | 130 | | `cstring_array_max_size(type)` | Get the maximum number of elements a vector of strings of the specified character type is able to hold. | 131 | | `cstring_array_reserve(arr, n)` | Request that the vector capacity be at least enough to contain `n` strings. | 132 | | `cstring_array_capacity(arr)` | Get the current capacity of the vector. | 133 | | `cstring_array_shrink_to_fit(arr)` | Request the container to reduce its capacity to fit its size. | 134 | | `cstring_array_clear(arr)` | Erase all of the strings in the vector. | 135 | | `cstring_array_insert(arr, pos, ptr, count)` | Insert a string at position `pos` into the vector. | 136 | | `cstring_array_erase(arr, pos, n)` | Remove the strings beginning at offset `pos` from the cstring_array. | 137 | | `cstring_array_push_back(arr, ptr, count)` | Add a string to the end of the vector. | 138 | | `cstring_array_pop_back(arr)` | Remove the last string from the cstring_array. | 139 | | `cstring_array_copy(from, to)` | Copy a cstring_array. | 140 | | `cstring_array_resize(arr, n, ptr, count)` | Resize the container to contain `count` strings. | 141 | | `cstring_array_swap(arr, other)` | Exchange the content of the cstring_array by the content of another cstring_array of the same type. | 142 | | `cstring_array_slice(from, pos, n, to)` | Copy a part of a vector. | 143 | | `cstring_array_join(arr, ptr, count, ret_str)` | Concatenate the strings of a vector using the specified joiner. | 144 | 145 | ---- 146 | 147 | Neil Henning's [unit test header](https://github.com/sheredom/utest.h) is used to verify the proper functionality of all API macros. 148 | 149 | ---- 150 | 151 | [^1]: Initializes a NULL string. Nothing similar for `std::basic_string`. 152 | [^2]: Declares a static cstring literal of `const type`. This is comparable with a C++20 `constexpr std::basic_string`. 153 | [^3]: Allocates and initializes a zero-length string. 154 | [^4]: Manually updates the size property after an update of the string buffer in a third party API. 155 | [^5]: This creates a duplicate of the string. For copying a substring see `cstring_substring()`. 156 | [^6]: Removes contiguous occurrences of the specified character from the begin and/or the end of a cstring. 157 | [^7]: Updates the cstring to a fixed length by either padding or shortening. 158 | [^8]: Reverses the character order in the cstring. 159 | 160 | -------------------------------------------------------------------------------- /cstring-unit-tests.c: -------------------------------------------------------------------------------- 1 | #include "cstring.h" 2 | #include "utest/utest.h" 3 | #include 4 | 5 | static const char literal[] = "abcde"; 6 | static const wchar_t wliteral[] = L"abcde"; 7 | 8 | #define strlen_of(lit) \ 9 | (sizeof(lit) / sizeof((lit)[0]) - 1) 10 | 11 | #define wcseq(s1, s2) \ 12 | (wcscmp((s1), (s2)) == 0) 13 | 14 | UTEST(string, cstring_literal) { 15 | cstring_literal(lit, char, "abc"); 16 | ASSERT_EQ(cstring_size(lit), 3U); 17 | ASSERT_EQ(cstring_capacity(lit), 3U); 18 | ASSERT_STREQ(lit, "abc"); 19 | 20 | /* -- wide string -- */ 21 | 22 | cstring_literal(wlit, wchar_t, L"abc"); 23 | ASSERT_EQ(cstring_size(wlit), 3U); 24 | ASSERT_EQ(cstring_capacity(wlit), 3U); 25 | ASSERT_TRUE(wcseq(wlit, L"abc")); 26 | } 27 | 28 | UTEST(string, cstring_init) { 29 | cstring_init(str, char); 30 | 31 | ASSERT_NE(str, NULL); 32 | ASSERT_EQ(cstring_size(str), 0U); 33 | ASSERT_NE(cstring_capacity(str), 0U); 34 | 35 | cstring_free(str); 36 | 37 | /* -- wide string -- */ 38 | 39 | cstring_init(wstr, wchar_t); 40 | 41 | ASSERT_NE(wstr, NULL); 42 | ASSERT_EQ(cstring_size(wstr), 0U); 43 | ASSERT_NE(cstring_capacity(wstr), 0U); 44 | 45 | cstring_free(wstr); 46 | } 47 | 48 | UTEST(string, cstring_assign_clear) { 49 | cstring_string_type(char) str = NULL; 50 | 51 | cstring_assign(str, literal + 1, 3); 52 | ASSERT_EQ(cstring_size(str), 3U); 53 | ASSERT_EQ(cstring_capacity(str), 3U); 54 | ASSERT_STREQ(str, "bcd"); 55 | 56 | cstring_assign(str, literal, strlen_of(literal)); 57 | ASSERT_EQ(cstring_size(str), strlen_of(literal)); 58 | ASSERT_EQ(cstring_capacity(str), strlen_of(literal)); 59 | ASSERT_STREQ(str, literal); 60 | 61 | cstring_assign(str, literal + 1, 3); 62 | ASSERT_EQ(cstring_size(str), 3U); 63 | ASSERT_EQ(cstring_capacity(str), strlen_of(literal)); 64 | ASSERT_STREQ(str, "bcd"); 65 | 66 | cstring_clear(str); 67 | ASSERT_EQ(cstring_size(str), 0U); 68 | ASSERT_EQ(cstring_capacity(str), strlen_of(literal)); 69 | ASSERT_EQ(str[0], '\0'); 70 | 71 | cstring_free(str); 72 | 73 | /* -- wide string -- */ 74 | 75 | cstring_string_type(wchar_t) wstr = NULL; 76 | 77 | cstring_assign(wstr, wliteral + 1, 3); 78 | ASSERT_EQ(cstring_size(wstr), 3U); 79 | ASSERT_EQ(cstring_capacity(wstr), 3U); 80 | ASSERT_TRUE(wcseq(wstr, L"bcd")); 81 | 82 | cstring_assign(wstr, wliteral, strlen_of(wliteral)); 83 | ASSERT_EQ(cstring_size(wstr), strlen_of(wliteral)); 84 | ASSERT_EQ(cstring_capacity(wstr), strlen_of(wliteral)); 85 | ASSERT_TRUE(wcseq(wstr, wliteral)); 86 | 87 | cstring_assign(wstr, wliteral + 1, 3); 88 | ASSERT_EQ(cstring_size(wstr), 3U); 89 | ASSERT_EQ(cstring_capacity(wstr), strlen_of(wliteral)); 90 | ASSERT_TRUE(wcseq(wstr, L"bcd")); 91 | 92 | cstring_clear(wstr); 93 | ASSERT_EQ(cstring_size(wstr), 0U); 94 | ASSERT_EQ(cstring_capacity(wstr), strlen_of(wliteral)); 95 | ASSERT_EQ(wstr[0], L'\0'); 96 | 97 | cstring_free(wstr); 98 | } 99 | 100 | UTEST(string, cstring_at) { 101 | cstring_string_type(char) str = NULL; 102 | cstring_assign(str, literal, strlen_of(literal)); 103 | 104 | ASSERT_EQ((char)cstring_at(str, 0), 'a'); 105 | ASSERT_EQ((char)cstring_at(str, 4), 'e'); 106 | ASSERT_EQ(cstring_at(str, 5), -1); 107 | 108 | cstring_free(str); 109 | 110 | /* -- wide string -- */ 111 | 112 | cstring_string_type(wchar_t) wstr = NULL; 113 | cstring_assign(wstr, wliteral, strlen_of(wliteral)); 114 | 115 | ASSERT_EQ((wchar_t)cstring_at(wstr, 0), L'a'); 116 | ASSERT_EQ((wchar_t)cstring_at(wstr, 4), L'e'); 117 | ASSERT_EQ(cstring_at(wstr, 5), -1); 118 | 119 | cstring_free(wstr); 120 | 121 | /* -- special cases -- */ 122 | 123 | cstring_string_type(char) nullstr = NULL; 124 | ASSERT_EQ(cstring_at(nullstr, 0), -1); 125 | 126 | cstring_init(zerolenstr, char); 127 | ASSERT_EQ(cstring_at(zerolenstr, 0), -1); 128 | cstring_free(zerolenstr); 129 | } 130 | 131 | UTEST(string, cstring_front_back) { 132 | /* see cstring_insert string for the typical use */ 133 | 134 | /* -- special cases -- */ 135 | 136 | cstring_string_type(char) nullstr = NULL; 137 | ASSERT_EQ(cstring_front(nullstr), -1); 138 | ASSERT_EQ(cstring_back(nullstr), -1); 139 | 140 | cstring_init(zerolenstr, char); 141 | ASSERT_EQ(cstring_front(zerolenstr), -1); 142 | ASSERT_EQ(cstring_back(zerolenstr), -1); 143 | cstring_free(zerolenstr); 144 | } 145 | 146 | UTEST(string, cstring_begin_end) { 147 | cstring_string_type(char) str = NULL; 148 | 149 | cstring_assign(str, literal, strlen_of(literal)); 150 | cstring_iterator(char) it = cstring_begin(str); 151 | cstring_iterator(char) end = cstring_end(str); 152 | int i = 0; 153 | while (it < end) { 154 | ASSERT_EQ(*it, (char)('a' + i)); 155 | ++it; 156 | ++i; 157 | } 158 | 159 | cstring_free(str); 160 | 161 | /* -- wide string -- */ 162 | 163 | cstring_string_type(wchar_t) wstr = NULL; 164 | 165 | cstring_assign(wstr, wliteral, strlen_of(wliteral)); 166 | cstring_iterator(wchar_t) wit = cstring_begin(wstr); 167 | cstring_iterator(wchar_t) wend = cstring_end(wstr); 168 | i = 0; 169 | while (wit < wend) { 170 | ASSERT_EQ(*wit, (wchar_t)(L'a' + i)); 171 | ++wit; 172 | ++i; 173 | } 174 | 175 | cstring_free(wstr); 176 | 177 | /* -- special cases -- */ 178 | 179 | cstring_string_type(char) nullstr = NULL; 180 | ASSERT_EQ(cstring_begin(nullstr), NULL); 181 | ASSERT_EQ(cstring_end(nullstr), NULL); 182 | 183 | cstring_init(zerolenstr, char); 184 | ASSERT_EQ(*cstring_begin(zerolenstr), '\0'); 185 | ASSERT_EQ(*cstring_end(zerolenstr), '\0'); 186 | cstring_free(zerolenstr); 187 | } 188 | 189 | UTEST(string, cstring_empty) { 190 | /* see cstring_erase string for the typical use */ 191 | 192 | /* -- special cases -- */ 193 | 194 | cstring_string_type(char) nullstr = NULL; 195 | ASSERT_TRUE(cstring_empty(nullstr)); 196 | } 197 | 198 | UTEST(string, cstring_size_length_capacity) { 199 | /* the typical use is found in more places, note that cstring_length is the same as cstring_size */ 200 | 201 | /* -- special cases -- */ 202 | 203 | cstring_string_type(char) nullstr = NULL; 204 | ASSERT_EQ(cstring_size(nullstr), 0U); 205 | ASSERT_EQ(cstring_length(nullstr), 0U); 206 | ASSERT_EQ(cstring_capacity(nullstr), 0U); 207 | 208 | cstring_string_type(char) zerolenstr = NULL; 209 | cstring_reserve(zerolenstr, 0); 210 | ASSERT_NE(zerolenstr, NULL); 211 | ASSERT_EQ(cstring_size(zerolenstr), 0U); 212 | ASSERT_EQ(cstring_length(zerolenstr), 0U); 213 | ASSERT_EQ(cstring_capacity(zerolenstr), 0U); 214 | cstring_free(zerolenstr); 215 | } 216 | 217 | UTEST(string, cstring_max_size) { 218 | ASSERT_TRUE(cstring_max_size(char) && (cstring_max_size(char) & 1) == 0); 219 | 220 | /* -- wide string -- */ 221 | 222 | ASSERT_TRUE(cstring_max_size(wchar_t) && (cstring_max_size(wchar_t) & 1) == 0); 223 | } 224 | 225 | UTEST(string, cstring_reserve) { 226 | cstring_string_type(char) str = NULL; 227 | 228 | cstring_reserve(str, 10); 229 | ASSERT_EQ(cstring_size(str), 0U); 230 | ASSERT_EQ(cstring_capacity(str), 10U); 231 | ASSERT_EQ(*str, '\0'); 232 | 233 | cstring_free(str); 234 | 235 | /* -- wide string -- */ 236 | 237 | cstring_string_type(wchar_t) wstr = NULL; 238 | 239 | cstring_reserve(wstr, 10); 240 | ASSERT_EQ(cstring_size(wstr), 0U); 241 | ASSERT_EQ(cstring_capacity(wstr), 10U); 242 | ASSERT_EQ(*wstr, L'\0'); 243 | 244 | cstring_free(wstr); 245 | } 246 | 247 | UTEST(string, cstring_shrink_to_fit) { 248 | cstring_string_type(char) str = NULL; 249 | cstring_assign(str, literal, strlen_of(literal)); 250 | 251 | cstring_pop_back(str); 252 | ASSERT_EQ(cstring_capacity(str), strlen_of(literal)); 253 | ASSERT_EQ(cstring_size(str), strlen_of(literal) - 1); 254 | 255 | cstring_shrink_to_fit(str); 256 | ASSERT_EQ(cstring_capacity(str), strlen_of(literal) - 1); 257 | ASSERT_EQ(cstring_size(str), strlen_of(literal) - 1); 258 | 259 | cstring_free(str); 260 | 261 | /* -- wide string -- */ 262 | 263 | cstring_string_type(wchar_t) wstr = NULL; 264 | cstring_assign(wstr, wliteral, strlen_of(wliteral)); 265 | 266 | cstring_pop_back(wstr); 267 | ASSERT_EQ(cstring_capacity(wstr), strlen_of(wliteral)); 268 | ASSERT_EQ(cstring_size(wstr), strlen_of(wliteral) - 1); 269 | 270 | cstring_shrink_to_fit(wstr); 271 | ASSERT_EQ(cstring_capacity(wstr), strlen_of(wliteral) - 1); 272 | ASSERT_EQ(cstring_size(wstr), strlen_of(wliteral) - 1); 273 | 274 | cstring_free(wstr); 275 | 276 | /* -- special cases -- */ 277 | 278 | cstring_string_type(char) nullstr = NULL; 279 | cstring_shrink_to_fit(nullstr); 280 | ASSERT_EQ(nullstr, NULL); 281 | } 282 | 283 | static size_t unsafe_buffer_update(char *buffer, size_t buffer_size) { 284 | static const char data[] = "1234567890"; 285 | const size_t to_copy = (sizeof(data) - 1) < buffer_size ? (sizeof(data) - 1) : buffer_size; 286 | memcpy(buffer, data, to_copy); 287 | return to_copy; 288 | } 289 | 290 | static size_t unsafe_wbuffer_update(wchar_t *wbuffer, size_t wbuffer_size) { 291 | static const wchar_t data[] = L"1234567890"; 292 | const size_t to_copy = (sizeof(data) / sizeof(wchar_t) - 1) < wbuffer_size ? (sizeof(data) / sizeof(wchar_t) - 1) : wbuffer_size; 293 | memcpy(wbuffer, data, to_copy * sizeof(wchar_t)); 294 | return to_copy; 295 | } 296 | 297 | UTEST(string, cstring_unsafe_set_size) { 298 | size_t len; 299 | cstring_string_type(char) str = NULL; 300 | 301 | cstring_reserve(str, 5); 302 | len = unsafe_buffer_update(str, cstring_capacity(str)); 303 | cstring_unsafe_set_size(str, len); 304 | ASSERT_EQ(cstring_size(str), 5U); 305 | ASSERT_STREQ(str, "12345"); 306 | 307 | cstring_reserve(str, 30); 308 | len = unsafe_buffer_update(str, cstring_capacity(str)); 309 | cstring_unsafe_set_size(str, len); 310 | ASSERT_EQ(cstring_size(str), 10U); 311 | ASSERT_STREQ(str, "1234567890"); 312 | 313 | cstring_free(str); 314 | 315 | /* -- wide string -- */ 316 | 317 | cstring_string_type(wchar_t) wstr = NULL; 318 | 319 | cstring_reserve(wstr, 5); 320 | len = unsafe_wbuffer_update(wstr, cstring_capacity(wstr)); 321 | cstring_unsafe_set_size(wstr, len); 322 | ASSERT_EQ(cstring_size(wstr), 5U); 323 | ASSERT_TRUE(wcseq(wstr, L"12345")); 324 | 325 | cstring_reserve(wstr, 30); 326 | len = unsafe_wbuffer_update(wstr, cstring_capacity(wstr)); 327 | cstring_unsafe_set_size(wstr, len); 328 | ASSERT_EQ(cstring_size(wstr), 10U); 329 | ASSERT_TRUE(wcseq(wstr, L"1234567890")); 330 | 331 | cstring_free(wstr); 332 | } 333 | 334 | UTEST(string, cstring_insert) { 335 | static const char lit1[] = "0145"; 336 | static const char lit2[] = "23"; 337 | cstring_string_type(char) str = NULL; 338 | 339 | cstring_assign(str, lit1, strlen_of(lit1)); 340 | cstring_insert(str, 2, lit2, strlen_of(lit2)); 341 | ASSERT_EQ(cstring_size(str), 6U); 342 | ASSERT_STREQ(str, "012345"); 343 | 344 | cstring_insert(str, 6, lit2, strlen_of(lit2)); 345 | ASSERT_EQ(cstring_size(str), 8U); 346 | ASSERT_STREQ(str, "01234523"); 347 | 348 | cstring_insert(str, 0, lit2, strlen_of(lit2)); 349 | ASSERT_EQ(cstring_size(str), 10U); 350 | ASSERT_EQ((char)cstring_front(str), '2'); 351 | ASSERT_EQ((char)cstring_back(str), '3'); 352 | ASSERT_STREQ(str, "2301234523"); 353 | 354 | cstring_free(str); 355 | 356 | /* -- wide string -- */ 357 | 358 | static const wchar_t wlit1[] = L"0145"; 359 | static const wchar_t wlit2[] = L"23"; 360 | cstring_string_type(wchar_t) wstr = NULL; 361 | 362 | cstring_assign(wstr, wlit1, strlen_of(wlit1)); 363 | cstring_insert(wstr, 2, wlit2, strlen_of(wlit2)); 364 | ASSERT_EQ(cstring_size(wstr), 6U); 365 | ASSERT_TRUE(wcseq(wstr, L"012345")); 366 | 367 | cstring_insert(wstr, 6, wlit2, strlen_of(wlit2)); 368 | ASSERT_EQ(cstring_size(wstr), 8U); 369 | ASSERT_TRUE(wcseq(wstr, L"01234523")); 370 | 371 | cstring_insert(wstr, 0, wlit2, strlen_of(wlit2)); 372 | ASSERT_EQ(cstring_size(wstr), 10U); 373 | ASSERT_EQ((wchar_t)cstring_front(wstr), L'2'); 374 | ASSERT_EQ((wchar_t)cstring_back(wstr), L'3'); 375 | ASSERT_TRUE(wcseq(wstr, L"2301234523")); 376 | 377 | cstring_free(wstr); 378 | 379 | /* -- special cases -- */ 380 | 381 | cstring_string_type(char) nullstr = NULL; 382 | cstring_insert(nullstr, 0, literal, strlen_of(literal)); 383 | ASSERT_EQ(nullstr, NULL); 384 | 385 | cstring_init(zerolenstr, char); 386 | cstring_insert(zerolenstr, 1, literal, strlen_of(literal)); 387 | ASSERT_STREQ(zerolenstr, ""); 388 | 389 | cstring_insert(zerolenstr, 0, literal, strlen_of(literal)); 390 | ASSERT_STREQ(zerolenstr, literal); 391 | cstring_free(zerolenstr); 392 | } 393 | 394 | UTEST(string, cstring_erase) { 395 | cstring_string_type(char) str = NULL; 396 | 397 | cstring_assign(str, literal, strlen_of(literal)); 398 | ASSERT_EQ(cstring_size(str), strlen_of(literal)); 399 | ASSERT_EQ(cstring_capacity(str), strlen_of(literal)); 400 | ASSERT_STREQ(str, literal); 401 | 402 | cstring_erase(str, 3, 1); 403 | ASSERT_EQ(cstring_size(str), strlen_of(literal) - 1); 404 | ASSERT_EQ(cstring_capacity(str), strlen_of(literal)); 405 | ASSERT_STREQ(str, "abce"); 406 | 407 | cstring_erase(str, 3, 1); 408 | ASSERT_EQ(cstring_size(str), strlen_of(literal) - 2); 409 | ASSERT_EQ(cstring_capacity(str), strlen_of(literal)); 410 | ASSERT_STREQ(str, "abc"); 411 | 412 | cstring_erase(str, 0, 2); 413 | ASSERT_EQ(cstring_size(str), strlen_of(literal) - 4); 414 | ASSERT_EQ(cstring_capacity(str), strlen_of(literal)); 415 | ASSERT_STREQ(str, "c"); 416 | 417 | cstring_erase(str, 0, 1000); 418 | ASSERT_TRUE(cstring_empty(str)); 419 | ASSERT_EQ(cstring_capacity(str), strlen_of(literal)); 420 | ASSERT_STREQ(str, ""); 421 | 422 | cstring_free(str); 423 | 424 | /* -- wide string -- */ 425 | 426 | cstring_string_type(wchar_t) wstr = NULL; 427 | 428 | cstring_assign(wstr, wliteral, strlen_of(wliteral)); 429 | ASSERT_EQ(cstring_size(wstr), strlen_of(wliteral)); 430 | ASSERT_EQ(cstring_capacity(wstr), strlen_of(wliteral)); 431 | ASSERT_TRUE(wcseq(wstr, wliteral)); 432 | 433 | cstring_erase(wstr, 3, 1); 434 | ASSERT_EQ(cstring_size(wstr), strlen_of(wliteral) - 1); 435 | ASSERT_EQ(cstring_capacity(wstr), strlen_of(wliteral)); 436 | ASSERT_TRUE(wcseq(wstr, L"abce")); 437 | 438 | cstring_erase(wstr, 3, 1); 439 | ASSERT_EQ(cstring_size(wstr), strlen_of(wliteral) - 2); 440 | ASSERT_EQ(cstring_capacity(wstr), strlen_of(wliteral)); 441 | ASSERT_TRUE(wcseq(wstr, L"abc")); 442 | 443 | cstring_erase(wstr, 0, 2); 444 | ASSERT_EQ(cstring_size(wstr), strlen_of(wliteral) - 4); 445 | ASSERT_EQ(cstring_capacity(wstr), strlen_of(wliteral)); 446 | ASSERT_TRUE(wcseq(wstr, L"c")); 447 | 448 | cstring_erase(wstr, 0, 1000); 449 | ASSERT_TRUE(cstring_empty(wstr)); 450 | ASSERT_EQ(cstring_capacity(wstr), strlen_of(wliteral)); 451 | ASSERT_TRUE(wcseq(wstr, L"")); 452 | 453 | cstring_free(wstr); 454 | } 455 | 456 | UTEST(string, cstring_push_back) { 457 | cstring_string_type(char) str = NULL; 458 | cstring_assign(str, literal, strlen_of(literal)); 459 | 460 | cstring_push_back(str, 'f'); 461 | ASSERT_EQ(cstring_size(str), strlen_of(literal) + 1); 462 | ASSERT_STREQ(str, "abcdef"); 463 | 464 | cstring_free(str); 465 | 466 | /* -- wide string -- */ 467 | 468 | cstring_string_type(wchar_t) wstr = NULL; 469 | cstring_assign(wstr, wliteral, strlen_of(wliteral)); 470 | 471 | cstring_push_back(wstr, L'f'); 472 | ASSERT_EQ(cstring_size(wstr), strlen_of(wliteral) + 1); 473 | ASSERT_TRUE(wcseq(wstr, L"abcdef")); 474 | 475 | cstring_free(wstr); 476 | 477 | /* -- special cases -- */ 478 | 479 | cstring_string_type(char) nullstr = NULL; 480 | cstring_push_back(nullstr, 'x'); 481 | ASSERT_EQ(cstring_size(nullstr), 1U); 482 | ASSERT_EQ(cstring_capacity(nullstr), 1U); 483 | ASSERT_STREQ(nullstr, "x"); 484 | cstring_free(nullstr); 485 | } 486 | 487 | UTEST(string, cstring_pop_back) { 488 | cstring_string_type(char) str = NULL; 489 | cstring_assign(str, literal, strlen_of(literal)); 490 | 491 | cstring_pop_back(str); 492 | ASSERT_EQ(cstring_size(str), strlen_of(literal) - 1); 493 | ASSERT_EQ(str[cstring_size(str)], '\0'); 494 | 495 | cstring_free(str); 496 | 497 | /* -- wide string -- */ 498 | 499 | cstring_string_type(wchar_t) wstr = NULL; 500 | cstring_assign(wstr, wliteral, strlen_of(wliteral)); 501 | 502 | cstring_pop_back(wstr); 503 | ASSERT_EQ(cstring_size(wstr), strlen_of(wliteral) - 1); 504 | ASSERT_EQ(wstr[cstring_size(wstr)], L'\0'); 505 | 506 | cstring_free(wstr); 507 | 508 | /* -- special cases -- */ 509 | 510 | cstring_string_type(char) nullstr = NULL; 511 | cstring_pop_back(nullstr); 512 | ASSERT_EQ(nullstr, NULL); 513 | 514 | cstring_init(zerolenstr, char); 515 | cstring_pop_back(zerolenstr); 516 | ASSERT_STREQ(zerolenstr, ""); 517 | cstring_free(zerolenstr); 518 | } 519 | 520 | UTEST(string, cstring_append) { 521 | cstring_string_type(char) str = NULL; 522 | cstring_assign(str, literal, strlen_of(literal)); 523 | 524 | cstring_append(str, literal, strlen_of(literal)); 525 | ASSERT_EQ(cstring_size(str), 2 * strlen_of(literal)); 526 | ASSERT_STREQ(str, "abcdeabcde"); 527 | 528 | cstring_free(str); 529 | 530 | /* -- wide string -- */ 531 | 532 | cstring_string_type(wchar_t) wstr = NULL; 533 | cstring_assign(wstr, wliteral, strlen_of(wliteral)); 534 | 535 | cstring_append(wstr, wliteral, strlen_of(wliteral)); 536 | ASSERT_EQ(cstring_size(wstr), 2 * strlen_of(wliteral)); 537 | ASSERT_TRUE(wcseq(wstr, L"abcdeabcde")); 538 | 539 | cstring_free(wstr); 540 | 541 | /* -- special cases -- */ 542 | 543 | cstring_string_type(char) nullstr = NULL; 544 | cstring_append(nullstr, literal, strlen_of(literal)); 545 | ASSERT_STREQ(nullstr, literal); 546 | cstring_free(nullstr); 547 | 548 | cstring_init(zerolenstr, char); 549 | cstring_append(zerolenstr, literal, strlen_of(literal)); 550 | ASSERT_STREQ(zerolenstr, literal); 551 | cstring_free(zerolenstr); 552 | } 553 | 554 | UTEST(string, cstring_replace) { 555 | cstring_string_type(char) str = NULL; 556 | cstring_assign(str, literal, strlen_of(literal)); 557 | 558 | static const char repl[] = "foo"; 559 | cstring_replace(str, 1, 2, repl, strlen_of(repl)); 560 | 561 | ASSERT_EQ(cstring_size(str), strlen_of(literal) + 1); 562 | ASSERT_STREQ(str, "afoode"); 563 | 564 | cstring_free(str); 565 | 566 | /* -- wide string -- */ 567 | 568 | cstring_string_type(wchar_t) wstr = NULL; 569 | cstring_assign(wstr, wliteral, strlen_of(wliteral)); 570 | 571 | static const wchar_t wrepl[] = L"foo"; 572 | cstring_replace(wstr, 1, 2, wrepl, strlen_of(wrepl)); 573 | 574 | ASSERT_EQ(cstring_size(wstr), strlen_of(wliteral) + 1); 575 | ASSERT_TRUE(wcseq(wstr, L"afoode")); 576 | 577 | cstring_free(wstr); 578 | 579 | /* -- special cases -- */ 580 | 581 | cstring_string_type(char) nullstr = NULL; 582 | cstring_replace(nullstr, 0, 0, repl, strlen_of(repl)); 583 | ASSERT_EQ(nullstr, NULL); 584 | 585 | cstring_init(zerolenstr, char); 586 | cstring_replace(zerolenstr, 0, 0, repl, strlen_of(repl)); 587 | ASSERT_STREQ(zerolenstr, repl); 588 | cstring_free(zerolenstr); 589 | } 590 | 591 | UTEST(string, cstring_copy) { 592 | cstring_string_type(char) str = NULL; 593 | cstring_string_type(char) other = NULL; 594 | cstring_assign(str, literal, strlen_of(literal)); 595 | 596 | cstring_copy(str, other); 597 | ASSERT_EQ(cstring_size(str), cstring_size(other)); 598 | ASSERT_EQ(other[cstring_size(other)], '\0'); 599 | ASSERT_STREQ(other, literal); 600 | 601 | cstring_free(other); 602 | cstring_free(str); 603 | 604 | /* -- wide string -- */ 605 | 606 | cstring_string_type(wchar_t) wstr = NULL; 607 | cstring_string_type(wchar_t) wother = NULL; 608 | cstring_assign(wstr, wliteral, strlen_of(wliteral)); 609 | 610 | cstring_copy(wstr, wother); 611 | ASSERT_EQ(cstring_size(wstr), cstring_size(wother)); 612 | ASSERT_EQ(wother[cstring_size(wother)], L'\0'); 613 | ASSERT_TRUE(wcseq(wother, wliteral)); 614 | 615 | cstring_free(wother); 616 | cstring_free(wstr); 617 | 618 | /* -- special cases -- */ 619 | 620 | cstring_string_type(char) nullstr = NULL; 621 | cstring_copy(nullstr, other); 622 | ASSERT_EQ(nullstr, NULL); 623 | ASSERT_EQ(other, NULL); 624 | 625 | cstring_assign(str, literal, strlen_of(literal)); 626 | cstring_assign(other, "a", 1); 627 | cstring_copy(str, other); 628 | ASSERT_EQ(cstring_size(other), strlen_of(literal)); 629 | ASSERT_STREQ(other, literal); 630 | 631 | static const char lit2[] = "1234567890"; 632 | cstring_assign(other, lit2, strlen_of(lit2)); 633 | cstring_copy(str, other); 634 | ASSERT_EQ(cstring_size(other), strlen_of(literal)); 635 | ASSERT_STREQ(other, literal); 636 | 637 | cstring_free(other); 638 | cstring_free(str); 639 | } 640 | 641 | UTEST(string, cstring_resize) { 642 | cstring_string_type(char) str = NULL; 643 | cstring_assign(str, literal, strlen_of(literal)); 644 | 645 | cstring_resize(str, strlen_of(literal) + 1, 'x'); 646 | ASSERT_EQ(cstring_size(str), strlen_of(literal) + 1); 647 | ASSERT_STREQ(str, "abcdex"); 648 | 649 | cstring_resize(str, 4, 'x'); 650 | ASSERT_EQ(cstring_size(str), 4U); 651 | ASSERT_STREQ(str, "abcd"); 652 | 653 | cstring_free(str); 654 | 655 | /* -- wide string -- */ 656 | 657 | cstring_string_type(wchar_t) wstr = NULL; 658 | cstring_assign(wstr, wliteral, strlen_of(wliteral)); 659 | 660 | cstring_resize(wstr, strlen_of(wliteral) + 1, L'x'); 661 | ASSERT_EQ(cstring_size(wstr), strlen_of(wliteral) + 1); 662 | ASSERT_TRUE(wcseq(wstr, L"abcdex")); 663 | 664 | cstring_resize(wstr, 4, L'x'); 665 | ASSERT_EQ(cstring_size(wstr), 4U); 666 | ASSERT_TRUE(wcseq(wstr, L"abcd")); 667 | 668 | cstring_free(wstr); 669 | 670 | /* -- special cases -- */ 671 | 672 | cstring_string_type(char) nullstr = NULL; 673 | cstring_resize(nullstr, 10, 'x'); 674 | ASSERT_STREQ(nullstr, "xxxxxxxxxx"); 675 | cstring_free(nullstr); 676 | 677 | cstring_init(zerolenstr, char); 678 | cstring_resize(zerolenstr, 10, 'x'); 679 | ASSERT_STREQ(zerolenstr, "xxxxxxxxxx"); 680 | cstring_free(zerolenstr); 681 | } 682 | 683 | UTEST(string, cstring_swap) { 684 | cstring_string_type(char) str1 = NULL; 685 | cstring_string_type(char) str2 = NULL; 686 | cstring_assign(str1, literal, 2); 687 | cstring_assign(str2, literal + 2, 3); 688 | 689 | ASSERT_EQ(cstring_size(str1), 2U); 690 | ASSERT_EQ(cstring_size(str2), 3U); 691 | ASSERT_STREQ(str1, "ab"); 692 | ASSERT_STREQ(str2, "cde"); 693 | 694 | cstring_swap(str1, str2); 695 | ASSERT_EQ(cstring_size(str1), 3U); 696 | ASSERT_EQ(cstring_size(str2), 2U); 697 | ASSERT_STREQ(str1, "cde"); 698 | ASSERT_STREQ(str2, "ab"); 699 | 700 | cstring_free(str2); 701 | cstring_free(str1); 702 | 703 | /* -- wide string -- */ 704 | 705 | cstring_string_type(wchar_t) wstr1 = NULL; 706 | cstring_string_type(wchar_t) wstr2 = NULL; 707 | cstring_assign(wstr1, wliteral, 2); 708 | cstring_assign(wstr2, wliteral + 2, 3); 709 | 710 | ASSERT_EQ(cstring_size(wstr1), 2U); 711 | ASSERT_EQ(cstring_size(wstr2), 3U); 712 | ASSERT_TRUE(wcseq(wstr1, L"ab")); 713 | ASSERT_TRUE(wcseq(wstr2, L"cde")); 714 | 715 | cstring_swap(wstr1, wstr2); 716 | ASSERT_EQ(cstring_size(wstr1), 3U); 717 | ASSERT_EQ(cstring_size(wstr2), 2U); 718 | ASSERT_TRUE(wcseq(wstr1, L"cde")); 719 | ASSERT_TRUE(wcseq(wstr2, L"ab")); 720 | 721 | cstring_free(wstr2); 722 | cstring_free(wstr1); 723 | 724 | /* -- special cases -- */ 725 | 726 | ASSERT_EQ(str1, NULL); 727 | ASSERT_EQ(str2, NULL); 728 | cstring_swap(str1, str2); 729 | ASSERT_EQ(str1, NULL); 730 | ASSERT_EQ(str2, NULL); 731 | 732 | cstring_assign(str1, literal, strlen_of(literal)); 733 | cstring_swap(str1, str2); 734 | ASSERT_EQ(str1, NULL); 735 | ASSERT_STREQ(str2, literal); 736 | cstring_swap(str1, str2); 737 | ASSERT_STREQ(str1, literal); 738 | ASSERT_EQ(str2, NULL); 739 | cstring_free(str1); 740 | } 741 | 742 | UTEST(string, cstring_trim) { 743 | cstring_string_type(char) str = NULL; 744 | cstring_assign(str, " ab ", strlen_of(" ab ")); 745 | cstring_trim(str, ' ', 3); 746 | ASSERT_EQ(cstring_size(str), 2U); 747 | ASSERT_STREQ(str, "ab"); 748 | 749 | cstring_assign(str, "ab ", strlen_of("ab ")); 750 | cstring_trim(str, ' ', 1); 751 | ASSERT_EQ(cstring_size(str), 4U); 752 | ASSERT_STREQ(str, "ab "); 753 | cstring_trim(str, ' ', 3); 754 | ASSERT_EQ(cstring_size(str), 2U); 755 | ASSERT_STREQ(str, "ab"); 756 | 757 | cstring_assign(str, " ab", strlen_of(" ab")); 758 | cstring_trim(str, ' ', 2); 759 | ASSERT_EQ(cstring_size(str), 4U); 760 | ASSERT_STREQ(str, " ab"); 761 | cstring_trim(str, ' ', 3); 762 | ASSERT_EQ(cstring_size(str), 2U); 763 | ASSERT_STREQ(str, "ab"); 764 | 765 | cstring_assign(str, " ", strlen_of(" ")); 766 | cstring_trim(str, ' ', 1); 767 | ASSERT_EQ(cstring_size(str), 0U); 768 | ASSERT_STREQ(str, ""); 769 | cstring_assign(str, " ", strlen_of(" ")); 770 | cstring_trim(str, ' ', 2); 771 | ASSERT_EQ(cstring_size(str), 0U); 772 | ASSERT_STREQ(str, ""); 773 | cstring_assign(str, " ", strlen_of(" ")); 774 | cstring_trim(str, ' ', 3); 775 | ASSERT_EQ(cstring_size(str), 0U); 776 | ASSERT_STREQ(str, ""); 777 | 778 | cstring_assign(str, "ab", strlen_of("ab")); 779 | cstring_trim(str, ' ', 3); 780 | ASSERT_EQ(cstring_size(str), 2U); 781 | ASSERT_STREQ(str, "ab"); 782 | cstring_free(str); 783 | 784 | /* -- wide string -- */ 785 | 786 | cstring_string_type(wchar_t) wstr = NULL; 787 | cstring_assign(wstr, L" ab ", strlen_of(L" ab ")); 788 | cstring_trim(wstr, L' ', 3); 789 | ASSERT_EQ(cstring_size(wstr), 2U); 790 | ASSERT_TRUE(wcseq(wstr, L"ab")); 791 | 792 | cstring_assign(wstr, L"ab ", strlen_of(L"ab ")); 793 | cstring_trim(wstr, L' ', 1); 794 | ASSERT_EQ(cstring_size(wstr), 4U); 795 | ASSERT_TRUE(wcseq(wstr, L"ab ")); 796 | cstring_trim(wstr, L' ', 3); 797 | ASSERT_EQ(cstring_size(wstr), 2U); 798 | ASSERT_TRUE(wcseq(wstr, L"ab")); 799 | 800 | cstring_assign(wstr, L" ab", strlen_of(L" ab")); 801 | cstring_trim(wstr, L' ', 2); 802 | ASSERT_EQ(cstring_size(wstr), 4U); 803 | ASSERT_TRUE(wcseq(wstr, L" ab")); 804 | cstring_trim(wstr, L' ', 3); 805 | ASSERT_EQ(cstring_size(wstr), 2U); 806 | ASSERT_TRUE(wcseq(wstr, L"ab")); 807 | 808 | cstring_assign(wstr, L" ", strlen_of(L" ")); 809 | cstring_trim(wstr, L' ', 1); 810 | ASSERT_EQ(cstring_size(wstr), 0U); 811 | ASSERT_TRUE(wcseq(wstr, L"")); 812 | cstring_assign(wstr, L" ", strlen_of(L" ")); 813 | cstring_trim(wstr, L' ', 2); 814 | ASSERT_EQ(cstring_size(wstr), 0U); 815 | ASSERT_TRUE(wcseq(wstr, L"")); 816 | cstring_assign(wstr, L" ", strlen_of(L" ")); 817 | cstring_trim(wstr, L' ', 3); 818 | ASSERT_EQ(cstring_size(wstr), 0U); 819 | ASSERT_TRUE(wcseq(wstr, L"")); 820 | 821 | cstring_assign(wstr, L"ab", strlen_of(L"ab")); 822 | cstring_trim(wstr, L' ', 3); 823 | ASSERT_EQ(cstring_size(wstr), 2U); 824 | ASSERT_TRUE(wcseq(wstr, L"ab")); 825 | cstring_free(wstr); 826 | 827 | /* -- special cases -- */ 828 | 829 | cstring_string_type(char) nullstr = NULL; 830 | cstring_trim(nullstr, ' ', 3); 831 | ASSERT_EQ(cstring_size(nullstr), 0U); 832 | ASSERT_EQ(nullstr, NULL); 833 | } 834 | 835 | UTEST(string, cstring_fix) { 836 | cstring_string_type(char) str = NULL; 837 | cstring_assign(str, "abc", 3); 838 | cstring_fix(str, 5, ' ', 1); 839 | ASSERT_EQ(cstring_size(str), 5U); 840 | ASSERT_STREQ(str, " abc"); 841 | 842 | cstring_assign(str, "abc", 3); 843 | cstring_fix(str, 5, ' ', 2); 844 | ASSERT_EQ(cstring_size(str), 5U); 845 | ASSERT_STREQ(str, "abc "); 846 | 847 | cstring_assign(str, "abc", 3); 848 | cstring_fix(str, 5, ' ', 3); 849 | ASSERT_EQ(cstring_size(str), 5U); 850 | ASSERT_STREQ(str, " abc "); 851 | 852 | cstring_assign(str, "abc", 3); 853 | cstring_fix(str, 6, ' ', 3); 854 | ASSERT_EQ(cstring_size(str), 6U); 855 | ASSERT_STREQ(str, " abc "); 856 | 857 | cstring_assign(str, "abc", 3); 858 | cstring_fix(str, 1, ' ', 1); 859 | ASSERT_EQ(cstring_size(str), 1U); 860 | ASSERT_STREQ(str, "c"); 861 | 862 | cstring_assign(str, "abc", 3); 863 | cstring_fix(str, 1, ' ', 2); 864 | ASSERT_EQ(cstring_size(str), 1U); 865 | ASSERT_STREQ(str, "a"); 866 | 867 | cstring_assign(str, "abc", 3); 868 | cstring_fix(str, 1, ' ', 3); 869 | ASSERT_EQ(cstring_size(str), 1U); 870 | ASSERT_STREQ(str, "b"); 871 | 872 | cstring_free(str); 873 | 874 | /* -- wide string -- */ 875 | 876 | cstring_string_type(wchar_t) wstr = NULL; 877 | cstring_assign(wstr, L"abc", 3); 878 | cstring_fix(wstr, 5, L' ', 1); 879 | ASSERT_EQ(cstring_size(wstr), 5U); 880 | ASSERT_TRUE(wcseq(wstr, L" abc")); 881 | 882 | cstring_assign(wstr, L"abc", 3); 883 | cstring_fix(wstr, 5, L' ', 2); 884 | ASSERT_EQ(cstring_size(wstr), 5U); 885 | ASSERT_TRUE(wcseq(wstr, L"abc ")); 886 | 887 | cstring_assign(wstr, L"abc", 3); 888 | cstring_fix(wstr, 5, L' ', 3); 889 | ASSERT_EQ(cstring_size(wstr), 5U); 890 | ASSERT_TRUE(wcseq(wstr, L" abc ")); 891 | 892 | cstring_assign(wstr, L"abc", 3); 893 | cstring_fix(wstr, 6, L' ', 3); 894 | ASSERT_EQ(cstring_size(wstr), 6U); 895 | ASSERT_TRUE(wcseq(wstr, L" abc ")); 896 | 897 | cstring_assign(wstr, L"abc", 3); 898 | cstring_fix(wstr, 1, L' ', 1); 899 | ASSERT_EQ(cstring_size(wstr), 1U); 900 | ASSERT_TRUE(wcseq(wstr, L"c")); 901 | 902 | cstring_assign(wstr, L"abc", 3); 903 | cstring_fix(wstr, 1, L' ', 2); 904 | ASSERT_EQ(cstring_size(wstr), 1U); 905 | ASSERT_TRUE(wcseq(wstr, L"a")); 906 | 907 | cstring_assign(wstr, L"abc", 3); 908 | cstring_fix(wstr, 1, L' ', 3); 909 | ASSERT_EQ(cstring_size(wstr), 1U); 910 | ASSERT_TRUE(wcseq(wstr, L"b")); 911 | 912 | cstring_free(wstr); 913 | 914 | /* -- special cases -- */ 915 | 916 | cstring_assign(str, "", 0); 917 | cstring_fix(str, 5, ' ', 3); 918 | ASSERT_EQ(cstring_size(str), 5U); 919 | ASSERT_STREQ(str, " "); 920 | 921 | cstring_assign(str, "x", 1); 922 | cstring_fix(str, 0, ' ', 3); 923 | ASSERT_EQ(cstring_size(str), 0U); 924 | ASSERT_STREQ(str, ""); 925 | cstring_free(str); 926 | 927 | cstring_string_type(char) nullstr = NULL; 928 | cstring_fix(nullstr, 5, ' ', 3); 929 | ASSERT_EQ(cstring_size(nullstr), 0U); 930 | ASSERT_EQ(nullstr, NULL); 931 | } 932 | 933 | UTEST(string, cstring_reverse) { 934 | cstring_string_type(char) str = NULL; 935 | cstring_assign(str, "abc", 3); 936 | cstring_reverse(str); 937 | ASSERT_EQ(cstring_size(str), 3U); 938 | ASSERT_STREQ(str, "cba"); 939 | 940 | cstring_assign(str, "ab", 2); 941 | cstring_reverse(str); 942 | ASSERT_EQ(cstring_size(str), 2U); 943 | ASSERT_STREQ(str, "ba"); 944 | 945 | cstring_assign(str, "a", 1); 946 | cstring_reverse(str); 947 | ASSERT_EQ(cstring_size(str), 1U); 948 | ASSERT_STREQ(str, "a"); 949 | cstring_free(str); 950 | 951 | /* -- wide string -- */ 952 | 953 | cstring_string_type(wchar_t) wstr = NULL; 954 | cstring_assign(wstr, L"abc", 3); 955 | cstring_reverse(wstr); 956 | ASSERT_EQ(cstring_size(wstr), 3U); 957 | ASSERT_TRUE(wcseq(wstr, L"cba")); 958 | 959 | cstring_assign(wstr, L"ab", 2); 960 | cstring_reverse(wstr); 961 | ASSERT_EQ(cstring_size(wstr), 2U); 962 | ASSERT_TRUE(wcseq(wstr, L"ba")); 963 | 964 | cstring_assign(wstr, L"a", 1); 965 | cstring_reverse(wstr); 966 | ASSERT_EQ(cstring_size(wstr), 1U); 967 | ASSERT_TRUE(wcseq(wstr, L"a")); 968 | cstring_free(wstr); 969 | 970 | /* -- special cases -- */ 971 | 972 | cstring_assign(str, "", 0); 973 | cstring_reverse(str); 974 | ASSERT_EQ(cstring_size(str), 0U); 975 | ASSERT_STREQ(str, ""); 976 | cstring_free(str); 977 | 978 | cstring_string_type(char) nullstr = NULL; 979 | cstring_reverse(nullstr); 980 | ASSERT_EQ(cstring_size(nullstr), 0U); 981 | ASSERT_EQ(nullstr, NULL); 982 | } 983 | 984 | UTEST(string, cstring_find_rfind) { 985 | ptrdiff_t off; 986 | cstring_string_type(char) str = NULL; 987 | cstring_assign(str, "abcdefghabcdefgh", 16); 988 | 989 | cstring_find(str, 0, "gh", 2, off); 990 | ASSERT_EQ(off, 6); 991 | cstring_find(str, 5, "gh", 2, off); 992 | ASSERT_EQ(off, 6); 993 | cstring_find(str, 6, "gh", 2, off); 994 | ASSERT_EQ(off, 6); 995 | cstring_find(str, 7, "gh", 2, off); 996 | ASSERT_EQ(off, 14); 997 | cstring_find(str, 8, "h", 1, off); 998 | ASSERT_EQ(off, 15); 999 | 1000 | cstring_rfind(str, -1, "gh", 2, off); 1001 | ASSERT_EQ(off, 14); 1002 | cstring_rfind(str, 15, "gh", 2, off); 1003 | ASSERT_EQ(off, 14); 1004 | cstring_rfind(str, 14, "gh", 2, off); 1005 | ASSERT_EQ(off, 14); 1006 | cstring_rfind(str, 13, "gh", 2, off); 1007 | ASSERT_EQ(off, 6); 1008 | cstring_rfind(str, 13, "h", 1, off); 1009 | ASSERT_EQ(off, 7); 1010 | 1011 | cstring_free(str); 1012 | 1013 | /* -- wide string -- */ 1014 | 1015 | cstring_string_type(wchar_t) wstr = NULL; 1016 | cstring_assign(wstr, L"abcdefghabcdefgh", 16); 1017 | 1018 | cstring_find(wstr, 0, L"gh", 2, off); 1019 | ASSERT_EQ(off, 6); 1020 | cstring_find(wstr, 5, L"gh", 2, off); 1021 | ASSERT_EQ(off, 6); 1022 | cstring_find(wstr, 6, L"gh", 2, off); 1023 | ASSERT_EQ(off, 6); 1024 | cstring_find(wstr, 7, L"gh", 2, off); 1025 | ASSERT_EQ(off, 14); 1026 | cstring_find(wstr, 8, L"h", 1, off); 1027 | ASSERT_EQ(off, 15); 1028 | 1029 | cstring_rfind(wstr, -1, L"gh", 2, off); 1030 | ASSERT_EQ(off, 14); 1031 | cstring_rfind(wstr, 15, L"gh", 2, off); 1032 | ASSERT_EQ(off, 14); 1033 | cstring_rfind(wstr, 14, L"gh", 2, off); 1034 | ASSERT_EQ(off, 14); 1035 | cstring_rfind(wstr, 13, L"gh", 2, off); 1036 | ASSERT_EQ(off, 6); 1037 | cstring_rfind(wstr, 13, L"h", 1, off); 1038 | ASSERT_EQ(off, 7); 1039 | 1040 | cstring_free(wstr); 1041 | 1042 | /* -- special cases -- */ 1043 | 1044 | cstring_assign(str, "x", 0); 1045 | cstring_find(str, 0, "", 0, off); 1046 | ASSERT_EQ(off, -1); 1047 | cstring_rfind(str, -1, "", 0, off); 1048 | ASSERT_EQ(off, -1); 1049 | 1050 | cstring_assign(str, "", 0); 1051 | cstring_find(str, 0, "", 0, off); 1052 | ASSERT_EQ(off, -1); 1053 | cstring_rfind(str, -1, "", 0, off); 1054 | ASSERT_EQ(off, -1); 1055 | cstring_free(str); 1056 | 1057 | cstring_string_type(char) nullstr = NULL; 1058 | cstring_find(nullstr, 0, "", 0, off); 1059 | ASSERT_EQ(off, -1); 1060 | cstring_rfind(nullstr, -1, "", 0, off); 1061 | ASSERT_EQ(off, -1); 1062 | } 1063 | 1064 | UTEST(string, cstring_find_first_of) { 1065 | ptrdiff_t off; 1066 | cstring_string_type(char) str = NULL; 1067 | cstring_assign(str, "abcdefghabcdefgh", 16); 1068 | 1069 | cstring_find_first_of(str, 0, "gh", 2, off); 1070 | ASSERT_EQ(off, 6); 1071 | cstring_find_first_of(str, 6, "gh", 2, off); 1072 | ASSERT_EQ(off, 6); 1073 | cstring_find_first_of(str, 7, "gh", 2, off); 1074 | ASSERT_EQ(off, 7); 1075 | cstring_find_first_of(str, 8, "gh", 2, off); 1076 | ASSERT_EQ(off, 14); 1077 | cstring_find_first_of(str, 8, "h", 1, off); 1078 | ASSERT_EQ(off, 15); 1079 | cstring_find_first_of(str, 0, "xh", 2, off); 1080 | ASSERT_EQ(off, 7); 1081 | cstring_find_first_of(str, 0, "xy", 2, off); 1082 | ASSERT_EQ(off, -1); 1083 | 1084 | cstring_free(str); 1085 | 1086 | /* -- wide string -- */ 1087 | 1088 | cstring_string_type(wchar_t) wstr = NULL; 1089 | cstring_assign(wstr, L"abcdefghabcdefgh", 16); 1090 | 1091 | cstring_find_first_of(wstr, 0, L"gh", 2, off); 1092 | ASSERT_EQ(off, 6); 1093 | cstring_find_first_of(wstr, 6, L"gh", 2, off); 1094 | ASSERT_EQ(off, 6); 1095 | cstring_find_first_of(wstr, 7, L"gh", 2, off); 1096 | ASSERT_EQ(off, 7); 1097 | cstring_find_first_of(wstr, 8, L"gh", 2, off); 1098 | ASSERT_EQ(off, 14); 1099 | cstring_find_first_of(wstr, 8, L"h", 1, off); 1100 | ASSERT_EQ(off, 15); 1101 | cstring_find_first_of(wstr, 0, L"xh", 2, off); 1102 | ASSERT_EQ(off, 7); 1103 | cstring_find_first_of(wstr, 0, L"xy", 2, off); 1104 | ASSERT_EQ(off, -1); 1105 | 1106 | cstring_free(wstr); 1107 | } 1108 | 1109 | UTEST(string, cstring_find_first_not_of) { 1110 | ptrdiff_t off; 1111 | cstring_string_type(char) str = NULL; 1112 | cstring_assign(str, "abcdefghabcdefgh", 16); 1113 | 1114 | cstring_find_first_not_of(str, 0, "gh", 2, off); 1115 | ASSERT_EQ(off, 0); 1116 | cstring_find_first_not_of(str, 5, "gh", 2, off); 1117 | ASSERT_EQ(off, 5); 1118 | cstring_find_first_not_of(str, 6, "gh", 2, off); 1119 | ASSERT_EQ(off, 8); 1120 | cstring_find_first_not_of(str, 7, "gh", 2, off); 1121 | ASSERT_EQ(off, 8); 1122 | cstring_find_first_not_of(str, 7, "h", 1, off); 1123 | ASSERT_EQ(off, 8); 1124 | cstring_find_first_not_of(str, 0, "abcdefgh", 8, off); 1125 | ASSERT_EQ(off, -1); 1126 | 1127 | cstring_free(str); 1128 | 1129 | /* -- wide string -- */ 1130 | 1131 | cstring_string_type(wchar_t) wstr = NULL; 1132 | cstring_assign(wstr, L"abcdefghabcdefgh", 16); 1133 | 1134 | cstring_find_first_not_of(wstr, 0, L"gh", 2, off); 1135 | ASSERT_EQ(off, 0); 1136 | cstring_find_first_not_of(wstr, 5, L"gh", 2, off); 1137 | ASSERT_EQ(off, 5); 1138 | cstring_find_first_not_of(wstr, 6, L"gh", 2, off); 1139 | ASSERT_EQ(off, 8); 1140 | cstring_find_first_not_of(wstr, 7, L"gh", 2, off); 1141 | ASSERT_EQ(off, 8); 1142 | cstring_find_first_not_of(wstr, 7, L"h", 1, off); 1143 | ASSERT_EQ(off, 8); 1144 | cstring_find_first_not_of(wstr, 0, L"abcdefgh", 8, off); 1145 | ASSERT_EQ(off, -1); 1146 | 1147 | cstring_free(wstr); 1148 | } 1149 | 1150 | UTEST(string, cstring_find_last_of) { 1151 | ptrdiff_t off; 1152 | cstring_string_type(char) str = NULL; 1153 | cstring_assign(str, "abcdefghabcdefgh", 16); 1154 | 1155 | cstring_find_last_of(str, -1, "gh", 2, off); 1156 | ASSERT_EQ(off, 15); 1157 | cstring_find_last_of(str, 15, "gh", 2, off); 1158 | ASSERT_EQ(off, 15); 1159 | cstring_find_last_of(str, 14, "gh", 2, off); 1160 | ASSERT_EQ(off, 14); 1161 | cstring_find_last_of(str, 13, "gh", 2, off); 1162 | ASSERT_EQ(off, 7); 1163 | cstring_find_last_of(str, 13, "h", 1, off); 1164 | ASSERT_EQ(off, 7); 1165 | cstring_find_last_of(str, -1, "xh", 2, off); 1166 | ASSERT_EQ(off, 15); 1167 | cstring_find_last_of(str, -1, "xy", 2, off); 1168 | ASSERT_EQ(off, -1); 1169 | 1170 | cstring_free(str); 1171 | 1172 | /* -- wide string -- */ 1173 | 1174 | cstring_string_type(wchar_t) wstr = NULL; 1175 | cstring_assign(wstr, L"abcdefghabcdefgh", 16); 1176 | 1177 | cstring_find_last_of(wstr, -1, L"gh", 2, off); 1178 | ASSERT_EQ(off, 15); 1179 | cstring_find_last_of(wstr, 15, L"gh", 2, off); 1180 | ASSERT_EQ(off, 15); 1181 | cstring_find_last_of(wstr, 14, L"gh", 2, off); 1182 | ASSERT_EQ(off, 14); 1183 | cstring_find_last_of(wstr, 13, L"gh", 2, off); 1184 | ASSERT_EQ(off, 7); 1185 | cstring_find_last_of(wstr, 13, L"h", 1, off); 1186 | ASSERT_EQ(off, 7); 1187 | cstring_find_last_of(wstr, -1, L"xh", 2, off); 1188 | ASSERT_EQ(off, 15); 1189 | cstring_find_last_of(wstr, -1, L"xy", 2, off); 1190 | ASSERT_EQ(off, -1); 1191 | 1192 | cstring_free(wstr); 1193 | } 1194 | 1195 | UTEST(string, cstring_find_last_not_of) { 1196 | ptrdiff_t off; 1197 | cstring_string_type(char) str = NULL; 1198 | cstring_assign(str, "abcdefghabcdefgh", 16); 1199 | 1200 | cstring_find_last_not_of(str, -1, "gh", 2, off); 1201 | ASSERT_EQ(off, 13); 1202 | cstring_find_last_not_of(str, 15, "gh", 2, off); 1203 | ASSERT_EQ(off, 13); 1204 | cstring_find_last_not_of(str, 14, "gh", 2, off); 1205 | ASSERT_EQ(off, 13); 1206 | cstring_find_last_not_of(str, 13, "gh", 2, off); 1207 | ASSERT_EQ(off, 13); 1208 | cstring_find_last_not_of(str, 12, "gh", 2, off); 1209 | ASSERT_EQ(off, 12); 1210 | cstring_find_last_not_of(str, 12, "h", 1, off); 1211 | ASSERT_EQ(off, 12); 1212 | cstring_find_last_not_of(str, -1, "abcdefgh", 8, off); 1213 | ASSERT_EQ(off, -1); 1214 | 1215 | cstring_free(str); 1216 | 1217 | /* -- wide string -- */ 1218 | 1219 | cstring_string_type(wchar_t) wstr = NULL; 1220 | cstring_assign(wstr, L"abcdefghabcdefgh", 16); 1221 | 1222 | cstring_find_last_not_of(wstr, -1, L"gh", 2, off); 1223 | ASSERT_EQ(off, 13); 1224 | cstring_find_last_not_of(wstr, 15, L"gh", 2, off); 1225 | ASSERT_EQ(off, 13); 1226 | cstring_find_last_not_of(wstr, 14, L"gh", 2, off); 1227 | ASSERT_EQ(off, 13); 1228 | cstring_find_last_not_of(wstr, 13, L"gh", 2, off); 1229 | ASSERT_EQ(off, 13); 1230 | cstring_find_last_not_of(wstr, 12, L"gh", 2, off); 1231 | ASSERT_EQ(off, 12); 1232 | cstring_find_last_not_of(wstr, 12, L"h", 1, off); 1233 | ASSERT_EQ(off, 12); 1234 | cstring_find_last_not_of(wstr, -1, L"abcdefgh", 8, off); 1235 | ASSERT_EQ(off, -1); 1236 | 1237 | cstring_free(wstr); 1238 | } 1239 | 1240 | UTEST(string, cstring_compare) { 1241 | int res = 999; 1242 | cstring_string_type(char) str1 = NULL; 1243 | cstring_string_type(char) str2 = NULL; 1244 | 1245 | cstring_assign(str1, "abc", 3); 1246 | cstring_assign(str2, "abc", 3); 1247 | cstring_compare(str1, str2, res); 1248 | ASSERT_EQ(res, 0); 1249 | 1250 | str1[2] = 'x'; 1251 | cstring_compare(str1, str2, res); 1252 | ASSERT_EQ(res, 1); 1253 | 1254 | cstring_pop_back(str1); 1255 | cstring_compare(str1, str2, res); 1256 | ASSERT_EQ(res, -1); 1257 | 1258 | cstring_assign(str1, "\xFF", 1); 1259 | cstring_compare(str1, str2, res); 1260 | ASSERT_EQ(res, 1); 1261 | 1262 | cstring_assign(str1, "", 0); 1263 | cstring_assign(str2, "", 0); 1264 | cstring_compare(str1, str2, res); 1265 | ASSERT_EQ(res, 0); 1266 | 1267 | cstring_free(str2); 1268 | cstring_free(str1); 1269 | 1270 | /* -- wide string -- */ 1271 | 1272 | res = 999; 1273 | cstring_string_type(wchar_t) wstr1 = NULL; 1274 | cstring_string_type(wchar_t) wstr2 = NULL; 1275 | cstring_assign(wstr1, L"abc", 3); 1276 | cstring_assign(wstr2, L"abc", 3); 1277 | 1278 | cstring_compare(wstr1, wstr2, res); 1279 | ASSERT_EQ(res, 0); 1280 | 1281 | wstr1[2] = 'x'; 1282 | cstring_compare(wstr1, wstr2, res); 1283 | ASSERT_EQ(res, 1); 1284 | 1285 | cstring_pop_back(wstr1); 1286 | cstring_compare(wstr1, wstr2, res); 1287 | ASSERT_EQ(res, -1); 1288 | 1289 | cstring_assign(wstr1, L"\xFF", 1); 1290 | cstring_compare(wstr1, wstr2, res); 1291 | ASSERT_EQ(res, 1); 1292 | 1293 | cstring_assign(wstr1, L"", 0); 1294 | cstring_assign(wstr2, L"", 0); 1295 | cstring_compare(wstr1, wstr2, res); 1296 | ASSERT_EQ(res, 0); 1297 | 1298 | cstring_free(wstr2); 1299 | cstring_free(wstr1); 1300 | 1301 | /* -- special cases -- */ 1302 | 1303 | res = 999; 1304 | cstring_string_type(char) nullstr1 = NULL; 1305 | cstring_string_type(char) nullstr2 = NULL; 1306 | cstring_compare(nullstr1, nullstr2, res); 1307 | ASSERT_EQ(res, 999); 1308 | } 1309 | 1310 | UTEST(string, cstring_starts_ends_with) { 1311 | int res; 1312 | cstring_string_type(char) str = NULL; 1313 | cstring_assign(str, literal, strlen_of(literal)); 1314 | 1315 | cstring_starts_with(str, "a", 1, res); 1316 | ASSERT_EQ(res, 1); 1317 | cstring_starts_with(str, "ab", 2, res); 1318 | ASSERT_EQ(res, 1); 1319 | cstring_starts_with(str, "abcde", 5, res); 1320 | ASSERT_EQ(res, 1); 1321 | cstring_starts_with(str, "b", 1, res); 1322 | ASSERT_EQ(res, 0); 1323 | 1324 | cstring_ends_with(str, "e", 1, res); 1325 | ASSERT_EQ(res, 1); 1326 | cstring_ends_with(str, "de", 2, res); 1327 | ASSERT_EQ(res, 1); 1328 | cstring_ends_with(str, "abcde", 5, res); 1329 | ASSERT_EQ(res, 1); 1330 | cstring_ends_with(str, "d", 1, res); 1331 | ASSERT_EQ(res, 0); 1332 | 1333 | cstring_free(str); 1334 | 1335 | /* -- wide string -- */ 1336 | 1337 | cstring_string_type(wchar_t) wstr = NULL; 1338 | cstring_assign(wstr, wliteral, strlen_of(wliteral)); 1339 | 1340 | cstring_starts_with(wstr, L"a", 1, res); 1341 | ASSERT_EQ(res, 1); 1342 | cstring_starts_with(wstr, L"ab", 2, res); 1343 | ASSERT_EQ(res, 1); 1344 | cstring_starts_with(wstr, L"abcde", 5, res); 1345 | ASSERT_EQ(res, 1); 1346 | cstring_starts_with(wstr, L"b", 1, res); 1347 | ASSERT_EQ(res, 0); 1348 | 1349 | cstring_ends_with(wstr, L"e", 1, res); 1350 | ASSERT_EQ(res, 1); 1351 | cstring_ends_with(wstr, L"de", 2, res); 1352 | ASSERT_EQ(res, 1); 1353 | cstring_ends_with(wstr, L"abcde", 5, res); 1354 | ASSERT_EQ(res, 1); 1355 | cstring_ends_with(wstr, L"d", 1, res); 1356 | ASSERT_EQ(res, 0); 1357 | 1358 | cstring_free(wstr); 1359 | 1360 | /* -- special cases -- */ 1361 | 1362 | res = 999; 1363 | cstring_string_type(char) nullstr = NULL; 1364 | cstring_starts_with(nullstr, "x", 1, res); 1365 | ASSERT_EQ(res, 0); 1366 | cstring_ends_with(nullstr, L"x", 1, res); 1367 | ASSERT_EQ(res, 0); 1368 | } 1369 | 1370 | UTEST(string, cstring_contains) { 1371 | int res; 1372 | cstring_string_type(char) str = NULL; 1373 | cstring_assign(str, literal, strlen_of(literal)); 1374 | 1375 | cstring_contains(str, "de", 2, res); 1376 | ASSERT_EQ(res, 1); 1377 | cstring_contains(str, "ed", 2, res); 1378 | ASSERT_EQ(res, 0); 1379 | 1380 | cstring_free(str); 1381 | 1382 | /* -- wide string -- */ 1383 | 1384 | cstring_string_type(wchar_t) wstr = NULL; 1385 | cstring_assign(wstr, wliteral, strlen_of(wliteral)); 1386 | 1387 | cstring_contains(wstr, L"de", 2, res); 1388 | ASSERT_EQ(res, 1); 1389 | cstring_contains(wstr, L"ed", 2, res); 1390 | ASSERT_EQ(res, 0); 1391 | 1392 | cstring_free(wstr); 1393 | } 1394 | 1395 | UTEST(string, cstring_substring) { 1396 | cstring_string_type(char) str = NULL; 1397 | cstring_string_type(char) substr = NULL; 1398 | cstring_assign(str, literal, strlen_of(literal)); 1399 | 1400 | cstring_substring(str, 1, 3, substr); 1401 | ASSERT_EQ(cstring_size(substr), 3U); 1402 | ASSERT_STREQ(substr, "bcd"); 1403 | 1404 | cstring_free(substr); 1405 | cstring_free(str); 1406 | 1407 | /* -- wide string -- */ 1408 | 1409 | cstring_string_type(wchar_t) wstr = NULL; 1410 | cstring_string_type(wchar_t) wsubstr = NULL; 1411 | cstring_assign(wstr, wliteral, strlen_of(wliteral)); 1412 | 1413 | cstring_substring(wstr, 1, 3, wsubstr); 1414 | ASSERT_EQ(cstring_size(wsubstr), 3U); 1415 | ASSERT_TRUE(wcseq(wsubstr, L"bcd")); 1416 | 1417 | cstring_free(wsubstr); 1418 | cstring_free(wstr); 1419 | 1420 | /* -- special cases -- */ 1421 | 1422 | cstring_string_type(char) nullstr = NULL; 1423 | cstring_substring(nullstr, 0, 0, substr); 1424 | ASSERT_EQ(nullstr, NULL); 1425 | ASSERT_EQ(substr, NULL); 1426 | 1427 | cstring_init(zerolenstr, char); 1428 | cstring_substring(zerolenstr, 1, 0, substr); 1429 | ASSERT_EQ(substr, NULL); 1430 | 1431 | cstring_substring(zerolenstr, 0, 0, substr); 1432 | ASSERT_STREQ(substr, ""); 1433 | cstring_free(zerolenstr); 1434 | 1435 | cstring_assign(str, literal, strlen_of(literal)); 1436 | cstring_assign(substr, literal, strlen_of(literal)); 1437 | cstring_substring(str, 1, 1000, substr); 1438 | ASSERT_EQ(cstring_size(substr), 4U); 1439 | ASSERT_STREQ(substr, "bcde"); 1440 | 1441 | cstring_assign(substr, "a", 1); 1442 | cstring_substring(str, 1, 1000, substr); 1443 | ASSERT_EQ(cstring_size(substr), 4U); 1444 | ASSERT_STREQ(substr, "bcde"); 1445 | 1446 | cstring_free(substr); 1447 | cstring_free(str); 1448 | } 1449 | 1450 | UTEST(array, cstring_split) { 1451 | int i; 1452 | cstring_string_type(char) str = NULL; 1453 | cstring_array_type(char) arr = NULL; 1454 | 1455 | cstring_assign(str, "a;b;c", 5); 1456 | cstring_split(str, -1, ";", 1, arr); 1457 | ASSERT_EQ(cstring_array_size(arr), 3U); 1458 | ASSERT_EQ(cstring_array_capacity(arr), 63U); 1459 | ASSERT_EQ(cstring_size(arr[0]), 1U); 1460 | ASSERT_STREQ(cstring_array_at(arr, 0), "a"); 1461 | ASSERT_EQ(cstring_size(arr[1]), 1U); 1462 | ASSERT_STREQ(cstring_array_at(arr, 1), "b"); 1463 | ASSERT_EQ(cstring_size(arr[2]), 1U); 1464 | ASSERT_STREQ(cstring_array_at(arr, 2), "c"); 1465 | 1466 | cstring_split(str, 2, ";", 1, arr); 1467 | ASSERT_EQ(cstring_array_size(arr), 2U); 1468 | ASSERT_EQ(cstring_size(arr[0]), 1U); 1469 | ASSERT_STREQ(arr[0], "a"); 1470 | ASSERT_EQ(cstring_size(arr[1]), 3U); 1471 | ASSERT_STREQ(arr[1], "b;c"); 1472 | 1473 | cstring_split(str, 1000, ";", 1, arr); 1474 | ASSERT_EQ(cstring_array_size(arr), 3U); 1475 | ASSERT_EQ(cstring_size(arr[0]), 1U); 1476 | ASSERT_STREQ(arr[0], "a"); 1477 | ASSERT_EQ(cstring_size(arr[1]), 1U); 1478 | ASSERT_STREQ(arr[1], "b"); 1479 | ASSERT_EQ(cstring_size(arr[2]), 1U); 1480 | ASSERT_STREQ(arr[2], "c"); 1481 | 1482 | cstring_assign(str, ";;;;", 4); 1483 | cstring_split(str, -1, ";", 1, arr); 1484 | ASSERT_EQ(cstring_array_size(arr), 5U); 1485 | for (i = 0; i < 5; ++i) { 1486 | ASSERT_EQ(cstring_size(arr[i]), 0U); 1487 | ASSERT_STREQ(arr[i], ""); 1488 | } 1489 | 1490 | cstring_assign(str, ";b;", 3); 1491 | cstring_split(str, -1, ";", 1, arr); 1492 | ASSERT_EQ(cstring_array_size(arr), 3U); 1493 | ASSERT_EQ(cstring_size(arr[0]), 0U); 1494 | ASSERT_STREQ(arr[0], ""); 1495 | ASSERT_EQ(cstring_size(arr[1]), 1U); 1496 | ASSERT_STREQ(arr[1], "b"); 1497 | ASSERT_EQ(cstring_size(arr[2]), 0U); 1498 | ASSERT_STREQ(arr[2], ""); 1499 | 1500 | cstring_assign(str, "abc", 3); 1501 | cstring_split(str, -1, ";", 1, arr); 1502 | ASSERT_EQ(cstring_array_size(arr), 1U); 1503 | ASSERT_EQ(cstring_size(arr[0]), 3U); 1504 | ASSERT_STREQ(arr[0], "abc"); 1505 | 1506 | cstring_assign(str, "abc;defg", 8); 1507 | cstring_split(str, -1, ";", 1, arr); 1508 | ASSERT_EQ(cstring_array_size(arr), 2U); 1509 | ASSERT_EQ(cstring_size(arr[0]), 3U); 1510 | ASSERT_STREQ(arr[0], "abc"); 1511 | ASSERT_EQ(cstring_size(arr[1]), 4U); 1512 | ASSERT_STREQ(arr[1], "defg"); 1513 | 1514 | cstring_assign(str, "a;;b;;c", 7); 1515 | cstring_split(str, -1, ";", 1, arr); 1516 | ASSERT_EQ(cstring_array_size(arr), 5U); 1517 | ASSERT_EQ(cstring_size(arr[0]), 1U); 1518 | ASSERT_STREQ(arr[0], "a"); 1519 | ASSERT_EQ(cstring_size(arr[1]), 0U); 1520 | ASSERT_STREQ(arr[1], ""); 1521 | ASSERT_EQ(cstring_size(arr[2]), 1U); 1522 | ASSERT_STREQ(arr[2], "b"); 1523 | ASSERT_EQ(cstring_size(arr[3]), 0U); 1524 | ASSERT_STREQ(arr[3], ""); 1525 | ASSERT_EQ(cstring_size(arr[4]), 1U); 1526 | ASSERT_STREQ(arr[4], "c"); 1527 | 1528 | cstring_split(str, -1, ";;", 2, arr); 1529 | ASSERT_EQ(cstring_array_size(arr), 3U); 1530 | ASSERT_EQ(cstring_size(arr[0]), 1U); 1531 | ASSERT_STREQ(arr[0], "a"); 1532 | ASSERT_EQ(cstring_size(arr[1]), 1U); 1533 | ASSERT_STREQ(arr[1], "b"); 1534 | ASSERT_EQ(cstring_size(arr[2]), 1U); 1535 | ASSERT_STREQ(arr[2], "c"); 1536 | 1537 | cstring_array_free(arr); 1538 | cstring_free(str); 1539 | 1540 | /* -- wide string -- */ 1541 | 1542 | cstring_string_type(wchar_t) wstr = NULL; 1543 | cstring_array_type(wchar_t) warr = NULL; 1544 | 1545 | cstring_assign(wstr, L"a;b;c", 5); 1546 | cstring_split(wstr, -1, L";", 1, warr); 1547 | ASSERT_EQ(cstring_array_size(warr), 3U); 1548 | ASSERT_EQ(cstring_array_capacity(warr), 63U); 1549 | ASSERT_EQ(cstring_size(warr[0]), 1U); 1550 | ASSERT_TRUE(wcseq(warr[0], L"a")); 1551 | ASSERT_EQ(cstring_size(warr[1]), 1U); 1552 | ASSERT_TRUE(wcseq(warr[1], L"b")); 1553 | ASSERT_EQ(cstring_size(warr[2]), 1U); 1554 | ASSERT_TRUE(wcseq(warr[2], L"c")); 1555 | 1556 | cstring_split(wstr, 2, L";", 1, warr); 1557 | ASSERT_EQ(cstring_array_size(warr), 2U); 1558 | ASSERT_EQ(cstring_size(warr[0]), 1U); 1559 | ASSERT_TRUE(wcseq(warr[0], L"a")); 1560 | ASSERT_EQ(cstring_size(warr[1]), 3U); 1561 | ASSERT_TRUE(wcseq(warr[1], L"b;c")); 1562 | 1563 | cstring_split(wstr, 1000, L";", 1, warr); 1564 | ASSERT_EQ(cstring_array_size(warr), 3U); 1565 | ASSERT_EQ(cstring_size(warr[0]), 1U); 1566 | ASSERT_TRUE(wcseq(warr[0], L"a")); 1567 | ASSERT_EQ(cstring_size(warr[1]), 1U); 1568 | ASSERT_TRUE(wcseq(warr[1], L"b")); 1569 | ASSERT_EQ(cstring_size(warr[2]), 1U); 1570 | ASSERT_TRUE(wcseq(warr[2], L"c")); 1571 | 1572 | cstring_assign(wstr, L";;;;", 4); 1573 | cstring_split(wstr, -1, L";", 1, warr); 1574 | ASSERT_EQ(cstring_array_size(warr), 5U); 1575 | for (i = 0; i < 5; ++i) { 1576 | ASSERT_EQ(cstring_size(warr[i]), 0U); 1577 | ASSERT_TRUE(wcseq(warr[i], L"")); 1578 | } 1579 | 1580 | cstring_assign(wstr, L";b;", 3); 1581 | cstring_split(wstr, -1, L";", 1, warr); 1582 | ASSERT_EQ(cstring_array_size(warr), 3U); 1583 | ASSERT_EQ(cstring_size(warr[0]), 0U); 1584 | ASSERT_TRUE(wcseq(warr[0], L"")); 1585 | ASSERT_EQ(cstring_size(warr[1]), 1U); 1586 | ASSERT_TRUE(wcseq(warr[1], L"b")); 1587 | ASSERT_EQ(cstring_size(warr[2]), 0U); 1588 | ASSERT_TRUE(wcseq(warr[2], L"")); 1589 | 1590 | cstring_assign(wstr, L"abc", 3); 1591 | cstring_split(wstr, -1, L";", 1, warr); 1592 | ASSERT_EQ(cstring_array_size(warr), 1U); 1593 | ASSERT_EQ(cstring_size(warr[0]), 3U); 1594 | ASSERT_TRUE(wcseq(warr[0], L"abc")); 1595 | 1596 | cstring_assign(wstr, L"abc;defg", 8); 1597 | cstring_split(wstr, -1, L";", 1, warr); 1598 | ASSERT_EQ(cstring_array_size(warr), 2U); 1599 | ASSERT_EQ(cstring_size(warr[0]), 3U); 1600 | ASSERT_TRUE(wcseq(warr[0], L"abc")); 1601 | ASSERT_EQ(cstring_size(warr[1]), 4U); 1602 | ASSERT_TRUE(wcseq(warr[1], L"defg")); 1603 | 1604 | cstring_assign(wstr, L"a;;b;;c", 7); 1605 | cstring_split(wstr, -1, L";", 1, warr); 1606 | ASSERT_EQ(cstring_array_size(warr), 5U); 1607 | ASSERT_EQ(cstring_size(warr[0]), 1U); 1608 | ASSERT_TRUE(wcseq(warr[0], L"a")); 1609 | ASSERT_EQ(cstring_size(warr[1]), 0U); 1610 | ASSERT_TRUE(wcseq(warr[1], L"")); 1611 | ASSERT_EQ(cstring_size(warr[2]), 1U); 1612 | ASSERT_TRUE(wcseq(warr[2], L"b")); 1613 | ASSERT_EQ(cstring_size(warr[3]), 0U); 1614 | ASSERT_TRUE(wcseq(warr[3], L"")); 1615 | ASSERT_EQ(cstring_size(warr[4]), 1U); 1616 | ASSERT_TRUE(wcseq(warr[4], L"c")); 1617 | 1618 | cstring_split(wstr, -1, L";;", 2, warr); 1619 | ASSERT_EQ(cstring_array_size(warr), 3U); 1620 | ASSERT_EQ(cstring_size(warr[0]), 1U); 1621 | ASSERT_TRUE(wcseq(warr[0], L"a")); 1622 | ASSERT_EQ(cstring_size(warr[1]), 1U); 1623 | ASSERT_TRUE(wcseq(warr[1], L"b")); 1624 | ASSERT_EQ(cstring_size(warr[2]), 1U); 1625 | ASSERT_TRUE(wcseq(warr[2], L"c")); 1626 | 1627 | cstring_array_free(warr); 1628 | cstring_free(wstr); 1629 | 1630 | /* -- special cases -- */ 1631 | 1632 | cstring_string_type(char) nullstr = NULL; 1633 | cstring_array_type(char) nullarr = NULL; 1634 | cstring_split(nullstr, -1, L";", 2, nullarr); 1635 | ASSERT_EQ(nullarr, NULL); 1636 | } 1637 | 1638 | UTEST(array, cstring_array_front_at_back_begin_end) { 1639 | cstring_string_type(char) str = NULL; 1640 | cstring_array_type(char) arr = NULL; 1641 | 1642 | cstring_assign(str, "ab_cd_ef", 8); 1643 | cstring_split(str, -1, "_", 1, arr); 1644 | 1645 | cstring_free(str); 1646 | 1647 | str = cstring_array_front(arr); 1648 | ASSERT_TRUE(str); 1649 | ASSERT_STREQ(str, "ab"); 1650 | 1651 | str = cstring_array_at(arr, 1); 1652 | ASSERT_TRUE(str); 1653 | ASSERT_STREQ(str, "cd"); 1654 | 1655 | str = cstring_array_back(arr); 1656 | ASSERT_TRUE(str); 1657 | ASSERT_STREQ(str, "ef"); 1658 | 1659 | cstring_array_iterator(char) it = cstring_array_begin(arr); 1660 | cstring_array_iterator(char) end = cstring_array_end(arr); 1661 | ASSERT_TRUE(it && end); 1662 | for (; it < end; ++it) { 1663 | ASSERT_EQ(cstring_size(*it), 2U); 1664 | } 1665 | 1666 | cstring_array_free(arr); 1667 | 1668 | /* -- wide string -- */ 1669 | 1670 | cstring_string_type(wchar_t) wstr = NULL; 1671 | cstring_array_type(wchar_t) warr = NULL; 1672 | 1673 | cstring_assign(wstr, L"ab_cd_ef", 8); 1674 | cstring_split(wstr, -1, L"_", 1, warr); 1675 | 1676 | cstring_free(wstr); 1677 | 1678 | wstr = cstring_array_front(warr); 1679 | ASSERT_TRUE(wstr); 1680 | ASSERT_TRUE(wcseq(wstr, L"ab")); 1681 | 1682 | wstr = cstring_array_at(warr, 1); 1683 | ASSERT_TRUE(wstr); 1684 | ASSERT_TRUE(wcseq(wstr, L"cd")); 1685 | 1686 | wstr = cstring_array_back(warr); 1687 | ASSERT_TRUE(wstr); 1688 | ASSERT_TRUE(wcseq(wstr, L"ef")); 1689 | 1690 | cstring_array_iterator(wchar_t) wit = cstring_array_begin(warr); 1691 | cstring_array_iterator(wchar_t) wend = cstring_array_end(warr); 1692 | ASSERT_TRUE(wit && wend); 1693 | for (; wit < wend; ++wit) { 1694 | ASSERT_EQ(cstring_size(*wit), 2U); 1695 | } 1696 | 1697 | cstring_array_free(warr); 1698 | } 1699 | 1700 | UTEST(array, cstring_array_empty_max_size) { 1701 | cstring_array_type(char) arr = NULL; 1702 | ASSERT_TRUE(cstring_array_empty(arr)); 1703 | const size_t ptr_siz = sizeof(char *); 1704 | if (ptr_siz == 4) { 1705 | ASSERT_EQ(cstring_array_max_size(char), 536870907U); 1706 | } else { 1707 | ASSERT_EQ_MSG(8U, ptr_siz, "unexpected pointer size"); 1708 | ASSERT_EQ(cstring_array_max_size(char), 1152921504606846971ULL); 1709 | } 1710 | 1711 | /* -- wide string -- */ 1712 | 1713 | cstring_array_type(wchar_t) warr = NULL; 1714 | ASSERT_TRUE(cstring_array_empty(warr)); 1715 | const size_t wptr_siz = sizeof(wchar_t *); 1716 | if (wptr_siz == 4) { 1717 | ASSERT_EQ(cstring_array_max_size(wchar_t), 536870907U); 1718 | } else { 1719 | ASSERT_EQ_MSG(8U, wptr_siz, "unexpected pointer size"); 1720 | ASSERT_EQ(cstring_array_max_size(wchar_t), 1152921504606846971ULL); 1721 | } 1722 | } 1723 | 1724 | UTEST(array, cstring_array_reserve_shrink_to_fit) { 1725 | cstring_array_type(char) arr = NULL; 1726 | cstring_array_reserve(arr, 50); 1727 | ASSERT_TRUE(cstring_array_empty(arr)); 1728 | ASSERT_EQ(cstring_array_capacity(arr), 50U); 1729 | cstring_array_shrink_to_fit(arr); 1730 | ASSERT_EQ(cstring_array_capacity(arr), 0U); 1731 | cstring_array_free(arr); 1732 | 1733 | /* -- wide string -- */ 1734 | 1735 | cstring_array_type(wchar_t) warr = NULL; 1736 | cstring_array_reserve(warr, 50); 1737 | ASSERT_TRUE(cstring_array_empty(warr)); 1738 | ASSERT_EQ(cstring_array_capacity(warr), 50U); 1739 | cstring_array_shrink_to_fit(warr); 1740 | ASSERT_EQ(cstring_array_capacity(warr), 0U); 1741 | cstring_array_free(warr); 1742 | } 1743 | 1744 | UTEST(array, cstring_array_insert_clear) { 1745 | cstring_string_type(char) str = NULL; 1746 | cstring_array_type(char) arr = NULL; 1747 | 1748 | cstring_assign(str, "aa;cc;dd", 8); 1749 | cstring_split(str, -1, ";", 1, arr); 1750 | 1751 | cstring_free(str); 1752 | 1753 | ASSERT_EQ(cstring_array_size(arr), 3U); 1754 | cstring_array_insert(arr, 1, "bb", 2); 1755 | ASSERT_EQ(cstring_array_size(arr), 4U); 1756 | 1757 | cstring_array_clear(arr); 1758 | ASSERT_EQ(cstring_array_capacity(arr), 63U); 1759 | ASSERT_EQ(cstring_array_size(arr), 0U); 1760 | cstring_array_free(arr); 1761 | } 1762 | 1763 | UTEST(array, cstring_array_erase) { 1764 | cstring_string_type(char) str = NULL; 1765 | cstring_array_type(char) arr = NULL; 1766 | 1767 | cstring_assign(str, "aa;bb;cc;dd", 11); 1768 | cstring_split(str, -1, ";", 1, arr); 1769 | 1770 | cstring_free(str); 1771 | 1772 | ASSERT_EQ(cstring_array_size(arr), 4U); 1773 | cstring_array_erase(arr, 1, 2); 1774 | ASSERT_EQ(cstring_array_size(arr), 2U); 1775 | ASSERT_STREQ(arr[0], "aa"); 1776 | ASSERT_STREQ(arr[1], "dd"); 1777 | 1778 | cstring_array_free(arr); 1779 | } 1780 | 1781 | UTEST(array, cstring_array_push_pop_back) { 1782 | cstring_array_type(char) arr = NULL; 1783 | 1784 | cstring_array_push_back(arr, "abc", 3); 1785 | ASSERT_EQ(cstring_array_size(arr), 1U); 1786 | ASSERT_EQ(cstring_size(arr[0]), 3U); 1787 | 1788 | cstring_array_push_back(arr, "defg", 4); 1789 | ASSERT_EQ(cstring_array_size(arr), 2U); 1790 | ASSERT_EQ(cstring_size(arr[1]), 4U); 1791 | 1792 | cstring_array_pop_back(arr); 1793 | ASSERT_EQ(cstring_array_size(arr), 1U); 1794 | 1795 | cstring_array_pop_back(arr); 1796 | ASSERT_EQ(cstring_array_size(arr), 0U); 1797 | 1798 | cstring_array_free(arr); 1799 | } 1800 | 1801 | UTEST(array, cstring_array_copy_resize_swap) { 1802 | cstring_string_type(char) str = NULL; 1803 | cstring_array_type(char) from = NULL; 1804 | cstring_array_type(char) to = NULL; 1805 | 1806 | cstring_assign(str, "aa;cc;dd", 8); 1807 | cstring_split(str, -1, ";", 1, from); 1808 | 1809 | cstring_free(str); 1810 | 1811 | cstring_array_copy(from, to); 1812 | ASSERT_EQ(cstring_array_size(to), 3U); 1813 | 1814 | cstring_array_resize(to, 4, "ee", 2); 1815 | ASSERT_EQ(cstring_array_size(to), 4U); 1816 | ASSERT_STREQ(to[3], "ee"); 1817 | 1818 | cstring_array_resize(to, 2, "", 0); 1819 | ASSERT_EQ(cstring_array_size(to), 2U); 1820 | 1821 | cstring_array_swap(from, to); 1822 | ASSERT_EQ(cstring_array_size(from), 2U); 1823 | 1824 | cstring_array_free(to); 1825 | cstring_array_free(from); 1826 | } 1827 | 1828 | UTEST(array, cstring_array_slice) { 1829 | cstring_string_type(char) str = NULL; 1830 | cstring_array_type(char) from = NULL; 1831 | cstring_array_type(char) to = NULL; 1832 | 1833 | cstring_assign(str, "aa;bb;cc;dd", 11); 1834 | cstring_split(str, -1, ";", 1, from); 1835 | 1836 | cstring_free(str); 1837 | 1838 | cstring_array_slice(from, 1, 2, to); 1839 | ASSERT_EQ(cstring_array_size(to), 2U); 1840 | ASSERT_STREQ(to[0], "bb"); 1841 | ASSERT_STREQ(to[1], "cc"); 1842 | 1843 | cstring_array_slice(from, 0, 4, to); 1844 | ASSERT_EQ(cstring_array_size(to), 4U); 1845 | ASSERT_STREQ(to[0], "aa"); 1846 | ASSERT_STREQ(to[3], "dd"); 1847 | 1848 | cstring_array_slice(from, 3, 100, to); 1849 | ASSERT_EQ(cstring_array_size(to), 1U); 1850 | ASSERT_STREQ(to[0], "dd"); 1851 | 1852 | cstring_array_free(to); 1853 | cstring_array_free(from); 1854 | } 1855 | 1856 | UTEST(array, cstring_array_join) { 1857 | cstring_string_type(char) str = NULL; 1858 | cstring_array_type(char) arr = NULL; 1859 | 1860 | cstring_assign(str, "aa;bb;cc;dd", 11); 1861 | ASSERT_EQ(cstring_size(str), 11U); 1862 | ASSERT_EQ(cstring_capacity(str), 11U); 1863 | 1864 | cstring_split(str, -1, ";", 1, arr); 1865 | ASSERT_EQ(cstring_array_size(arr), 4U); 1866 | 1867 | cstring_array_join(arr, ",", 1, str); 1868 | ASSERT_EQ(cstring_size(str), 11U); 1869 | ASSERT_EQ(cstring_capacity(str), 11U); 1870 | ASSERT_STREQ(str, "aa,bb,cc,dd"); 1871 | 1872 | cstring_array_join(arr, ",,", 2, str); 1873 | ASSERT_EQ(cstring_size(str), 14U); 1874 | ASSERT_EQ(cstring_capacity(str), 14U); 1875 | ASSERT_STREQ(str, "aa,,bb,,cc,,dd"); 1876 | 1877 | cstring_array_join(arr, "", 0, str); 1878 | ASSERT_EQ(cstring_size(str), 8U); 1879 | ASSERT_EQ(cstring_capacity(str), 14U); 1880 | ASSERT_STREQ(str, "aabbccdd"); 1881 | 1882 | cstring_array_free(arr); 1883 | 1884 | cstring_array_join(arr, ",", 1, str); 1885 | ASSERT_TRUE(cstring_empty(str)); 1886 | ASSERT_EQ(cstring_capacity(str), 14U); 1887 | ASSERT_STREQ(str, ""); 1888 | 1889 | cstring_free(str); 1890 | } 1891 | 1892 | UTEST_MAIN() 1893 | -------------------------------------------------------------------------------- /utest/utest.h: -------------------------------------------------------------------------------- 1 | /* 2 | The latest version of this library is available on GitHub; 3 | https://github.com/sheredom/utest.h 4 | */ 5 | 6 | /* 7 | This is free and unencumbered software released into the public domain. 8 | 9 | Anyone is free to copy, modify, publish, use, compile, sell, or 10 | distribute this software, either in source code form or as a compiled 11 | binary, for any purpose, commercial or non-commercial, and by any 12 | means. 13 | 14 | In jurisdictions that recognize copyright laws, the author or authors 15 | of this software dedicate any and all copyright interest in the 16 | software to the public domain. We make this dedication for the benefit 17 | of the public at large and to the detriment of our heirs and 18 | successors. We intend this dedication to be an overt act of 19 | relinquishment in perpetuity of all present and future rights to this 20 | software under copyright law. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 25 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 26 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 27 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 28 | OTHER DEALINGS IN THE SOFTWARE. 29 | 30 | For more information, please refer to 31 | */ 32 | 33 | #ifndef SHEREDOM_UTEST_H_INCLUDED 34 | #define SHEREDOM_UTEST_H_INCLUDED 35 | 36 | #ifdef _MSC_VER 37 | /* 38 | Disable warning about not inlining 'inline' functions. 39 | */ 40 | #pragma warning(disable : 4710) 41 | 42 | /* 43 | Disable warning about inlining functions that are not marked 'inline'. 44 | */ 45 | #pragma warning(disable : 4711) 46 | 47 | /* 48 | Disable warning for alignment padding added 49 | */ 50 | #pragma warning(disable : 4820) 51 | 52 | #if _MSC_VER > 1900 53 | /* 54 | Disable warning about preprocessor macros not being defined in MSVC headers. 55 | */ 56 | #pragma warning(disable : 4668) 57 | 58 | /* 59 | Disable warning about no function prototype given in MSVC headers. 60 | */ 61 | #pragma warning(disable : 4255) 62 | 63 | /* 64 | Disable warning about pointer or reference to potentially throwing function. 65 | */ 66 | #pragma warning(disable : 5039) 67 | 68 | /* 69 | Disable warning about macro expansion producing 'defined' has undefined 70 | behavior. 71 | */ 72 | #pragma warning(disable : 5105) 73 | #endif 74 | 75 | #if _MSC_VER > 1930 76 | /* 77 | Disable warning about 'const' variable is not used. 78 | */ 79 | #pragma warning(disable : 5264) 80 | #endif 81 | 82 | #pragma warning(push, 1) 83 | #endif 84 | 85 | #if defined(_MSC_VER) && (_MSC_VER < 1920) 86 | typedef __int64 utest_int64_t; 87 | typedef unsigned __int64 utest_uint64_t; 88 | typedef unsigned __int32 utest_uint32_t; 89 | #else 90 | #include 91 | typedef int64_t utest_int64_t; 92 | typedef uint64_t utest_uint64_t; 93 | typedef uint32_t utest_uint32_t; 94 | #endif 95 | 96 | #include 97 | #include 98 | #include 99 | #include 100 | #include 101 | 102 | #if defined(__cplusplus) 103 | #if defined(_MSC_VER) && !defined(_CPPUNWIND) 104 | /* We're on MSVC and the compiler is compiling without exception support! */ 105 | #elif !defined(_MSC_VER) && !defined(__EXCEPTIONS) 106 | /* We're on a GCC/Clang compiler that doesn't have exception support! */ 107 | #else 108 | #define UTEST_HAS_EXCEPTIONS 1 109 | #endif 110 | #endif 111 | 112 | #if defined(UTEST_HAS_EXCEPTIONS) 113 | #include 114 | #endif 115 | 116 | #if defined(_MSC_VER) 117 | #pragma warning(pop) 118 | #endif 119 | 120 | #if defined(__cplusplus) 121 | #define UTEST_C_FUNC extern "C" 122 | #else 123 | #define UTEST_C_FUNC 124 | #endif 125 | 126 | #define UTEST_TEST_PASSED (0) 127 | #define UTEST_TEST_FAILURE (1) 128 | #define UTEST_TEST_SKIPPED (2) 129 | 130 | #if defined(__TINYC__) 131 | #define UTEST_ATTRIBUTE(a) __attribute((a)) 132 | #else 133 | #define UTEST_ATTRIBUTE(a) __attribute__((a)) 134 | #endif 135 | 136 | #if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__) 137 | 138 | #if defined(__MINGW64__) || defined(__MINGW32__) 139 | #pragma GCC diagnostic push 140 | #pragma GCC diagnostic ignored "-Wpragmas" 141 | #pragma GCC diagnostic ignored "-Wunknown-pragmas" 142 | #endif 143 | 144 | #if defined(_WINDOWS_) || defined(_WINDOWS_H) 145 | typedef LARGE_INTEGER utest_large_integer; 146 | #else 147 | // use old QueryPerformanceCounter definitions (not sure is this needed in some 148 | // edge cases or not) on Win7 with VS2015 these extern declaration cause "second 149 | // C linkage of overloaded function not allowed" error 150 | typedef union { 151 | struct { 152 | unsigned long LowPart; 153 | long HighPart; 154 | } DUMMYSTRUCTNAME; 155 | struct { 156 | unsigned long LowPart; 157 | long HighPart; 158 | } u; 159 | utest_int64_t QuadPart; 160 | } utest_large_integer; 161 | 162 | UTEST_C_FUNC __declspec(dllimport) int __stdcall QueryPerformanceCounter( 163 | utest_large_integer *); 164 | UTEST_C_FUNC __declspec(dllimport) int __stdcall QueryPerformanceFrequency( 165 | utest_large_integer *); 166 | 167 | #if defined(__MINGW64__) || defined(__MINGW32__) 168 | #pragma GCC diagnostic pop 169 | #endif 170 | #endif 171 | 172 | #elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ 173 | defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun__) || \ 174 | defined(__HAIKU__) 175 | /* 176 | slightly obscure include here - we need to include glibc's features.h, but 177 | we don't want to just include a header that might not be defined for other 178 | c libraries like musl. Instead we include limits.h, which we know on all 179 | glibc distributions includes features.h 180 | */ 181 | #include 182 | 183 | #if defined(__GLIBC__) && defined(__GLIBC_MINOR__) 184 | #include 185 | 186 | #if ((2 < __GLIBC__) || ((2 == __GLIBC__) && (17 <= __GLIBC_MINOR__))) 187 | /* glibc is version 2.17 or above, so we can just use clock_gettime */ 188 | #define UTEST_USE_CLOCKGETTIME 189 | #else 190 | #include 191 | #include 192 | #endif 193 | #else // Other libc implementations 194 | #include 195 | #define UTEST_USE_CLOCKGETTIME 196 | #endif 197 | 198 | #elif defined(__APPLE__) 199 | #include 200 | #endif 201 | 202 | #if defined(_MSC_VER) && (_MSC_VER < 1920) 203 | #define UTEST_PRId64 "I64d" 204 | #define UTEST_PRIu64 "I64u" 205 | #else 206 | #include 207 | 208 | #define UTEST_PRId64 PRId64 209 | #define UTEST_PRIu64 PRIu64 210 | #endif 211 | 212 | #if defined(__cplusplus) 213 | #define UTEST_INLINE inline 214 | 215 | #if defined(__clang__) 216 | #define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ 217 | _Pragma("clang diagnostic push") \ 218 | _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") 219 | 220 | #define UTEST_INITIALIZER_END_DISABLE_WARNINGS _Pragma("clang diagnostic pop") 221 | #else 222 | #define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS 223 | #define UTEST_INITIALIZER_END_DISABLE_WARNINGS 224 | #endif 225 | 226 | #define UTEST_INITIALIZER(f) \ 227 | struct f##_cpp_struct { \ 228 | f##_cpp_struct(); \ 229 | }; \ 230 | UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS static f##_cpp_struct \ 231 | f##_cpp_global UTEST_INITIALIZER_END_DISABLE_WARNINGS; \ 232 | f##_cpp_struct::f##_cpp_struct() 233 | #elif defined(_MSC_VER) 234 | #define UTEST_INLINE __forceinline 235 | 236 | #if defined(_WIN64) 237 | #define UTEST_SYMBOL_PREFIX 238 | #else 239 | #define UTEST_SYMBOL_PREFIX "_" 240 | #endif 241 | 242 | #if defined(__clang__) 243 | #define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ 244 | _Pragma("clang diagnostic push") \ 245 | _Pragma("clang diagnostic ignored \"-Wmissing-variable-declarations\"") 246 | 247 | #define UTEST_INITIALIZER_END_DISABLE_WARNINGS _Pragma("clang diagnostic pop") 248 | #else 249 | #define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS 250 | #define UTEST_INITIALIZER_END_DISABLE_WARNINGS 251 | #endif 252 | 253 | #pragma section(".CRT$XCU", read) 254 | #define UTEST_INITIALIZER(f) \ 255 | static void __cdecl f(void); \ 256 | UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ 257 | __pragma(comment(linker, "/include:" UTEST_SYMBOL_PREFIX #f "_")) \ 258 | UTEST_C_FUNC \ 259 | __declspec(allocate(".CRT$XCU")) void(__cdecl * f##_)(void) = f; \ 260 | UTEST_INITIALIZER_END_DISABLE_WARNINGS \ 261 | static void __cdecl f(void) 262 | #else 263 | #if defined(__linux__) 264 | #if defined(__clang__) 265 | #if __has_warning("-Wreserved-id-macro") 266 | #pragma clang diagnostic push 267 | #pragma clang diagnostic ignored "-Wreserved-id-macro" 268 | #endif 269 | #endif 270 | 271 | #define __STDC_FORMAT_MACROS 1 272 | 273 | #if defined(__clang__) 274 | #if __has_warning("-Wreserved-id-macro") 275 | #pragma clang diagnostic pop 276 | #endif 277 | #endif 278 | #endif 279 | 280 | #define UTEST_INLINE inline 281 | 282 | #define UTEST_INITIALIZER(f) \ 283 | static void f(void) UTEST_ATTRIBUTE(constructor); \ 284 | static void f(void) 285 | #endif 286 | 287 | #if defined(__cplusplus) 288 | #define UTEST_CAST(type, x) static_cast(x) 289 | #define UTEST_PTR_CAST(type, x) reinterpret_cast(x) 290 | #define UTEST_EXTERN extern "C" 291 | #define UTEST_NULL NULL 292 | #else 293 | #define UTEST_CAST(type, x) ((type)(x)) 294 | #define UTEST_PTR_CAST(type, x) ((type)(x)) 295 | #define UTEST_EXTERN extern 296 | #define UTEST_NULL 0 297 | #endif 298 | 299 | #ifdef _MSC_VER 300 | /* 301 | io.h contains definitions for some structures with natural padding. This is 302 | uninteresting, but for some reason MSVC's behaviour is to warn about 303 | including this system header. That *is* interesting 304 | */ 305 | #pragma warning(disable : 4820) 306 | #pragma warning(push, 1) 307 | #include 308 | #pragma warning(pop) 309 | #define UTEST_COLOUR_OUTPUT() (_isatty(_fileno(stdout))) 310 | #else 311 | #if defined(__EMSCRIPTEN__) 312 | #include 313 | #define UTEST_COLOUR_OUTPUT() false 314 | #else 315 | #include 316 | #define UTEST_COLOUR_OUTPUT() (isatty(STDOUT_FILENO)) 317 | #endif 318 | #endif 319 | 320 | static UTEST_INLINE void *utest_realloc(void *const pointer, size_t new_size) { 321 | void *const new_pointer = realloc(pointer, new_size); 322 | 323 | if (UTEST_NULL == new_pointer) { 324 | free(pointer); 325 | } 326 | 327 | return new_pointer; 328 | } 329 | 330 | // Prevent 64-bit integer overflow when computing a timestamp by using a trick 331 | // from Sokol: 332 | // https://github.com/floooh/sokol/blob/189843bf4f86969ca4cc4b6d94e793a37c5128a7/sokol_time.h#L204 333 | static UTEST_INLINE utest_int64_t utest_mul_div(const utest_int64_t value, 334 | const utest_int64_t numer, 335 | const utest_int64_t denom) { 336 | const utest_int64_t q = value / denom; 337 | const utest_int64_t r = value % denom; 338 | return q * numer + r * numer / denom; 339 | } 340 | 341 | static UTEST_INLINE utest_int64_t utest_ns(void) { 342 | #if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__) 343 | utest_large_integer counter; 344 | utest_large_integer frequency; 345 | QueryPerformanceCounter(&counter); 346 | QueryPerformanceFrequency(&frequency); 347 | return utest_mul_div(counter.QuadPart, 1000000000, frequency.QuadPart); 348 | #elif defined(__linux__) && defined(__STRICT_ANSI__) 349 | return utest_mul_div(clock(), 1000000000, CLOCKS_PER_SEC); 350 | #elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ 351 | defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun__) || \ 352 | defined(__HAIKU__) 353 | struct timespec ts; 354 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ 355 | !defined(__HAIKU__) 356 | timespec_get(&ts, TIME_UTC); 357 | #else 358 | const clockid_t cid = CLOCK_REALTIME; 359 | #if defined(UTEST_USE_CLOCKGETTIME) 360 | clock_gettime(cid, &ts); 361 | #else 362 | syscall(SYS_clock_gettime, cid, &ts); 363 | #endif 364 | #endif 365 | return UTEST_CAST(utest_int64_t, ts.tv_sec) * 1000 * 1000 * 1000 + ts.tv_nsec; 366 | #elif __APPLE__ 367 | return UTEST_CAST(utest_int64_t, clock_gettime_nsec_np(CLOCK_UPTIME_RAW)); 368 | #elif __EMSCRIPTEN__ 369 | return emscripten_performance_now() * 1000000.0; 370 | #else 371 | #error Unsupported platform! 372 | #endif 373 | } 374 | 375 | typedef void (*utest_testcase_t)(int *, size_t); 376 | 377 | struct utest_test_state_s { 378 | utest_testcase_t func; 379 | size_t index; 380 | char *name; 381 | }; 382 | 383 | struct utest_state_s { 384 | struct utest_test_state_s *tests; 385 | size_t tests_length; 386 | FILE *output; 387 | }; 388 | 389 | /* extern to the global state utest needs to execute */ 390 | UTEST_EXTERN struct utest_state_s utest_state; 391 | 392 | #if defined(_MSC_VER) 393 | #define UTEST_WEAK __forceinline 394 | #elif defined(__MINGW32__) || defined(__MINGW64__) 395 | #define UTEST_WEAK static UTEST_ATTRIBUTE(used) 396 | #elif defined(__clang__) || defined(__GNUC__) || defined(__TINYC__) 397 | #define UTEST_WEAK UTEST_ATTRIBUTE(weak) 398 | #else 399 | #error Non clang, non gcc, non MSVC, non tcc compiler found! 400 | #endif 401 | 402 | #if defined(_MSC_VER) 403 | #define UTEST_UNUSED 404 | #else 405 | #define UTEST_UNUSED UTEST_ATTRIBUTE(unused) 406 | #endif 407 | 408 | #ifdef __clang__ 409 | #pragma clang diagnostic push 410 | #pragma clang diagnostic ignored "-Wvariadic-macros" 411 | #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" 412 | #endif 413 | #define UTEST_PRINTF(...) \ 414 | if (utest_state.output) { \ 415 | fprintf(utest_state.output, __VA_ARGS__); \ 416 | } \ 417 | printf(__VA_ARGS__) 418 | #ifdef __clang__ 419 | #pragma clang diagnostic pop 420 | #endif 421 | 422 | #ifdef __clang__ 423 | #pragma clang diagnostic push 424 | #pragma clang diagnostic ignored "-Wvariadic-macros" 425 | #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" 426 | #endif 427 | 428 | #ifdef _MSC_VER 429 | #define UTEST_SNPRINTF(BUFFER, N, ...) _snprintf_s(BUFFER, N, N, __VA_ARGS__) 430 | #else 431 | #define UTEST_SNPRINTF(...) snprintf(__VA_ARGS__) 432 | #endif 433 | 434 | #ifdef __clang__ 435 | #pragma clang diagnostic pop 436 | #endif 437 | 438 | #if defined(__cplusplus) 439 | /* if we are using c++ we can use overloaded methods (its in the language) */ 440 | #define UTEST_OVERLOADABLE 441 | #elif defined(__clang__) 442 | /* otherwise, if we are using clang with c - use the overloadable attribute */ 443 | #define UTEST_OVERLOADABLE UTEST_ATTRIBUTE(overloadable) 444 | #endif 445 | 446 | #if defined(__cplusplus) && (__cplusplus >= 201103L) 447 | 448 | #ifdef __clang__ 449 | #pragma clang diagnostic push 450 | #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" 451 | #endif 452 | 453 | #include 454 | 455 | template ::value> 456 | struct utest_type_deducer final { 457 | static void _(const T t); 458 | }; 459 | 460 | template <> struct utest_type_deducer { 461 | static void _(const char c) { 462 | if (std::is_signed::value) { 463 | UTEST_PRINTF("%d", static_cast(c)); 464 | } else { 465 | UTEST_PRINTF("%u", static_cast(c)); 466 | } 467 | } 468 | }; 469 | template <> struct utest_type_deducer { 470 | static void _(const signed char c) { 471 | UTEST_PRINTF("%d", static_cast(c)); 472 | } 473 | }; 474 | 475 | template <> struct utest_type_deducer { 476 | static void _(const unsigned char c) { 477 | UTEST_PRINTF("%u", static_cast(c)); 478 | } 479 | }; 480 | 481 | template <> struct utest_type_deducer { 482 | static void _(const short s) { UTEST_PRINTF("%d", static_cast(s)); } 483 | }; 484 | 485 | template <> struct utest_type_deducer { 486 | static void _(const unsigned short s) { 487 | UTEST_PRINTF("%u", static_cast(s)); 488 | } 489 | }; 490 | 491 | template <> struct utest_type_deducer { 492 | static void _(const float f) { UTEST_PRINTF("%f", static_cast(f)); } 493 | }; 494 | 495 | template <> struct utest_type_deducer { 496 | static void _(const double d) { UTEST_PRINTF("%f", d); } 497 | }; 498 | 499 | template <> struct utest_type_deducer { 500 | static void _(const long double d) { 501 | #if defined(__MINGW32__) || defined(__MINGW64__) 502 | /* MINGW is weird - doesn't like LF at all?! */ 503 | UTEST_PRINTF("%f", (double)d); 504 | #else 505 | UTEST_PRINTF("%Lf", d); 506 | #endif 507 | } 508 | }; 509 | 510 | template <> struct utest_type_deducer { 511 | static void _(const int i) { UTEST_PRINTF("%d", i); } 512 | }; 513 | 514 | template <> struct utest_type_deducer { 515 | static void _(const unsigned int i) { UTEST_PRINTF("%u", i); } 516 | }; 517 | 518 | template <> struct utest_type_deducer { 519 | static void _(const long i) { UTEST_PRINTF("%ld", i); } 520 | }; 521 | 522 | template <> struct utest_type_deducer { 523 | static void _(const unsigned long i) { UTEST_PRINTF("%lu", i); } 524 | }; 525 | 526 | template <> struct utest_type_deducer { 527 | static void _(const long long i) { UTEST_PRINTF("%lld", i); } 528 | }; 529 | 530 | template <> struct utest_type_deducer { 531 | static void _(const unsigned long long i) { UTEST_PRINTF("%llu", i); } 532 | }; 533 | 534 | template <> struct utest_type_deducer { 535 | static void _(const bool i) { UTEST_PRINTF(i ? "true" : "false"); } 536 | }; 537 | 538 | template struct utest_type_deducer { 539 | static void _(const T *t) { 540 | UTEST_PRINTF("%p", static_cast(const_cast(t))); 541 | } 542 | }; 543 | 544 | template struct utest_type_deducer { 545 | static void _(T *t) { UTEST_PRINTF("%p", static_cast(t)); } 546 | }; 547 | 548 | template struct utest_type_deducer { 549 | static void _(const T t) { 550 | UTEST_PRINTF("%llu", static_cast(t)); 551 | } 552 | }; 553 | 554 | template <> struct utest_type_deducer { 555 | static void _(std::nullptr_t t) { 556 | UTEST_PRINTF("%p", static_cast(t)); 557 | } 558 | }; 559 | 560 | template 561 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(const T t) { 562 | utest_type_deducer::_(t); 563 | } 564 | 565 | #ifdef __clang__ 566 | #pragma clang diagnostic pop 567 | #endif 568 | 569 | #elif defined(UTEST_OVERLOADABLE) 570 | 571 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(signed char c); 572 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(signed char c) { 573 | UTEST_PRINTF("%d", UTEST_CAST(int, c)); 574 | } 575 | 576 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned char c); 577 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned char c) { 578 | UTEST_PRINTF("%u", UTEST_CAST(unsigned int, c)); 579 | } 580 | 581 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(float f); 582 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(float f) { 583 | UTEST_PRINTF("%f", UTEST_CAST(double, f)); 584 | } 585 | 586 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(double d); 587 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(double d) { 588 | UTEST_PRINTF("%f", d); 589 | } 590 | 591 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long double d); 592 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long double d) { 593 | #if defined(__MINGW32__) || defined(__MINGW64__) 594 | /* MINGW is weird - doesn't like LF at all?! */ 595 | UTEST_PRINTF("%f", (double)d); 596 | #else 597 | UTEST_PRINTF("%Lf", d); 598 | #endif 599 | } 600 | 601 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(int i); 602 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(int i) { 603 | UTEST_PRINTF("%d", i); 604 | } 605 | 606 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned int i); 607 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned int i) { 608 | UTEST_PRINTF("%u", i); 609 | } 610 | 611 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long int i); 612 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long int i) { 613 | UTEST_PRINTF("%ld", i); 614 | } 615 | 616 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long unsigned int i); 617 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long unsigned int i) { 618 | UTEST_PRINTF("%lu", i); 619 | } 620 | 621 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(const void *p); 622 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(const void *p) { 623 | UTEST_PRINTF("%p", p); 624 | } 625 | 626 | /* 627 | long long is a c++11 extension 628 | */ 629 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) || \ 630 | defined(__cplusplus) && (__cplusplus >= 201103L) || \ 631 | (defined(__MINGW32__) || defined(__MINGW64__)) 632 | 633 | #ifdef __clang__ 634 | #pragma clang diagnostic push 635 | #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" 636 | #endif 637 | 638 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long int i); 639 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long int i) { 640 | UTEST_PRINTF("%lld", i); 641 | } 642 | 643 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long unsigned int i); 644 | UTEST_WEAK UTEST_OVERLOADABLE void 645 | utest_type_printer(long long unsigned int i) { 646 | UTEST_PRINTF("%llu", i); 647 | } 648 | 649 | #ifdef __clang__ 650 | #pragma clang diagnostic pop 651 | #endif 652 | 653 | #endif 654 | #elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ 655 | !(defined(__MINGW32__) || defined(__MINGW64__)) || \ 656 | defined(__TINYC__) 657 | #define utest_type_printer(val) \ 658 | UTEST_PRINTF( \ 659 | _Generic((val), \ 660 | signed char: "%d", \ 661 | unsigned char: "%u", \ 662 | short: "%d", \ 663 | unsigned short: "%u", \ 664 | int: "%d", \ 665 | long: "%ld", \ 666 | long long: "%lld", \ 667 | unsigned: "%u", \ 668 | unsigned long: "%lu", \ 669 | unsigned long long: "%llu", \ 670 | float: "%f", \ 671 | double: "%f", \ 672 | long double: "%Lf", \ 673 | default: _Generic((val - val), ptrdiff_t: "%p", default: "undef")), \ 674 | (val)) 675 | #else 676 | /* 677 | we don't have the ability to print the values we got, so we create a macro 678 | to tell our users we can't do anything fancy 679 | */ 680 | #define utest_type_printer(...) UTEST_PRINTF("undef") 681 | #endif 682 | 683 | #if defined(_MSC_VER) 684 | #define UTEST_SURPRESS_WARNING_BEGIN \ 685 | __pragma(warning(push)) __pragma(warning(disable : 4127)) \ 686 | __pragma(warning(disable : 4571)) __pragma(warning(disable : 4130)) 687 | #define UTEST_SURPRESS_WARNING_END __pragma(warning(pop)) 688 | #else 689 | #define UTEST_SURPRESS_WARNING_BEGIN 690 | #define UTEST_SURPRESS_WARNING_END 691 | #endif 692 | 693 | #if defined(__cplusplus) && (__cplusplus >= 201103L) 694 | #define UTEST_AUTO(x) auto 695 | #elif !defined(__cplusplus) 696 | 697 | #if defined(__clang__) 698 | /* clang-format off */ 699 | /* had to disable clang-format here because it malforms the pragmas */ 700 | #define UTEST_AUTO(x) \ 701 | _Pragma("clang diagnostic push") \ 702 | _Pragma("clang diagnostic ignored \"-Wgnu-auto-type\"") __auto_type \ 703 | _Pragma("clang diagnostic pop") 704 | /* clang-format on */ 705 | #else 706 | #define UTEST_AUTO(x) __typeof__(x + 0) 707 | #endif 708 | 709 | #else 710 | #define UTEST_AUTO(x) typeof(x + 0) 711 | #endif 712 | 713 | #if defined(__clang__) 714 | #define UTEST_STRNCMP(x, y, size) \ 715 | _Pragma("clang diagnostic push") \ 716 | _Pragma("clang diagnostic ignored \"-Wdisabled-macro-expansion\"") \ 717 | strncmp(x, y, size) _Pragma("clang diagnostic pop") 718 | #else 719 | #define UTEST_STRNCMP(x, y, size) strncmp(x, y, size) 720 | #endif 721 | 722 | #if defined(_MSC_VER) 723 | #define UTEST_STRNCPY(x, y, size) strcpy_s(x, size, y) 724 | #elif !defined(__clang__) && defined(__GNUC__) 725 | static UTEST_INLINE char * 726 | utest_strncpy_gcc(char *const dst, const char *const src, const size_t size) { 727 | #pragma GCC diagnostic push 728 | #pragma GCC diagnostic ignored "-Wstringop-overflow" 729 | return strncpy(dst, src, size); 730 | #pragma GCC diagnostic pop 731 | } 732 | 733 | #define UTEST_STRNCPY(x, y, size) utest_strncpy_gcc(x, y, size) 734 | #else 735 | #define UTEST_STRNCPY(x, y, size) strncpy(x, y, size) 736 | #endif 737 | 738 | #define UTEST_SKIP(msg) \ 739 | do { \ 740 | UTEST_PRINTF(" Skipped : '%s'\n", (msg)); \ 741 | *utest_result = UTEST_TEST_SKIPPED; \ 742 | return; \ 743 | } while (0) 744 | 745 | #if defined(__clang__) 746 | #define UTEST_COND(x, y, cond, msg, is_assert) \ 747 | UTEST_SURPRESS_WARNING_BEGIN do { \ 748 | _Pragma("clang diagnostic push") \ 749 | _Pragma("clang diagnostic ignored \"-Wlanguage-extension-token\"") \ 750 | _Pragma("clang diagnostic ignored \"-Wc++98-compat-pedantic\"") \ 751 | _Pragma("clang diagnostic ignored \"-Wfloat-equal\"") \ 752 | UTEST_AUTO(x) xEval = (x); \ 753 | UTEST_AUTO(y) yEval = (y); \ 754 | if (!((xEval)cond(yEval))) { \ 755 | const char *const xAsString = #x; \ 756 | const char *const yAsString = #y; \ 757 | _Pragma("clang diagnostic pop") \ 758 | UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ 759 | UTEST_PRINTF(" Expected : ("); \ 760 | UTEST_PRINTF("%s) " #cond " (%s", xAsString, yAsString); \ 761 | UTEST_PRINTF(")\n"); \ 762 | UTEST_PRINTF(" Actual : "); \ 763 | utest_type_printer(xEval); \ 764 | UTEST_PRINTF(" vs "); \ 765 | utest_type_printer(yEval); \ 766 | UTEST_PRINTF("\n"); \ 767 | if (strlen(msg) > 0) { \ 768 | UTEST_PRINTF(" Message : %s\n", msg); \ 769 | } \ 770 | *utest_result = UTEST_TEST_FAILURE; \ 771 | if (is_assert) { \ 772 | return; \ 773 | } \ 774 | } \ 775 | } \ 776 | while (0) \ 777 | UTEST_SURPRESS_WARNING_END 778 | #elif defined(__GNUC__) || defined(__TINYC__) 779 | #define UTEST_COND(x, y, cond, msg, is_assert) \ 780 | UTEST_SURPRESS_WARNING_BEGIN do { \ 781 | UTEST_AUTO(x) xEval = (x); \ 782 | UTEST_AUTO(y) yEval = (y); \ 783 | if (!((xEval)cond(yEval))) { \ 784 | const char *const xAsString = #x; \ 785 | const char *const yAsString = #y; \ 786 | UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ 787 | UTEST_PRINTF(" Expected : ("); \ 788 | UTEST_PRINTF("%s) " #cond " (%s", xAsString, yAsString); \ 789 | UTEST_PRINTF(")\n"); \ 790 | UTEST_PRINTF(" Actual : "); \ 791 | utest_type_printer(xEval); \ 792 | UTEST_PRINTF(" vs "); \ 793 | utest_type_printer(yEval); \ 794 | UTEST_PRINTF("\n"); \ 795 | if (strlen(msg) > 0) { \ 796 | UTEST_PRINTF(" Message : %s\n", msg); \ 797 | } \ 798 | *utest_result = UTEST_TEST_FAILURE; \ 799 | if (is_assert) { \ 800 | return; \ 801 | } \ 802 | } \ 803 | } \ 804 | while (0) \ 805 | UTEST_SURPRESS_WARNING_END 806 | #else 807 | #define UTEST_COND(x, y, cond, msg, is_assert) \ 808 | UTEST_SURPRESS_WARNING_BEGIN do { \ 809 | if (!((x)cond(y))) { \ 810 | UTEST_PRINTF("%s:%i: Failure (Expected " #cond " Actual)", __FILE__, \ 811 | __LINE__); \ 812 | if (strlen(msg) > 0) { \ 813 | UTEST_PRINTF(" Message : %s", msg); \ 814 | } \ 815 | UTEST_PRINTF("\n"); \ 816 | *utest_result = UTEST_TEST_FAILURE; \ 817 | if (is_assert) { \ 818 | return; \ 819 | } \ 820 | } \ 821 | } \ 822 | while (0) \ 823 | UTEST_SURPRESS_WARNING_END 824 | #endif 825 | 826 | #define EXPECT_EQ(x, y) UTEST_COND(x, y, ==, "", 0) 827 | #define EXPECT_EQ_MSG(x, y, msg) UTEST_COND(x, y, ==, msg, 0) 828 | #define ASSERT_EQ(x, y) UTEST_COND(x, y, ==, "", 1) 829 | #define ASSERT_EQ_MSG(x, y, msg) UTEST_COND(x, y, ==, msg, 1) 830 | 831 | #define EXPECT_NE(x, y) UTEST_COND(x, y, !=, "", 0) 832 | #define EXPECT_NE_MSG(x, y, msg) UTEST_COND(x, y, !=, msg, 0) 833 | #define ASSERT_NE(x, y) UTEST_COND(x, y, !=, "", 1) 834 | #define ASSERT_NE_MSG(x, y, msg) UTEST_COND(x, y, !=, msg, 1) 835 | 836 | #define EXPECT_LT(x, y) UTEST_COND(x, y, <, "", 0) 837 | #define EXPECT_LT_MSG(x, y, msg) UTEST_COND(x, y, <, msg, 0) 838 | #define ASSERT_LT(x, y) UTEST_COND(x, y, <, "", 1) 839 | #define ASSERT_LT_MSG(x, y, msg) UTEST_COND(x, y, <, msg, 1) 840 | 841 | #define EXPECT_LE(x, y) UTEST_COND(x, y, <=, "", 0) 842 | #define EXPECT_LE_MSG(x, y, msg) UTEST_COND(x, y, <=, msg, 0) 843 | #define ASSERT_LE(x, y) UTEST_COND(x, y, <=, "", 1) 844 | #define ASSERT_LE_MSG(x, y, msg) UTEST_COND(x, y, <=, msg, 1) 845 | 846 | #define EXPECT_GT(x, y) UTEST_COND(x, y, >, "", 0) 847 | #define EXPECT_GT_MSG(x, y, msg) UTEST_COND(x, y, >, msg, 0) 848 | #define ASSERT_GT(x, y) UTEST_COND(x, y, >, "", 1) 849 | #define ASSERT_GT_MSG(x, y, msg) UTEST_COND(x, y, >, msg, 1) 850 | 851 | #define EXPECT_GE(x, y) UTEST_COND(x, y, >=, "", 0) 852 | #define EXPECT_GE_MSG(x, y, msg) UTEST_COND(x, y, >=, msg, 0) 853 | #define ASSERT_GE(x, y) UTEST_COND(x, y, >=, "", 1) 854 | #define ASSERT_GE_MSG(x, y, msg) UTEST_COND(x, y, >=, msg, 1) 855 | 856 | #define UTEST_TRUE(x, msg, is_assert) \ 857 | UTEST_SURPRESS_WARNING_BEGIN do { \ 858 | const int xEval = !!(x); \ 859 | if (!(xEval)) { \ 860 | UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ 861 | UTEST_PRINTF(" Expected : true\n"); \ 862 | UTEST_PRINTF(" Actual : %s\n", (xEval) ? "true" : "false"); \ 863 | if (strlen(msg) > 0) { \ 864 | UTEST_PRINTF(" Message : %s\n", msg); \ 865 | } \ 866 | *utest_result = UTEST_TEST_FAILURE; \ 867 | if (is_assert) { \ 868 | return; \ 869 | } \ 870 | } \ 871 | } \ 872 | while (0) \ 873 | UTEST_SURPRESS_WARNING_END 874 | 875 | #define EXPECT_TRUE(x) UTEST_TRUE(x, "", 0) 876 | #define EXPECT_TRUE_MSG(x, msg) UTEST_TRUE(x, msg, 0) 877 | #define ASSERT_TRUE(x) UTEST_TRUE(x, "", 1) 878 | #define ASSERT_TRUE_MSG(x, msg) UTEST_TRUE(x, msg, 1) 879 | 880 | #define UTEST_FALSE(x, msg, is_assert) \ 881 | UTEST_SURPRESS_WARNING_BEGIN do { \ 882 | const int xEval = !!(x); \ 883 | if (xEval) { \ 884 | UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ 885 | UTEST_PRINTF(" Expected : false\n"); \ 886 | UTEST_PRINTF(" Actual : %s\n", (xEval) ? "true" : "false"); \ 887 | if (strlen(msg) > 0) { \ 888 | UTEST_PRINTF(" Message : %s\n", msg); \ 889 | } \ 890 | *utest_result = UTEST_TEST_FAILURE; \ 891 | if (is_assert) { \ 892 | return; \ 893 | } \ 894 | } \ 895 | } \ 896 | while (0) \ 897 | UTEST_SURPRESS_WARNING_END 898 | 899 | #define EXPECT_FALSE(x) UTEST_FALSE(x, "", 0) 900 | #define EXPECT_FALSE_MSG(x, msg) UTEST_FALSE(x, msg, 0) 901 | #define ASSERT_FALSE(x) UTEST_FALSE(x, "", 1) 902 | #define ASSERT_FALSE_MSG(x, msg) UTEST_FALSE(x, msg, 1) 903 | 904 | #define UTEST_STREQ(x, y, msg, is_assert) \ 905 | UTEST_SURPRESS_WARNING_BEGIN do { \ 906 | const char *xEval = (x); \ 907 | const char *yEval = (y); \ 908 | if (UTEST_NULL == xEval || UTEST_NULL == yEval || \ 909 | 0 != strcmp(xEval, yEval)) { \ 910 | UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ 911 | UTEST_PRINTF(" Expected : \"%s\"\n", xEval); \ 912 | UTEST_PRINTF(" Actual : \"%s\"\n", yEval); \ 913 | if (strlen(msg) > 0) { \ 914 | UTEST_PRINTF(" Message : %s\n", msg); \ 915 | } \ 916 | *utest_result = UTEST_TEST_FAILURE; \ 917 | if (is_assert) { \ 918 | return; \ 919 | } \ 920 | } \ 921 | } \ 922 | while (0) \ 923 | UTEST_SURPRESS_WARNING_END 924 | 925 | #define EXPECT_STREQ(x, y) UTEST_STREQ(x, y, "", 0) 926 | #define EXPECT_STREQ_MSG(x, y, msg) UTEST_STREQ(x, y, msg, 0) 927 | #define ASSERT_STREQ(x, y) UTEST_STREQ(x, y, "", 1) 928 | #define ASSERT_STREQ_MSG(x, y, msg) UTEST_STREQ(x, y, msg, 1) 929 | 930 | #define UTEST_STRNE(x, y, msg, is_assert) \ 931 | UTEST_SURPRESS_WARNING_BEGIN do { \ 932 | const char *xEval = (x); \ 933 | const char *yEval = (y); \ 934 | if (UTEST_NULL == xEval || UTEST_NULL == yEval || \ 935 | 0 == strcmp(xEval, yEval)) { \ 936 | UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ 937 | UTEST_PRINTF(" Expected : \"%s\"\n", xEval); \ 938 | UTEST_PRINTF(" Actual : \"%s\"\n", yEval); \ 939 | if (strlen(msg) > 0) { \ 940 | UTEST_PRINTF(" Message : %s\n", msg); \ 941 | } \ 942 | *utest_result = UTEST_TEST_FAILURE; \ 943 | if (is_assert) { \ 944 | return; \ 945 | } \ 946 | } \ 947 | } \ 948 | while (0) \ 949 | UTEST_SURPRESS_WARNING_END 950 | 951 | #define EXPECT_STRNE(x, y) UTEST_STRNE(x, y, "", 0) 952 | #define EXPECT_STRNE_MSG(x, y, msg) UTEST_STRNE(x, y, msg, 0) 953 | #define ASSERT_STRNE(x, y) UTEST_STRNE(x, y, "", 1) 954 | #define ASSERT_STRNE_MSG(x, y, msg) UTEST_STRNE(x, y, msg, 1) 955 | 956 | #define UTEST_STRNEQ(x, y, n, msg, is_assert) \ 957 | UTEST_SURPRESS_WARNING_BEGIN do { \ 958 | const char *xEval = (x); \ 959 | const char *yEval = (y); \ 960 | const size_t nEval = UTEST_CAST(size_t, n); \ 961 | if (UTEST_NULL == xEval || UTEST_NULL == yEval || \ 962 | 0 != UTEST_STRNCMP(xEval, yEval, nEval)) { \ 963 | UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ 964 | UTEST_PRINTF(" Expected : \"%.*s\"\n", UTEST_CAST(int, nEval), xEval); \ 965 | UTEST_PRINTF(" Actual : \"%.*s\"\n", UTEST_CAST(int, nEval), yEval); \ 966 | if (strlen(msg) > 0) { \ 967 | UTEST_PRINTF(" Message : %s\n", msg); \ 968 | } \ 969 | *utest_result = UTEST_TEST_FAILURE; \ 970 | if (is_assert) { \ 971 | return; \ 972 | } \ 973 | } \ 974 | } \ 975 | while (0) \ 976 | UTEST_SURPRESS_WARNING_END 977 | 978 | #define EXPECT_STRNEQ(x, y, n) UTEST_STRNEQ(x, y, n, "", 0) 979 | #define EXPECT_STRNEQ_MSG(x, y, n, msg) UTEST_STRNEQ(x, y, n, msg, 0) 980 | #define ASSERT_STRNEQ(x, y, n) UTEST_STRNEQ(x, y, n, "", 1) 981 | #define ASSERT_STRNEQ_MSG(x, y, n, msg) UTEST_STRNEQ(x, y, n, msg, 1) 982 | 983 | #define UTEST_STRNNE(x, y, n, msg, is_assert) \ 984 | UTEST_SURPRESS_WARNING_BEGIN do { \ 985 | const char *xEval = (x); \ 986 | const char *yEval = (y); \ 987 | const size_t nEval = UTEST_CAST(size_t, n); \ 988 | if (UTEST_NULL == xEval || UTEST_NULL == yEval || \ 989 | 0 == UTEST_STRNCMP(xEval, yEval, nEval)) { \ 990 | UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ 991 | UTEST_PRINTF(" Expected : \"%.*s\"\n", UTEST_CAST(int, nEval), xEval); \ 992 | UTEST_PRINTF(" Actual : \"%.*s\"\n", UTEST_CAST(int, nEval), yEval); \ 993 | if (strlen(msg) > 0) { \ 994 | UTEST_PRINTF(" Message : %s\n", msg); \ 995 | } \ 996 | *utest_result = UTEST_TEST_FAILURE; \ 997 | if (is_assert) { \ 998 | return; \ 999 | } \ 1000 | } \ 1001 | } \ 1002 | while (0) \ 1003 | UTEST_SURPRESS_WARNING_END 1004 | 1005 | #define EXPECT_STRNNE(x, y, n) UTEST_STRNNE(x, y, n, "", 0) 1006 | #define EXPECT_STRNNE_MSG(x, y, n, msg) UTEST_STRNNE(x, y, n, msg, 0) 1007 | #define ASSERT_STRNNE(x, y, n) UTEST_STRNNE(x, y, n, "", 1) 1008 | #define ASSERT_STRNNE_MSG(x, y, n, msg) UTEST_STRNNE(x, y, n, msg, 1) 1009 | 1010 | #define UTEST_NEAR(x, y, epsilon, msg, is_assert) \ 1011 | UTEST_SURPRESS_WARNING_BEGIN do { \ 1012 | const double diff = \ 1013 | utest_fabs(UTEST_CAST(double, x) - UTEST_CAST(double, y)); \ 1014 | if (diff > UTEST_CAST(double, epsilon) || utest_isnan(diff)) { \ 1015 | UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ 1016 | UTEST_PRINTF(" Expected : %f\n", UTEST_CAST(double, x)); \ 1017 | UTEST_PRINTF(" Actual : %f\n", UTEST_CAST(double, y)); \ 1018 | if (strlen(msg) > 0) { \ 1019 | UTEST_PRINTF(" Message : %s\n", msg); \ 1020 | } \ 1021 | *utest_result = UTEST_TEST_FAILURE; \ 1022 | if (is_assert) { \ 1023 | return; \ 1024 | } \ 1025 | } \ 1026 | } \ 1027 | while (0) \ 1028 | UTEST_SURPRESS_WARNING_END 1029 | 1030 | #define EXPECT_NEAR(x, y, epsilon) UTEST_NEAR(x, y, epsilon, "", 0) 1031 | #define EXPECT_NEAR_MSG(x, y, epsilon, msg) UTEST_NEAR(x, y, epsilon, msg, 0) 1032 | #define ASSERT_NEAR(x, y, epsilon) UTEST_NEAR(x, y, epsilon, "", 1) 1033 | #define ASSERT_NEAR_MSG(x, y, epsilon, msg) UTEST_NEAR(x, y, epsilon, msg, 1) 1034 | 1035 | #if defined(UTEST_HAS_EXCEPTIONS) 1036 | #define UTEST_EXCEPTION(x, exception_type, msg, is_assert) \ 1037 | UTEST_SURPRESS_WARNING_BEGIN do { \ 1038 | int exception_caught = 0; \ 1039 | try { \ 1040 | x; \ 1041 | } catch (const exception_type &) { \ 1042 | exception_caught = 1; \ 1043 | } catch (...) { \ 1044 | exception_caught = 2; \ 1045 | } \ 1046 | if (1 != exception_caught) { \ 1047 | UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ 1048 | UTEST_PRINTF(" Expected : %s exception\n", #exception_type); \ 1049 | UTEST_PRINTF(" Actual : %s\n", (2 == exception_caught) \ 1050 | ? "Unexpected exception" \ 1051 | : "No exception"); \ 1052 | if (strlen(msg) > 0) { \ 1053 | UTEST_PRINTF(" Message : %s\n", msg); \ 1054 | } \ 1055 | *utest_result = UTEST_TEST_FAILURE; \ 1056 | if (is_assert) { \ 1057 | return; \ 1058 | } \ 1059 | } \ 1060 | } \ 1061 | while (0) \ 1062 | UTEST_SURPRESS_WARNING_END 1063 | 1064 | #define EXPECT_EXCEPTION(x, exception_type) \ 1065 | UTEST_EXCEPTION(x, exception_type, "", 0) 1066 | #define EXPECT_EXCEPTION_MSG(x, exception_type, msg) \ 1067 | UTEST_EXCEPTION(x, exception_type, msg, 0) 1068 | #define ASSERT_EXCEPTION(x, exception_type) \ 1069 | UTEST_EXCEPTION(x, exception_type, "", 1) 1070 | #define ASSERT_EXCEPTION_MSG(x, exception_type, msg) \ 1071 | UTEST_EXCEPTION(x, exception_type, msg, 1) 1072 | 1073 | #define UTEST_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message, \ 1074 | msg, is_assert) \ 1075 | UTEST_SURPRESS_WARNING_BEGIN do { \ 1076 | int exception_caught = 0; \ 1077 | char *message_caught = UTEST_NULL; \ 1078 | try { \ 1079 | x; \ 1080 | } catch (const exception_type &e) { \ 1081 | const char *const what = e.what(); \ 1082 | exception_caught = 1; \ 1083 | if (0 != \ 1084 | UTEST_STRNCMP(what, exception_message, strlen(exception_message))) { \ 1085 | const size_t message_size = strlen(what) + 1; \ 1086 | message_caught = UTEST_PTR_CAST(char *, malloc(message_size)); \ 1087 | UTEST_STRNCPY(message_caught, what, message_size); \ 1088 | } \ 1089 | } catch (...) { \ 1090 | exception_caught = 2; \ 1091 | } \ 1092 | if (1 != exception_caught) { \ 1093 | UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ 1094 | UTEST_PRINTF(" Expected : %s exception\n", #exception_type); \ 1095 | UTEST_PRINTF(" Actual : %s\n", (2 == exception_caught) \ 1096 | ? "Unexpected exception" \ 1097 | : "No exception"); \ 1098 | if (strlen(msg) > 0) { \ 1099 | UTEST_PRINTF(" Message : %s\n", msg); \ 1100 | } \ 1101 | *utest_result = UTEST_TEST_FAILURE; \ 1102 | if (is_assert) { \ 1103 | return; \ 1104 | } \ 1105 | } else if (UTEST_NULL != message_caught) { \ 1106 | UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ 1107 | UTEST_PRINTF(" Expected : %s exception with message %s\n", \ 1108 | #exception_type, exception_message); \ 1109 | UTEST_PRINTF(" Actual message : %s\n", message_caught); \ 1110 | if (strlen(msg) > 0) { \ 1111 | UTEST_PRINTF(" Message : %s\n", msg); \ 1112 | } \ 1113 | *utest_result = UTEST_TEST_FAILURE; \ 1114 | free(message_caught); \ 1115 | if (is_assert) { \ 1116 | return; \ 1117 | } \ 1118 | } \ 1119 | } \ 1120 | while (0) \ 1121 | UTEST_SURPRESS_WARNING_END 1122 | 1123 | #define EXPECT_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message) \ 1124 | UTEST_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message, "", 0) 1125 | #define EXPECT_EXCEPTION_WITH_MESSAGE_MSG(x, exception_type, \ 1126 | exception_message, msg) \ 1127 | UTEST_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message, msg, 0) 1128 | #define ASSERT_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message) \ 1129 | UTEST_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message, "", 1) 1130 | #define ASSERT_EXCEPTION_WITH_MESSAGE_MSG(x, exception_type, \ 1131 | exception_message, msg) \ 1132 | UTEST_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message, msg, 1) 1133 | #endif 1134 | 1135 | #if defined(__clang__) 1136 | #if __has_warning("-Wunsafe-buffer-usage") 1137 | #define UTEST_SURPRESS_WARNINGS_BEGIN \ 1138 | _Pragma("clang diagnostic push") \ 1139 | _Pragma("clang diagnostic ignored \"-Wunsafe-buffer-usage\"") 1140 | #define UTEST_SURPRESS_WARNINGS_END _Pragma("clang diagnostic pop") 1141 | #else 1142 | #define UTEST_SURPRESS_WARNINGS_BEGIN 1143 | #define UTEST_SURPRESS_WARNINGS_END 1144 | #endif 1145 | #elif defined(__GNUC__) && __GNUC__ >= 8 && defined(__cplusplus) 1146 | #define UTEST_SURPRESS_WARNINGS_BEGIN \ 1147 | _Pragma("GCC diagnostic push") \ 1148 | _Pragma("GCC diagnostic ignored \"-Wclass-memaccess\"") 1149 | #define UTEST_SURPRESS_WARNINGS_END _Pragma("GCC diagnostic pop") 1150 | #else 1151 | #define UTEST_SURPRESS_WARNINGS_BEGIN 1152 | #define UTEST_SURPRESS_WARNINGS_END 1153 | #endif 1154 | 1155 | #define UTEST(SET, NAME) \ 1156 | UTEST_SURPRESS_WARNINGS_BEGIN \ 1157 | UTEST_EXTERN struct utest_state_s utest_state; \ 1158 | static void utest_run_##SET##_##NAME(int *utest_result); \ 1159 | static void utest_##SET##_##NAME(int *utest_result, size_t utest_index) { \ 1160 | (void)utest_index; \ 1161 | utest_run_##SET##_##NAME(utest_result); \ 1162 | } \ 1163 | UTEST_INITIALIZER(utest_register_##SET##_##NAME) { \ 1164 | const size_t index = utest_state.tests_length++; \ 1165 | const char name_part[] = #SET "." #NAME; \ 1166 | const size_t name_size = strlen(name_part) + 1; \ 1167 | char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ 1168 | utest_state.tests = UTEST_PTR_CAST( \ 1169 | struct utest_test_state_s *, \ 1170 | utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ 1171 | sizeof(struct utest_test_state_s) * \ 1172 | utest_state.tests_length)); \ 1173 | if (utest_state.tests && name) { \ 1174 | utest_state.tests[index].func = &utest_##SET##_##NAME; \ 1175 | utest_state.tests[index].name = name; \ 1176 | utest_state.tests[index].index = 0; \ 1177 | UTEST_SNPRINTF(name, name_size, "%s", name_part); \ 1178 | } else { \ 1179 | if (utest_state.tests) { \ 1180 | free(utest_state.tests); \ 1181 | utest_state.tests = NULL; \ 1182 | } \ 1183 | if (name) { \ 1184 | free(name); \ 1185 | } \ 1186 | } \ 1187 | } \ 1188 | UTEST_SURPRESS_WARNINGS_END \ 1189 | void utest_run_##SET##_##NAME(int *utest_result) 1190 | 1191 | #define UTEST_F_SETUP(FIXTURE) \ 1192 | static void utest_f_setup_##FIXTURE(int *utest_result, \ 1193 | struct FIXTURE *utest_fixture) 1194 | 1195 | #define UTEST_F_TEARDOWN(FIXTURE) \ 1196 | static void utest_f_teardown_##FIXTURE(int *utest_result, \ 1197 | struct FIXTURE *utest_fixture) 1198 | 1199 | #define UTEST_F(FIXTURE, NAME) \ 1200 | UTEST_SURPRESS_WARNINGS_BEGIN \ 1201 | UTEST_EXTERN struct utest_state_s utest_state; \ 1202 | static void utest_f_setup_##FIXTURE(int *, struct FIXTURE *); \ 1203 | static void utest_f_teardown_##FIXTURE(int *, struct FIXTURE *); \ 1204 | static void utest_run_##FIXTURE##_##NAME(int *, struct FIXTURE *); \ 1205 | static void utest_f_##FIXTURE##_##NAME(int *utest_result, \ 1206 | size_t utest_index) { \ 1207 | struct FIXTURE fixture; \ 1208 | (void)utest_index; \ 1209 | memset(&fixture, 0, sizeof(fixture)); \ 1210 | utest_f_setup_##FIXTURE(utest_result, &fixture); \ 1211 | if (UTEST_TEST_PASSED != *utest_result) { \ 1212 | return; \ 1213 | } \ 1214 | utest_run_##FIXTURE##_##NAME(utest_result, &fixture); \ 1215 | utest_f_teardown_##FIXTURE(utest_result, &fixture); \ 1216 | } \ 1217 | UTEST_INITIALIZER(utest_register_##FIXTURE##_##NAME) { \ 1218 | const size_t index = utest_state.tests_length++; \ 1219 | const char name_part[] = #FIXTURE "." #NAME; \ 1220 | const size_t name_size = strlen(name_part) + 1; \ 1221 | char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ 1222 | utest_state.tests = UTEST_PTR_CAST( \ 1223 | struct utest_test_state_s *, \ 1224 | utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ 1225 | sizeof(struct utest_test_state_s) * \ 1226 | utest_state.tests_length)); \ 1227 | if (utest_state.tests && name) { \ 1228 | utest_state.tests[index].func = &utest_f_##FIXTURE##_##NAME; \ 1229 | utest_state.tests[index].name = name; \ 1230 | UTEST_SNPRINTF(name, name_size, "%s", name_part); \ 1231 | } else { \ 1232 | if (utest_state.tests) { \ 1233 | free(utest_state.tests); \ 1234 | utest_state.tests = NULL; \ 1235 | } \ 1236 | if (name) { \ 1237 | free(name); \ 1238 | } \ 1239 | } \ 1240 | } \ 1241 | UTEST_SURPRESS_WARNINGS_END \ 1242 | void utest_run_##FIXTURE##_##NAME(int *utest_result, \ 1243 | struct FIXTURE *utest_fixture) 1244 | 1245 | #define UTEST_I_SETUP(FIXTURE) \ 1246 | static void utest_i_setup_##FIXTURE( \ 1247 | int *utest_result, struct FIXTURE *utest_fixture, size_t utest_index) 1248 | 1249 | #define UTEST_I_TEARDOWN(FIXTURE) \ 1250 | static void utest_i_teardown_##FIXTURE( \ 1251 | int *utest_result, struct FIXTURE *utest_fixture, size_t utest_index) 1252 | 1253 | #define UTEST_I(FIXTURE, NAME, INDEX) \ 1254 | UTEST_SURPRESS_WARNINGS_BEGIN \ 1255 | UTEST_EXTERN struct utest_state_s utest_state; \ 1256 | static void utest_run_##FIXTURE##_##NAME##_##INDEX(int *, struct FIXTURE *); \ 1257 | static void utest_i_##FIXTURE##_##NAME##_##INDEX(int *utest_result, \ 1258 | size_t index) { \ 1259 | struct FIXTURE fixture; \ 1260 | memset(&fixture, 0, sizeof(fixture)); \ 1261 | utest_i_setup_##FIXTURE(utest_result, &fixture, index); \ 1262 | if (UTEST_TEST_PASSED != *utest_result) { \ 1263 | return; \ 1264 | } \ 1265 | utest_run_##FIXTURE##_##NAME##_##INDEX(utest_result, &fixture); \ 1266 | utest_i_teardown_##FIXTURE(utest_result, &fixture, index); \ 1267 | } \ 1268 | UTEST_INITIALIZER(utest_register_##FIXTURE##_##NAME##_##INDEX) { \ 1269 | size_t i; \ 1270 | utest_uint64_t iUp; \ 1271 | for (i = 0; i < (INDEX); i++) { \ 1272 | const size_t index = utest_state.tests_length++; \ 1273 | const char name_part[] = #FIXTURE "." #NAME; \ 1274 | const size_t name_size = strlen(name_part) + 32; \ 1275 | char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ 1276 | utest_state.tests = UTEST_PTR_CAST( \ 1277 | struct utest_test_state_s *, \ 1278 | utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ 1279 | sizeof(struct utest_test_state_s) * \ 1280 | utest_state.tests_length)); \ 1281 | if (utest_state.tests && name) { \ 1282 | utest_state.tests[index].func = &utest_i_##FIXTURE##_##NAME##_##INDEX; \ 1283 | utest_state.tests[index].index = i; \ 1284 | utest_state.tests[index].name = name; \ 1285 | iUp = UTEST_CAST(utest_uint64_t, i); \ 1286 | UTEST_SNPRINTF(name, name_size, "%s/%" UTEST_PRIu64, name_part, iUp); \ 1287 | } else { \ 1288 | if (utest_state.tests) { \ 1289 | free(utest_state.tests); \ 1290 | utest_state.tests = NULL; \ 1291 | } \ 1292 | if (name) { \ 1293 | free(name); \ 1294 | } \ 1295 | } \ 1296 | } \ 1297 | } \ 1298 | UTEST_SURPRESS_WARNINGS_END \ 1299 | void utest_run_##FIXTURE##_##NAME##_##INDEX(int *utest_result, \ 1300 | struct FIXTURE *utest_fixture) 1301 | 1302 | #ifdef __clang__ 1303 | #pragma clang diagnostic push 1304 | #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" 1305 | #endif 1306 | 1307 | UTEST_WEAK 1308 | double utest_fabs(double d); 1309 | UTEST_WEAK 1310 | double utest_fabs(double d) { 1311 | union { 1312 | double d; 1313 | utest_uint64_t u; 1314 | } both; 1315 | both.d = d; 1316 | both.u &= 0x7fffffffffffffffu; 1317 | return both.d; 1318 | } 1319 | 1320 | UTEST_WEAK 1321 | int utest_isnan(double d); 1322 | UTEST_WEAK 1323 | int utest_isnan(double d) { 1324 | union { 1325 | double d; 1326 | utest_uint64_t u; 1327 | } both; 1328 | both.d = d; 1329 | both.u &= 0x7fffffffffffffffu; 1330 | return both.u > 0x7ff0000000000000u; 1331 | } 1332 | 1333 | #ifdef __clang__ 1334 | #pragma clang diagnostic pop 1335 | #endif 1336 | 1337 | #if defined(__clang__) 1338 | #if __has_warning("-Wunsafe-buffer-usage") 1339 | #pragma clang diagnostic push 1340 | #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" 1341 | #endif 1342 | #endif 1343 | 1344 | UTEST_WEAK 1345 | int utest_should_filter_test(const char *filter, const char *testcase); 1346 | UTEST_WEAK int utest_should_filter_test(const char *filter, 1347 | const char *testcase) { 1348 | if (filter) { 1349 | const char *filter_cur = filter; 1350 | const char *testcase_cur = testcase; 1351 | const char *filter_wildcard = UTEST_NULL; 1352 | 1353 | while (('\0' != *filter_cur) && ('\0' != *testcase_cur)) { 1354 | if ('*' == *filter_cur) { 1355 | /* store the position of the wildcard */ 1356 | filter_wildcard = filter_cur; 1357 | 1358 | /* skip the wildcard character */ 1359 | filter_cur++; 1360 | 1361 | while (('\0' != *filter_cur) && ('\0' != *testcase_cur)) { 1362 | if ('*' == *filter_cur) { 1363 | /* 1364 | we found another wildcard (filter is something like *foo*) so we 1365 | exit the current loop, and return to the parent loop to handle 1366 | the wildcard case 1367 | */ 1368 | break; 1369 | } else if (*filter_cur != *testcase_cur) { 1370 | /* otherwise our filter didn't match, so reset it */ 1371 | filter_cur = filter_wildcard; 1372 | } 1373 | 1374 | /* move testcase along */ 1375 | testcase_cur++; 1376 | 1377 | /* move filter along */ 1378 | filter_cur++; 1379 | } 1380 | 1381 | if (('\0' == *filter_cur) && ('\0' == *testcase_cur)) { 1382 | return 0; 1383 | } 1384 | 1385 | /* if the testcase has been exhausted, we don't have a match! */ 1386 | if ('\0' == *testcase_cur) { 1387 | return 1; 1388 | } 1389 | } else { 1390 | if (*testcase_cur != *filter_cur) { 1391 | /* test case doesn't match filter */ 1392 | return 1; 1393 | } else { 1394 | /* move our filter and testcase forward */ 1395 | testcase_cur++; 1396 | filter_cur++; 1397 | } 1398 | } 1399 | } 1400 | 1401 | if (('\0' != *filter_cur) || 1402 | (('\0' != *testcase_cur) && 1403 | ((filter == filter_cur) || ('*' != filter_cur[-1])))) { 1404 | /* we have a mismatch! */ 1405 | return 1; 1406 | } 1407 | } 1408 | 1409 | return 0; 1410 | } 1411 | 1412 | static UTEST_INLINE FILE *utest_fopen(const char *filename, const char *mode) { 1413 | #ifdef _MSC_VER 1414 | FILE *file; 1415 | if (0 == fopen_s(&file, filename, mode)) { 1416 | return file; 1417 | } else { 1418 | return UTEST_NULL; 1419 | } 1420 | #else 1421 | return fopen(filename, mode); 1422 | #endif 1423 | } 1424 | 1425 | static UTEST_INLINE int utest_main(int argc, const char *const argv[]); 1426 | int utest_main(int argc, const char *const argv[]) { 1427 | utest_uint64_t failed = 0; 1428 | utest_uint64_t skipped = 0; 1429 | size_t index = 0; 1430 | size_t *failed_testcases = UTEST_NULL; 1431 | size_t failed_testcases_length = 0; 1432 | size_t *skipped_testcases = UTEST_NULL; 1433 | size_t skipped_testcases_length = 0; 1434 | const char *filter = UTEST_NULL; 1435 | utest_uint64_t ran_tests = 0; 1436 | int enable_mixed_units = 0; 1437 | int random_order = 0; 1438 | utest_uint32_t seed = 0; 1439 | 1440 | enum colours { RESET, GREEN, RED, YELLOW }; 1441 | 1442 | const int use_colours = UTEST_COLOUR_OUTPUT(); 1443 | const char *colours[] = {"\033[0m", "\033[32m", "\033[31m", "\033[33m"}; 1444 | 1445 | if (!use_colours) { 1446 | for (index = 0; index < sizeof colours / sizeof colours[0]; index++) { 1447 | colours[index] = ""; 1448 | } 1449 | } 1450 | /* loop through all arguments looking for our options */ 1451 | for (index = 1; index < UTEST_CAST(size_t, argc); index++) { 1452 | /* Informational switches */ 1453 | const char help_str[] = "--help"; 1454 | const char list_str[] = "--list-tests"; 1455 | /* Test config switches */ 1456 | const char filter_str[] = "--filter="; 1457 | const char output_str[] = "--output="; 1458 | const char enable_mixed_units_str[] = "--enable-mixed-units"; 1459 | const char random_order_str[] = "--random-order"; 1460 | const char random_order_with_seed_str[] = "--random-order="; 1461 | 1462 | if (0 == UTEST_STRNCMP(argv[index], help_str, strlen(help_str))) { 1463 | printf("utest.h - the single file unit testing solution for C/C++!\n" 1464 | "Command line Options:\n" 1465 | " --help Show this message and exit.\n" 1466 | " --filter= Filter the test cases to run (EG. " 1467 | "MyTest*.a would run MyTestCase.a but not MyTestCase.b).\n" 1468 | " --list-tests List testnames, one per line. Output " 1469 | "names can be passed to --filter.\n"); 1470 | printf(" --output= Output an xunit XML file to the file " 1471 | "specified in .\n" 1472 | " --enable-mixed-units Enable the per-test output to contain " 1473 | "mixed units (s/ms/us/ns).\n" 1474 | " --random-order[=] Randomize the order that the tests are " 1475 | "ran in. If the optional argument is not provided, then a " 1476 | "random starting seed is used.\n"); 1477 | goto cleanup; 1478 | } else if (0 == 1479 | UTEST_STRNCMP(argv[index], filter_str, strlen(filter_str))) { 1480 | /* user wants to filter what test cases run! */ 1481 | filter = argv[index] + strlen(filter_str); 1482 | } else if (0 == 1483 | UTEST_STRNCMP(argv[index], output_str, strlen(output_str))) { 1484 | utest_state.output = utest_fopen(argv[index] + strlen(output_str), "w+"); 1485 | } else if (0 == UTEST_STRNCMP(argv[index], list_str, strlen(list_str))) { 1486 | for (index = 0; index < utest_state.tests_length; index++) { 1487 | UTEST_PRINTF("%s\n", utest_state.tests[index].name); 1488 | } 1489 | /* when printing the test list, don't actually run the tests */ 1490 | return 0; 1491 | } else if (0 == UTEST_STRNCMP(argv[index], enable_mixed_units_str, 1492 | strlen(enable_mixed_units_str))) { 1493 | enable_mixed_units = 1; 1494 | } else if (0 == UTEST_STRNCMP(argv[index], random_order_with_seed_str, 1495 | strlen(random_order_with_seed_str))) { 1496 | seed = 1497 | UTEST_CAST(utest_uint32_t, 1498 | strtoul(argv[index] + strlen(random_order_with_seed_str), 1499 | UTEST_NULL, 10)); 1500 | random_order = 1; 1501 | } else if (0 == UTEST_STRNCMP(argv[index], random_order_str, 1502 | strlen(random_order_str))) { 1503 | const utest_int64_t ns = utest_ns(); 1504 | 1505 | // Some really poor pseudo-random using the current time. I do this 1506 | // because I really want to avoid using C's rand() because that'd mean our 1507 | // random would be affected by any srand() usage by the user (which I 1508 | // don't want). 1509 | seed = UTEST_CAST(utest_uint32_t, ns >> 32) * 31 + 1510 | UTEST_CAST(utest_uint32_t, ns & 0xffffffff); 1511 | random_order = 1; 1512 | } 1513 | } 1514 | 1515 | if (random_order) { 1516 | // Use Fisher-Yates with the Durstenfield's version to randomly re-order the 1517 | // tests. 1518 | for (index = utest_state.tests_length; index > 1; index--) { 1519 | // For the random order we'll use PCG. 1520 | const utest_uint32_t state = seed; 1521 | const utest_uint32_t word = 1522 | ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; 1523 | const utest_uint32_t next = 1524 | ((word >> 22u) ^ word) % UTEST_CAST(utest_uint32_t, index); 1525 | 1526 | // Swap the randomly chosen element into the last location. 1527 | const struct utest_test_state_s copy = utest_state.tests[index - 1]; 1528 | utest_state.tests[index - 1] = utest_state.tests[next]; 1529 | utest_state.tests[next] = copy; 1530 | 1531 | // Move the seed onwards. 1532 | seed = seed * 747796405u + 2891336453u; 1533 | } 1534 | } 1535 | 1536 | for (index = 0; index < utest_state.tests_length; index++) { 1537 | if (utest_should_filter_test(filter, utest_state.tests[index].name)) { 1538 | continue; 1539 | } 1540 | 1541 | ran_tests++; 1542 | } 1543 | 1544 | printf("%s[==========]%s Running %" UTEST_PRIu64 " test cases.\n", 1545 | colours[GREEN], colours[RESET], UTEST_CAST(utest_uint64_t, ran_tests)); 1546 | 1547 | if (utest_state.output) { 1548 | fprintf(utest_state.output, "\n"); 1549 | fprintf(utest_state.output, 1550 | "\n", 1551 | UTEST_CAST(utest_uint64_t, ran_tests)); 1552 | fprintf(utest_state.output, 1553 | "\n", 1554 | UTEST_CAST(utest_uint64_t, ran_tests)); 1555 | } 1556 | 1557 | for (index = 0; index < utest_state.tests_length; index++) { 1558 | int result = UTEST_TEST_PASSED; 1559 | utest_int64_t ns = 0; 1560 | 1561 | if (utest_should_filter_test(filter, utest_state.tests[index].name)) { 1562 | continue; 1563 | } 1564 | 1565 | printf("%s[ RUN ]%s %s\n", colours[GREEN], colours[RESET], 1566 | utest_state.tests[index].name); 1567 | 1568 | if (utest_state.output) { 1569 | fprintf(utest_state.output, "", 1570 | utest_state.tests[index].name); 1571 | } 1572 | 1573 | ns = utest_ns(); 1574 | errno = 0; 1575 | #if defined(UTEST_HAS_EXCEPTIONS) 1576 | UTEST_SURPRESS_WARNING_BEGIN 1577 | try { 1578 | utest_state.tests[index].func(&result, utest_state.tests[index].index); 1579 | } catch (const std::exception &err) { 1580 | printf(" Exception : %s\n", err.what()); 1581 | result = UTEST_TEST_FAILURE; 1582 | } catch (...) { 1583 | printf(" Exception : Unknown\n"); 1584 | result = UTEST_TEST_FAILURE; 1585 | } 1586 | UTEST_SURPRESS_WARNING_END 1587 | #else 1588 | utest_state.tests[index].func(&result, utest_state.tests[index].index); 1589 | #endif 1590 | ns = utest_ns() - ns; 1591 | 1592 | if (utest_state.output) { 1593 | fprintf(utest_state.output, "\n"); 1594 | } 1595 | 1596 | // Record the failing test. 1597 | if (UTEST_TEST_FAILURE == result) { 1598 | const size_t failed_testcase_index = failed_testcases_length++; 1599 | failed_testcases = UTEST_PTR_CAST( 1600 | size_t *, utest_realloc(UTEST_PTR_CAST(void *, failed_testcases), 1601 | sizeof(size_t) * failed_testcases_length)); 1602 | if (UTEST_NULL != failed_testcases) { 1603 | failed_testcases[failed_testcase_index] = index; 1604 | } 1605 | failed++; 1606 | } else if (UTEST_TEST_SKIPPED == result) { 1607 | const size_t skipped_testcase_index = skipped_testcases_length++; 1608 | skipped_testcases = UTEST_PTR_CAST( 1609 | size_t *, utest_realloc(UTEST_PTR_CAST(void *, skipped_testcases), 1610 | sizeof(size_t) * skipped_testcases_length)); 1611 | if (UTEST_NULL != skipped_testcases) { 1612 | skipped_testcases[skipped_testcase_index] = index; 1613 | } 1614 | skipped++; 1615 | } 1616 | 1617 | { 1618 | const char *const units[] = {"ns", "us", "ms", "s", UTEST_NULL}; 1619 | unsigned int unit_index = 0; 1620 | utest_int64_t time = ns; 1621 | 1622 | if (enable_mixed_units) { 1623 | for (unit_index = 0; UTEST_NULL != units[unit_index]; unit_index++) { 1624 | if (10000 > time) { 1625 | break; 1626 | } 1627 | 1628 | time /= 1000; 1629 | } 1630 | } 1631 | 1632 | if (UTEST_TEST_FAILURE == result) { 1633 | printf("%s[ FAILED ]%s %s (%" UTEST_PRId64 "%s)\n", colours[RED], 1634 | colours[RESET], utest_state.tests[index].name, time, 1635 | units[unit_index]); 1636 | } else if (UTEST_TEST_SKIPPED == result) { 1637 | printf("%s[ SKIPPED ]%s %s (%" UTEST_PRId64 "%s)\n", colours[YELLOW], 1638 | colours[RESET], utest_state.tests[index].name, time, 1639 | units[unit_index]); 1640 | } else { 1641 | printf("%s[ OK ]%s %s (%" UTEST_PRId64 "%s)\n", colours[GREEN], 1642 | colours[RESET], utest_state.tests[index].name, time, 1643 | units[unit_index]); 1644 | } 1645 | } 1646 | } 1647 | 1648 | printf("%s[==========]%s %" UTEST_PRIu64 " test cases ran.\n", colours[GREEN], 1649 | colours[RESET], ran_tests); 1650 | printf("%s[ PASSED ]%s %" UTEST_PRIu64 " tests.\n", colours[GREEN], 1651 | colours[RESET], ran_tests - failed - skipped); 1652 | 1653 | if (0 != skipped) { 1654 | printf("%s[ SKIPPED ]%s %" UTEST_PRIu64 " tests, listed below:\n", 1655 | colours[YELLOW], colours[RESET], skipped); 1656 | for (index = 0; index < skipped_testcases_length; index++) { 1657 | printf("%s[ SKIPPED ]%s %s\n", colours[YELLOW], colours[RESET], 1658 | utest_state.tests[skipped_testcases[index]].name); 1659 | } 1660 | } 1661 | 1662 | if (0 != failed) { 1663 | printf("%s[ FAILED ]%s %" UTEST_PRIu64 " tests, listed below:\n", 1664 | colours[RED], colours[RESET], failed); 1665 | for (index = 0; index < failed_testcases_length; index++) { 1666 | printf("%s[ FAILED ]%s %s\n", colours[RED], colours[RESET], 1667 | utest_state.tests[failed_testcases[index]].name); 1668 | } 1669 | } 1670 | 1671 | if (utest_state.output) { 1672 | fprintf(utest_state.output, "\n\n"); 1673 | } 1674 | 1675 | cleanup: 1676 | for (index = 0; index < utest_state.tests_length; index++) { 1677 | free(UTEST_PTR_CAST(void *, utest_state.tests[index].name)); 1678 | } 1679 | 1680 | free(UTEST_PTR_CAST(void *, skipped_testcases)); 1681 | free(UTEST_PTR_CAST(void *, failed_testcases)); 1682 | free(UTEST_PTR_CAST(void *, utest_state.tests)); 1683 | 1684 | if (utest_state.output) { 1685 | fclose(utest_state.output); 1686 | } 1687 | 1688 | return UTEST_CAST(int, failed); 1689 | } 1690 | 1691 | #if defined(__clang__) 1692 | #if __has_warning("-Wunsafe-buffer-usage") 1693 | #pragma clang diagnostic pop 1694 | #endif 1695 | #endif 1696 | 1697 | /* 1698 | we need, in exactly one source file, define the global struct that will hold 1699 | the data we need to run utest. This macro allows the user to declare the 1700 | data without having to use the UTEST_MAIN macro, thus allowing them to write 1701 | their own main() function. 1702 | */ 1703 | #define UTEST_STATE() struct utest_state_s utest_state = {0, 0, 0} 1704 | 1705 | /* 1706 | define a main() function to call into utest.h and start executing tests! A 1707 | user can optionally not use this macro, and instead define their own main() 1708 | function and manually call utest_main. The user must, in exactly one source 1709 | file, use the UTEST_STATE macro to declare a global struct variable that 1710 | utest requires. 1711 | */ 1712 | #define UTEST_MAIN() \ 1713 | UTEST_STATE(); \ 1714 | int main(int argc, const char *const argv[]) { \ 1715 | return utest_main(argc, argv); \ 1716 | } 1717 | 1718 | #endif /* SHEREDOM_UTEST_H_INCLUDED */ 1719 | --------------------------------------------------------------------------------