diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a6e40f3..9967548 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ on: - rc1 - rc2 - rc3 - - perf-tests-win + - directories pull_request: concurrency: diff --git a/CHANGES.md b/CHANGES.md index ad45af0..21c87f3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,32 @@ # cstring - Changes +## 4.2.0 - 6th October 2026 + +* performance optimisations; + + +## 4.0.19 - 4th October 2026 + +* Corrected `cstring_readline()` so `numRead` counts every character read from the stream for the line, including the terminator; CR and LF are not stored; +* Treated a lone CR (not followed by LF, including CR at end of stream) as a line terminator that returns `CSTRING_RC_SUCCESS` and pushes the following character back; +* Freed a zero-size realloc-arena block on every platform (`realloc(pv, 0)` allocates on some), leaving a `NULL` pointer unchanged; +* `cstring_readline()` returns the result of its opening `cstring_truncate()`, so a readonly destination yields `CSTRING_RC_READONLY` for an empty line or immediate end of file, and the payload and stream position are left unchanged; +* Suppressed the GCC 15 `-Wfree-nonheap-object` false positive on inlined `stlsoft::auto_buffer` destruction in **test.performance.cstring**; +* Documented memory contracts, allocators, and a default-use sketch in **README.md**, including `cstring_getStatusCodeStringLength()`, `CSTRING_VER`, and the default and index macros; +* Added **test.performance.cstring_readline** (filesystem timings gated on **p99**); +* Added **test.component.cstring_readline** case `TEST_cstring_readline_READONLY_RETAINS_PAYLOAD`; +* Added single-line LF, CRLF, and CR cases to **test.component.cstring_readline**; **test.component.cstring_vector_readLines** splits an embedded CR into its own line; +* Added borrowed-fixed construct and assign scenarios to **test.performance.cstring**, using `stlsoft::auto_buffer` for the borrowed anchor; +* Split regular unit-test into **test.unit.cstring** (C) and **unit.cstring.cxx** (C++); +* Shared component file fixtures in **test/component/component_fixture.hpp**, and shared `time_iterations`, `emit_row`, and `write_lines_file` in **test/performance/perf_harness.hpp**; +* Named unit cases `TEST_` in shouting snake case, keeping each API or type in its real spelling, and switched assertions to the terse xTests API; +* Passed `temp_file::CloseOnOpen` in **test.component.cstring_readline** so the creating handle is closed before `fopen`, which Windows otherwise rejects as a sharing violation; +* Removed empty unit cases and unused temporary-file names; +* Shortened unit-test directories under **test/unit/** to the subject: **auto-buffer** (**test.unit.auto-buffer**, formerly **test.unit.cstring.auto_buffer**), **cstring.cxx** (**test.unit.cstring.cxx**, formerly **test.unit.cstring.1**), **insert-replace** (**test.unit.insert-replace**, formerly **test.unit.cstring.2**), **status-codes** (**test.unit.status-codes**, formerly **test.unit.cstring_getStatusCodeString**), and **cstring_vector** (**test.unit.cstring_vector**); +* Shortened example directories under **examples/** to the subject (**auto-buffer**, **cstring**, **cstring_create**, **cstring_vector**, **cstring.dynload**, **cstring.global_memory**), with CMake targets **example.c.cstring.auto_buffer**, **example.c.cstring**, **example.c.cstring_create**, **example.c.cstring_vector**, **example.cpp.cstring.dynload**, and **example.cpp.cstring.global_memory** (formerly **example.cpp.HGLOBAL_on_x64**); + + ## 4.0.18 - 29th September 2026 * Gated Windows arena flags and WinAPI allocators on `_WIN32` in **cstring.h** and **cstring.core.c**, so 32- and 64-bit Windows builds expose them without a `WIN32` or `WIN64` define; diff --git a/CMakeLists.txt b/CMakeLists.txt index 82e1af4..0a13548 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ # Purpose: Top-level CMake lists file for cstring # # Created: 21st December 2023 -# Updated: 29th September 2026 +# Updated: 3rd October 2026 # # ######################################################################## # @@ -137,8 +137,8 @@ option(NO_SHWILD "Do not recognise shwild for xTests pattern-match assertions" O # # optional: # - p99 - optional for performance tests: per-iteration percentiles -# throughout when found; also gates the filesystem file_lines vs -# cstring_vector_readLines suite (unless NO_P99); +# throughout when found; also gates the filesystem suites +# (file_lines, cstring_readline) unless NO_P99; # - shwild - for enhanced match constructs in xTests (unless NO_SHWILD); # # note: NO_CSTRING_CPP_API skips C++ examples and remaining C++ tests; C @@ -214,7 +214,7 @@ endif(BUILD_TESTING) if(BUILD_TESTING) - set(xTests_REQUIRED_VERSION_ 0.26) + set(xTests_REQUIRED_VERSION_ 0.26.5) find_package(xTests ${xTests_REQUIRED_VERSION_} REQUIRED) @@ -236,11 +236,11 @@ if(BUILD_TESTING) message("-- CMake package p99 found (version ${p99_VERSION})") else() - message("-- CMake package p99 not found; performance percentiles and file_lines suite disabled") + message("-- CMake package p99 not found; performance percentiles and filesystem suites disabled") endif() else() - message("-- p99 recognition disabled (NO_P99); performance percentiles and file_lines suite disabled") + message("-- p99 recognition disabled (NO_P99); performance percentiles and filesystem suites disabled") endif() endif(BUILD_TESTING) diff --git a/Doxyfile b/Doxyfile index 361b9a9..eefd0e6 100644 --- a/Doxyfile +++ b/Doxyfile @@ -2,7 +2,7 @@ PROJECT_NAME = "cstring" PROJECT_BRIEF = "Extensible C-style strings and vectors of such, for Unix and Windows" -PROJECT_NUMBER = 4.0.18 +PROJECT_NUMBER = 4.2.0 # Prefer SIS_CMAKE_BUILD_DIR-aligned output (same convention as Diagnosticism). # ./dox/ remains gitignored for legacy/local runs that override OUTPUT_DIRECTORY. diff --git a/FAQ.md b/FAQ.md index 1f6b307..cbfe4b4 100644 --- a/FAQ.md +++ b/FAQ.md @@ -96,10 +96,10 @@ require **STLSoft** and **xTests** (and may optionally recognise **shwild**). ## Q5: "How do I build without the C++ examples and tests?" Pass `--no-cpp` (or `-C`) to **prepare_cmake.sh**, which sets CMake -`NO_CSTRING_CPP_API=ON`. That omits C++ examples and remaining C++ tests. +`NO_CSTRING_CPP_API=ON`. That omits C++ examples and **test.unit.cstring.cxx**. -The **C** unit-tests still require **STLSoft** and **xTests** unless you also -pass `--disable-testing` / `-T`. +The **C** unit-tests, including **test.unit.cstring**, still require +**STLSoft** and **xTests** unless you also pass `--disable-testing` / `-T`. ## Q6: "Where are the examples?" diff --git a/NEWS.md b/NEWS.md index b1106c3..230fdc1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,8 @@ | Date | News Item | Details | | ------------------- | -------------------------------- | ------- | | Available from [**cstring** project on GitHub](https://synesissoftware.com/cstring): | +| 5th October 2026 | Release of [cstring 4.2.0](https://github.com/synesissoftware/cstring/releases/tag/4.2.0) | Performance optimisations | +| 4th October 2026 | Release of [cstring 4.0.19](https://github.com/synesissoftware/cstring/releases/tag/4.0.19) | lone CR ends a line; `numRead` includes EOL; readonly truncate; zero-size realloc frees; **test.performance.cstring_readline**; shortened test and example directories; **test.unit.cstring**; README usage modes | | 29th September 2026 | Release of [cstring 4.0.18](https://github.com/synesissoftware/cstring/releases/tag/4.0.18) | `_WIN32` arena gate; **win.c**; static `CoTaskMem*` (**ole32**) | | 29th September 2026 | Release of [cstring 4.0.17](https://github.com/synesissoftware/cstring/releases/tag/4.0.17) | Perf tests; `insertAt` fix; component I/O; Windows arena rename | | 27th September 2026 | Release of [cstring 4.0.16](https://github.com/synesissoftware/cstring/releases/tag/4.0.16) | Phase 4b helpers, native `.cmd`, CI dogfood | diff --git a/README.md b/README.md index 5ab4fff..bce147c 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@ # cstring -**C**-style **string**s is a small, standalone library, that provides extensible C-style string instances and extensible arrays of such, for Unix and Windows. +Small standalone C library that provides extensible C-style strings and extensible arrays of those strings, for Unix and Windows. ![C](https://img.shields.io/badge/C-00599C?style=flat&logo=c&logoColor=white) -![C++](https://img.shields.io/badge/C%2B%2B-00599C?style=flat&logo=c%2B%2B&logoColor=white) [![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![GitHub release](https://img.shields.io/github/v/release/synesissoftware/cstring.svg)](https://github.com/synesissoftware/cstring/releases/latest) [![Last Commit](https://img.shields.io/github/last-commit/synesissoftware/cstring)](https://github.com/synesissoftware/cstring/commits/master) @@ -14,9 +13,14 @@ ## Table of Contents - [Introduction](#introduction) +- [Usage modes](#usage-modes) + - [Default use](#default-use) + - [Storage contract](#storage-contract) + - [Allocators](#allocators) - [Installation](#installation) - [Components](#components) - [Types](#types) + - [Constants](#constants) - [String API](#string-api) - [Status and capacity](#status-and-capacity) - [Creation/destruction functions](#creationdestruction-functions) @@ -34,9 +38,79 @@ ## Introduction -**cstring** is a small, standalone library that provides extensible C-style string instances and extensible arrays of such, for Unix and Windows. +**cstring** provides one resizeable string, `cstring_t`, and a vector of those strings, `cstring_vector_t`. A `cstring_t` is always a length, a pointer, a capacity, and flags. The flags select a memory contract. An owned growable string is the default. Fixed, borrowed, auto-buffer, and readonly are the other contracts. Which heap owns the memory is a separate choice: `realloc` by default, and the Windows heaps where those flags exist. -The **C** API has no non-standard dependencies. Optional C++ examples and remaining C++ tests may be omitted with `--no-cpp` / `NO_CSTRING_CPP_API`. Building tests requires **STLSoft** and **xTests** (and may optionally recognise **shwild**). +The **C** API has no non-standard dependencies. Building tests requires **STLSoft** and **xTests** (and may optionally recognise **shwild**). + + +## Usage modes + +Ordinary code uses the default contract and never sets a flag. The other contracts exist so a caller can cap growth, write into a buffer they already have, or select a Windows heap, without a second string type. + + +### Default use + +```c +#include + +#include +#include + +int main(void) +{ + cstring_t cs; + CSTRING_RC rc = cstring_create(&cs, "Hello"); + + if (CSTRING_RC_SUCCESS != rc) + { + return EXIT_FAILURE; + } + + printf("%s\n", cs.ptr); + + cstring_destroy(&cs); + + return EXIT_SUCCESS; +} +``` + + +### Storage contract + +Pass memory flags to `cstring_createEx()` or `cstring_createLenEx()`. For a borrowed buffer, `arena` is that buffer and `capacity` is its size. With no memory flags, those parameters are ignored and the string is an owned heap allocation. + +| Mode | Flags | Familiar form | If it cannot grow | +| --- | --- | --- | --- | +| Owned, growable | (none) | A heap `std::string`, or Rust `String` | `CSTRING_RC_OUTOFMEMORY` | +| Owned, fixed | `CSTRING_F_MEMORY_IS_FIXED` | That same owned string, with a hard ceiling | `CSTRING_RC_EXCEEDFIXEDCAPACITY` | +| Borrowed | `CSTRING_F_MEMORY_IS_BORROWED` (implies fixed) | A caller-owned `char buf[N]`, writable up to `N` | `CSTRING_RC_EXCEEDBORROWEDCAPACITY` | +| Auto-buffer | `CSTRING_F_MEMORY_IS_BORROWED` \| `CSTRING_F_MEMORY_CAN_GROW_TO_HEAP` | **`stlsoft::auto_buffer`** / `llvm::SmallString` | Spills to the heap, then stays there | +| Readonly | `CSTRING_F_MEMORY_IS_READONLY` | A frozen instance; with borrowed, `std::string_view` or Rust `&str` | `CSTRING_RC_READONLY` | + +`std::string` SSO keeps a small buffer inside the object; **cstring**'s auto-buffer uses a buffer you supply, of a size you choose, and `cstring_t` stays four fields; after a spill the instance stays on the heap (Rust's standard `String` has no SSO). + +* Owned, fixed, and borrowed (including Windows allocators, where the host has them): [**example.c.cstring**](./examples/c/cstring/); +* Auto-buffer: [**example.c.cstring.auto_buffer**](./examples/c/auto-buffer/); +* Win32 global memory: [**example.cpp.cstring.global_memory**](./examples/cpp/cstring.global_memory/). + + +### Allocators + +The arena flags apply to memory the library owns: the default heap, a fixed owned buffer, and the heap side of an auto-buffer. + +| Arena | Flag | Where | +| --- | --- | --- | +| `realloc` | `CSTRING_F_USE_REALLOC` (the default) | Unix and Windows | +| Win32 global memory | `CSTRING_F_USE_WINDOWS_GLOBAL_MEMORY` | Windows | +| Process heap | `CSTRING_F_USE_WINDOWS_PROCESSHEAP_MEMORY` | Windows | +| COM task allocator | `CSTRING_F_USE_WINDOWS_COM_TASK_MEMORY` | Windows | + +A few further rules: + +* `cstring_init()` stores `cstring_t_DEFAULT`. That instance does not need `cstring_destroy()`. Every `cstring_create*` does; +* `cstring_yield2()` hands back an owned payload. Borrowed and readonly instances refuse it. A Windows DLL built on `realloc` returns `CSTRING_RC_CANNOTYIELDFROMSO`; +* The character type is `char` unless `CSTRING_USE_WIDE_STRINGS` is set (normally both `UNICODE` and `_UNICODE` on Windows). `CSTRING_NO_USE_WIDE_STRINGS` forces `char`. That choice is made at compile time; +* Custom arenas (`CSTRING_F_USE_CUSTOMARENAFUNCTIONS`) are declared and return `CSTRING_RC_CUSTOMARENANOTSUPPORTED`. `CSTRING_F_MEMORY_IS_OFFSET` is set by the implementation and is not a client mode. ## Installation @@ -76,6 +150,15 @@ The C API is based around two structures: ``` +### Constants + +* `CSTRING_VER` — the composite library version; +* `cstring_t_DEFAULT` — `{ 0, NULL, 0, 0 }`, an uninitialised `cstring_t`. `cstring_init()` assigns this; +* `cstring_vector_t_DEFAULT` — the same shape for a `cstring_vector_t`; +* `cstring_vector_DEFAULT_CAPACITY` — sentinel (`~(size_t)0`) passed to creators so the implementation chooses the capacity; +* `CSTRING_FROM_END(x)` — reverse index for `cstring_insert()`, `cstring_insertLen()`, `cstring_replace()`, and `cstring_replaceLen()`; + + ### String API Defined in **cstring/cstring.h**: @@ -84,6 +167,7 @@ Defined in **cstring/cstring.h**: #### Status and capacity * `cstring_getStatusCodeString()` — returns a nul-terminated description of a `CSTRING_RC` code; +* `cstring_getStatusCodeStringLength()` — returns the length of that description, or 0 if the code is not recognised; * `cstring_setCapacity()` — adjusts capacity (subject to fixed / borrowed / readonly rules); * `cstring_yield2()` — yields ownership of the payload (and raw buffer) to the caller; @@ -134,16 +218,16 @@ Defined in **cstring/cstring.vector.h**: ## Examples -Examples live under **examples/** (`c/` and `cpp/`), each with a short **README.md**. Build them with `BUILD_EXAMPLES` (on by default); run via **run_all_examples.sh**. +Examples live under **examples/** (`c/` and `cpp/`). The directory is the subject; the built program is `example..`. Each has a short **README.md**. Build them with `BUILD_EXAMPLES` (on by default); run via **run_all_examples.sh**. | Example | Language | Notes | | ------- | -------- | ----- | -| [**example.c.auto_buffer**](./examples/c/example.c.auto_buffer/) | C | Borrowed buffer that may grow to the heap | -| [**example.c.cstring**](./examples/c/example.c.cstring/) | C | Core `cstring_t` create / assign / append / truncate / copy / swap | -| [**example.c.cstring_create**](./examples/c/example.c.cstring_create/) | C | Minimal `cstring_create()` | -| [**example.c.cstring_vector**](./examples/c/example.c.cstring_vector/) | C | Read lines into `cstring_vector_t` and sort (input path or `--`; `SIS_EXAMPLE_SMOKE` enables no-arg demo) | -| [**example.cpp.cstring.dynload**](./examples/cpp/example.cpp.cstring.dynload/) | C++ | Windows-only dynamic load of the cstring DLL | -| [**example.cpp.HGLOBAL_on_x64**](./examples/cpp/example.cpp.HGLOBAL_on_x64/) | C++ | Windows-only `CSTRING_F_USE_WINDOWS_GLOBAL_MEMORY` | +| [**example.c.cstring**](./examples/c/cstring/) | C | Core `cstring_t` create / assign / append / truncate / copy / swap | +| [**example.c.cstring.auto_buffer**](./examples/c/auto-buffer/) | C | Borrowed buffer that may grow to the heap | +| [**example.c.cstring_create**](./examples/c/cstring_create/) | C | Minimal `cstring_create()` | +| [**example.c.cstring_vector**](./examples/c/cstring_vector/) | C | Read lines into `cstring_vector_t` and sort (input path or `--`; `SIS_EXAMPLE_SMOKE` enables no-arg demo) | +| [**example.cpp.cstring.dynload**](./examples/cpp/cstring.dynload/) | C++ | Windows-only dynamic load of the cstring DLL | +| [**example.cpp.cstring.global_memory**](./examples/cpp/cstring.global_memory/) | C++ | Windows-only `CSTRING_F_USE_WINDOWS_GLOBAL_MEMORY` | ## Project Information @@ -197,4 +281,3 @@ Projects in which **cstring** is used include: - diff --git a/TODO.md b/TODO.md index a6c3bf2..e936f3a 100644 --- a/TODO.md +++ b/TODO.md @@ -15,8 +15,12 @@ * [x] ~~~Delete Visual Studio 98 files~~~ - ✅; * [x] ~~~Delete Visual Studio 2003+ files~~~ - ✅; * [x] ~~~discriminate on `_WIN32` in implementation (and maybe also in API)~~~ - ✅; -* [ ] check `CSTRING_USE_WINAPI_`; +* [x] ~~~check `CSTRING_USE_WINAPI_`~~~; * [ ] custom arena(s); +* [ ] `cstring_vector_readlineEx()` that takes a flag to prevent truncate, thereby allowing client code to add to an existing string; +* [ ] when go to 5.x, change the name of `cstring_vector_readLines()` to `cstring_vector_readlines()`; +* [ ] when go to 5.x, consider use of SSO; +* [ ] when go to 5.x, consider expanding `cstring_vector_t` to allow it to own the memory of the strings it manages, such that can do a single file read and then break up into strings without allocating payload memory; ## Performance improvements @@ -38,6 +42,7 @@ * [x] ~~~`CMAKE_INSTALL_LIBDIR` (replaces legacy `LIB_INSTALL_DIR`)~~~ - ✅; * [x] ~~~**CTest**~~~ - ✅; * [ ] build DLL on Windows; + * [ ] build dylib on macOS; * [x] ~~~`/MT` build option for Visual C++ (`--msvc-mt` / `MSVC_USE_MT`)~~~ - ✅; * [x] ~~~**shwild** dependency (testing only; `--no-shwild` / `NO_SHWILD`)~~~ - ✅; * [x] ~~~Doxygen (**Doxyfile**, **doc/mainpage.md**, **generate_doxygen.sh**)~~~ - ✅; diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index bd7b7b5..8343e22 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -1,5 +1,5 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten -add_subdirectory(example.c.auto_buffer) -add_subdirectory(example.c.cstring) -add_subdirectory(example.c.cstring_create) -add_subdirectory(example.c.cstring_vector) +add_subdirectory(auto-buffer) +add_subdirectory(cstring) +add_subdirectory(cstring_create) +add_subdirectory(cstring_vector) diff --git a/examples/cpp/example.cpp.HGLOBAL_on_x64/CMakeLists.txt b/examples/c/auto-buffer/CMakeLists.txt similarity index 60% rename from examples/cpp/example.cpp.HGLOBAL_on_x64/CMakeLists.txt rename to examples/c/auto-buffer/CMakeLists.txt index 722def6..5139df0 100644 --- a/examples/cpp/example.cpp.HGLOBAL_on_x64/CMakeLists.txt +++ b/examples/c/auto-buffer/CMakeLists.txt @@ -1,2 +1,2 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten -define_example_program(example.cpp.HGLOBAL_on_x64 main.cpp) +define_example_program(example.c.cstring.auto_buffer main.c) diff --git a/examples/c/example.c.auto_buffer/README.md b/examples/c/auto-buffer/README.md similarity index 83% rename from examples/c/example.c.auto_buffer/README.md rename to examples/c/auto-buffer/README.md index 265181a..d069e1e 100644 --- a/examples/c/example.c.auto_buffer/README.md +++ b/examples/c/auto-buffer/README.md @@ -1,4 +1,4 @@ -# example.c.auto_buffer +# example.c.cstring.auto_buffer ## Purpose @@ -14,7 +14,7 @@ Built when `BUILD_EXAMPLES` is enabled (the default). ## Run ```sh -${SIS_CMAKE_BUILD_DIR:-./_build}/examples/c/example.c.auto_buffer/example.c.auto_buffer +${SIS_CMAKE_BUILD_DIR:-./_build}/examples/c/auto-buffer/example.c.cstring.auto_buffer ``` diff --git a/examples/c/example.c.auto_buffer/main.c b/examples/c/auto-buffer/main.c similarity index 96% rename from examples/c/example.c.auto_buffer/main.c rename to examples/c/auto-buffer/main.c index 75bfe32..578a322 100644 --- a/examples/c/example.c.auto_buffer/main.c +++ b/examples/c/auto-buffer/main.c @@ -1,12 +1,12 @@ /* ///////////////////////////////////////////////////////////////////////// - * File: examples/c/example.c.auto_buffer/main.c + * File: examples/c/auto-buffer/main.c * * Purpose: Example illustrating borrowed buffer growth to the heap * (`CSTRING_F_MEMORY_IS_BORROWED` + * `CSTRING_F_MEMORY_CAN_GROW_TO_HEAP`). * * Created: 28th July 2011 - * Updated: 2nd August 2026 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ diff --git a/examples/c/example.c.cstring/CMakeLists.txt b/examples/c/cstring/CMakeLists.txt similarity index 100% rename from examples/c/example.c.cstring/CMakeLists.txt rename to examples/c/cstring/CMakeLists.txt diff --git a/examples/c/example.c.cstring/README.md b/examples/c/cstring/README.md similarity index 86% rename from examples/c/example.c.cstring/README.md rename to examples/c/cstring/README.md index 8d1810f..6fe641c 100644 --- a/examples/c/example.c.cstring/README.md +++ b/examples/c/cstring/README.md @@ -14,7 +14,7 @@ Built when `BUILD_EXAMPLES` is enabled (the default). ## Run ```sh -${SIS_CMAKE_BUILD_DIR:-./_build}/examples/c/example.c.cstring/example.c.cstring +${SIS_CMAKE_BUILD_DIR:-./_build}/examples/c/cstring/example.c.cstring ``` @@ -34,7 +34,7 @@ cstring_createLen(): Contents: some more content Appending another C-style string to the instance: - Contents: some more content - + Contents: some more content - Appending part of another C-style string to the instance: Contents: some more content - and another @@ -46,19 +46,19 @@ cstring_createLen(): Contents: some more content - and another; and a whole lot more stringy stuff to try and precipitate a reallocation, in order to check that the 3.3 change will be tested; and a last bit to try and get the remaining reallocation Truncating the instance to 10 characters: - Contents: some more + Contents: some more Copying the instance: - Contents: 0x… some more - Contents of copy: 0x… some more + Contents: 0x… some more + Contents of copy: 0x… some more Copying the instance (by C-style string): - Contents: 0x… some more - Contents of copy: 0x… some more + Contents: 0x… some more + Contents of copy: 0x… some more Swapping with the copy: - Contents: 0x… some more - Contents of copy: 0x… some more + Contents: 0x… some more + Contents of copy: 0x… some more Destroying the copy: diff --git a/examples/c/example.c.cstring/main.c b/examples/c/cstring/main.c similarity index 99% rename from examples/c/example.c.cstring/main.c rename to examples/c/cstring/main.c index daaf369..84f9208 100644 --- a/examples/c/example.c.cstring/main.c +++ b/examples/c/cstring/main.c @@ -1,12 +1,12 @@ /* ///////////////////////////////////////////////////////////////////////// - * File: examples/c/example.c.cstring/main.c + * File: examples/c/cstring/main.c * * Purpose: Example exercising core `cstring_t` create/assign/append/ * truncate/copy/swap paths (including Windows allocator flags * where available). * * Created: 7th July 2005 - * Updated: 27th September 2026 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -154,7 +154,6 @@ int main(int argc, char *argv[]) } - /* ///////////////////////////////////////////////////////////////////////// * function implementations */ diff --git a/examples/c/example.c.cstring_create/CMakeLists.txt b/examples/c/cstring_create/CMakeLists.txt similarity index 100% rename from examples/c/example.c.cstring_create/CMakeLists.txt rename to examples/c/cstring_create/CMakeLists.txt diff --git a/examples/c/example.c.cstring_create/README.md b/examples/c/cstring_create/README.md similarity index 85% rename from examples/c/example.c.cstring_create/README.md rename to examples/c/cstring_create/README.md index 117f0c9..6b56703 100644 --- a/examples/c/example.c.cstring_create/README.md +++ b/examples/c/cstring_create/README.md @@ -14,7 +14,7 @@ Built when `BUILD_EXAMPLES` is enabled (the default). ## Run ```sh -${SIS_CMAKE_BUILD_DIR:-./_build}/examples/c/example.c.cstring_create/example.c.cstring_create +${SIS_CMAKE_BUILD_DIR:-./_build}/examples/c/cstring_create/example.c.cstring_create ``` diff --git a/examples/c/example.c.cstring_create/main.c b/examples/c/cstring_create/main.c similarity index 91% rename from examples/c/example.c.cstring_create/main.c rename to examples/c/cstring_create/main.c index fcca051..fff221d 100644 --- a/examples/c/example.c.cstring_create/main.c +++ b/examples/c/cstring_create/main.c @@ -1,10 +1,10 @@ /* ///////////////////////////////////////////////////////////////////////// - * File: examples/c/example.c.cstring_create/main.c + * File: examples/c/cstring_create/main.c * * Purpose: Minimal example of `cstring_create()` / `cstring_destroy()`. * - * Created: ... - * Updated: 2nd August 2026 + * Created: 12th January 2024 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -15,6 +15,7 @@ /* Standard C header files */ #include + /* ///////////////////////////////////////////////////////////////////////// * main() */ diff --git a/examples/c/example.c.cstring_vector/CMakeLists.txt b/examples/c/cstring_vector/CMakeLists.txt similarity index 100% rename from examples/c/example.c.cstring_vector/CMakeLists.txt rename to examples/c/cstring_vector/CMakeLists.txt diff --git a/examples/c/example.c.cstring_vector/README.md b/examples/c/cstring_vector/README.md similarity index 79% rename from examples/c/example.c.cstring_vector/README.md rename to examples/c/cstring_vector/README.md index 98d25e2..568575e 100644 --- a/examples/c/example.c.cstring_vector/README.md +++ b/examples/c/cstring_vector/README.md @@ -16,9 +16,9 @@ Built when `BUILD_EXAMPLES` is enabled (the default). Requires an input source (path, or `--` for stdin). Intended for interactive / scripted human use: ```sh -${SIS_CMAKE_BUILD_DIR:-./_build}/examples/c/example.c.cstring_vector/example.c.cstring_vector path/to/file.txt -${SIS_CMAKE_BUILD_DIR:-./_build}/examples/c/example.c.cstring_vector/example.c.cstring_vector -- # stdin -printf 'beta\nalpha\ngamma\n' | ${SIS_CMAKE_BUILD_DIR:-./_build}/examples/c/example.c.cstring_vector/example.c.cstring_vector -- +${SIS_CMAKE_BUILD_DIR:-./_build}/examples/c/cstring_vector/example.c.cstring_vector path/to/file.txt +${SIS_CMAKE_BUILD_DIR:-./_build}/examples/c/cstring_vector/example.c.cstring_vector -- # stdin +printf 'beta\nalpha\ngamma\n' | ${SIS_CMAKE_BUILD_DIR:-./_build}/examples/c/cstring_vector/example.c.cstring_vector -- ``` With **`SIS_EXAMPLE_SMOKE`** set, a no-argument invocation uses a small in-process `tmpfile()` sample (no child re-exec). **`run_all_examples.sh`** exports this for automated smoke runs. Without the variable, no arguments fails with a usage error. diff --git a/examples/c/example.c.cstring_vector/main.c b/examples/c/cstring_vector/main.c similarity index 98% rename from examples/c/example.c.cstring_vector/main.c rename to examples/c/cstring_vector/main.c index a460381..35972ed 100644 --- a/examples/c/example.c.cstring_vector/main.c +++ b/examples/c/cstring_vector/main.c @@ -1,11 +1,11 @@ /* ///////////////////////////////////////////////////////////////////////// - * File: examples/c/example.c.cstring_vector/main.c + * File: examples/c/cstring_vector/main.c * * Purpose: Example reading lines into a `cstring_vector_t` and sorting * them. * - * Created: ... - * Updated: 23rd September 2026 + * Created: 12th January 2024 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ diff --git a/examples/cpp/CMakeLists.txt b/examples/cpp/CMakeLists.txt index cffad6f..5d84add 100644 --- a/examples/cpp/CMakeLists.txt +++ b/examples/cpp/CMakeLists.txt @@ -1,7 +1,7 @@ if(_BUILD_AS_WIN32) - add_subdirectory(example.cpp.cstring.dynload) - add_subdirectory(example.cpp.HGLOBAL_on_x64) + add_subdirectory(cstring.dynload) + add_subdirectory(cstring.global_memory) endif(_BUILD_AS_WIN32) diff --git a/examples/cpp/example.cpp.cstring.dynload/CMakeLists.txt b/examples/cpp/cstring.dynload/CMakeLists.txt similarity index 100% rename from examples/cpp/example.cpp.cstring.dynload/CMakeLists.txt rename to examples/cpp/cstring.dynload/CMakeLists.txt diff --git a/examples/cpp/example.cpp.cstring.dynload/README.md b/examples/cpp/cstring.dynload/README.md similarity index 90% rename from examples/cpp/example.cpp.cstring.dynload/README.md rename to examples/cpp/cstring.dynload/README.md index e33bffc..0f4bb01 100644 --- a/examples/cpp/example.cpp.cstring.dynload/README.md +++ b/examples/cpp/cstring.dynload/README.md @@ -14,7 +14,7 @@ Built when `BUILD_EXAMPLES` is enabled, `NO_CSTRING_CPP_API` is off, and the tar ## Run ```sh -${SIS_CMAKE_BUILD_DIR:-./_build}/examples/cpp/example.cpp.cstring.dynload/example.cpp.cstring.dynload +${SIS_CMAKE_BUILD_DIR:-./_build}/examples/cpp/cstring.dynload/example.cpp.cstring.dynload ``` Expects a suitable **cstring** DLL (for example `cstring.3.debug.dll`) to be loadable on the process search path. diff --git a/examples/cpp/example.cpp.cstring.dynload/main.cpp b/examples/cpp/cstring.dynload/main.cpp similarity index 97% rename from examples/cpp/example.cpp.cstring.dynload/main.cpp rename to examples/cpp/cstring.dynload/main.cpp index e2b327c..5b0378f 100644 --- a/examples/cpp/example.cpp.cstring.dynload/main.cpp +++ b/examples/cpp/cstring.dynload/main.cpp @@ -1,11 +1,11 @@ /* ///////////////////////////////////////////////////////////////////////// - * File: examples/cpp/example.cpp.cstring.dynload/main.cpp + * File: examples/cpp/cstring.dynload/main.cpp * * Purpose: Windows-only example loading the cstring DLL dynamically via * `LoadLibrary` / `GetProcAddress`. * * Created: 19th August 2005 - * Updated: 27th September 2026 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -38,6 +38,7 @@ namespace std /* Standard C header files */ #include + /* ///////////////////////////////////////////////////////////////////////// * forward declarations */ diff --git a/examples/cpp/cstring.global_memory/CMakeLists.txt b/examples/cpp/cstring.global_memory/CMakeLists.txt new file mode 100644 index 0000000..7011da3 --- /dev/null +++ b/examples/cpp/cstring.global_memory/CMakeLists.txt @@ -0,0 +1,2 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +define_example_program(example.cpp.cstring.global_memory main.cpp) diff --git a/examples/cpp/example.cpp.HGLOBAL_on_x64/README.md b/examples/cpp/cstring.global_memory/README.md similarity index 75% rename from examples/cpp/example.cpp.HGLOBAL_on_x64/README.md rename to examples/cpp/cstring.global_memory/README.md index 16bc989..5bc0cea 100644 --- a/examples/cpp/example.cpp.HGLOBAL_on_x64/README.md +++ b/examples/cpp/cstring.global_memory/README.md @@ -1,4 +1,4 @@ -# example.cpp.HGLOBAL_on_x64 +# example.cpp.cstring.global_memory ## Purpose @@ -14,7 +14,7 @@ Built when `BUILD_EXAMPLES` is enabled, `NO_CSTRING_CPP_API` is off, and the tar ## Run ```sh -${SIS_CMAKE_BUILD_DIR:-./_build}/examples/cpp/example.cpp.HGLOBAL_on_x64/example.cpp.HGLOBAL_on_x64 +${SIS_CMAKE_BUILD_DIR:-./_build}/examples/cpp/cstring.global_memory/example.cpp.cstring.global_memory ``` diff --git a/examples/cpp/example.cpp.HGLOBAL_on_x64/main.cpp b/examples/cpp/cstring.global_memory/main.cpp similarity index 100% rename from examples/cpp/example.cpp.HGLOBAL_on_x64/main.cpp rename to examples/cpp/cstring.global_memory/main.cpp diff --git a/include/cstring/cstring.h b/include/cstring/cstring.h index edd5026..a4cfa05 100644 --- a/include/cstring/cstring.h +++ b/include/cstring/cstring.h @@ -4,7 +4,7 @@ * Purpose: Definition of the cstring.core API. * * Created: 16th June 1994 - * Updated: 29th September 2026 + * Updated: 4th October 2026 * * Home: http://synesis.com.au/software/ * @@ -55,8 +55,8 @@ #ifndef CSTRING_DOCUMENTATION_SKIP_SECTION # define CSTRING_VER_CSTRING_H_CSTRING_MAJOR 3 # define CSTRING_VER_CSTRING_H_CSTRING_MINOR 12 -# define CSTRING_VER_CSTRING_H_CSTRING_REVISION 1 -# define CSTRING_VER_CSTRING_H_CSTRING_EDIT 90 +# define CSTRING_VER_CSTRING_H_CSTRING_REVISION 2 +# define CSTRING_VER_CSTRING_H_CSTRING_EDIT 94 #endif /* !CSTRING_DOCUMENTATION_SKIP_SECTION */ @@ -103,6 +103,8 @@ # define CSTRING_VER_4_0_16 0x040010ff # define CSTRING_VER_4_0_17 0x040011ff # define CSTRING_VER_4_0_18 0x040012ff +# define CSTRING_VER_4_0_19 0x040013ff +# define CSTRING_VER_4_2_0_A1 0x04020041 #endif /* !CSTRING_DOCUMENTATION_SKIP_SECTION */ /** \def CSTRING_VER_MAJOR @@ -122,9 +124,9 @@ */ #define CSTRING_VER_MAJOR 4 -#define CSTRING_VER_MINOR 0 -#define CSTRING_VER_PATCH 18 -#define CSTRING_VER_ALPHABETA 0xFF +#define CSTRING_VER_MINOR 2 +#define CSTRING_VER_PATCH 0 +#define CSTRING_VER_ALPHABETA 0x41 #define CSTRING_VER \ (0\ @@ -826,17 +828,21 @@ cstring_swap( /** Reads in a line of text from the given text stream + * + * A line ends at LF, at CRLF, or at a lone CR, including a CR at end of + * stream. The terminator is not stored. A lone CR pushes the next character + * back onto the stream; * * \param stm The stream from which the line will be read; * \param pcs The initialised string instance into which the line will be * stored; * \param numRead An optional pointer to a variable to receive the number of - * characters appended to the string. May be NULL; + * characters read from the stream, including any terminator. May be NULL; * * \retval CSTRING_RC_INVALIDSTREAM The stream was not valid (as tested by * ferror()); - * \retval CSTRING_RC_SUCCESS A line terminated by a carriage return was - * read in; + * \retval CSTRING_RC_SUCCESS A line terminated by CR, LF, or CRLF was read + * in; * \retval CSTRING_RC_EOF A line terminated by the end-of-file was read in; * \retval -other- Any other value returned by cstring_appendLen(); * diff --git a/include/cstring/cstring.vector.h b/include/cstring/cstring.vector.h index 25df446..9e7c3d3 100644 --- a/include/cstring/cstring.vector.h +++ b/include/cstring/cstring.vector.h @@ -4,7 +4,7 @@ * Purpose: Definition of the cstring.vector API. * * Created: 21st January 2012 - * Updated: 28th September 2026 + * Updated: 29th September 2026 * * Home: http://synesis.com.au/software/ * diff --git a/prepare_cmake.sh b/prepare_cmake.sh index d067722..c9ce8f3 100755 --- a/prepare_cmake.sh +++ b/prepare_cmake.sh @@ -237,7 +237,7 @@ Flags/options: --no-p99 prevents recognising p99 library (NO_P99=ON); performance tests - then omit percentiles and the file_lines suite + then omit percentiles and the filesystem suites --no-shwild prevents recognising shwild library (NO_SHWILD=ON); xTests diff --git a/run_all_component_tests.sh b/run_all_component_tests.sh index 2d432f3..c0bd27e 100755 --- a/run_all_component_tests.sh +++ b/run_all_component_tests.sh @@ -175,7 +175,7 @@ Flags/options: does not execute a build before running programs --verbosity - specifies an explicit verbosity (forwarded when supported) + specifies an explicit verbosity, forwarded to each program standard flags: @@ -275,10 +275,7 @@ if [ $status -eq 0 ]; then echo "executing ${fClr}:" fi - if "$f" --verbosity="$Verbosity" 2>/dev/null; then - - : - elif "$f"; then + if "$f" --verbosity="$Verbosity"; then : else diff --git a/run_all_examples.sh b/run_all_examples.sh index a2b652e..967f82c 100755 --- a/run_all_examples.sh +++ b/run_all_examples.sh @@ -32,7 +32,6 @@ AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} ListOnly=0 RunMake=1 SisUseColours=0 -Verbosity=${XTESTS_VERBOSITY:-${TEST_VERBOSITY:-3}} # ########################################################## @@ -135,11 +134,6 @@ while [[ $# -gt 0 ]]; do RunMake=0 ;; - --verbosity) - - shift - Verbosity=$1 - ;; --help) [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" @@ -165,9 +159,6 @@ Flags/options: --no-make does not execute a build before running programs - --verbosity - specifies an explicit verbosity (forwarded when supported) - standard flags: @@ -261,19 +252,10 @@ if [ $status -eq 0 ]; then continue fi - if [ $Verbosity -ge 3 ]; then - - echo - fi - if [ $Verbosity -ge 2 ]; then - - echo "executing ${fClr}:" - fi - - if "$f" --verbosity="$Verbosity" 2>/dev/null; then + echo + echo "executing ${fClr}:" - : - elif "$f"; then + if "$f"; then : else diff --git a/run_all_performance_tests.sh b/run_all_performance_tests.sh index 077d839..6f3c373 100755 --- a/run_all_performance_tests.sh +++ b/run_all_performance_tests.sh @@ -33,7 +33,6 @@ GapGroups=0 ListOnly=0 RunMake=1 SisUseColours=0 -Verbosity=${XTESTS_VERBOSITY:-${TEST_VERBOSITY:-3}} # ########################################################## @@ -140,11 +139,6 @@ while [[ $# -gt 0 ]]; do RunMake=0 ;; - --verbosity) - - shift - Verbosity=$1 - ;; --help) [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" @@ -174,9 +168,6 @@ Flags/options: --no-make does not execute a build before running programs - --verbosity - specifies an explicit verbosity (forwarded when supported) - standard flags: @@ -271,19 +262,10 @@ if [ $status -eq 0 ]; then continue fi - if [ $Verbosity -ge 3 ]; then - - echo - fi - if [ $Verbosity -ge 2 ]; then - - echo "executing ${fClr}:" - fi - - if "$f" --verbosity="$Verbosity" 2>/dev/null; then + echo + echo "executing ${fClr}:" - : - elif "$f"; then + if "$f"; then : else diff --git a/run_all_scratch_tests.sh b/run_all_scratch_tests.sh index 2d83450..5e6659a 100755 --- a/run_all_scratch_tests.sh +++ b/run_all_scratch_tests.sh @@ -32,7 +32,6 @@ AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} ListOnly=0 RunMake=1 SisUseColours=0 -Verbosity=${XTESTS_VERBOSITY:-${TEST_VERBOSITY:-3}} # ########################################################## @@ -135,11 +134,6 @@ while [[ $# -gt 0 ]]; do RunMake=0 ;; - --verbosity) - - shift - Verbosity=$1 - ;; --help) [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" @@ -165,9 +159,6 @@ Flags/options: --no-make does not execute a build before running programs - --verbosity - specifies an explicit verbosity (forwarded when supported) - standard flags: @@ -257,19 +248,10 @@ if [ $status -eq 0 ]; then continue fi - if [ $Verbosity -ge 3 ]; then - - echo - fi - if [ $Verbosity -ge 2 ]; then - - echo "executing ${fClr}:" - fi - - if "$f" --verbosity="$Verbosity" 2>/dev/null; then + echo + echo "executing ${fClr}:" - : - elif "$f"; then + if "$f"; then : else diff --git a/run_all_unit_tests.sh b/run_all_unit_tests.sh index eb49322..301ccae 100755 --- a/run_all_unit_tests.sh +++ b/run_all_unit_tests.sh @@ -173,7 +173,7 @@ Flags/options: accepted for compatibility; this script always runs unit tests only --verbosity - specifies an explicit verbosity (forwarded when supported) + specifies an explicit verbosity, forwarded to each program standard flags: @@ -273,10 +273,7 @@ if [ $status -eq 0 ]; then echo "executing ${fClr}:" fi - if "$f" --verbosity="$Verbosity" 2>/dev/null; then - - : - elif "$f"; then + if "$f" --verbosity="$Verbosity"; then : else diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5200656..9a592f8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,8 +4,8 @@ # # Purpose: CMake lists file for cstring core library # -# Created: ... -# Updated: 29th September 2026 +# Created: 21st December 2023 +# Updated: 3rd October 2026 # # ######################################################################## # diff --git a/src/cstring.core.c b/src/cstring.core.c index 00f77c3..ad566b0 100644 --- a/src/cstring.core.c +++ b/src/cstring.core.c @@ -4,7 +4,7 @@ * Purpose: The implementation of the cstring core API * * Created: 16th June 1994 - * Updated: 29th September 2026 + * Updated: 3rd October 2026 * * Home: http://synesis.com.au/software/ * @@ -59,11 +59,9 @@ /* Standard C header files */ -#include #include #include #include - #ifdef CSTRING_USE_WIDE_STRINGS # include #endif /* CSTRING_USE_WIDE_STRINGS */ @@ -78,13 +76,6 @@ #define CSTRING_OFFSET_SIZE (16) -/* ///////////////////////////////////////////////////////////////////////// - * debugging - */ - -#define CSTRING_ASSERT(expr) assert(expr) - - /* ///////////////////////////////////////////////////////////////////////// * character encoding */ @@ -178,12 +169,10 @@ convert_negative_index_( * \param src Source. May be \c NULL; * \param lim Maximum number of elements in \c dst; * - * \return The actual length of src if not \c NULL; 0 otherwise - * * \pre NULL != dst */ static -size_t +void cstring_strlcpy_safe_( cstring_char_t dst[] , cstring_char_t const* src @@ -195,30 +184,10 @@ cstring_strlcpy_safe_( if (NULL == src) { memset(dst, 0, sizeof(cstring_char_t) * lim); - - return 0; } else { - size_t i; - - for (i = 0; i != lim; ++i, ++dst, ++src) - { - *dst = *src; - - if ('\0' == src[0]) - { - break; - } - } - - memset(dst, 0, sizeof(cstring_char_t) * (lim - i)); - - for (; '\0' != src[0]; ++i, ++src) - { - } - - return i; + memcpy(dst, src, sizeof(cstring_char_t) * lim); } } @@ -291,17 +260,22 @@ cstring_realloc_2_( { case CSTRING_F_USE_REALLOC: -#if defined(_MSC_VER) && \ - defined(_DEBUG) + /* realloc(pv, 0) allocates on some platforms; free the block, + * and do nothing when pv is NULL. + */ if (0 == cb) { - free(pv); + if (NULL != pv) + { + free(pv); + } + return NULL; } -#endif /* _DEBUG */ + pvNew = realloc(pv, cb); break; -#ifdef CSTRING_USE_WINAPI_ +#ifdef _WIN32 case CSTRING_F_USE_WINDOWS_GLOBAL_MEMORY: pvNew = win32_global_realloc(pv, cb); @@ -314,7 +288,7 @@ cstring_realloc_2_( pvNew = win32_comtask_realloc(pv, cb); break; -#endif /* CSTRING_USE_WINAPI_ */ +#endif /* _WIN32 */ #if defined(CSTRING_USE_SYNESIS_APIS) case CSTRING_F_USE_SYNESIS_HATOR: #endif /* CSTRING_USE_SYNESIS_APIS */ @@ -367,6 +341,31 @@ cstring_realloc_( return cstring_realloc_2_(pv, cch, flags, prc, NULL, NULL); } +/** Releases owned storage and leaves \c pcs empty with a NULL pointer. */ +static +CSTRING_RC +cstring_release_to_empty_( + struct cstring_t* pcs +) +{ + CSTRING_RC rc = CSTRING_RC_SUCCESS; + + CSTRING_ASSERT(NULL != pcs); + + if (!(CSTRING_F_MEMORY_IS_BORROWED & pcs->flags) && + NULL != pcs->ptr) + { + (void)cstring_realloc_(pcs->ptr, 0, pcs->flags, &rc); + } + + pcs->len = 0; + pcs->ptr = NULL; + pcs->capacity = 0; + pcs->flags = 0; + + return rc; +} + /* ///////////////////////////////////////////////////////////////////////// * API @@ -477,7 +476,17 @@ cstring_create( CSTRING_ASSERT(NULL != pcs); - cch = (0 == len) ? 1u : len; + if (0 == len) + { + pcs->len = 0; + pcs->ptr = NULL; + pcs->capacity = 0; + pcs->flags = 0; + + return CSTRING_RC_SUCCESS; + } + + cch = len; cch = (cch + (CSTRING_ALLOC_GRANULARITY - 1)) & ~(CSTRING_ALLOC_GRANULARITY - 1); pcs->ptr = (cstring_char_t*)cstring_realloc_(NULL, cch + 1, 0, &rc); @@ -511,7 +520,17 @@ cstring_createLen( CSTRING_ASSERT(NULL != pcs); - cch = sizeof(cstring_char_t) * ((0 == len) ? 1u : len); + if (0 == len) + { + pcs->len = 0; + pcs->ptr = NULL; + pcs->capacity = 0; + pcs->flags = 0; + + return CSTRING_RC_SUCCESS; + } + + cch = len; cch = (cch + (CSTRING_ALLOC_GRANULARITY - 1)) & ~(CSTRING_ALLOC_GRANULARITY - 1); pcs->ptr = (cstring_char_t*)cstring_realloc_(NULL, cch + 1, 0, &rc); @@ -652,6 +671,16 @@ cstring_createLenFn( pcs->ptr = (cstring_char_t*)arena; pcs->capacity = capacity - 1; } + else if (0 == len && + 0 == capacity) + { + pcs->len = 0; + pcs->ptr = NULL; + pcs->capacity = 0; + pcs->flags = 0; + + return CSTRING_RC_SUCCESS; + } else { size_t cch; @@ -689,21 +718,9 @@ cstring_destroy( struct cstring_t* pcs ) { - CSTRING_RC rc = CSTRING_RC_SUCCESS; - CSTRING_ASSERT(NULL != pcs); - if (!(CSTRING_F_MEMORY_IS_BORROWED & pcs->flags)) - { - (void)cstring_realloc_(pcs->ptr, 0, pcs->flags, &rc); - } - - pcs->len = 0; - pcs->ptr = NULL; - pcs->capacity = 0; - pcs->flags = 0; - - return rc; + return cstring_release_to_empty_(pcs); } #ifndef CSTRING_OBSOLETE @@ -904,12 +921,30 @@ cstring_assignFn( { const size_t len = cstring_strlen_safe_(s); + if (0 == len) + { + if (NULL != pcs->ptr) + { + pcs->len = 0; + pcs->ptr[0] = '\0'; + + return CSTRING_RC_SUCCESS; + } + + pcs->len = 0; + pcs->ptr = NULL; + pcs->capacity = 0; + pcs->flags = 0; + + return CSTRING_RC_SUCCESS; + } + if (0 == pcs->capacity || pcs->capacity < len) { size_t cch; - cch = (0 == len) ? 1u : len; + cch = len; cch = (cch + (CSTRING_ALLOC_GRANULARITY - 1)) & ~(CSTRING_ALLOC_GRANULARITY - 1); if (cch < pcs->capacity * 2) @@ -1004,12 +1039,30 @@ cstring_assignLenFn( } else { + if (0 == len) + { + if (NULL != pcs->ptr) + { + pcs->len = 0; + pcs->ptr[0] = '\0'; + + return CSTRING_RC_SUCCESS; + } + + pcs->len = 0; + pcs->ptr = NULL; + pcs->capacity = 0; + pcs->flags = 0; + + return CSTRING_RC_SUCCESS; + } + if (0 == pcs->capacity || pcs->capacity < len) { size_t cch; - cch = (0 == len) ? 1u : len; + cch = len; cch = (cch + (CSTRING_ALLOC_GRANULARITY - 1)) & ~(CSTRING_ALLOC_GRANULARITY - 1); if (cch < pcs->capacity * 2) @@ -1116,7 +1169,23 @@ cstring_appendFn( size_t const len = cstring_strlen_safe_(s); size_t const newLen = pcs->len + len; - if (pcs->capacity < newLen) + if (0 == len) + { + return CSTRING_RC_SUCCESS; + } + + /* Capacity already covers the write: copy in place. */ + if (pcs->capacity >= newLen) + { + CSTRING_ASSERT(NULL != pcs->ptr); + + cstring_memcpy_safe_(pcs->ptr + pcs->len, s, len * sizeof(cstring_char_t)); + pcs->len = newLen; + pcs->ptr[pcs->len] = '\0'; + + return CSTRING_RC_SUCCESS; + } + { size_t cch; @@ -1222,13 +1291,29 @@ cstring_appendLenFn( } else { - size_t newLen = pcs->len + len; + size_t const newLen = pcs->len + len; + + if (0 == len) + { + return CSTRING_RC_SUCCESS; + } + + /* Capacity already covers the write: copy in place. */ + if (pcs->capacity >= newLen) + { + CSTRING_ASSERT(NULL != pcs->ptr); + + cstring_memcpy_safe_(pcs->ptr + pcs->len, s, len * sizeof(cstring_char_t)); + pcs->len = newLen; + pcs->ptr[pcs->len] = '\0'; + + return CSTRING_RC_SUCCESS; + } - if (pcs->capacity < newLen) { size_t cch; - cch = pcs->len + len; + cch = newLen; cch = (cch + (CSTRING_ALLOC_GRANULARITY - 1)) & ~(CSTRING_ALLOC_GRANULARITY - 1); if (cch < pcs->capacity * 2) @@ -1293,7 +1378,7 @@ cstring_appendLenFn( } } - cstring_strlcpy_safe_(pcs->ptr + pcs->len, s, len); + cstring_memcpy_safe_(pcs->ptr + pcs->len, s, len * sizeof(cstring_char_t)); pcs->len += len; pcs->ptr[pcs->len] = '\0'; @@ -1370,16 +1455,19 @@ cstring_readline( , size_t* numRead /* = NULL */ ) { - int previous = '\0'; - size_t numRead_; + int previous = '\0'; + size_t dummy; + CSTRING_RC rc0; CSTRING_ASSERT(NULL != pcs); if (NULL == numRead) { - numRead = &numRead_; + numRead = &dummy; } + *numRead = 0u; + if (NULL == stm || 0 != ferror(stm)) { @@ -1388,7 +1476,17 @@ cstring_readline( *numRead = 0u; - cstring_truncate(pcs, 0); + rc0 = cstring_truncate(pcs, 0); + + if (CSTRING_RC_SUCCESS != rc0) + { + /* NOTE: have to check for failure here, because a non-empty + * readonly string could be left if the stream is empty or its first + * character is LF. + */ + + return rc0; + } for (;;) { @@ -1396,10 +1494,19 @@ cstring_readline( if (EOF == ch) { + if ('\r' == previous) + { + cstring_truncate(pcs, pcs->len - 1u); + + return CSTRING_RC_SUCCESS; + } + return CSTRING_RC_EOF; } else { + ++*numRead; + if ('\n' == ch) { if ('\r' == previous) @@ -1411,19 +1518,29 @@ cstring_readline( } else { - cstring_char_t c1 = (char)ch; - CSTRING_RC rc = cstring_appendLen(pcs, &c1, 1u); - - if (CSTRING_RC_SUCCESS != rc) + if ('\r' == previous) { - return rc; - } + ungetc(ch, stm); - ++*numRead; + --*numRead; - previous = ch; + cstring_truncate(pcs, pcs->len - 1u); + + return CSTRING_RC_SUCCESS; + } + else + { + cstring_char_t c1 = (char)ch; + CSTRING_RC rc = cstring_appendLen(pcs, &c1, 1u); + + if (CSTRING_RC_SUCCESS != rc) + { + return rc; + } + } } + previous = ch; } } } @@ -1438,13 +1555,13 @@ cstring_write_( ) { int r; - size_t numWritten_; + size_t dummy; CSTRING_ASSERT(NULL != pcs); if (NULL == numWritten) { - numWritten = &numWritten_; + numWritten = &dummy; } if (NULL == stm || @@ -1455,7 +1572,12 @@ cstring_write_( *numWritten = 0u; - r = fprintf(stm, fmt, (int)pcs->len, pcs->ptr); + r = fprintf( + stm + , fmt + , (int)pcs->len + , (NULL != pcs->ptr) ? pcs->ptr : "" + ); if (r < 0) { @@ -1657,13 +1779,13 @@ cstring_replaceAll( , size_t* numReplaced /* = NULL */ ) { - size_t numReplaced_; + size_t dummy; CSTRING_ASSERT(NULL != pcs); if (NULL == numReplaced) { - numReplaced = &numReplaced_; + numReplaced = &dummy; } *numReplaced = 0u; diff --git a/src/cstring.vector.c b/src/cstring.vector.c index 59152c8..278c60e 100644 --- a/src/cstring.vector.c +++ b/src/cstring.vector.c @@ -8,7 +8,7 @@ * * Home: http://synesis.com.au/software/ * - * Copyright (c) 2019-2025, Matthew Wilson and Synesis Information Systems + * Copyright (c) 2019-2026, Matthew Wilson and Synesis Information Systems * Copyright (c) 1994-2019, Matthew Wilson and Synesis Software * All rights reserved. * @@ -51,6 +51,7 @@ /* cstring header files */ #include +#include "internal.h" #ifndef CSTRING_INCL_CSTRING_INTERNAL_H_SAFESTR # include @@ -58,7 +59,6 @@ /* Standard C header files */ -#include #include #include #include @@ -78,6 +78,92 @@ #define CSTRING_VECTOR_ASSERT(expr) assert(expr) +/* ///////////////////////////////////////////////////////////////////////// + * helper functions + */ + +/** Zero-fills \c n slots to the empty \c cstring_t_DEFAULT form. + * + * Equivalent to calling \c cstring_init on each slot; that function always + * succeeds and only clears the four fields. + */ +static +void +cstring_vector_init_empty_slots_( + cstring_t* slots +, size_t n +) +{ + if (0 != n) + { + CSTRING_VECTOR_ASSERT(NULL != slots); + + memset(slots, 0, sizeof(cstring_t) * n); + } +} + +/** Destroys live slots only; already-empty slots are left alone. + * + * An empty slot (as from \c cstring_init / \c cstring_t_DEFAULT) has a NULL + * pointer, zero length, zero capacity, and zero flags. Skipping those avoids + * a per-element call on \c cstring_vector_create / destroy of empty vectors. + */ +static +CSTRING_RC +cstring_vector_destroy_slots_( + cstring_t* slots +, size_t n +) +{ + CSTRING_RC rc = CSTRING_RC_SUCCESS; + size_t i; + + if (0 == n) + { + return CSTRING_RC_SUCCESS; + } + + CSTRING_VECTOR_ASSERT(NULL != slots); + + for (i = 0; i != n; ++i) + { + cstring_t* const pcs = slots + i; + + if (0 != pcs->len || + NULL != pcs->ptr || + 0 != pcs->capacity || + 0 != pcs->flags) + { + CSTRING_RC const rc2 = cstring_destroy(pcs); + + if (CSTRING_RC_SUCCESS != rc2 && + CSTRING_RC_SUCCESS == rc) + { + rc = rc2; + } + } + } + + return rc; +} + + +/* ///////////////////////////////////////////////////////////////////////// + * compiler warnings + */ + +#if defined(_MSC_VER) +# if _MSC_VER >= 1200 +# pragma warning(push) +# endif /* _MSC_VER >= 1200 */ +# if _MSC_VER >= 1310 +# if !defined(__COMO__) +# pragma warning(disable : 4055) +# endif /* !__COMO__ */ +# endif /* _MSC_VER >= 1310 */ +#endif /* compiler */ + + /* ///////////////////////////////////////////////////////////////////////// * compiler compatibility */ @@ -114,7 +200,7 @@ cstring_vector_init( , size_t minCapacity ) { - CSTRING_VECTOR_ASSERT(NULL != pcsv); + CSTRING_ASSERT(NULL != pcsv); if (cstring_vector_DEFAULT_CAPACITY == minCapacity) { @@ -155,21 +241,11 @@ cstring_vector_destroy( cstring_vector_t* pcsv ) { - CSTRING_RC rc = CSTRING_RC_SUCCESS; - size_t i; + CSTRING_RC rc; - CSTRING_VECTOR_ASSERT(NULL != pcsv); + CSTRING_ASSERT(NULL != pcsv); - for (i = 0; i != pcsv->len; ++i) - { - CSTRING_RC rc2 = cstring_destroy(pcsv->ptr + i); - - if (CSTRING_RC_SUCCESS != rc2 && - CSTRING_RC_SUCCESS == rc) - { - rc = rc2; - } - } + rc = cstring_vector_destroy_slots_(pcsv->ptr, pcsv->len); free(pcsv->ptr); @@ -187,22 +263,12 @@ cstring_vector_truncate( , size_t len ) { - CSTRING_RC rc = CSTRING_RC_SUCCESS; - size_t i; - - CSTRING_VECTOR_ASSERT(NULL != pcsv); - CSTRING_VECTOR_ASSERT(len <= pcsv->len); + CSTRING_RC rc; - for (i = len; i != pcsv->len; ++i) - { - CSTRING_RC rc2 = cstring_destroy(pcsv->ptr + i); + CSTRING_ASSERT(NULL != pcsv); + CSTRING_ASSERT(len <= pcsv->len); - if (CSTRING_RC_SUCCESS != rc2 && - CSTRING_RC_SUCCESS == rc) - { - rc = rc2; - } - } + rc = cstring_vector_destroy_slots_(pcsv->ptr + len, pcsv->len - len); pcsv->len = len; @@ -215,7 +281,7 @@ cstring_vector_create( , size_t initialSize ) { - CSTRING_VECTOR_ASSERT(NULL != pcsv); + CSTRING_ASSERT(NULL != pcsv); if (cstring_vector_DEFAULT_CAPACITY == initialSize) { @@ -227,36 +293,10 @@ cstring_vector_create( if (CSTRING_RC_SUCCESS == rc) { - size_t i; + CSTRING_ASSERT(pcsv->capacity >= initialSize); - CSTRING_VECTOR_ASSERT(pcsv->capacity >= initialSize); + cstring_vector_init_empty_slots_(pcsv->ptr, initialSize); - for (i = 0; i != initialSize; ++i) - { - CSTRING_RC rc2 = cstring_init(pcsv->ptr + i); - - if (CSTRING_RC_SUCCESS != rc2) - { - while (0 != i) - { - cstring_destroy(pcsv->ptr + (i - 1)); - - --i; - } - - rc = rc2; - - pcsv->len = 0; - - cstring_vector_destroy(pcsv); - - break; - } - } - } - - if (CSTRING_RC_SUCCESS == rc) - { pcsv->len = initialSize; } @@ -272,8 +312,8 @@ cstring_vector_insertAt( , size_t numStrings ) { - CSTRING_VECTOR_ASSERT(NULL != pcsv); - CSTRING_VECTOR_ASSERT(NULL != strings || 0 == numStrings); + CSTRING_ASSERT(NULL != pcsv); + CSTRING_ASSERT(NULL != strings || 0 == numStrings); if (0 == numStrings) { @@ -287,7 +327,8 @@ cstring_vector_insertAt( if (newSize > pcsv->capacity) { - cstring_t* newPtr = realloc(pcsv->ptr, sizeof(cstring_t) * newSize); + size_t const newCapacity = (newSize * 3) / 2; + cstring_t* newPtr = realloc(pcsv->ptr, sizeof(cstring_t) * newCapacity); if (NULL == newPtr) { @@ -296,7 +337,7 @@ cstring_vector_insertAt( else { pcsv->ptr = newPtr; - pcsv->capacity = newSize; + pcsv->capacity = newCapacity; } } @@ -327,11 +368,9 @@ cstring_vector_insertAt( if (CSTRING_RC_SUCCESS != rc2) { - while (0 != i) + for (; 0 != i; --i) { cstring_destroy(pcsv->ptr + position + (i - 1)); - - --i; } rc = rc2; @@ -373,7 +412,7 @@ cstring_vector_readLines( size_t numLinesRead_; size_t initialLen; - CSTRING_VECTOR_ASSERT(NULL != pcsv); + CSTRING_ASSERT(NULL != pcsv); initialLen = pcsv->len; diff --git a/src/dllmain.c b/src/dllmain.c index 382b994..432a924 100644 --- a/src/dllmain.c +++ b/src/dllmain.c @@ -4,7 +4,7 @@ * Purpose: Definition of DllMain for the Win32 platform * * Created: 19th August 2005 - * Updated: 2nd August 2026 + * Updated: 29th September 2026 * * Home: http://synesis.com.au/software/ * @@ -49,6 +49,8 @@ */ #include +#include "internal.h" + #if !defined(__MWERKS__) # include #else /* ? compiler */ diff --git a/src/internal.h b/src/internal.h index c4f1c69..74cb2a4 100644 --- a/src/internal.h +++ b/src/internal.h @@ -48,29 +48,50 @@ #define CSTRING_INCL_SRC_H_INTERNAL +/* ///////////////////////////////////////////////////////////////////////// + * checks + */ + +#ifndef CSTRING_INCL_CSTRING_H_CSTRING +# error cstring/cstring.h must be included before this file +#endif /* !CSTRING_INCL_CSTRING_H_CSTRING */ + + /* ///////////////////////////////////////////////////////////////////////// * includes */ -#include +#include /* ///////////////////////////////////////////////////////////////////////// * constants & definitions */ -#if defined(WIN32) || \ - defined(WIN64) -# define CSTRING_USE_WINAPI_ -#endif /* WIN32 || WIN64 */ +/* ///////////////////////////////////////////////////////////////////////// + * macros + */ + + +/* ///////////////////////////////////////////////////////// + * constract enforcements + */ + +#define CSTRING_ASSERT(expr) assert(expr) /* ///////////////////////////////////////////////////////////////////////// + * internal functions + */ + + + +/* ///////////////////////////////////////////////////////// * Windows arena allocators */ -#if defined(CSTRING_USE_WINAPI_) +#ifdef _WIN32 CSTRING_EXTERN_C void* @@ -92,7 +113,7 @@ win32_comtask_realloc( void* pv , size_t cb ); -#endif /* CSTRING_USE_WINAPI_ */ +#endif /* _WIN32 */ /* ////////////////////////////////////////////////////////////////////// */ diff --git a/src/win.c b/src/win.c index 2490256..1d07a52 100644 --- a/src/win.c +++ b/src/win.c @@ -48,6 +48,7 @@ * includes */ +#include #include "internal.h" diff --git a/test/component/CMakeLists.txt b/test/component/CMakeLists.txt index fd37bfb..f31e871 100644 --- a/test/component/CMakeLists.txt +++ b/test/component/CMakeLists.txt @@ -1,6 +1,14 @@ +# Shared component fixtures (component_fixture.hpp) + + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + if(NOT NO_CSTRING_CPP_API) - add_subdirectory(test.component.cstring_readline) - add_subdirectory(test.component.cstring_vector_readLines) - add_subdirectory(test.component.cstring_writeline) + add_subdirectory(cstring_readline) + add_subdirectory(cstring_vector_readLines) + add_subdirectory(cstring_writeline) endif() + + +# ############################## end of file ############################# # diff --git a/test/component/component_fixture.hpp b/test/component/component_fixture.hpp new file mode 100644 index 0000000..4f144bc --- /dev/null +++ b/test/component/component_fixture.hpp @@ -0,0 +1,152 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: test/component/component_fixture.hpp + * + * Purpose: Shared file fixtures and setup/teardown for cstring component + * tests. + * + * Created: 3rd October 2026 + * Updated: 3rd October 2026 + * + * ////////////////////////////////////////////////////////////////////// */ + + +#ifndef CSTRING_TEST_COMPONENT_COMPONENT_FIXTURE_HPP_INCLUDED +#define CSTRING_TEST_COMPONENT_COMPONENT_FIXTURE_HPP_INCLUDED + + +/* ///////////////////////////////////////////////////////////////////////// + * includes + */ + +#include + +#include +#include +#include + +#include + +#include +#include + + +/* ///////////////////////////////////////////////////////////////////////// + * namespace + */ + +namespace cstring_component +{ + + +/* ///////////////////////////////////////////////////////////////////////// + * file fixtures + */ + +inline +FILE* +fopen_or_throw( + char const* fileName +, char const* mode +) +{ +#ifdef CSTRING_USING_SAFE_STR_FUNCTIONS + FILE* f; + + if (0 != ::fopen_s(&f, fileName, mode)) +#else /* ? CSTRING_USING_SAFE_STR_FUNCTIONS */ + FILE* f = ::fopen(fileName, mode); + + if (NULL == f) +#endif /* CSTRING_USING_SAFE_STR_FUNCTIONS */ + { + throw platformstl::platform_exception((std::string("Could not open file '") + fileName + "'").c_str(), platformstl::system_traits::get_last_error()); + } + + return f; +} + +inline +void +write_bytes( + char const* fileName +, void const* bytes +, size_t cb +) +{ + FILE* f = fopen_or_throw(fileName, "wb"); + + stlsoft::scoped_handle scoper(f, ::fclose); + + if (cb != ::fwrite(bytes, 1, cb, f)) + { + throw platformstl::platform_exception("Could not write test file", platformstl::system_traits::get_last_error()); + } +} + +inline +void +write_string_bytes( + char const* fileName +, std::string const& bytes +) +{ + write_bytes(fileName, bytes.data(), bytes.size()); +} + +inline +std::string +make_filled( + size_t n +, char ch +) +{ + return std::string(n, ch); +} + + +/* ///////////////////////////////////////////////////////////////////////// + * xTests runner + */ + +inline +int +setup(void*) +{ + return 0; +} + +inline +int +teardown(void* arg) +{ + char const* path = static_cast(arg); + + if (NULL != path && + '\0' != path[0]) + { + ::remove(path); + } + + return 0; +} + + +/* ///////////////////////////////////////////////////////////////////////// + * namespace + */ + +} // namespace cstring_component + + +/* ///////////////////////////////////////////////////////////////////////// + * inclusion control + */ + +#ifdef STLSOFT_PPF_pragma_once_SUPPORT +# pragma once +#endif /* STLSOFT_PPF_pragma_once_SUPPORT */ + +#endif /* !CSTRING_TEST_COMPONENT_COMPONENT_FIXTURE_HPP_INCLUDED */ + +/* ///////////////////////////// end of file //////////////////////////// */ + diff --git a/test/component/cstring_readline/CMakeLists.txt b/test/component/cstring_readline/CMakeLists.txt new file mode 100644 index 0000000..9d95889 --- /dev/null +++ b/test/component/cstring_readline/CMakeLists.txt @@ -0,0 +1,2 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +define_automated_test_program(test.component.cstring_readline entry.cpp) diff --git a/test/component/cstring_readline/entry.cpp b/test/component/cstring_readline/entry.cpp new file mode 100644 index 0000000..fa4ef14 --- /dev/null +++ b/test/component/cstring_readline/entry.cpp @@ -0,0 +1,778 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: test/component/cstring_readline/entry.cpp + * + * Purpose: Component-tests `cstring_readline()`. + * + * Created: 23rd May 2009 + * Updated: 4th October 2026 + * + * ////////////////////////////////////////////////////////////////////// */ + + +/* ///////////////////////////////////////////////////////////////////////// + * includes + */ + +/* ///////////////////////////////////// + * test component header file include(s) + */ + +#include + +/* ///////////////////////////////////// + * general includes + */ + +#include "component_fixture.hpp" + +/* xTests header files */ +#include +#include + +/* STLSoft header files */ +#include + +/* Standard C++ header files */ +#include +#include + +/* Standard C header files */ +#include +#include +#include + + +/* ///////////////////////////////////////////////////////////////////////// + * forward declarations + */ + +namespace +{ + + static void TEST_cstring_readline_CALLABILITY(); + static void TEST_cstring_readline_INVALID_STREAM(); + static void TEST_cstring_readline_EMPTY_FILE(); + static void TEST_cstring_readline_SINGLE_LINE_NO_EOL(); + static void TEST_cstring_readline_SINGLE_LINE_WITH_LF(); + static void TEST_cstring_readline_SINGLE_LINE_WITH_CRLF(); + static void TEST_cstring_readline_SINGLE_LINE_WITH_CR(); + static void TEST_cstring_readline_SINGLE_LONG_LINE_WITH_CRLF(); + static void TEST_cstring_readline_SHORT_MULTILINE(); + static void TEST_cstring_readline_CRLF_AND_MIXED_EOL(); + static void TEST_cstring_readline_FINAL_EOL_VS_NO_EOL(); + static void TEST_cstring_readline_CONSECUTIVE_EMPTY_LINES_BY_LF(); + static void TEST_cstring_readline_CONSECUTIVE_EMPTY_LINES_BY_CRLF(); + static void TEST_cstring_readline_CONSECUTIVE_EMPTY_LINES_BY_CR(); + static void TEST_cstring_readline_LONG_LINES(); + static void TEST_cstring_readline_MANY_SHORT_LINES(); + static void TEST_cstring_readline_REUSE_AFTER_LONG_LINE(); + static void TEST_cstring_readline_READONLY_RETAINS_PAYLOAD(); +} // anonymous namespace + + +/* ///////////////////////////////////////////////////////////////////////// + * constants & definitions + */ + +const char TEST_FILE_NAME[] = "test.component.cstring_readline.txt"; + + +/* ///////////////////////////////////////////////////////////////////////// + * compiler compatibility + */ + +#if defined(STLSOFT_COMPILER_IS_MSVC) +# if _MSC_VER >= 1200 +# pragma warning(push) +# endif /* compiler */ +# pragma warning(disable : 4702) +#endif /* compiler */ + + +/* ///////////////////////////////////////////////////////////////////////// + * main + */ + +int main(int argc, char* argv[]) +{ + int retCode = EXIT_SUCCESS; + int verbosity = 2; + + XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); + + if (XTESTS_START_RUNNER_WITH_SETUP_FNS("test.component.cstring_readline", verbosity, cstring_component::setup, cstring_component::teardown, (void*)TEST_FILE_NAME)) + { + XTESTS_RUN_CASE(TEST_cstring_readline_CALLABILITY); + XTESTS_RUN_CASE(TEST_cstring_readline_INVALID_STREAM); + XTESTS_RUN_CASE(TEST_cstring_readline_EMPTY_FILE); + XTESTS_RUN_CASE(TEST_cstring_readline_SINGLE_LINE_NO_EOL); + XTESTS_RUN_CASE(TEST_cstring_readline_SINGLE_LINE_WITH_LF); + XTESTS_RUN_CASE(TEST_cstring_readline_SINGLE_LINE_WITH_CRLF); + XTESTS_RUN_CASE(TEST_cstring_readline_SINGLE_LINE_WITH_CR); + XTESTS_RUN_CASE(TEST_cstring_readline_SINGLE_LONG_LINE_WITH_CRLF); + XTESTS_RUN_CASE(TEST_cstring_readline_SHORT_MULTILINE); + XTESTS_RUN_CASE(TEST_cstring_readline_CRLF_AND_MIXED_EOL); + XTESTS_RUN_CASE(TEST_cstring_readline_FINAL_EOL_VS_NO_EOL); + XTESTS_RUN_CASE(TEST_cstring_readline_CONSECUTIVE_EMPTY_LINES_BY_LF); + XTESTS_RUN_CASE(TEST_cstring_readline_CONSECUTIVE_EMPTY_LINES_BY_CRLF); + XTESTS_RUN_CASE(TEST_cstring_readline_CONSECUTIVE_EMPTY_LINES_BY_CR); + XTESTS_RUN_CASE(TEST_cstring_readline_LONG_LINES); + XTESTS_RUN_CASE(TEST_cstring_readline_MANY_SHORT_LINES); + XTESTS_RUN_CASE(TEST_cstring_readline_REUSE_AFTER_LONG_LINE); + XTESTS_RUN_CASE(TEST_cstring_readline_READONLY_RETAINS_PAYLOAD); + + XTESTS_PRINT_RESULTS(); + + XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode); + } + + return retCode; +} + + +/* ///////////////////////////////////////////////////////////////////////// + * compiler compatibility + */ + +#if defined(STLSOFT_COMPILER_IS_MSVC) +# if _MSC_VER >= 1200 +# pragma warning(pop) +# endif /* compiler */ +#endif /* compiler */ + + +/* ///////////////////////////////////////////////////////////////////////// + * test function implementations + */ + +namespace +{ + + using namespace cstring_component; + using ::xtests::cpp::util::temp_file; + + +static void TEST_cstring_readline_CALLABILITY() +{ + false && cstring_readline(stdout, NULL, NULL); + + TEST_PASSED(); +} + +static void TEST_cstring_readline_INVALID_STREAM() +{ + cstring_t cs = cstring_t_DEFAULT; + + TEST_ENUM_EQ(CSTRING_RC_INVALIDSTREAM, cstring_readline(NULL, &cs, NULL)); + + size_t n; + + TEST_ENUM_EQ(CSTRING_RC_INVALIDSTREAM, cstring_readline(NULL, &cs, &n)); +} + +static void TEST_cstring_readline_EMPTY_FILE() +{ + /* empty file → first read is EOF with empty payload */ + + temp_file ft(temp_file::EmptyOnOpen | temp_file::DeleteOnClose | temp_file::CloseOnOpen); + FILE* const f = fopen_or_throw(ft.c_str(), "rb"); + stlsoft::scoped_handle scoper(f, ::fclose); + + cstring_t cs = cstring_t_DEFAULT; + size_t numRead = 123u; + CSTRING_RC rc = cstring_readline(f, &cs, &numRead); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); + TEST_INT_EQ(0u, numRead); + TEST_INT_EQ(0u, cs.len); + TEST_MS_EQ("", cs); + + cstring_destroy(&cs); +} + +static void TEST_cstring_readline_SINGLE_LINE_NO_EOL() +{ + static char const input[] = "abc"; + temp_file ft(temp_file::EmptyOnOpen | temp_file::DeleteOnClose | temp_file::CloseOnOpen, input, STLSOFT_NUM_ELEMENTS(input) - 1); + FILE* const f = fopen_or_throw(ft.c_str(), "rb"); + stlsoft::scoped_handle scoper(f, ::fclose); + + cstring_t cs = cstring_t_DEFAULT; + size_t numRead = 123u; + CSTRING_RC rc = cstring_readline(f, &cs, &numRead); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); + TEST_INT_EQ(3u, numRead); + TEST_INT_EQ(3u, cs.len); + TEST_MS_EQ("abc", cs); + + cstring_destroy(&cs); +} + +static void TEST_cstring_readline_SINGLE_LINE_WITH_LF() +{ + static char const input[] = "abc\n"; + temp_file ft(temp_file::EmptyOnOpen | temp_file::DeleteOnClose | temp_file::CloseOnOpen, input, STLSOFT_NUM_ELEMENTS(input) - 1); + FILE* const f = fopen_or_throw(ft.c_str(), "rb"); + stlsoft::scoped_handle scoper(f, ::fclose); + + cstring_t cs = cstring_t_DEFAULT; + size_t numRead = 123u; + CSTRING_RC rc = cstring_readline(f, &cs, &numRead); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(4u, numRead); + TEST_INT_EQ(3u, cs.len); + TEST_MS_EQ("abc", cs); + + cstring_destroy(&cs); +} + +static void TEST_cstring_readline_SINGLE_LINE_WITH_CRLF() +{ + static char const input[] = "abc\r\n"; + temp_file ft(temp_file::EmptyOnOpen | temp_file::DeleteOnClose | temp_file::CloseOnOpen, input, STLSOFT_NUM_ELEMENTS(input) - 1); + FILE* const f = fopen_or_throw(ft.c_str(), "rb"); + stlsoft::scoped_handle scoper(f, ::fclose); + + cstring_t cs = cstring_t_DEFAULT; + size_t numRead = 123u; + CSTRING_RC rc = cstring_readline(f, &cs, &numRead); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(5u, numRead); + TEST_INT_EQ(3u, cs.len); + TEST_MS_EQ("abc", cs); + + cstring_destroy(&cs); +} + +static void TEST_cstring_readline_SINGLE_LINE_WITH_CR() +{ + static char const input[] = "abc\r"; + temp_file ft(temp_file::EmptyOnOpen | temp_file::DeleteOnClose | temp_file::CloseOnOpen, input, STLSOFT_NUM_ELEMENTS(input) - 1); + FILE* const f = fopen_or_throw(ft.c_str(), "rb"); + stlsoft::scoped_handle scoper(f, ::fclose); + + cstring_t cs = cstring_t_DEFAULT; + size_t numRead = 123u; + CSTRING_RC rc = cstring_readline(f, &cs, &numRead); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(4u, numRead); + TEST_INT_EQ(3u, cs.len); + TEST_MS_EQ("abc", cs); + + cstring_destroy(&cs); +} + +static void TEST_cstring_readline_SINGLE_LONG_LINE_WITH_CRLF() +{ + static char const input[] = "01234567890123456789012345678901234567890123456789012345678901234567890123456789\r\n"; + temp_file ft(temp_file::EmptyOnOpen | temp_file::DeleteOnClose | temp_file::CloseOnOpen, input, STLSOFT_NUM_ELEMENTS(input) - 1); + FILE* const f = fopen_or_throw(ft.c_str(), "rb"); + stlsoft::scoped_handle scoper(f, ::fclose); + + cstring_t cs = cstring_t_DEFAULT; + size_t numRead = 123u; + CSTRING_RC rc = cstring_readline(f, &cs, &numRead); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(82u, numRead); + TEST_INT_EQ(80u, cs.len); + TEST_MS_EQ("01234567890123456789012345678901234567890123456789012345678901234567890123456789", cs); + + cstring_destroy(&cs); +} + +static void TEST_cstring_readline_SHORT_MULTILINE() +{ + { + FILE* f = fopen_or_throw(TEST_FILE_NAME, "w"); + + stlsoft::scoped_handle scoper(f, ::fclose); + + fprintf(f, "\n"); + fprintf(f, "abc\n"); + fprintf(f, "abcdef\n"); + fprintf(f, "abcdefghijkl\n"); + fprintf(f, "abcdefghijklmnopqrstuvwxyz\n"); + fprintf(f, "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"); + fprintf(f, "xyz"); + } + + { + FILE* f = fopen_or_throw(TEST_FILE_NAME, "r"); + + stlsoft::scoped_handle scoper(f, ::fclose); + + CSTRING_RC rc; + cstring_t strings[7] = { + cstring_t_DEFAULT, + cstring_t_DEFAULT, + cstring_t_DEFAULT, + cstring_t_DEFAULT, + cstring_t_DEFAULT, + cstring_t_DEFAULT, + cstring_t_DEFAULT, + }; + size_t readCounts[7]; + + rc = cstring_readline(f, &strings[0], &readCounts[0]); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(1u, readCounts[0]); + TEST_MS_EQ("", strings[0]); + + rc = cstring_readline(f, &strings[1], &readCounts[1]); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(4u, readCounts[1]); + TEST_MS_EQ("abc", strings[1]); + + rc = cstring_readline(f, &strings[2], &readCounts[2]); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(7u, readCounts[2]); + TEST_MS_EQ("abcdef", strings[2]); + + rc = cstring_readline(f, &strings[3], &readCounts[3]); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(13u, readCounts[3]); + TEST_MS_EQ("abcdefghijkl", strings[3]); + + rc = cstring_readline(f, &strings[4], &readCounts[4]); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(27u, readCounts[4]); + TEST_MS_EQ("abcdefghijklmnopqrstuvwxyz", strings[4]); + + rc = cstring_readline(f, &strings[5], &readCounts[5]); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(63u, readCounts[5]); + TEST_MS_EQ("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", strings[5]); + + rc = cstring_readline(f, &strings[6], &readCounts[6]); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); + TEST_INT_EQ(3u, readCounts[6]); + TEST_MS_EQ("xyz", strings[6]); + + { for (size_t i = 0; i != 7; ++i) + { + cstring_destroy(&strings[i]); + }} + } +} + +static void TEST_cstring_readline_CRLF_AND_MIXED_EOL() +{ + /* CRLF, mixed EOL, and lone CR mid-line (binary I/O so CR is visible) */ + { + std::string bytes; + + bytes.append("one\r\n"); + bytes.append("two\n"); + bytes.append("three\r\n"); + bytes.append("has\rembed\r\n"); + bytes.append("end"); + + write_string_bytes(TEST_FILE_NAME, bytes); + } + + FILE* f = fopen_or_throw(TEST_FILE_NAME, "rb"); + + stlsoft::scoped_handle scoper(f, ::fclose); + + cstring_t cs = cstring_t_DEFAULT; + size_t numRead; + CSTRING_RC rc; + + rc = cstring_readline(f, &cs, &numRead); + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + /* CRLF: CR is appended then stripped; numRead counts the CR and the LF */ + TEST_INT_EQ(5u, numRead); + TEST_INT_EQ(3u, cs.len); + TEST_MS_EQ("one", cs); + + rc = cstring_readline(f, &cs, &numRead); + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(4u, numRead); + TEST_INT_EQ(3u, cs.len); + TEST_MS_EQ("two", cs); + + rc = cstring_readline(f, &cs, &numRead); + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(7u, numRead); + TEST_INT_EQ(5u, cs.len); + TEST_MS_EQ("three", cs); + + rc = cstring_readline(f, &cs, &numRead); + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(4u, numRead); + TEST_INT_EQ(3u, cs.len); + TEST_MS_EQ("has", cs); + + rc = cstring_readline(f, &cs, &numRead); + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(7u, numRead); + TEST_INT_EQ(5u, cs.len); + TEST_MS_EQ("embed", cs); + + rc = cstring_readline(f, &cs, &numRead); + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); + TEST_INT_EQ(3u, numRead); + TEST_INT_EQ(3u, cs.len); + TEST_MS_EQ("end", cs); + + cstring_destroy(&cs); +} + +static void TEST_cstring_readline_FINAL_EOL_VS_NO_EOL() +{ + /* final line with EOL vs without EOL */ + + { + static char const input[] = "alpha\nbeta\n"; + temp_file ft(temp_file::EmptyOnOpen | temp_file::DeleteOnClose | temp_file::CloseOnOpen, input, STLSOFT_NUM_ELEMENTS(input) - 1); + FILE* const f = fopen_or_throw(ft.c_str(), "rb"); + stlsoft::scoped_handle scoper(f, ::fclose); + + cstring_t cs = cstring_t_DEFAULT; + size_t numRead; + CSTRING_RC rc; + + rc = cstring_readline(f, &cs, &numRead); + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_MS_EQ("alpha", cs); + + rc = cstring_readline(f, &cs, &numRead); + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_MS_EQ("beta", cs); + + rc = cstring_readline(f, &cs, &numRead); + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); + TEST_INT_EQ(0u, numRead); + TEST_INT_EQ(0u, cs.len); + + cstring_destroy(&cs); + } + + { + static char const input[] = "alpha\nbeta"; + temp_file ft(temp_file::EmptyOnOpen | temp_file::DeleteOnClose | temp_file::CloseOnOpen, input, STLSOFT_NUM_ELEMENTS(input) - 1); + FILE* const f = fopen_or_throw(ft.c_str(), "rb"); + stlsoft::scoped_handle scoper(f, ::fclose); + + cstring_t cs = cstring_t_DEFAULT; + size_t numRead; + CSTRING_RC rc; + + rc = cstring_readline(f, &cs, &numRead); + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_MS_EQ("alpha", cs); + + rc = cstring_readline(f, &cs, &numRead); + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); + TEST_INT_EQ(4u, numRead); + TEST_MS_EQ("beta", cs); + + cstring_destroy(&cs); + } +} + +static void TEST_cstring_readline_CONSECUTIVE_EMPTY_LINES_BY_LF() +{ + /* consecutive empty lines */ + + static char const input[] = "\n\n\n"; + temp_file ft(temp_file::EmptyOnOpen | temp_file::DeleteOnClose | temp_file::CloseOnOpen, input, STLSOFT_NUM_ELEMENTS(input) - 1); + FILE* const f = fopen_or_throw(ft.c_str(), "rb"); + stlsoft::scoped_handle scoper(f, ::fclose); + + cstring_t cs = cstring_t_DEFAULT; + size_t numRead; + CSTRING_RC rc; + + { for (int i = 0; i != 3; ++i) + { + rc = cstring_readline(f, &cs, &numRead); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(1u, numRead); + TEST_INT_EQ(0u, cs.len); + TEST_MS_EQ("", cs); + }} + + rc = cstring_readline(f, &cs, &numRead); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); + TEST_INT_EQ(0u, numRead); + + cstring_destroy(&cs); +} + +static void TEST_cstring_readline_CONSECUTIVE_EMPTY_LINES_BY_CRLF() +{ + /* consecutive empty lines */ + + static char const input[] = "\r\n\r\n\r\n"; + temp_file ft(temp_file::EmptyOnOpen | temp_file::DeleteOnClose | temp_file::CloseOnOpen, input, STLSOFT_NUM_ELEMENTS(input) - 1); + FILE* const f = fopen_or_throw(ft.c_str(), "rb"); + stlsoft::scoped_handle scoper(f, ::fclose); + + cstring_t cs = cstring_t_DEFAULT; + size_t numRead; + CSTRING_RC rc; + + { for (int i = 0; i != 3; ++i) + { + rc = cstring_readline(f, &cs, &numRead); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(2u, numRead); + TEST_INT_EQ(0u, cs.len); + TEST_MS_EQ("", cs); + }} + + rc = cstring_readline(f, &cs, &numRead); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); + TEST_INT_EQ(0u, numRead); + + cstring_destroy(&cs); +} + +static void TEST_cstring_readline_CONSECUTIVE_EMPTY_LINES_BY_CR() +{ + /* consecutive empty lines */ + + static char const input[] = "\r\r\r"; + temp_file ft(temp_file::EmptyOnOpen | temp_file::DeleteOnClose | temp_file::CloseOnOpen, input, STLSOFT_NUM_ELEMENTS(input) - 1); + FILE* const f = fopen_or_throw(ft.c_str(), "rb"); + stlsoft::scoped_handle scoper(f, ::fclose); + + cstring_t cs = cstring_t_DEFAULT; + size_t numRead; + CSTRING_RC rc; + + { for (int i = 0; i != 3; ++i) + { + rc = cstring_readline(f, &cs, &numRead); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(1u, numRead); + TEST_INT_EQ(0u, cs.len); + TEST_MS_EQ("", cs); + }} + + rc = cstring_readline(f, &cs, &numRead); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); + TEST_INT_EQ(0u, numRead); + + cstring_destroy(&cs); +} + +static void TEST_cstring_readline_LONG_LINES() +{ + /* long lines at several lengths */ + static size_t const lengths[] = { + 1024u, + 4096u, + 16384u, + 65536u, + }; + + { for (size_t i = 0; i != sizeof(lengths) / sizeof(lengths[0]); ++i) + { + size_t const len = lengths[i]; + std::string line = make_filled(len, static_cast('A' + static_cast(i % 26))); + std::string bytes = line; + + bytes.push_back('\n'); + bytes.append(make_filled(len / 2u == 0 ? 1u : len / 2u, 'z')); + + write_string_bytes(TEST_FILE_NAME, bytes); + + FILE* f = fopen_or_throw(TEST_FILE_NAME, "rb"); + + stlsoft::scoped_handle scoper(f, ::fclose); + + cstring_t cs = cstring_t_DEFAULT; + size_t numRead; + CSTRING_RC rc; + + rc = cstring_readline(f, &cs, &numRead); + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(len + 1, numRead); + TEST_INT_EQ(len, cs.len); + REQUIRE(TEST_MS_EQ(line.c_str(), cs)); + + rc = cstring_readline(f, &cs, &numRead); + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); + TEST_INT_EQ(len / 2u == 0 ? 1u : len / 2u, numRead); + + cstring_destroy(&cs); + }} +} + +static void TEST_cstring_readline_MANY_SHORT_LINES() +{ + /* many short lines */ + size_t const num_lines = 1000u; + std::string bytes; + std::vector expected; + + expected.reserve(num_lines); + + { for (size_t i = 0; i != num_lines; ++i) + { + char buf[32]; + + snprintf(buf, sizeof(buf), "line-%04u", static_cast(i)); + + expected.push_back(buf); + bytes.append(buf); + bytes.push_back('\n'); + }} + + write_string_bytes(TEST_FILE_NAME, bytes); + + FILE* f = fopen_or_throw(TEST_FILE_NAME, "rb"); + + stlsoft::scoped_handle scoper(f, ::fclose); + + cstring_t cs = cstring_t_DEFAULT; + size_t numRead; + CSTRING_RC rc; + + { for (size_t i = 0; i != num_lines; ++i) + { + rc = cstring_readline(f, &cs, &numRead); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(expected[i].size() + 1, numRead); + TEST_MS_EQ(expected[i].c_str(), cs); + }} + + rc = cstring_readline(f, &cs, &numRead); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); + TEST_INT_EQ(0u, numRead); + + cstring_destroy(&cs); +} + +static void TEST_cstring_readline_REUSE_AFTER_LONG_LINE() +{ + /* reuse one cstring_t after a long line then a short line; numRead may be NULL */ + std::string const long_line = make_filled(8192u, 'L'); + std::string bytes = long_line; + + bytes.push_back('\n'); + bytes.append("short"); + + write_string_bytes(TEST_FILE_NAME, bytes); + + FILE* f = fopen_or_throw(TEST_FILE_NAME, "rb"); + + stlsoft::scoped_handle scoper(f, ::fclose); + + cstring_t cs = cstring_t_DEFAULT; + CSTRING_RC rc; + + rc = cstring_readline(f, &cs, NULL); + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(8192u, cs.len); + TEST_MS_EQ(long_line.c_str(), cs); + + rc = cstring_readline(f, &cs, NULL); + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); + TEST_INT_EQ(5u, cs.len); + TEST_MS_EQ("short", cs); + + cstring_destroy(&cs); +} + +static void TEST_cstring_readline_READONLY_RETAINS_PAYLOAD() +{ + /* non-empty readonly string: a failed clear must be reported, and the + * stream must not be consumed + */ + + { + write_string_bytes(TEST_FILE_NAME, std::string("\nnext\n")); + + FILE* f = fopen_or_throw(TEST_FILE_NAME, "rb"); + + stlsoft::scoped_handle scoper(f, ::fclose); + + cstring_t cs; + CSTRING_RC rc; + + rc = cstring_createEx(&cs, "stale", CSTRING_F_MEMORY_IS_READONLY, NULL, 0); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + + size_t n = 99u; + + rc = cstring_readline(f, &cs, &n); + + TEST_ENUM_EQ(CSTRING_RC_READONLY, rc); + TEST_INT_EQ(5u, cs.len); + TEST_MS_EQ("stale", cs); + + cstring_t fresh = cstring_t_DEFAULT; + + rc = cstring_readline(f, &fresh, &n); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(1u, n); + TEST_MS_EQ("", fresh); + + cstring_destroy(&fresh); + cstring_destroy(&cs); + } + + { + write_bytes(TEST_FILE_NAME, "", 0); + + FILE* f = fopen_or_throw(TEST_FILE_NAME, "rb"); + + stlsoft::scoped_handle scoper(f, ::fclose); + + cstring_t cs; + CSTRING_RC rc; + + rc = cstring_createEx(&cs, "stale", CSTRING_F_MEMORY_IS_READONLY, NULL, 0); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + + size_t n = 99u; + + rc = cstring_readline(f, &cs, &n); + + TEST_ENUM_EQ(CSTRING_RC_READONLY, rc); + TEST_INT_EQ(5u, cs.len); + TEST_MS_EQ("stale", cs); + + cstring_t fresh = cstring_t_DEFAULT; + + rc = cstring_readline(f, &fresh, &n); + + REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); + TEST_INT_EQ(0u, n); + TEST_INT_EQ(0u, fresh.len); + + cstring_destroy(&fresh); + cstring_destroy(&cs); + } +} +} // anonymous namespace + + +/* ///////////////////////////// end of file //////////////////////////// */ + diff --git a/test/component/test.component.cstring_readline/CMakeLists.txt b/test/component/cstring_vector_readLines/CMakeLists.txt similarity index 53% rename from test/component/test.component.cstring_readline/CMakeLists.txt rename to test/component/cstring_vector_readLines/CMakeLists.txt index af6840b..f22eb90 100644 --- a/test/component/test.component.cstring_readline/CMakeLists.txt +++ b/test/component/cstring_vector_readLines/CMakeLists.txt @@ -1,2 +1,2 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten -define_automated_test_program(test_component_cstring_cstring_readline entry.cpp) +define_automated_test_program(test.component.cstring_vector_readLines entry.cpp) diff --git a/test/component/test.component.cstring_vector_readLines/entry.cpp b/test/component/cstring_vector_readLines/entry.cpp similarity index 84% rename from test/component/test.component.cstring_vector_readLines/entry.cpp rename to test/component/cstring_vector_readLines/entry.cpp index 5e6810b..e45cad6 100644 --- a/test/component/test.component.cstring_vector_readLines/entry.cpp +++ b/test/component/cstring_vector_readLines/entry.cpp @@ -1,32 +1,34 @@ /* ///////////////////////////////////////////////////////////////////////// - * File: test.component.cstring_vector_readLines/entry.cpp + * File: test/component/cstring_vector_readLines/entry.cpp * * Purpose: Component-tests `cstring_vector_readLines()`. * * Created: 27th September 2026 - * Updated: 27th September 2026 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ /* ///////////////////////////////////////////////////////////////////////// + * includes + */ + +/* ///////////////////////////////////// * test component header file include(s) */ #include -#include -/* ///////////////////////////////////////////////////////////////////////// - * includes +/* ///////////////////////////////////// + * general includes */ +#include "component_fixture.hpp" + /* xTests header files */ -#include #include /* STLSoft header files */ -#include -#include #include /* Standard C++ header files */ @@ -46,19 +48,16 @@ namespace { - static void TEST_cstring_vector_readLines_CALLABILITY(void); - static void TEST_cstring_vector_readLines_EMPTY_FILE(void); - static void TEST_cstring_vector_readLines_SHORT_MULTILINE(void); - static void TEST_cstring_vector_readLines_CRLF_AND_MIXED_EOL(void); - static void TEST_cstring_vector_readLines_FINAL_EOL_AND_EMPTY_AT_EOF(void); - static void TEST_cstring_vector_readLines_CONSECUTIVE_EMPTY_LINES(void); - static void TEST_cstring_vector_readLines_APPEND_ONTO_EXISTING(void); - static void TEST_cstring_vector_readLines_LONG_LINES(void); - static void TEST_cstring_vector_readLines_MANY_SHORT_LINES(void); - static void TEST_cstring_vector_readLines_EMPTY_LINE_IN_MIDDLE(void); - - int setup(void*); - int teardown(void*); + static void TEST_cstring_vector_readLines_CALLABILITY(); + static void TEST_cstring_vector_readLines_EMPTY_FILE(); + static void TEST_cstring_vector_readLines_SHORT_MULTILINE(); + static void TEST_cstring_vector_readLines_CRLF_AND_MIXED_EOL(); + static void TEST_cstring_vector_readLines_FINAL_EOL_AND_EMPTY_AT_EOF(); + static void TEST_cstring_vector_readLines_CONSECUTIVE_EMPTY_LINES(); + static void TEST_cstring_vector_readLines_APPEND_ONTO_EXISTING(); + static void TEST_cstring_vector_readLines_LONG_LINES(); + static void TEST_cstring_vector_readLines_MANY_SHORT_LINES(); + static void TEST_cstring_vector_readLines_EMPTY_LINE_IN_MIDDLE(); } // anonymous namespace @@ -68,6 +67,7 @@ namespace const char TEST_FILE_NAME[] = "test.component.cstring_vector_readLines.txt"; + /* ///////////////////////////////////////////////////////////////////////// * compiler compatibility */ @@ -79,6 +79,7 @@ const char TEST_FILE_NAME[] = "test.component.cstring_vector_readLines.txt"; # pragma warning(disable : 4702) #endif /* compiler */ + /* ///////////////////////////////////////////////////////////////////////// * main */ @@ -90,7 +91,7 @@ int main(int argc, char* argv[]) XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); - if (XTESTS_START_RUNNER_WITH_SETUP_FNS("test.component.cstring_vector_readLines", verbosity, setup, teardown, (void*)TEST_FILE_NAME)) + if (XTESTS_START_RUNNER_WITH_SETUP_FNS("test.component.cstring_vector_readLines", verbosity, cstring_component::setup, cstring_component::teardown, (void*)TEST_FILE_NAME)) { XTESTS_RUN_CASE(TEST_cstring_vector_readLines_CALLABILITY); XTESTS_RUN_CASE(TEST_cstring_vector_readLines_EMPTY_FILE); @@ -121,68 +122,6 @@ int main(int argc, char* argv[]) # endif /* compiler */ #endif /* compiler */ -/* ///////////////////////////////////////////////////////////////////////// - * helper functions - */ - -namespace -{ - - static FILE* fopen_or_throw(char const* fileName, char const* mode) - { -#ifdef CSTRING_USING_SAFE_STR_FUNCTIONS - FILE* f; - - if (0 != ::fopen_s(&f, fileName, mode)) -#else /* ? CSTRING_USING_SAFE_STR_FUNCTIONS */ - FILE* f = ::fopen(fileName, mode); - - if (NULL == f) -#endif /* CSTRING_USING_SAFE_STR_FUNCTIONS */ - { - throw platformstl::platform_exception((std::string("Could not open file '") + fileName + "'").c_str(), platformstl::system_traits::get_last_error()); - } - - return f; - } - - static void write_bytes(char const* fileName, void const* bytes, size_t cb) - { - FILE* f = fopen_or_throw(fileName, "wb"); - - stlsoft::scoped_handle scoper(f, ::fclose); - - if (cb != ::fwrite(bytes, 1, cb, f)) - { - throw platformstl::platform_exception("Could not write test file", platformstl::system_traits::get_last_error()); - } - } - - static void write_string_bytes(char const* fileName, std::string const& bytes) - { - write_bytes(fileName, bytes.data(), bytes.size()); - } - - static std::string make_filled(size_t n, char ch) - { - return std::string(n, ch); - } - - int setup(void*) - { - return 0; - } - - int teardown(void* arg) - { - char const* filename = static_cast(arg); - - ::remove(filename); - - return 0; - } -} // anonymous namespace - /* ///////////////////////////////////////////////////////////////////////// * test function implementations @@ -191,6 +130,9 @@ namespace namespace { + using namespace cstring_component; + + static void TEST_cstring_vector_readLines_CALLABILITY() { false && cstring_vector_readLines(stdout, NULL, NULL); @@ -281,13 +223,14 @@ static void TEST_cstring_vector_readLines_CRLF_AND_MIXED_EOL() CSTRING_RC rc = cstring_vector_readLines(f, &csv, &numLinesRead); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); - TEST_INT_EQ(5u, numLinesRead); - REQUIRE(TEST_INT_EQ(5u, csv.len)); + TEST_INT_EQ(6u, numLinesRead); + REQUIRE(TEST_INT_EQ(6u, csv.len)); TEST_MS_EQ("one", csv.ptr[0]); TEST_MS_EQ("two", csv.ptr[1]); TEST_MS_EQ("three", csv.ptr[2]); - TEST_MS_EQ("has\rembed", csv.ptr[3]); - TEST_MS_EQ("end", csv.ptr[4]); + TEST_MS_EQ("has", csv.ptr[3]); + TEST_MS_EQ("embed", csv.ptr[4]); + TEST_MS_EQ("end", csv.ptr[5]); cstring_vector_destroy(&csv); } diff --git a/test/component/cstring_writeline/CMakeLists.txt b/test/component/cstring_writeline/CMakeLists.txt new file mode 100644 index 0000000..1174d61 --- /dev/null +++ b/test/component/cstring_writeline/CMakeLists.txt @@ -0,0 +1,5 @@ +list(APPEND X_MSVC_CUSTOM_WARNINGS_TO_BE_SUPPRESSED + 4996 +) + +define_automated_test_program(test.component.cstring_writeline entry.cpp) diff --git a/test/component/test.component.cstring_writeline/entry.cpp b/test/component/cstring_writeline/entry.cpp similarity index 87% rename from test/component/test.component.cstring_writeline/entry.cpp rename to test/component/cstring_writeline/entry.cpp index fa52b71..8ef8166 100644 --- a/test/component/test.component.cstring_writeline/entry.cpp +++ b/test/component/cstring_writeline/entry.cpp @@ -1,39 +1,42 @@ /* ///////////////////////////////////////////////////////////////////////// - * File: test.component.cstring_writeline/entry.cpp + * File: test/component/cstring_writeline/entry.cpp * * Purpose: Unit-tests of `cstring_write()` and `cstring_writeline()`. * * Created: 10th August 2020 - * Updated: 27th September 2026 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ /* ///////////////////////////////////////////////////////////////////////// + * includes + */ + +/* ///////////////////////////////////// * test component header file include(s) */ #include -#include -/* ///////////////////////////////////////////////////////////////////////// - * includes +/* ///////////////////////////////////// + * general includes */ +#include "component_fixture.hpp" + /* xTests header files */ -#include #include #include /* STLSoft header files */ -#include #include -#include #include /* Standard C header files */ #include + /* ///////////////////////////////////////////////////////////////////////// * forward declarations */ @@ -41,22 +44,20 @@ namespace { - static void TEST_cstring_writeline_CALLABILITY(void); - static void TEST_cstring_writeline_INVALID_STREAM(void); - static void TEST_cstring_writeline_MULTIPLE_LINES(void); - static void TEST_cstring_write_CONCATENATED(void); - - int setup(void*); - int teardown(void*); - + static void TEST_cstring_writeline_CALLABILITY(); + static void TEST_cstring_writeline_INVALID_STREAM(); + static void TEST_cstring_writeline_MULTIPLE_LINES(); + static void TEST_cstring_write_CONCATENATED(); } // anonymous namespace + /* ///////////////////////////////////////////////////////////////////////// * constants & definitions */ const char TEST_FILE_NAME[] = "test.component.cstring_writeline.txt"; + /* ///////////////////////////////////////////////////////////////////////// * compiler compatibility */ @@ -68,6 +69,7 @@ const char TEST_FILE_NAME[] = "test.component.cstring_writeline.txt"; # pragma warning(disable : 4702) #endif /* compiler */ + /* ///////////////////////////////////////////////////////////////////////// * main */ @@ -79,7 +81,7 @@ int main(int argc, char* argv[]) XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); - if (XTESTS_START_RUNNER_WITH_SETUP_FNS("test.component.cstring_writeline", verbosity, setup, teardown, (void*)TEST_FILE_NAME)) + if (XTESTS_START_RUNNER_WITH_SETUP_FNS("test.component.cstring_writeline", verbosity, cstring_component::setup, cstring_component::teardown, (void*)TEST_FILE_NAME)) { XTESTS_RUN_CASE(TEST_cstring_writeline_CALLABILITY); XTESTS_RUN_CASE(TEST_cstring_writeline_INVALID_STREAM); @@ -94,6 +96,7 @@ int main(int argc, char* argv[]) return retCode; } + /* ///////////////////////////////////////////////////////////////////////// * compiler compatibility */ @@ -104,29 +107,15 @@ int main(int argc, char* argv[]) # endif /* compiler */ #endif /* compiler */ + /* ///////////////////////////////////////////////////////////////////////// * test function implementations */ namespace { - using ::xtests::cpp::util::temp_file; - - - int setup(void*) - { - return 0; - } - - int teardown(void* arg) - { - char const* filename = static_cast(arg); - - ::remove(filename); - - return 0; - } + using ::xtests::cpp::util::temp_file; static void TEST_cstring_writeline_CALLABILITY() @@ -259,10 +248,8 @@ static void TEST_cstring_write_CONCATENATED() TEST_MS_EQ(expected, out_lines[0]); } } - - - } // anonymous namespace + /* ///////////////////////////// end of file //////////////////////////// */ diff --git a/test/component/test.component.cstring_readline/entry.cpp b/test/component/test.component.cstring_readline/entry.cpp deleted file mode 100644 index 7bb3c7c..0000000 --- a/test/component/test.component.cstring_readline/entry.cpp +++ /dev/null @@ -1,585 +0,0 @@ -/* ///////////////////////////////////////////////////////////////////////// - * File: test.component.cstring_readline/entry.cpp - * - * Purpose: Component-tests `cstring_readline()`. - * - * Created: 23rd May 2009 - * Updated: 27th September 2026 - * - * ////////////////////////////////////////////////////////////////////// */ - - -/* ///////////////////////////////////////////////////////////////////////// - * test component header file include(s) - */ - -#include -#include - -/* ///////////////////////////////////////////////////////////////////////// - * includes - */ - -/* xTests header files */ -#include -#include - -/* STLSoft header files */ -#include -#include -#include - -/* Standard C++ header files */ -#include -#include - -/* Standard C header files */ -#include -#include -#include - - -/* ///////////////////////////////////////////////////////////////////////// - * forward declarations - */ - -namespace -{ - - static void TEST_cstring_readline_CALLABILITY(void); - static void TEST_cstring_readline_INVALID_STREAM(void); - static void TEST_cstring_readline_SHORT_MULTILINE(void); - static void TEST_cstring_readline_EMPTY_FILE(void); - static void TEST_cstring_readline_CRLF_AND_MIXED_EOL(void); - static void TEST_cstring_readline_FINAL_EOL_VS_NO_EOL(void); - static void TEST_cstring_readline_CONSECUTIVE_EMPTY_LINES(void); - static void TEST_cstring_readline_LONG_LINES(void); - static void TEST_cstring_readline_MANY_SHORT_LINES(void); - static void TEST_cstring_readline_REUSE_AFTER_LONG_LINE(void); - - int setup(void*); - int teardown(void*); -} // anonymous namespace - - -/* ///////////////////////////////////////////////////////////////////////// - * constants & definitions - */ - -const char TEST_FILE_NAME[] = "test.component.cstring_readline.txt"; - - -/* ///////////////////////////////////////////////////////////////////////// - * compiler compatibility - */ - -#if defined(STLSOFT_COMPILER_IS_MSVC) -# if _MSC_VER >= 1200 -# pragma warning(push) -# endif /* compiler */ -# pragma warning(disable : 4702) -#endif /* compiler */ - - -/* ///////////////////////////////////////////////////////////////////////// - * main - */ - -int main(int argc, char* argv[]) -{ - int retCode = EXIT_SUCCESS; - int verbosity = 2; - - XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); - - if (XTESTS_START_RUNNER_WITH_SETUP_FNS("test.component.cstring_readline", verbosity, setup, teardown, (void*)TEST_FILE_NAME)) - { - XTESTS_RUN_CASE(TEST_cstring_readline_CALLABILITY); - XTESTS_RUN_CASE(TEST_cstring_readline_INVALID_STREAM); - XTESTS_RUN_CASE(TEST_cstring_readline_SHORT_MULTILINE); - XTESTS_RUN_CASE(TEST_cstring_readline_EMPTY_FILE); - XTESTS_RUN_CASE(TEST_cstring_readline_CRLF_AND_MIXED_EOL); - XTESTS_RUN_CASE(TEST_cstring_readline_FINAL_EOL_VS_NO_EOL); - XTESTS_RUN_CASE(TEST_cstring_readline_CONSECUTIVE_EMPTY_LINES); - XTESTS_RUN_CASE(TEST_cstring_readline_LONG_LINES); - XTESTS_RUN_CASE(TEST_cstring_readline_MANY_SHORT_LINES); - XTESTS_RUN_CASE(TEST_cstring_readline_REUSE_AFTER_LONG_LINE); - - XTESTS_PRINT_RESULTS(); - - XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode); - } - - return retCode; -} - - -/* ///////////////////////////////////////////////////////////////////////// - * compiler compatibility - */ - -#if defined(STLSOFT_COMPILER_IS_MSVC) -# if _MSC_VER >= 1200 -# pragma warning(pop) -# endif /* compiler */ -#endif /* compiler */ - - -/* ///////////////////////////////////////////////////////////////////////// - * helper functions - */ - -namespace -{ - - static FILE* fopen_or_throw(char const* fileName, char const* mode) - { -#ifdef CSTRING_USING_SAFE_STR_FUNCTIONS - FILE* f; - - if (0 != ::fopen_s(&f, fileName, mode)) -#else /* ? CSTRING_USING_SAFE_STR_FUNCTIONS */ - FILE* f = ::fopen(fileName, mode); - - if (NULL == f) -#endif /* CSTRING_USING_SAFE_STR_FUNCTIONS */ - { - throw platformstl::platform_exception((std::string("Could not open file '") + fileName + "'").c_str(), platformstl::system_traits::get_last_error()); - } - - return f; - } - - static void write_bytes(char const* fileName, void const* bytes, size_t cb) - { - FILE* f = fopen_or_throw(fileName, "wb"); - - stlsoft::scoped_handle scoper(f, ::fclose); - - if (cb != ::fwrite(bytes, 1, cb, f)) - { - throw platformstl::platform_exception("Could not write test file", platformstl::system_traits::get_last_error()); - } - } - - static void write_string_bytes(char const* fileName, std::string const& bytes) - { - write_bytes(fileName, bytes.data(), bytes.size()); - } - - static std::string make_filled(size_t n, char ch) - { - return std::string(n, ch); - } - - int setup(void*) - { - return 0; - } - - int teardown(void* arg) - { - char const* filename = static_cast(arg); - - ::remove(filename); - - return 0; - } -} // anonymous namespace - - -/* ///////////////////////////////////////////////////////////////////////// - * test function implementations - */ - -namespace -{ - -static void TEST_cstring_readline_CALLABILITY() -{ - false && cstring_readline(stdout, NULL, NULL); - - TEST_PASSED(); -} - -static void TEST_cstring_readline_INVALID_STREAM() -{ - cstring_t cs = cstring_t_DEFAULT; - - TEST_ENUM_EQ(CSTRING_RC_INVALIDSTREAM, cstring_readline(NULL, &cs, NULL)); - - size_t n; - - TEST_ENUM_EQ(CSTRING_RC_INVALIDSTREAM, cstring_readline(NULL, &cs, &n)); -} - -static void TEST_cstring_readline_SHORT_MULTILINE() -{ - { - FILE* f = fopen_or_throw(TEST_FILE_NAME, "w"); - - stlsoft::scoped_handle scoper(f, ::fclose); - - fprintf(f, "\n"); - fprintf(f, "abc\n"); - fprintf(f, "abcdef\n"); - fprintf(f, "abcdefghijkl\n"); - fprintf(f, "abcdefghijklmnopqrstuvwxyz\n"); - fprintf(f, "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"); - fprintf(f, "xyz"); - } - - { - FILE* f = fopen_or_throw(TEST_FILE_NAME, "r"); - - stlsoft::scoped_handle scoper(f, ::fclose); - - CSTRING_RC rc; - cstring_t strings[7] = { - cstring_t_DEFAULT, - cstring_t_DEFAULT, - cstring_t_DEFAULT, - cstring_t_DEFAULT, - cstring_t_DEFAULT, - cstring_t_DEFAULT, - cstring_t_DEFAULT, - }; - size_t lengths[7]; - - rc = cstring_readline(f, &strings[0], &lengths[0]); - - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(0u, lengths[0]); - TEST_MS_EQ("", strings[0]); - - rc = cstring_readline(f, &strings[1], &lengths[1]); - - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(3u, lengths[1]); - TEST_MS_EQ("abc", strings[1]); - - rc = cstring_readline(f, &strings[2], &lengths[2]); - - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(6u, lengths[2]); - TEST_MS_EQ("abcdef", strings[2]); - - rc = cstring_readline(f, &strings[3], &lengths[3]); - - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(12u, lengths[3]); - TEST_MS_EQ("abcdefghijkl", strings[3]); - - rc = cstring_readline(f, &strings[4], &lengths[4]); - - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(26u, lengths[4]); - TEST_MS_EQ("abcdefghijklmnopqrstuvwxyz", strings[4]); - - rc = cstring_readline(f, &strings[5], &lengths[5]); - - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(62u, lengths[5]); - TEST_MS_EQ("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", strings[5]); - - rc = cstring_readline(f, &strings[6], &lengths[6]); - - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); - TEST_INT_EQ(3u, lengths[6]); - TEST_MS_EQ("xyz", strings[6]); - - { for (size_t i = 0; i != 7; ++i) - { - cstring_destroy(&strings[i]); - }} - } -} - -static void TEST_cstring_readline_EMPTY_FILE() -{ - /* empty file → first read is EOF with empty payload */ - write_bytes(TEST_FILE_NAME, "", 0); - - FILE* f = fopen_or_throw(TEST_FILE_NAME, "rb"); - - stlsoft::scoped_handle scoper(f, ::fclose); - - cstring_t cs = cstring_t_DEFAULT; - size_t n = 123u; - CSTRING_RC rc = cstring_readline(f, &cs, &n); - - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); - TEST_INT_EQ(0u, n); - TEST_INT_EQ(0u, cs.len); - TEST_MS_EQ("", cs); - - cstring_destroy(&cs); -} - -static void TEST_cstring_readline_CRLF_AND_MIXED_EOL() -{ - /* CRLF, mixed EOL, and lone CR mid-line (binary I/O so CR is visible) */ - { - std::string bytes; - - bytes.append("one\r\n"); - bytes.append("two\n"); - bytes.append("three\r\n"); - bytes.append("has\rembed\r\n"); - bytes.append("end"); - - write_string_bytes(TEST_FILE_NAME, bytes); - } - - FILE* f = fopen_or_throw(TEST_FILE_NAME, "rb"); - - stlsoft::scoped_handle scoper(f, ::fclose); - - cstring_t cs = cstring_t_DEFAULT; - size_t n; - CSTRING_RC rc; - - rc = cstring_readline(f, &cs, &n); - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - /* CRLF: CR is appended then stripped; numRead still counts the CR */ - TEST_INT_EQ(4u, n); - TEST_INT_EQ(3u, cs.len); - TEST_MS_EQ("one", cs); - - rc = cstring_readline(f, &cs, &n); - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(3u, n); - TEST_INT_EQ(3u, cs.len); - TEST_MS_EQ("two", cs); - - rc = cstring_readline(f, &cs, &n); - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(6u, n); - TEST_INT_EQ(5u, cs.len); - TEST_MS_EQ("three", cs); - - rc = cstring_readline(f, &cs, &n); - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(10u, n); - TEST_INT_EQ(9u, cs.len); - TEST_MS_EQ("has\rembed", cs); - - rc = cstring_readline(f, &cs, &n); - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); - TEST_INT_EQ(3u, n); - TEST_INT_EQ(3u, cs.len); - TEST_MS_EQ("end", cs); - - cstring_destroy(&cs); -} - -static void TEST_cstring_readline_FINAL_EOL_VS_NO_EOL() -{ - /* final line with EOL vs without EOL */ - { - write_string_bytes(TEST_FILE_NAME, std::string("alpha\nbeta\n")); - - FILE* f = fopen_or_throw(TEST_FILE_NAME, "rb"); - - stlsoft::scoped_handle scoper(f, ::fclose); - - cstring_t cs = cstring_t_DEFAULT; - size_t n; - CSTRING_RC rc; - - rc = cstring_readline(f, &cs, &n); - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_MS_EQ("alpha", cs); - - rc = cstring_readline(f, &cs, &n); - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_MS_EQ("beta", cs); - - rc = cstring_readline(f, &cs, &n); - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); - TEST_INT_EQ(0u, n); - TEST_INT_EQ(0u, cs.len); - - cstring_destroy(&cs); - } - - { - write_string_bytes(TEST_FILE_NAME, std::string("alpha\nbeta")); - - FILE* f = fopen_or_throw(TEST_FILE_NAME, "rb"); - - stlsoft::scoped_handle scoper(f, ::fclose); - - cstring_t cs = cstring_t_DEFAULT; - size_t n; - CSTRING_RC rc; - - rc = cstring_readline(f, &cs, &n); - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_MS_EQ("alpha", cs); - - rc = cstring_readline(f, &cs, &n); - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); - TEST_INT_EQ(4u, n); - TEST_MS_EQ("beta", cs); - - cstring_destroy(&cs); - } -} - -static void TEST_cstring_readline_CONSECUTIVE_EMPTY_LINES() -{ - /* consecutive empty lines */ - write_string_bytes(TEST_FILE_NAME, std::string("\n\n\n")); - - FILE* f = fopen_or_throw(TEST_FILE_NAME, "rb"); - - stlsoft::scoped_handle scoper(f, ::fclose); - - cstring_t cs = cstring_t_DEFAULT; - size_t n; - CSTRING_RC rc; - - { for (int i = 0; i != 3; ++i) - { - rc = cstring_readline(f, &cs, &n); - - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(0u, n); - TEST_INT_EQ(0u, cs.len); - TEST_MS_EQ("", cs); - }} - - rc = cstring_readline(f, &cs, &n); - - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); - TEST_INT_EQ(0u, n); - - cstring_destroy(&cs); -} - -static void TEST_cstring_readline_LONG_LINES() -{ - /* long lines at several lengths */ - static size_t const lengths[] = { - 1024u, - 4096u, - 16384u, - 65536u, - }; - - { for (size_t i = 0; i != sizeof(lengths) / sizeof(lengths[0]); ++i) - { - size_t const len = lengths[i]; - std::string line = make_filled(len, static_cast('A' + static_cast(i % 26))); - std::string bytes = line; - - bytes.push_back('\n'); - bytes.append(make_filled(len / 2u == 0 ? 1u : len / 2u, 'z')); - - write_string_bytes(TEST_FILE_NAME, bytes); - - FILE* f = fopen_or_throw(TEST_FILE_NAME, "rb"); - - stlsoft::scoped_handle scoper(f, ::fclose); - - cstring_t cs = cstring_t_DEFAULT; - size_t n; - CSTRING_RC rc; - - rc = cstring_readline(f, &cs, &n); - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(len, n); - TEST_INT_EQ(len, cs.len); - REQUIRE(TEST_MS_EQ(line.c_str(), cs)); - - rc = cstring_readline(f, &cs, &n); - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); - TEST_INT_EQ(len / 2u == 0 ? 1u : len / 2u, n); - - cstring_destroy(&cs); - }} -} - -static void TEST_cstring_readline_MANY_SHORT_LINES() -{ - /* many short lines */ - size_t const num_lines = 1000u; - std::string bytes; - std::vector expected; - - expected.reserve(num_lines); - - { for (size_t i = 0; i != num_lines; ++i) - { - char buf[32]; - - snprintf(buf, sizeof(buf), "line-%04u", static_cast(i)); - - expected.push_back(buf); - bytes.append(buf); - bytes.push_back('\n'); - }} - - write_string_bytes(TEST_FILE_NAME, bytes); - - FILE* f = fopen_or_throw(TEST_FILE_NAME, "rb"); - - stlsoft::scoped_handle scoper(f, ::fclose); - - cstring_t cs = cstring_t_DEFAULT; - size_t n; - CSTRING_RC rc; - - { for (size_t i = 0; i != num_lines; ++i) - { - rc = cstring_readline(f, &cs, &n); - - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(expected[i].size(), n); - TEST_MS_EQ(expected[i].c_str(), cs); - }} - - rc = cstring_readline(f, &cs, &n); - - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); - TEST_INT_EQ(0u, n); - - cstring_destroy(&cs); -} - -static void TEST_cstring_readline_REUSE_AFTER_LONG_LINE() -{ - /* reuse one cstring_t after a long line then a short line; numRead may be NULL */ - std::string const long_line = make_filled(8192u, 'L'); - std::string bytes = long_line; - - bytes.push_back('\n'); - bytes.append("short"); - - write_string_bytes(TEST_FILE_NAME, bytes); - - FILE* f = fopen_or_throw(TEST_FILE_NAME, "rb"); - - stlsoft::scoped_handle scoper(f, ::fclose); - - cstring_t cs = cstring_t_DEFAULT; - CSTRING_RC rc; - - rc = cstring_readline(f, &cs, NULL); - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(8192u, cs.len); - TEST_MS_EQ(long_line.c_str(), cs); - - rc = cstring_readline(f, &cs, NULL); - REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); - TEST_INT_EQ(5u, cs.len); - TEST_MS_EQ("short", cs); - - cstring_destroy(&cs); -} -} // anonymous namespace - - -/* ///////////////////////////// end of file //////////////////////////// */ - diff --git a/test/component/test.component.cstring_vector_readLines/CMakeLists.txt b/test/component/test.component.cstring_vector_readLines/CMakeLists.txt deleted file mode 100644 index 08eb713..0000000 --- a/test/component/test.component.cstring_vector_readLines/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten -define_automated_test_program(test_component_cstring_cstring_vector_readLines entry.cpp) diff --git a/test/component/test.component.cstring_writeline/CMakeLists.txt b/test/component/test.component.cstring_writeline/CMakeLists.txt deleted file mode 100644 index db887ae..0000000 --- a/test/component/test.component.cstring_writeline/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -list(APPEND X_MSVC_CUSTOM_WARNINGS_TO_BE_SUPPRESSED - 4996 -) - -define_automated_test_program(test_component_cstring_cstring_writeline entry.cpp) diff --git a/test/performance/CMakeLists.txt b/test/performance/CMakeLists.txt index 15d78ed..9b58e9a 100644 --- a/test/performance/CMakeLists.txt +++ b/test/performance/CMakeLists.txt @@ -3,8 +3,9 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -add_subdirectory(test.performance.cstring) -add_subdirectory(test.performance.cstring_vector) +add_subdirectory(cstring) +add_subdirectory(cstring_readline) +add_subdirectory(cstring_vector) # ############################## end of file ############################# # diff --git a/test/performance/cstring/CMakeLists.txt b/test/performance/cstring/CMakeLists.txt new file mode 100644 index 0000000..348b004 --- /dev/null +++ b/test/performance/cstring/CMakeLists.txt @@ -0,0 +1,10 @@ +# GCC 15 -Wfree-nonheap-object false positive: inlined +# stlsoft::auto_buffer::~auto_buffer() only deallocates external storage. +list(APPEND X_GCC_CUSTOM_WARNINGS_TO_BE_SUPPRESSED + free-nonheap-object +) +list(APPEND X_MSVC_CUSTOM_WARNINGS_TO_BE_SUPPRESSED + 4996 +) + +define_performance_test_program(test.performance.cstring main.cpp) diff --git a/test/performance/test.performance.cstring/main.cpp b/test/performance/cstring/main.cpp similarity index 72% rename from test/performance/test.performance.cstring/main.cpp rename to test/performance/cstring/main.cpp index f7b8c48..f3e2151 100644 --- a/test/performance/test.performance.cstring/main.cpp +++ b/test/performance/cstring/main.cpp @@ -7,7 +7,7 @@ * p99 is available, also report per-iteration percentiles. * * Created: 23rd September 2026 - * Updated: 29th September 2026 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -20,9 +20,11 @@ #include "perf_harness.hpp" +#include + #include -#include +#include #include #include #include @@ -39,7 +41,6 @@ char const* const IMPL_STD = "std::string"; char const* const IMPL_RAW = "raw_realloc"; char const* const IMPL_BORROWED = "cstring_borrowed"; char const* const IMPL_FIXEDBUF = "fixed_char_buf"; - #ifdef _WIN32 /* Realloc row is the ratio baseline. These arenas follow unit-test order @@ -59,7 +60,7 @@ cstring_arena const WINDOWS_CSTRING_ARENAS[] = }; #endif /* _WIN32 */ -std::size_t const SIZES[] = +size_t const SIZES[] = { 16u, 64u, @@ -68,6 +69,18 @@ std::size_t const SIZES[] = 65536u, }; +const size_t NUM_STACK_ELEMENTS = 512; + +/* append_len_inc8 appends this many characters, from an empty string up to + * the limit. The limit is a multiple of the chunk. + */ +size_t const APPEND_INC8_CHUNK = 8u; +size_t const APPEND_INC8_LIMIT = 8000u; + +static_assert( + 0u == (APPEND_INC8_LIMIT % APPEND_INC8_CHUNK) +, "APPEND_INC8_LIMIT must be a multiple of APPEND_INC8_CHUNK" +); } // anonymous namespace @@ -79,97 +92,33 @@ namespace { using namespace cstring_perf; -struct run_result -{ - interval_t tm_ns; - std::uint64_t anchor; -#ifdef HAS_P99 - - p99::histogram hist; -#endif /* HAS_P99 */ -}; - -template -run_result -time_iterations( - std::size_t num_iterations -, std::size_t num_warm_loops -, F fn -) -{ - run_result result = {}; - stopwatch_t sw; - - for (std::size_t w = num_warm_loops; 0 != w; --w) - { - result.anchor = 0; -#ifdef HAS_P99 - - result.hist.clear(); -#endif /* HAS_P99 */ - interval_t tm_ns = 0; -#ifdef HAS_P99 - - for (std::size_t i = 0; num_iterations != i; ++i) - { - sw.start(); - result.anchor += fn(); - sw.stop(); - - interval_t const sample = sw.get_nanoseconds(); - - tm_ns += sample; - (void)result.hist.push_ns(static_cast(sample)); - } -#else /* ? HAS_P99 */ - - sw.start(); - for (std::size_t i = 0; num_iterations != i; ++i) - { - result.anchor += fn(); - } - sw.stop(); - tm_ns = sw.get_nanoseconds(); -#endif /* HAS_P99 */ - - if (1 == w) - { - result.tm_ns = tm_ns; - } - } - - return result; -} - void emit_row( - char const* scenario -, std::size_t size -, char const* impl -, std::size_t num_iterations -, std::size_t num_actions -, run_result const& r -, interval_t cstring_tm_ns + char const* scenario +, size_t size +, char const* impl +, size_t num_iterations +, size_t num_actions +, run_result const& r +, interval_t cstring_tm_ns ) { - double const ratio = - (0 == ::strcmp(impl, IMPL_CSTRING) || 0 == ::strcmp(impl, IMPL_BORROWED)) - ? 1.0 - : ratio_or_dash(r.tm_ns, cstring_tm_ns) - ; + char const* const baselines[] = + { + IMPL_CSTRING, + IMPL_BORROWED, + }; - display_results( + cstring_perf::emit_row( scenario , size , impl , num_iterations , num_actions - , r.tm_ns - , ratio - , r.anchor -#ifdef HAS_P99 - , &r.hist -#endif /* HAS_P99 */ + , r + , cstring_tm_ns + , baselines + , STLSOFT_NUM_ELEMENTS(baselines) ); } @@ -180,18 +129,18 @@ emit_row( template void emit_windows_cstring_arenas( - char const* scenario -, std::size_t size -, std::size_t num_iterations -, std::size_t num_warm_loops -, std::size_t num_actions -, interval_t baseline_ns -, F body + char const* scenario +, size_t size +, size_t num_iterations +, size_t num_warm_loops +, size_t num_actions +, interval_t baseline_ns +, F body ) { #ifdef _WIN32 - for (std::size_t i = 0; STLSOFT_NUM_ELEMENTS(WINDOWS_CSTRING_ARENAS) != i; ++i) + for (size_t i = 0; STLSOFT_NUM_ELEMENTS(WINDOWS_CSTRING_ARENAS) != i; ++i) { cstring_flags_t const flags = WINDOWS_CSTRING_ARENAS[i].flags; @@ -236,8 +185,8 @@ namespace { void scenario_create_destroy_empty( - std::size_t num_iterations -, std::size_t num_warm_loops + size_t num_iterations +, size_t num_warm_loops ) { run_result const cs = time_iterations(num_iterations, num_warm_loops, []() -> std::uint64_t { @@ -289,9 +238,9 @@ scenario_create_destroy_empty( void scenario_create_destroy_len( - std::size_t n -, std::size_t num_iterations -, std::size_t num_warm_loops + size_t n +, size_t num_iterations +, size_t num_warm_loops ) { std::string const payload = make_payload(n); @@ -348,9 +297,9 @@ scenario_create_destroy_len( void scenario_assign_len_grow( - std::size_t n -, std::size_t num_iterations -, std::size_t num_warm_loops + size_t n +, size_t num_iterations +, size_t num_warm_loops ) { std::string const payload = make_payload(n); @@ -408,10 +357,10 @@ scenario_assign_len_grow( void scenario_append_len_growth( - std::size_t chunk -, std::size_t num_appends -, std::size_t num_iterations -, std::size_t num_warm_loops + size_t chunk +, size_t num_appends +, size_t num_iterations +, size_t num_warm_loops ) { std::string const payload = make_payload(chunk); @@ -420,7 +369,7 @@ scenario_append_len_growth( run_result const cs = time_iterations(num_iterations, num_warm_loops, [p, chunk, num_appends]() -> std::uint64_t { cstring_t s = cstring_t_DEFAULT; cstring_create(&s, ""); - for (std::size_t i = 0; num_appends != i; ++i) + for (size_t i = 0; num_appends != i; ++i) { cstring_appendLen(&s, p, chunk); } @@ -431,7 +380,7 @@ scenario_append_len_growth( run_result const st = time_iterations(num_iterations, num_warm_loops, [p, chunk, num_appends]() -> std::uint64_t { std::string s; - for (std::size_t i = 0; num_appends != i; ++i) + for (size_t i = 0; num_appends != i; ++i) { s.append(p, chunk); } @@ -441,7 +390,7 @@ scenario_append_len_growth( run_result const raw = time_iterations(num_iterations, num_warm_loops, [p, chunk, num_appends]() -> std::uint64_t { raw_string s; raw_init(&s); - for (std::size_t i = 0; num_appends != i; ++i) + for (size_t i = 0; num_appends != i; ++i) { raw_append_len(&s, p, chunk); } @@ -467,7 +416,7 @@ scenario_append_len_growth( cstring_createEx(&s, "", flags, NULL, 0); - for (std::size_t i = 0; num_appends != i; ++i) + for (size_t i = 0; num_appends != i; ++i) { cstring_appendLen(&s, p, chunk); } @@ -483,23 +432,103 @@ scenario_append_len_growth( emit_row(scenario, chunk, IMPL_RAW, num_iterations, num_appends, raw, cs.tm_ns); } +/* Repeated 8-character appends growing a string from length 0 to 8,000. The + * size column is the final length, and each action is one append. The chunk + * matches cstring's allocation granularity. std::string and an exact + * realloc floor run the same growth. + */ +void +scenario_append_len_inc8( + size_t num_iterations +, size_t num_warm_loops +) +{ + size_t const chunk = APPEND_INC8_CHUNK; + size_t const final_len = APPEND_INC8_LIMIT; + size_t const num_appends = final_len / chunk; + std::string const payload = make_payload(chunk); + char const* const p = payload.data(); + + run_result const cs = time_iterations(num_iterations, num_warm_loops, [p, chunk, num_appends]() -> std::uint64_t { + cstring_t s = cstring_t_DEFAULT; + cstring_create(&s, ""); + for (size_t i = 0; num_appends != i; ++i) + { + cstring_appendLen(&s, p, chunk); + } + std::uint64_t const a = s.len; + cstring_destroy(&s); + return a; + }); + + run_result const st = time_iterations(num_iterations, num_warm_loops, [p, chunk, num_appends]() -> std::uint64_t { + std::string s; + for (size_t i = 0; num_appends != i; ++i) + { + s.append(p, chunk); + } + return s.size(); + }); + + run_result const raw = time_iterations(num_iterations, num_warm_loops, [p, chunk, num_appends]() -> std::uint64_t { + raw_string s; + raw_init(&s); + for (size_t i = 0; num_appends != i; ++i) + { + raw_append_len(&s, p, chunk); + } + std::uint64_t const a = s.len; + raw_destroy(&s); + return a; + }); + + emit_row("append_len_inc8", final_len, IMPL_CSTRING, num_iterations, num_appends, cs, cs.tm_ns); + emit_windows_cstring_arenas( + "append_len_inc8" + , final_len + , num_iterations + , num_warm_loops + , num_appends + , cs.tm_ns + , [p, chunk, num_appends](cstring_flags_t flags) -> std::uint64_t + { + cstring_t s = cstring_t_DEFAULT; + + cstring_createEx(&s, "", flags, NULL, 0); + + for (size_t i = 0; num_appends != i; ++i) + { + cstring_appendLen(&s, p, chunk); + } + + std::uint64_t const a = s.len; + + cstring_destroy(&s); + + return a; + } + ); + emit_row("append_len_inc8", final_len, IMPL_STD, num_iterations, num_appends, st, cs.tm_ns); + emit_row("append_len_inc8", final_len, IMPL_RAW, num_iterations, num_appends, raw, cs.tm_ns); +} + void scenario_append_len_reserved( - std::size_t n -, std::size_t num_iterations -, std::size_t num_warm_loops + size_t n +, size_t num_iterations +, size_t num_warm_loops ) { std::string const payload = make_payload(n); char const* const p = payload.data(); - std::size_t const chunk = (n < 16u) ? n : 16u; - std::size_t const num_appends = (0 == chunk) ? 0u : (n / chunk); + size_t const chunk = (n < 16u) ? n : 16u; + size_t const num_appends = (0 == chunk) ? 0u : (n / chunk); run_result const cs = time_iterations(num_iterations, num_warm_loops, [p, chunk, n, num_appends]() -> std::uint64_t { cstring_t s = cstring_t_DEFAULT; cstring_create(&s, ""); cstring_setCapacity(&s, n); - for (std::size_t i = 0; num_appends != i; ++i) + for (size_t i = 0; num_appends != i; ++i) { cstring_appendLen(&s, p, chunk); } @@ -511,7 +540,7 @@ scenario_append_len_reserved( run_result const st = time_iterations(num_iterations, num_warm_loops, [p, chunk, n, num_appends]() -> std::uint64_t { std::string s; s.reserve(n); - for (std::size_t i = 0; num_appends != i; ++i) + for (size_t i = 0; num_appends != i; ++i) { s.append(p, chunk); } @@ -522,7 +551,7 @@ scenario_append_len_reserved( raw_string s; raw_init(&s); raw_reserve(&s, n); - for (std::size_t i = 0; num_appends != i; ++i) + for (size_t i = 0; num_appends != i; ++i) { raw_append_len(&s, p, chunk); } @@ -546,7 +575,7 @@ scenario_append_len_reserved( cstring_createEx(&s, "", flags, NULL, 0); cstring_setCapacity(&s, n); - for (std::size_t i = 0; num_appends != i; ++i) + for (size_t i = 0; num_appends != i; ++i) { cstring_appendLen(&s, p, chunk); } @@ -564,16 +593,16 @@ scenario_append_len_reserved( void scenario_insert_len_mid( - std::size_t n -, std::size_t num_iterations -, std::size_t num_warm_loops + size_t n +, size_t num_iterations +, size_t num_warm_loops ) { std::string const base = make_payload(n); std::string const ins = make_payload(n / 4u == 0 ? 1u : n / 4u); char const* const bp = base.data(); char const* const ip = ins.data(); - std::size_t const in = ins.size(); + size_t const in = ins.size(); int const pos = static_cast(n / 2u); run_result const cs = time_iterations(num_iterations, num_warm_loops, [bp, n, ip, in, pos]() -> std::uint64_t { @@ -629,9 +658,9 @@ scenario_insert_len_mid( void scenario_copy( - std::size_t n -, std::size_t num_iterations -, std::size_t num_warm_loops + size_t n +, size_t num_iterations +, size_t num_warm_loops ) { std::string const payload = make_payload(n); @@ -701,17 +730,64 @@ scenario_copy( } void -scenario_borrowed_fixed( - std::size_t n -, std::size_t num_iterations -, std::size_t num_warm_loops +scenario_borrowed_fixed_construct( + size_t n +, size_t num_iterations +, size_t num_warm_loops ) { std::string const payload = make_payload(n); char const* const p = payload.data(); run_result const cs = time_iterations(num_iterations, num_warm_loops, [p, n]() -> std::uint64_t { - std::vector buf(n + 1u, '\0'); + stlsoft::auto_buffer buf(n + 1); + cstring_t s = cstring_t_DEFAULT; + cstring_createLenEx( + &s + , p + , n + , CSTRING_F_MEMORY_IS_BORROWED | CSTRING_F_MEMORY_IS_FIXED + , &buf[0] + , n + 1u + ); + + std::uint64_t const r = s.len + (s.len == 0 ? 0 : size_t(s.ptr[s.len - 1])); + cstring_destroy(&s); + + return r; + }); + + run_result const st = time_iterations(num_iterations, num_warm_loops, [p, n]() -> std::uint64_t { + std::string s(p, n); + + return s.size() + (s.empty() ? 0 : size_t(s.back())); + }); + + run_result const fx = time_iterations(num_iterations, num_warm_loops, [p, n]() -> std::uint64_t { + stlsoft::auto_buffer buf(n + 1); + ::memcpy(&buf[0], p, n); + buf[n] = '\0'; + + return n + (n == 0 ? 0 : size_t(buf[n - 1])); + }); + + emit_row("borrowed_fixed_construct", n, IMPL_BORROWED, num_iterations, 1, cs, cs.tm_ns); + emit_row("borrowed_fixed_construct", n, IMPL_STD, num_iterations, 1, st, cs.tm_ns); + emit_row("borrowed_fixed_construct", n, IMPL_FIXEDBUF, num_iterations, 1, fx, cs.tm_ns); +} + +void +scenario_borrowed_fixed_assign( + size_t n +, size_t num_iterations +, size_t num_warm_loops +) +{ + std::string const payload = make_payload(n); + char const* const p = payload.data(); + + run_result const cs = time_iterations(num_iterations, num_warm_loops, [p, n]() -> std::uint64_t { + stlsoft::auto_buffer buf(n + 1); cstring_t s = cstring_t_DEFAULT; cstring_createEx( &s @@ -721,22 +797,25 @@ scenario_borrowed_fixed( , n + 1u ); cstring_assignLen(&s, p, n); - std::uint64_t const a = s.len; + std::uint64_t const r = s.len + (s.len == 0 ? 0 : size_t(s.ptr[s.len - 1])); cstring_destroy(&s); - return a; + + return r; }); run_result const st = time_iterations(num_iterations, num_warm_loops, [p, n]() -> std::uint64_t { std::string s; s.assign(p, n); - return s.size(); + + return s.size() + (s.empty() ? 0 : size_t(s.back())); }); run_result const fx = time_iterations(num_iterations, num_warm_loops, [p, n]() -> std::uint64_t { - std::vector buf(n + 1u, '\0'); + stlsoft::auto_buffer buf(n + 1); ::memcpy(&buf[0], p, n); buf[n] = '\0'; - return n + static_cast(buf[0]); + + return n + (n == 0 ? 0 : size_t(buf[n - 1])); }); emit_row("borrowed_fixed_assign", n, IMPL_BORROWED, num_iterations, 1, cs, cs.tm_ns); @@ -752,11 +831,11 @@ scenario_borrowed_fixed( int main(int /*argc*/, char* /*argv*/[]) { - std::size_t const num_iterations = cstring_perf::default_iterations(); - std::size_t const num_warm_loops = cstring_perf::default_warmups(); + size_t const num_iterations = cstring_perf::default_iterations(); + size_t const num_warm_loops = cstring_perf::default_warmups(); // Fewer outer iters for heavy growth / large sizes - std::size_t const heavy_iters = + size_t const heavy_iters = (num_iterations > 10000u) ? (num_iterations / 10u) : num_iterations ; @@ -778,16 +857,17 @@ int main(int /*argc*/, char* /*argv*/[]) scenario_create_destroy_empty(num_iterations, num_warm_loops); - for (std::size_t i = 0; STLSOFT_NUM_ELEMENTS(SIZES) != i; ++i) + for (size_t i = 0; STLSOFT_NUM_ELEMENTS(SIZES) != i; ++i) { - std::size_t const n = SIZES[i]; - std::size_t const iters = (n >= 4096u) ? heavy_iters : num_iterations; + size_t const n = SIZES[i]; + size_t const iters = (n >= 4096u) ? heavy_iters : num_iterations; scenario_create_destroy_len(n, iters, num_warm_loops); scenario_assign_len_grow(n, iters, num_warm_loops); scenario_append_len_reserved(n, iters, num_warm_loops); scenario_copy(n, iters, num_warm_loops); - scenario_borrowed_fixed(n, iters, num_warm_loops); + scenario_borrowed_fixed_construct(n, iters, num_warm_loops); + scenario_borrowed_fixed_assign(n, iters, num_warm_loops); if (n <= 4096u) { @@ -796,8 +876,11 @@ int main(int /*argc*/, char* /*argv*/[]) } } + scenario_append_len_inc8(heavy_iters, num_warm_loops); + return EXIT_SUCCESS; } /* ///////////////////////////// end of file //////////////////////////// */ + diff --git a/test/performance/cstring_readline/CMakeLists.txt b/test/performance/cstring_readline/CMakeLists.txt new file mode 100644 index 0000000..7588498 --- /dev/null +++ b/test/performance/cstring_readline/CMakeLists.txt @@ -0,0 +1,5 @@ +list(APPEND X_MSVC_CUSTOM_WARNINGS_TO_BE_SUPPRESSED + 4996 +) + +define_performance_test_program(test.performance.cstring_readline main.cpp) diff --git a/test/performance/cstring_readline/main.cpp b/test/performance/cstring_readline/main.cpp new file mode 100644 index 0000000..da1a1e0 --- /dev/null +++ b/test/performance/cstring_readline/main.cpp @@ -0,0 +1,523 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: test/performance/test.performance.cstring_readline/main.cpp + * + * Purpose: Competitive performance tests for cstring_readline() against + * std::getline, fgets (bounded buffer), and a char-at-a-time fgetc + * floor. Scenarios cover reused capacity, CRLF stripping, a fresh + * instance per line, a pre-reserved buffer, empty lines, and an + * unterminated final line. Percentiles and this filesystem suite + * require p99. + * + * Created: 27th September 2026 + * Updated: 4th October 2026 + * + * ////////////////////////////////////////////////////////////////////// */ + + +/* ///////////////////////////////////////////////////////////////////////// + * includes + */ + +#include + +#include "perf_harness.hpp" + +#ifdef HAS_P99 +# include +# include +# include + +# include +# include +#endif /* HAS_P99 */ + +#include + + +/* ///////////////////////////////////////////////////////////////////////// + * constants + */ + +#ifdef HAS_P99 +namespace { + +char const* const IMPL_READLINE = "cstring_readline"; +char const* const IMPL_GETLINE = "std::getline"; +char const* const IMPL_FGETS = "fgets"; +char const* const IMPL_RAW = "raw_fgetc"; + +char const TEST_FILE_NAME[] = "test.performance.cstring_readline.lines.txt"; + +using namespace cstring_perf; + +enum instance_mode_t +{ + INSTANCE_REUSE = 0, + INSTANCE_FRESH, + INSTANCE_PRERESERVED, +}; + +struct readline_case +{ + char const* scenario; + size_t num_lines; + size_t line_len; + line_ending_t ending; + instance_mode_t mode; +}; + +readline_case const CASES[] = +{ + { "readline_empty_lines", 2000u, 0u, LINE_ENDING_LF, INSTANCE_REUSE, }, + { "readline_fresh_each_line", 200u, 80u, LINE_ENDING_LF, INSTANCE_FRESH, }, + { "readline_prereserved", 100u, 1024u, LINE_ENDING_LF, INSTANCE_PRERESERVED, }, + { "readline_reuse_crlf", 1000u, 80u, LINE_ENDING_CRLF, INSTANCE_REUSE, }, + { "readline_reuse_lf", 1000u, 16u, LINE_ENDING_LF, INSTANCE_REUSE, }, + { "readline_reuse_lf", 1000u, 80u, LINE_ENDING_LF, INSTANCE_REUSE, }, + { "readline_reuse_lf", 100u, 1024u, LINE_ENDING_LF, INSTANCE_REUSE, }, + { "readline_unterminated_eof", 1u, 16384u, LINE_ENDING_NONE, INSTANCE_REUSE, }, +}; +} // anonymous namespace +#endif /* HAS_P99 */ + + +/* ///////////////////////////////////////////////////////////////////////// + * helpers + */ + +#ifdef HAS_P99 +namespace { + +using namespace cstring_perf; + +void +emit_row( + char const* scenario +, size_t size +, char const* impl +, size_t num_iterations +, size_t num_actions +, run_result const& r +, interval_t cstring_tm_ns +) +{ + char const* const baselines[] = + { + IMPL_READLINE, + }; + + cstring_perf::emit_row( + scenario + , size + , impl + , num_iterations + , num_actions + , r + , cstring_tm_ns + , baselines + , STLSOFT_NUM_ELEMENTS(baselines) + ); +} + +std::uint64_t +line_anchor( + size_t len +, unsigned char head +) +{ + return static_cast(len) + 1u + static_cast(head); +} + +unsigned char +content_head( + char const* p +, size_t n +) +{ + if (0 == n || NULL == p) + { + return 0; + } + + return static_cast(p[0]); +} + +std::uint64_t +read_all_cstring( + char const* path +, size_t line_len +, instance_mode_t mode +) +{ + FILE* const f = std::fopen(path, "rb"); + + if (NULL == f) + { + return 0; + } + + cstring_t cs = cstring_t_DEFAULT; + std::uint64_t anchor = 0; + + if (INSTANCE_PRERESERVED == mode && 0 != line_len) + { + if (CSTRING_RC_SUCCESS != cstring_setCapacity(&cs, line_len)) + { + cstring_destroy(&cs); + std::fclose(f); + + return 0; + } + } + + for (;;) + { + if (INSTANCE_FRESH == mode && NULL != cs.ptr) + { + cstring_destroy(&cs); + cs = cstring_t_DEFAULT; + } + + CSTRING_RC const rc = cstring_readline(f, &cs, NULL); + + if (CSTRING_RC_SUCCESS != rc && CSTRING_RC_EOF != rc) + { + anchor = 0; + break; + } + + if (0 != cs.len || CSTRING_RC_EOF != rc) + { + anchor += line_anchor(cs.len, content_head(cs.ptr, cs.len)); + } + + if (CSTRING_RC_EOF == rc) + { + break; + } + } + + cstring_destroy(&cs); + std::fclose(f); + + return anchor; +} + +std::uint64_t +read_all_getline( + char const* path +, size_t line_len +, instance_mode_t mode +) +{ + std::ifstream in(path, std::ios::in | std::ios::binary); + + if (!in) + { + return 0; + } + + std::string line; + std::uint64_t anchor = 0; + + if (INSTANCE_PRERESERVED == mode && 0 != line_len) + { + line.reserve(line_len); + } + + while (std::getline(in, line)) + { + if (!line.empty() && '\r' == line[line.size() - 1u]) + { + line.pop_back(); + } + + anchor += line_anchor(line.size(), content_head(line.data(), line.size())); + + if (INSTANCE_FRESH == mode) + { + std::string empty; + + line.swap(empty); + } + } + + return anchor; +} + +std::uint64_t +read_all_fgets( + char const* path +, size_t line_len +) +{ + FILE* const f = std::fopen(path, "rb"); + + if (NULL == f) + { + return 0; + } + + /* line_len + 4 covers content, optional CR, LF, and the NUL. */ + std::vector buf(line_len + 4u); + std::uint64_t anchor = 0; + + while (NULL != std::fgets(&buf[0], static_cast(buf.size()), f)) + { + size_t n = std::strlen(&buf[0]); + + if (0 != n && '\n' == buf[n - 1u]) + { + --n; + + if (0 != n && '\r' == buf[n - 1u]) + { + --n; + } + } + + anchor += line_anchor(n, content_head(&buf[0], n)); + } + + std::fclose(f); + + return anchor; +} + +std::uint64_t +read_all_raw_fgetc( + char const* path +, size_t line_len +, instance_mode_t mode +) +{ + FILE* const f = std::fopen(path, "rb"); + + if (NULL == f) + { + return 0; + } + + raw_string s; + raw_init(&s); + + if (INSTANCE_PRERESERVED == mode && 0 != line_len) + { + if (0 != raw_reserve(&s, line_len)) + { + raw_destroy(&s); + std::fclose(f); + + return 0; + } + } + + std::uint64_t anchor = 0; + int previous = '\0'; + + for (;;) + { + int const ch = std::fgetc(f); + + if (EOF == ch) + { + if (0 != s.len) + { + anchor += line_anchor(s.len, content_head(s.ptr, s.len)); + } + + break; + } + + if ('\n' == ch) + { + if ('\r' == previous && 0 != s.len) + { + --s.len; + s.ptr[s.len] = '\0'; + } + + anchor += line_anchor(s.len, content_head(s.ptr, s.len)); + s.len = 0; + + if (NULL != s.ptr) + { + s.ptr[0] = '\0'; + } + + previous = '\0'; + + if (INSTANCE_FRESH == mode) + { + raw_destroy(&s); + raw_init(&s); + } + + continue; + } + + char const c = static_cast(ch); + + if (0 != raw_append_len(&s, &c, 1u)) + { + anchor = 0; + break; + } + + previous = ch; + } + + raw_destroy(&s); + std::fclose(f); + + return anchor; +} + +bool +anchors_agree( + char const* scenario +, size_t line_len +, run_result const& cs +, run_result const& gl +, run_result const& fg +, run_result const& raw +) +{ + if (0 != cs.anchor && + cs.anchor == gl.anchor && + cs.anchor == fg.anchor && + cs.anchor == raw.anchor) + { + return true; + } + + std::cerr + << "anchor mismatch in " << scenario + << " size " << line_len + << ": cstring_readline=" << cs.anchor + << " std::getline=" << gl.anchor + << " fgets=" << fg.anchor + << " raw_fgetc=" << raw.anchor + << std::endl + ; + + return false; +} + +bool +scenario_readline( + readline_case const& spec +, size_t num_trials +, size_t num_warm_loops +) +{ + if (LINE_ENDING_NONE == spec.ending && 1u != spec.num_lines) + { + std::cerr + << spec.scenario + << ": unterminated input must be a single line" + << std::endl + ; + + return false; + } + + if (!write_lines_file(TEST_FILE_NAME, spec.num_lines, spec.line_len, spec.ending)) + { + std::cerr + << "failed to write " << TEST_FILE_NAME + << "; skipping " << spec.scenario + << std::endl + ; + std::remove(TEST_FILE_NAME); + + return true; + } + + char const* const path = TEST_FILE_NAME; + size_t const line_len = spec.line_len; + instance_mode_t const mode = spec.mode; + + run_result const cs = time_iterations(num_trials, num_warm_loops, [path, line_len, mode]() -> std::uint64_t { + return read_all_cstring(path, line_len, mode); + }); + + run_result const gl = time_iterations(num_trials, num_warm_loops, [path, line_len, mode]() -> std::uint64_t { + return read_all_getline(path, line_len, mode); + }); + + run_result const fg = time_iterations(num_trials, num_warm_loops, [path, line_len]() -> std::uint64_t { + return read_all_fgets(path, line_len); + }); + + run_result const raw = time_iterations(num_trials, num_warm_loops, [path, line_len, mode]() -> std::uint64_t { + return read_all_raw_fgetc(path, line_len, mode); + }); + + bool const ok = anchors_agree(spec.scenario, spec.line_len, cs, gl, fg, raw); + + if (ok) + { + emit_row(spec.scenario, spec.line_len, IMPL_READLINE, num_trials, spec.num_lines, cs, cs.tm_ns); + emit_row(spec.scenario, spec.line_len, IMPL_GETLINE, num_trials, spec.num_lines, gl, cs.tm_ns); + emit_row(spec.scenario, spec.line_len, IMPL_FGETS, num_trials, spec.num_lines, fg, cs.tm_ns); + emit_row(spec.scenario, spec.line_len, IMPL_RAW, num_trials, spec.num_lines, raw, cs.tm_ns); + } + + std::remove(TEST_FILE_NAME); + + return ok; +} +} // anonymous namespace +#endif /* HAS_P99 */ + + +/* ///////////////////////////////////////////////////////////////////////// + * main + */ + +int main(int /*argc*/, char* /*argv*/[]) +{ +#ifdef HAS_P99 + + size_t const num_trials = cstring_perf::default_file_trials(); + size_t const num_warm_loops = cstring_perf::default_warmups(); + + cstring_perf::display_banner("test.performance.cstring_readline"); + + std::cout + << "readline suite (#acts = lines, so ns/op is per line;" + << std::endl + << " cstring_readline is fgetc + one-character append;" + << std::endl + << " fgets uses a buffer sized to the known line):" + << std::endl + ; + + cstring_perf::display_results_title(); + + for (size_t i = 0; STLSOFT_NUM_ELEMENTS(CASES) != i; ++i) + { + if (!scenario_readline(CASES[i], num_trials, num_warm_loops)) + { + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +#else /* ? HAS_P99 */ + + cstring_perf::display_banner("test.performance.cstring_readline"); + + std::cout + << "\nreadline suite skipped (p99 not available)." + << std::endl + ; + + return EXIT_SUCCESS; +#endif /* HAS_P99 */ +} + + +/* ///////////////////////////// end of file //////////////////////////// */ + diff --git a/test/performance/test.performance.cstring_vector/CMakeLists.txt b/test/performance/cstring_vector/CMakeLists.txt similarity index 100% rename from test/performance/test.performance.cstring_vector/CMakeLists.txt rename to test/performance/cstring_vector/CMakeLists.txt diff --git a/test/performance/test.performance.cstring_vector/main.cpp b/test/performance/cstring_vector/main.cpp similarity index 77% rename from test/performance/test.performance.cstring_vector/main.cpp rename to test/performance/cstring_vector/main.cpp index f783477..b0a066f 100644 --- a/test/performance/test.performance.cstring_vector/main.cpp +++ b/test/performance/cstring_vector/main.cpp @@ -8,7 +8,7 @@ * getline, platformstl::file_lines). * * Created: 23rd September 2026 - * Updated: 23rd September 2026 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -62,104 +62,39 @@ namespace { using namespace cstring_perf; -struct run_result -{ - interval_t tm_ns; - std::uint64_t anchor; -#ifdef HAS_P99 - - p99::histogram hist; -#endif /* HAS_P99 */ -}; - -template -run_result -time_iterations( - std::size_t num_iterations -, std::size_t num_warm_loops -, F fn -) -{ - run_result result = {}; - stopwatch_t sw; - - for (std::size_t w = num_warm_loops; 0 != w; --w) - { - result.anchor = 0; -#ifdef HAS_P99 - - result.hist.clear(); -#endif /* HAS_P99 */ - interval_t tm_ns = 0; -#ifdef HAS_P99 - - for (std::size_t i = 0; num_iterations != i; ++i) - { - sw.start(); - result.anchor += fn(); - sw.stop(); - - interval_t const sample = sw.get_nanoseconds(); - - tm_ns += sample; - (void)result.hist.push_ns(static_cast(sample)); - } -#else /* ? HAS_P99 */ - - sw.start(); - for (std::size_t i = 0; num_iterations != i; ++i) - { - result.anchor += fn(); - } - sw.stop(); - tm_ns = sw.get_nanoseconds(); -#endif /* HAS_P99 */ - - if (1 == w) - { - result.tm_ns = tm_ns; - } - } - - return result; -} - void emit_row( char const* scenario -, std::size_t size +, size_t size , char const* impl -, std::size_t num_iterations -, std::size_t num_actions +, size_t num_iterations +, size_t num_actions , run_result const& r , interval_t cstring_tm_ns ) { - double const ratio = - (0 == ::strcmp(impl, IMPL_CSTRING_VECTOR) || - 0 == ::strcmp(impl, IMPL_READLINES)) - ? 1.0 - : ratio_or_dash(r.tm_ns, cstring_tm_ns) - ; + char const* const baselines[] = + { + IMPL_CSTRING_VECTOR, + IMPL_READLINES, + }; - display_results( + cstring_perf::emit_row( scenario , size , impl , num_iterations , num_actions - , r.tm_ns - , ratio - , r.anchor -#ifdef HAS_P99 - , &r.hist -#endif /* HAS_P99 */ + , r + , cstring_tm_ns + , baselines + , STLSOFT_NUM_ELEMENTS(baselines) ); } cstring_t make_cstring_payload( - std::size_t n + size_t n ) { std::string const s = make_payload(n); @@ -169,7 +104,6 @@ make_cstring_payload( return cs; } - } // anonymous namespace @@ -181,9 +115,9 @@ namespace { void scenario_create_destroy( - std::size_t n -, std::size_t num_iterations -, std::size_t num_warm_loops + size_t n +, size_t num_iterations +, size_t num_warm_loops ) { run_result const cs = time_iterations(num_iterations, num_warm_loops, [n]() -> std::uint64_t { @@ -204,7 +138,7 @@ scenario_create_destroy( raw_vec_init(&v); raw_vec_reserve(&v, n); v.len = n; - for (std::size_t i = 0; n != i; ++i) + for (size_t i = 0; n != i; ++i) { v.ptr[i] = static_cast(std::calloc(1, 1)); } @@ -220,10 +154,10 @@ scenario_create_destroy( void scenario_append_one_by_one( - std::size_t payload_len -, std::size_t k -, std::size_t num_iterations -, std::size_t num_warm_loops + size_t payload_len +, size_t k +, size_t num_iterations +, size_t num_warm_loops ) { cstring_t payload = make_cstring_payload(payload_len); @@ -232,7 +166,7 @@ scenario_append_one_by_one( run_result const cs = time_iterations(num_iterations, num_warm_loops, [&payload, k]() -> std::uint64_t { cstring_vector_t v = cstring_vector_t_DEFAULT; cstring_vector_init(&v, 0); - for (std::size_t i = 0; k != i; ++i) + for (size_t i = 0; k != i; ++i) { cstring_vector_append(&v, &payload, 1); } @@ -243,7 +177,7 @@ scenario_append_one_by_one( run_result const st = time_iterations(num_iterations, num_warm_loops, [&payload_s, k]() -> std::uint64_t { std::vector v; - for (std::size_t i = 0; k != i; ++i) + for (size_t i = 0; k != i; ++i) { v.push_back(payload_s); } @@ -253,7 +187,7 @@ scenario_append_one_by_one( run_result const raw = time_iterations(num_iterations, num_warm_loops, [&payload_s, payload_len, k]() -> std::uint64_t { raw_string_vector v; raw_vec_init(&v); - for (std::size_t i = 0; k != i; ++i) + for (size_t i = 0; k != i; ++i) { raw_vec_append_cstr(&v, payload_s.data(), payload_len); } @@ -271,17 +205,17 @@ scenario_append_one_by_one( void scenario_append_batch( - std::size_t payload_len -, std::size_t k -, std::size_t num_iterations -, std::size_t num_warm_loops + size_t payload_len +, size_t k +, size_t num_iterations +, size_t num_warm_loops ) { std::vector payloads(k); std::vector payloads_s(k); std::string const proto = make_payload(payload_len); - for (std::size_t i = 0; k != i; ++i) + for (size_t i = 0; k != i; ++i) { payloads[i] = cstring_t_DEFAULT; cstring_createLen(&payloads[i], proto.data(), payload_len); @@ -307,7 +241,7 @@ scenario_append_batch( raw_string_vector v; raw_vec_init(&v); raw_vec_reserve(&v, k); - for (std::size_t i = 0; k != i; ++i) + for (size_t i = 0; k != i; ++i) { raw_vec_append_cstr(&v, payloads_s[i].data(), payload_len); } @@ -316,7 +250,7 @@ scenario_append_batch( return a; }); - for (std::size_t i = 0; k != i; ++i) + for (size_t i = 0; k != i; ++i) { cstring_destroy(&payloads[i]); } @@ -328,10 +262,10 @@ scenario_append_batch( void scenario_prepend_one_by_one( - std::size_t payload_len -, std::size_t k -, std::size_t num_iterations -, std::size_t num_warm_loops + size_t payload_len +, size_t k +, size_t num_iterations +, size_t num_warm_loops ) { cstring_t payload = make_cstring_payload(payload_len); @@ -340,7 +274,7 @@ scenario_prepend_one_by_one( run_result const cs = time_iterations(num_iterations, num_warm_loops, [&payload, k]() -> std::uint64_t { cstring_vector_t v = cstring_vector_t_DEFAULT; cstring_vector_init(&v, 0); - for (std::size_t i = 0; k != i; ++i) + for (size_t i = 0; k != i; ++i) { cstring_vector_prepend(&v, &payload, 1); } @@ -351,7 +285,7 @@ scenario_prepend_one_by_one( run_result const st = time_iterations(num_iterations, num_warm_loops, [&payload_s, k]() -> std::uint64_t { std::vector v; - for (std::size_t i = 0; k != i; ++i) + for (size_t i = 0; k != i; ++i) { v.insert(v.begin(), payload_s); } @@ -361,7 +295,7 @@ scenario_prepend_one_by_one( run_result const raw = time_iterations(num_iterations, num_warm_loops, [&payload_s, payload_len, k]() -> std::uint64_t { raw_string_vector v; raw_vec_init(&v); - for (std::size_t i = 0; k != i; ++i) + for (size_t i = 0; k != i; ++i) { raw_vec_prepend_cstr(&v, payload_s.data(), payload_len); } @@ -378,39 +312,15 @@ scenario_prepend_one_by_one( } #ifdef HAS_P99 -bool -write_lines_file( - char const* path -, std::size_t num_lines -, std::size_t line_len -) -{ - std::ofstream out(path, std::ios::binary | std::ios::trunc); - - if (!out) - { - return false; - } - - std::string const line = make_payload(line_len); - - for (std::size_t i = 0; num_lines != i; ++i) - { - out << line << '\n'; - } - - return static_cast(out); -} - void scenario_file_lines_vs_readlines( - std::size_t num_lines -, std::size_t line_len -, std::size_t num_trials -, std::size_t num_warm_loops + size_t num_lines +, size_t line_len +, size_t num_trials +, size_t num_warm_loops ) { - if (!write_lines_file(TEST_FILE_NAME, num_lines, line_len)) + if (!write_lines_file(TEST_FILE_NAME, num_lines, line_len, LINE_ENDING_LF)) { std::cerr << "failed to write " << TEST_FILE_NAME << "; skipping file_lines suite" @@ -483,9 +393,9 @@ scenario_file_lines_vs_readlines( int main(int /*argc*/, char* /*argv*/[]) { - std::size_t const num_iterations = cstring_perf::default_iterations(); - std::size_t const num_warm_loops = cstring_perf::default_warmups(); - std::size_t const heavy_iters = + size_t const num_iterations = cstring_perf::default_iterations(); + size_t const num_warm_loops = cstring_perf::default_warmups(); + size_t const heavy_iters = (num_iterations > 10000u) ? (num_iterations / 10u) : num_iterations ; @@ -504,7 +414,7 @@ int main(int /*argc*/, char* /*argv*/[]) scenario_prepend_one_by_one(16u, 32u, heavy_iters, num_warm_loops); #ifdef HAS_P99 - std::size_t const file_trials = cstring_perf::default_file_trials(); + size_t const file_trials = cstring_perf::default_file_trials(); scenario_file_lines_vs_readlines(1000u, 64u, file_trials, num_warm_loops); scenario_file_lines_vs_readlines(5000u, 80u, file_trials, num_warm_loops); diff --git a/test/performance/perf_harness.hpp b/test/performance/perf_harness.hpp index 5b91e6f..4e5ea63 100644 --- a/test/performance/perf_harness.hpp +++ b/test/performance/perf_harness.hpp @@ -4,7 +4,7 @@ * Purpose: Shared helpers for cstring performance programs. * * Created: 23rd September 2026 - * Updated: 29th September 2026 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -27,6 +27,7 @@ # include #endif +#include #include #include #include @@ -148,6 +149,120 @@ make_payload( } +/* ///////////////////////////////////////////////////////////////////////// + * filesystem fixtures + */ + +enum line_ending_t +{ + LINE_ENDING_LF = 0, + LINE_ENDING_CRLF, + LINE_ENDING_NONE, +}; + +inline +bool +write_lines_file( + char const* path +, size_t num_lines +, size_t line_len +, line_ending_t ending +) +{ + std::ofstream out(path, std::ios::binary | std::ios::trunc); + + if (!out) + { + return false; + } + + std::string const line = make_payload(line_len); + + for (size_t i = 0; num_lines != i; ++i) + { + out.write(line.data(), static_cast(line.size())); + + if (LINE_ENDING_CRLF == ending) + { + out.write("\r\n", 2); + } + else if (LINE_ENDING_LF == ending) + { + out.put('\n'); + } + } + + return static_cast(out); +} + + +/* ///////////////////////////////////////////////////////////////////////// + * timing + */ + +struct run_result +{ + interval_t tm_ns; + std::uint64_t anchor; +#ifdef HAS_P99 + + p99::histogram hist; +#endif /* HAS_P99 */ +}; + +template +run_result +time_iterations( + size_t num_iterations +, size_t num_warm_loops +, F fn +) +{ + run_result result = {}; + stopwatch_t sw; + + for (size_t w = num_warm_loops; 0 != w; --w) + { + result.anchor = 0; +#ifdef HAS_P99 + + result.hist.clear(); +#endif /* HAS_P99 */ + interval_t tm_ns = 0; +#ifdef HAS_P99 + + for (size_t i = 0; num_iterations != i; ++i) + { + sw.start(); + result.anchor += fn(); + sw.stop(); + + interval_t const sample = sw.get_nanoseconds(); + + tm_ns += sample; + (void)result.hist.push_ns(static_cast(sample)); + } +#else /* ? HAS_P99 */ + + sw.start(); + for (size_t i = 0; num_iterations != i; ++i) + { + result.anchor += fn(); + } + sw.stop(); + tm_ns = sw.get_nanoseconds(); +#endif /* HAS_P99 */ + + if (1 == w) + { + result.tm_ns = tm_ns; + } + } + + return result; +} + + /* ///////////////////////////////////////////////////////////////////////// * display */ @@ -186,7 +301,7 @@ display_banner( << ", CSTRING_PERF_FILE_TRIALS" << ", SIS_PERFTESTS_GROUPGAPS." #else /* ? HAS_P99 */ - << " p99: not linked — mean ns/op only; file_lines suite skipped." + << " p99: not linked — mean ns/op only; filesystem suites skipped." << std::endl << " Env: CSTRING_PERF_ITERATIONS, CSTRING_PERF_WARMUPS" << ", SIS_PERFTESTS_GROUPGAPS." @@ -277,7 +392,7 @@ maybe_emit_group_gap( static bool have_prev = false; static std::string prev_scenario; - static size_t prev_size = 0; + static size_t prev_size = 0; if (have_prev && ( prev_scenario != scenario || @@ -417,6 +532,63 @@ ratio_or_dash( return static_cast(subject_ns) / static_cast(baseline_ns); } +inline +bool +impl_is_baseline( + char const* impl +, char const* const* baseline_impls +, size_t num_baselines +) +{ + for (size_t i = 0; num_baselines != i; ++i) + { + if (0 == ::strcmp(impl, baseline_impls[i])) + { + return true; + } + } + + return false; +} + +/* Baseline impl names are ratio 1.0. Every other impl is timed against + * baseline_ns. + */ +inline +void +emit_row( + char const* scenario +, size_t size +, char const* impl +, size_t num_iterations +, size_t num_actions +, run_result const& r +, interval_t baseline_ns +, char const* const* baseline_impls +, size_t num_baselines +) +{ + double const ratio = + impl_is_baseline(impl, baseline_impls, num_baselines) + ? 1.0 + : ratio_or_dash(r.tm_ns, baseline_ns) + ; + + display_results( + scenario + , size + , impl + , num_iterations + , num_actions + , r.tm_ns + , ratio + , r.anchor +#ifdef HAS_P99 + , &r.hist +#endif /* HAS_P99 */ + ); +} + /* ///////////////////////////////////////////////////////////////////////// * hand-rolled string (competitive floor) diff --git a/test/performance/test.performance.cstring/CMakeLists.txt b/test/performance/test.performance.cstring/CMakeLists.txt deleted file mode 100644 index 96b4531..0000000 --- a/test/performance/test.performance.cstring/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -list(APPEND X_MSVC_CUSTOM_WARNINGS_TO_BE_SUPPRESSED - 4996 -) - -define_performance_test_program(test.performance.cstring main.cpp) diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 5fb1b6f..ecb3f12 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -1,12 +1,13 @@ +add_subdirectory(auto-buffer) +add_subdirectory(cstring) +add_subdirectory(cstring_vector) if(NOT NO_CSTRING_CPP_API) - add_subdirectory(test.unit.cstring.1) + add_subdirectory(cstring.cxx) endif() -add_subdirectory(test.unit.cstring.2) -add_subdirectory(test.unit.cstring.auto_buffer) -add_subdirectory(test.unit.cstring.cstring_getStatusCodeString) -add_subdirectory(test.unit.cstring.cstring_vector) +add_subdirectory(insert-replace) +add_subdirectory(status-codes) # ############################## end of file ############################# # diff --git a/test/unit/test.unit.cstring.2/CMakeLists.txt b/test/unit/auto-buffer/CMakeLists.txt similarity index 60% rename from test/unit/test.unit.cstring.2/CMakeLists.txt rename to test/unit/auto-buffer/CMakeLists.txt index 1912881..57a2d51 100644 --- a/test/unit/test.unit.cstring.2/CMakeLists.txt +++ b/test/unit/auto-buffer/CMakeLists.txt @@ -1,2 +1,2 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten -define_automated_test_program(test_unit_cstring_2 entry.c) +define_automated_test_program(test.unit.auto-buffer entry.c) diff --git a/test/unit/auto-buffer/entry.c b/test/unit/auto-buffer/entry.c new file mode 100644 index 0000000..c301667 --- /dev/null +++ b/test/unit/auto-buffer/entry.c @@ -0,0 +1,506 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: test/unit/auto-buffer/entry.c + * + * Purpose: Tests borrowed string functionality. + * + * Created: 28th July 2011 + * Updated: 4th October 2026 + * + * ////////////////////////////////////////////////////////////////////// */ + + +/* ///////////////////////////////////////////////////////////////////////// + * includes + */ + +/* ///////////////////////////////////// + * test component header file include(s) + */ + +#include + +/* ///////////////////////////////////// + * general includes + */ + +/* xTests header files */ +#include + +/* STLSoft header files */ +#include + +/* Standard C header files */ +#include + + +/* ///////////////////////////////////////////////////////////////////////// + * forward declarations + */ + +static void TEST_cstring_createEx_FLAG_COMBINATIONS(void); +static void TEST_cstring_createEx_GROW_TO_HEAP(void); +static void TEST_cstring_createLenEx_GROW_TO_HEAP(void); +static void TEST_cstring_setCapacity_GROW_TO_HEAP(void); +static void TEST_cstring_assign_GROW_TO_HEAP(void); +static void TEST_cstring_assignLen_GROW_TO_HEAP(void); +static void TEST_cstring_append_GROW_TO_HEAP(void); +static void TEST_cstring_appendLen_GROW_TO_HEAP(void); + + +/* ///////////////////////////////////////////////////////////////////////// + * constants & definitions + */ + + +/* ///////////////////////////////////////////////////////////////////////// + * compiler compatibility + */ + +#if defined(STLSOFT_COMPILER_IS_MSVC) +# if _MSC_VER >= 1200 +# pragma warning(push) +# endif /* compiler */ +# pragma warning(disable : 4702) +#endif /* compiler */ + + +/* ///////////////////////////////////////////////////////////////////////// + * main + */ + +int main(int argc, char **argv) +{ + int retCode = EXIT_SUCCESS; + int verbosity = 2; + + XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); + + if (XTESTS_START_RUNNER("test.unit.auto-buffer", verbosity)) + { + XTESTS_RUN_CASE(TEST_cstring_createEx_FLAG_COMBINATIONS); + XTESTS_RUN_CASE(TEST_cstring_createEx_GROW_TO_HEAP); + XTESTS_RUN_CASE(TEST_cstring_createLenEx_GROW_TO_HEAP); + XTESTS_RUN_CASE(TEST_cstring_setCapacity_GROW_TO_HEAP); + XTESTS_RUN_CASE(TEST_cstring_assign_GROW_TO_HEAP); + XTESTS_RUN_CASE(TEST_cstring_assignLen_GROW_TO_HEAP); + XTESTS_RUN_CASE(TEST_cstring_append_GROW_TO_HEAP); + XTESTS_RUN_CASE(TEST_cstring_appendLen_GROW_TO_HEAP); + + XTESTS_PRINT_RESULTS(); + + XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode); + } + + return retCode; +} + + +/* ///////////////////////////////////////////////////////////////////////// + * compiler compatibility + */ + +#if defined(STLSOFT_COMPILER_IS_MSVC) +# if _MSC_VER >= 1200 +# pragma warning(pop) +# endif /* compiler */ +#endif /* compiler */ + + +/* ///////////////////////////////////////////////////////////////////////// + * test function implementations + */ + +static void TEST_cstring_createEx_FLAG_COMBINATIONS(void) +{ + static int const goods[] = + { + CSTRING_F_MEMORY_IS_BORROWED + , CSTRING_F_MEMORY_IS_BORROWED | CSTRING_F_MEMORY_IS_FIXED + , CSTRING_F_MEMORY_IS_BORROWED | CSTRING_F_MEMORY_CAN_GROW_TO_HEAP + }; + + static int const bads[] = + { + 0 + , CSTRING_F_MEMORY_IS_FIXED + , CSTRING_F_MEMORY_IS_OFFSET + }; + + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(goods); ++i) + { + int const flags = 0 + | goods[i] + ; + char buff[10]; + cstring_t cs; + CSTRING_RC rc; + + rc = cstring_createEx(&cs, "string: ", flags, buff, sizeof(buff)); + + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc); + }} + + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(bads); ++i) + { + int const flags = 0 + | bads[i] + ; + char buff[10]; + cstring_t cs; + CSTRING_RC rc; + + rc = cstring_createEx(&cs, "string: ", flags, buff, sizeof(buff)); + + TEST_ENUM_NE(CSTRING_RC_SUCCESS, rc); + }} +} + +static void TEST_cstring_createEx_GROW_TO_HEAP(void) +{ + int const flags = 0 + | CSTRING_F_MEMORY_IS_BORROWED + | CSTRING_F_MEMORY_CAN_GROW_TO_HEAP + ; + char buff[8]; + cstring_t cs; + CSTRING_RC rc; + + + /* must succeed in stack buffer. */ + + rc = cstring_createEx(&cs, "small", flags, buff, sizeof(buff)); + + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc); + TEST_INT_EQ(5u, cs.len); + TEST_INT_EQ(sizeof(buff) - 1u, cs.capacity); + + cstring_destroy(&cs); + + + /* try to resize. */ + + rc = cstring_createEx(&cs, "oversized", flags, buff, sizeof(buff)); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_ENUM_EQ(CSTRING_RC_OUTOFMEMORY, rc); + TEST_INT_EQ(flags, cs.flags); + TEST_INT_EQ(5u, cs.len); + TEST_PTR_EQ(buff, cs.ptr); + TEST_MS_EQ("small", cs.ptr); + TEST_INT_EQ(sizeof(buff) - 1u, cs.capacity); + } + else + { + TEST_INT_EQ(0, cs.flags); + TEST_MS_EQ("oversized", cs.ptr); + TEST_PTR_NE(buff, cs.ptr); + + cstring_destroy(&cs); + } +} + +static void TEST_cstring_createLenEx_GROW_TO_HEAP(void) +{ + int const flags = 0 + | CSTRING_F_MEMORY_IS_BORROWED + | CSTRING_F_MEMORY_CAN_GROW_TO_HEAP + ; + char buff[8]; + cstring_t cs; + CSTRING_RC rc; + + + /* must succeed in stack buffer. */ + + rc = cstring_createEx(&cs, "small", flags, buff, sizeof(buff)); + + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc); + TEST_INT_EQ(5u, cs.len); + TEST_INT_EQ(sizeof(buff) - 1u, cs.capacity); + + cstring_destroy(&cs); + + + /* try to resize. */ + + rc = cstring_createLenEx(&cs, "oversized string", 9, flags, buff, sizeof(buff)); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_ENUM_EQ(CSTRING_RC_OUTOFMEMORY, rc); + TEST_INT_EQ(flags, cs.flags); + TEST_INT_EQ(5u, cs.len); + TEST_PTR_EQ(buff, cs.ptr); + TEST_MS_EQ("small", cs.ptr); + TEST_INT_EQ(sizeof(buff) - 1u, cs.capacity); + } + else + { + TEST_INT_EQ(0, cs.flags); + TEST_MS_EQ("oversized", cs.ptr); + TEST_PTR_NE(buff, cs.ptr); + + cstring_destroy(&cs); + } +} + +static void TEST_cstring_setCapacity_GROW_TO_HEAP(void) +{ + int const flags = 0 + | CSTRING_F_MEMORY_IS_BORROWED + | CSTRING_F_MEMORY_CAN_GROW_TO_HEAP + ; + char buff[8]; + cstring_t cs; + CSTRING_RC rc; + + + /* must succeed in stack buffer. */ + + rc = cstring_createEx(&cs, "small", flags, buff, sizeof(buff)); + + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc); + TEST_INT_EQ(5u, cs.len); + TEST_INT_EQ(sizeof(buff) - 1u, cs.capacity); + + + /* try to resize. */ + + rc = cstring_setCapacity(&cs, 9); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_ENUM_EQ(CSTRING_RC_OUTOFMEMORY, rc); + TEST_INT_EQ(flags, cs.flags); + TEST_INT_EQ(5u, cs.len); + TEST_PTR_EQ(buff, cs.ptr); + TEST_MS_EQ("small", cs.ptr); + TEST_INT_EQ(sizeof(buff) - 1u, cs.capacity); + } + else + { + TEST_INT_EQ(0, cs.flags); + TEST_INT_EQ(5u, cs.len); + TEST_PTR_NE(buff, cs.ptr); + TEST_MS_EQ("small", cs.ptr); + TEST_INT_GE(9u - 1u, cs.capacity); + } + + cstring_destroy(&cs); +} + +static void TEST_cstring_assign_GROW_TO_HEAP(void) +{ + int const flags = 0 + | CSTRING_F_MEMORY_IS_BORROWED + | CSTRING_F_MEMORY_CAN_GROW_TO_HEAP + ; + char buff[8]; + cstring_t cs; + CSTRING_RC rc; + + + /* must succeed in stack buffer. */ + + rc = cstring_createEx(&cs, "small", flags, buff, sizeof(buff)); + + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc); + TEST_INT_EQ(5u, cs.len); + TEST_INT_EQ(sizeof(buff) - 1u, cs.capacity); + + + /* try to resize. */ + + rc = cstring_assign(&cs, "oversized"); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_ENUM_EQ(CSTRING_RC_OUTOFMEMORY, rc); + TEST_INT_EQ(flags, cs.flags); + TEST_INT_EQ(5u, cs.len); + TEST_PTR_EQ(buff, cs.ptr); + TEST_MS_EQ("small", cs.ptr); + TEST_INT_EQ(sizeof(buff) - 1u, cs.capacity); + } + else + { + TEST_INT_EQ(0, cs.flags); + TEST_MS_EQ("oversized", cs.ptr); + TEST_PTR_NE(buff, cs.ptr); + + + /* shrink again */ + rc = cstring_assign(&cs, "small"); + + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc); + TEST_INT_EQ(0, cs.flags); + TEST_INT_EQ(5u, cs.len); + TEST_PTR_NE(buff, cs.ptr); + TEST_MS_EQ("small", cs.ptr); + } + + cstring_destroy(&cs); +} + +static void TEST_cstring_assignLen_GROW_TO_HEAP(void) +{ + int const flags = 0 + | CSTRING_F_MEMORY_IS_BORROWED + | CSTRING_F_MEMORY_CAN_GROW_TO_HEAP + ; + char buff[8]; + cstring_t cs; + CSTRING_RC rc; + + + /* must succeed in stack buffer. */ + + rc = cstring_createEx(&cs, "small", flags, buff, sizeof(buff)); + + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc); + TEST_INT_EQ(5u, cs.len); + TEST_INT_EQ(sizeof(buff) - 1u, cs.capacity); + + + /* try to resize. */ + + rc = cstring_assignLen(&cs, "oversized", 9); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_ENUM_EQ(CSTRING_RC_OUTOFMEMORY, rc); + TEST_INT_EQ(flags, cs.flags); + TEST_INT_EQ(5u, cs.len); + TEST_PTR_EQ(buff, cs.ptr); + TEST_MS_EQ("small", cs.ptr); + TEST_INT_EQ(sizeof(buff) - 1u, cs.capacity); + } + else + { + TEST_INT_EQ(0, cs.flags); + TEST_MS_EQ("oversized", cs.ptr); + TEST_PTR_NE(buff, cs.ptr); + + + /* shrink again */ + rc = cstring_assign(&cs, "small"); + + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc); + TEST_INT_EQ(0, cs.flags); + TEST_INT_EQ(5u, cs.len); + TEST_PTR_NE(buff, cs.ptr); + TEST_MS_EQ("small", cs.ptr); + } + + cstring_destroy(&cs); +} + +static void TEST_cstring_append_GROW_TO_HEAP(void) +{ + int const flags = 0 + | CSTRING_F_MEMORY_IS_BORROWED + | CSTRING_F_MEMORY_CAN_GROW_TO_HEAP + ; + char buff[8]; + cstring_t cs; + CSTRING_RC rc; + + + /* must succeed in stack buffer. */ + + rc = cstring_createEx(&cs, "small", flags, buff, sizeof(buff)); + + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc); + TEST_INT_EQ(5u, cs.len); + TEST_INT_EQ(sizeof(buff) - 1u, cs.capacity); + + + /* try to resize. */ + + rc = cstring_append(&cs, "ish, but big enough"); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_ENUM_EQ(CSTRING_RC_OUTOFMEMORY, rc); + TEST_INT_EQ(flags, cs.flags); + TEST_INT_EQ(5u, cs.len); + TEST_PTR_EQ(buff, cs.ptr); + TEST_MS_EQ("small", cs.ptr); + TEST_INT_EQ(sizeof(buff) - 1u, cs.capacity); + } + else + { + TEST_INT_EQ(0, cs.flags); + TEST_MS_EQ("smallish, but big enough", cs.ptr); + TEST_PTR_NE(buff, cs.ptr); + + + /* shrink again */ + rc = cstring_assign(&cs, "small"); + + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc); + TEST_INT_EQ(0, cs.flags); + TEST_INT_EQ(5u, cs.len); + TEST_PTR_NE(buff, cs.ptr); + TEST_MS_EQ("small", cs.ptr); + } + + cstring_destroy(&cs); +} + +static void TEST_cstring_appendLen_GROW_TO_HEAP(void) +{ + int const flags = 0 + | CSTRING_F_MEMORY_IS_BORROWED + | CSTRING_F_MEMORY_CAN_GROW_TO_HEAP + ; + char buff[8]; + cstring_t cs; + CSTRING_RC rc; + + + /* must succeed in stack buffer. */ + + rc = cstring_createEx(&cs, "small", flags, buff, sizeof(buff)); + + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc); + TEST_INT_EQ(5u, cs.len); + TEST_INT_EQ(sizeof(buff) - 1u, cs.capacity); + + + /* try to resize. */ + + rc = cstring_appendLen(&cs, "ish, but big enough", 19); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_ENUM_EQ(CSTRING_RC_OUTOFMEMORY, rc); + TEST_INT_EQ(flags, cs.flags); + TEST_INT_EQ(5u, cs.len); + TEST_PTR_EQ(buff, cs.ptr); + TEST_MS_EQ("small", cs.ptr); + TEST_INT_EQ(sizeof(buff) - 1u, cs.capacity); + } + else + { + TEST_INT_EQ(0, cs.flags); + TEST_MS_EQ("smallish, but big enough", cs.ptr); + TEST_PTR_NE(buff, cs.ptr); + + + /* shrink again */ + rc = cstring_assign(&cs, "small"); + + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc); + TEST_INT_EQ(0, cs.flags); + TEST_INT_EQ(5u, cs.len); + TEST_PTR_NE(buff, cs.ptr); + TEST_MS_EQ("small", cs.ptr); + } + + cstring_destroy(&cs); +} + + +/* ///////////////////////////// end of file //////////////////////////// */ diff --git a/test/unit/test.unit.cstring.1/CMakeLists.txt b/test/unit/cstring.cxx/CMakeLists.txt similarity index 59% rename from test/unit/test.unit.cstring.1/CMakeLists.txt rename to test/unit/cstring.cxx/CMakeLists.txt index f8fb9c2..0a5aa85 100644 --- a/test/unit/test.unit.cstring.1/CMakeLists.txt +++ b/test/unit/cstring.cxx/CMakeLists.txt @@ -1,2 +1,2 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten -define_automated_test_program(test_unit_cstring_1 entry.cpp) +define_automated_test_program(test.unit.cstring.cxx entry.cpp) diff --git a/test/unit/test.unit.cstring.1/entry.cpp b/test/unit/cstring.cxx/entry.cpp similarity index 70% rename from test/unit/test.unit.cstring.1/entry.cpp rename to test/unit/cstring.cxx/entry.cpp index 214a071..9da555e 100644 --- a/test/unit/test.unit.cstring.1/entry.cpp +++ b/test/unit/cstring.cxx/entry.cpp @@ -1,10 +1,10 @@ /* ///////////////////////////////////////////////////////////////////////// - * File: test.unit.cstring.1/entry.cpp + * File: test/unit/cstring.cxx/entry.cpp * - * Purpose: Unit-tests for general functionality. + * Purpose: Unit-tests for cstring instance general functionality. * * Created: 23rd May 2009 - * Updated: 29th September 2026 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -36,6 +36,7 @@ /* STLSoft header files */ #ifdef WIN32 # include +# include #endif #include @@ -51,29 +52,27 @@ namespace { - static void test_1_0(void); - static void test_1_1(void); - static void test_1_2(void); - static void test_1_3(void); - static void test_1_4(void); - static void test_1_5(void); - static void test_1_6(void); - static void test_1_7(void); - static void test_1_8(void); - static void test_1_9(void); - - static void test_2_0(void); - static void test_2_1(void); - static void test_2_2(void); - static void test_2_3(void); - static void test_2_4(void); - static void test_2_5(void); - static void test_2_6(void); - static void test_2_7(void); - static void test_2_8(void); - static void test_2_9(void); - static void test_2_10(void); - static void test_2_11(void); + static void TEST_cstring_t_DEFAULT(); + static void TEST_cstring_init(); + static void TEST_cstring_setCapacity(); + static void TEST_cstring_assign_AND_cstring_create_AND_cstring_createLen_NULL_AND_EMPTY(); + static void TEST_cstring_assign_AND_cstring_create(); + static void TEST_cstring_assignLen_AND_cstring_createLen(); + static void TEST_cstring_append_AND_cstring_appendLen(); + static void TEST_cstring_truncate(); + static void TEST_cstring_appendLen_KEEPS_CAPACITY(); + + static void TEST_cstring_init_AND_cstring_destroy(); + static void TEST_cstring_create(); + static void TEST_cstring_createLen(); + static void TEST_cstring_createEx(); + static void TEST_cstring_createLenEx(); + static void TEST_cstring_assignLen_KEEPS_CAPACITY(); + static void TEST_cstring_assign_KEEPS_CAPACITY(); + static void TEST_cstring_copy(); + static void TEST_cstring_yield(); + static void TEST_cstring_yield2(); + static void TEST_cstring_swap(); } // anonymous namespace @@ -81,8 +80,6 @@ namespace * constants & definitions */ -const char TEST_FILE_NAME[] = "test.unit.cstring.1.txt"; - const size_t APPEND_ITERATIONS = 1000000u; const size_t ASSIGN_ITERATIONS = 100000u; @@ -108,41 +105,43 @@ int main(int argc, char **argv) int retCode = EXIT_SUCCESS; int verbosity = 2; +#ifdef _WIN32 + + /* CoTaskMem* / CoGetMalloc require COM on this thread. */ + comstl::com_init init; +#endif /* Windows */ + XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); - if (XTESTS_START_RUNNER("test.unit.cstring.1", verbosity)) + if (XTESTS_START_RUNNER("test.unit.cstring.cxx", verbosity)) { - XTESTS_RUN_CASE(test_1_0); - XTESTS_RUN_CASE(test_1_1); - XTESTS_RUN_CASE(test_1_2); - XTESTS_RUN_CASE(test_1_3); - XTESTS_RUN_CASE(test_1_4); - XTESTS_RUN_CASE(test_1_5); - XTESTS_RUN_CASE(test_1_6); - XTESTS_RUN_CASE(test_1_7); - XTESTS_RUN_CASE(test_1_8); - XTESTS_RUN_CASE(test_1_9); - - XTESTS_RUN_CASE(test_2_0); - XTESTS_RUN_CASE(test_2_1); - XTESTS_RUN_CASE(test_2_2); - XTESTS_RUN_CASE(test_2_3); - XTESTS_RUN_CASE(test_2_4); - XTESTS_RUN_CASE(test_2_5); - XTESTS_RUN_CASE(test_2_6); - XTESTS_RUN_CASE(test_2_7); - XTESTS_RUN_CASE(test_2_8); - XTESTS_RUN_CASE(test_2_9); - XTESTS_RUN_CASE(test_2_10); - XTESTS_RUN_CASE(test_2_11); + XTESTS_RUN_CASE(TEST_cstring_t_DEFAULT); + XTESTS_RUN_CASE(TEST_cstring_init); + XTESTS_RUN_CASE(TEST_cstring_setCapacity); + XTESTS_RUN_CASE(TEST_cstring_assign_AND_cstring_create_AND_cstring_createLen_NULL_AND_EMPTY); + XTESTS_RUN_CASE(TEST_cstring_assign_AND_cstring_create); + XTESTS_RUN_CASE(TEST_cstring_assignLen_AND_cstring_createLen); + XTESTS_RUN_CASE(TEST_cstring_append_AND_cstring_appendLen); + XTESTS_RUN_CASE(TEST_cstring_truncate); + XTESTS_RUN_CASE(TEST_cstring_appendLen_KEEPS_CAPACITY); + + XTESTS_RUN_CASE(TEST_cstring_init_AND_cstring_destroy); + XTESTS_RUN_CASE(TEST_cstring_create); + XTESTS_RUN_CASE(TEST_cstring_createLen); + XTESTS_RUN_CASE(TEST_cstring_createEx); + XTESTS_RUN_CASE(TEST_cstring_createLenEx); + XTESTS_RUN_CASE(TEST_cstring_assignLen_KEEPS_CAPACITY); + XTESTS_RUN_CASE(TEST_cstring_assign_KEEPS_CAPACITY); + XTESTS_RUN_CASE(TEST_cstring_copy); + XTESTS_RUN_CASE(TEST_cstring_yield); + XTESTS_RUN_CASE(TEST_cstring_yield2); + XTESTS_RUN_CASE(TEST_cstring_swap); XTESTS_PRINT_RESULTS(); XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode); } - STLSOFT_SUPPRESS_UNUSED(TEST_FILE_NAME); - return retCode; } @@ -159,22 +158,31 @@ int main(int argc, char **argv) /* ///////////////////////////////////////////////////////////////////////// - * test function implementations + * compatibility */ #ifdef CSTRING_USE_WIDE_STRINGS # define CSTRING_T_(x) L ## x -# define XTESTS_TEST_STRING_EQUAL_ XTESTS_TEST_WIDE_STRING_EQUAL -# define XTESTS_TEST_STRING_EQUAL_N_ XTESTS_TEST_WIDE_STRING_EQUAL_N +# define TEST_STR_EQ_ TEST_WS_EQ +# define TEST_STR_EQ_N_ TEST_WS_EQ_N #else /* ? CSTRING_USE_WIDE_STRINGS */ # define CSTRING_T_(x) x -# define XTESTS_TEST_STRING_EQUAL_ XTESTS_TEST_MULTIBYTE_STRING_EQUAL -# define XTESTS_TEST_STRING_EQUAL_N_ XTESTS_TEST_MULTIBYTE_STRING_EQUAL_N +# define TEST_STR_EQ_ TEST_MS_EQ +# define TEST_STR_EQ_N_ TEST_MS_EQ_N #endif /* CSTRING_USE_WIDE_STRINGS */ +/* ///////////////////////////////////////////////////////////////////////// + * helper functions + */ + + +/* ///////////////////////////////////////////////////////////////////////// + * test function implementations + */ + namespace { @@ -187,7 +195,7 @@ namespace static const cstring_char_t alphabet[] = CSTRING_T_("abcdefghijklmnopqrstuvwxyz"); -static void test_1_0() +static void TEST_cstring_t_DEFAULT() { cstring_t str = cstring_t_DEFAULT; @@ -197,7 +205,7 @@ static void test_1_0() TEST_INT_GE(str.len, str.capacity); } -static void test_1_1() +static void TEST_cstring_init() { cstring_t str; @@ -209,14 +217,14 @@ static void test_1_1() TEST_INT_GE(str.len, str.capacity); } -static void test_1_2() +static void TEST_cstring_setCapacity() { cstring_t str = cstring_t_DEFAULT; CSTRING_RC rc = cstring_setCapacity(&str, 10); if (CSTRING_RC_SUCCESS != rc) { - XTESTS_TEST_FAIL_WITH_QUALIFIER("could not set capacity", cstring_getStatusCodeString(rc)); + TEST_FAIL_WITH_QUALIFIER("could not set capacity", cstring_getStatusCodeString(rc)); } else { @@ -234,7 +242,7 @@ static void test_1_2() } } -static void test_1_3() +static void TEST_cstring_assign_AND_cstring_create_AND_cstring_createLen_NULL_AND_EMPTY() { { cstring_t str = cstring_t_DEFAULT; @@ -242,15 +250,14 @@ static void test_1_3() if (CSTRING_RC_SUCCESS != rc) { - XTESTS_TEST_FAIL_WITH_QUALIFIER("could not assign", cstring_getStatusCodeString(rc)); + TEST_FAIL_WITH_QUALIFIER("could not assign", cstring_getStatusCodeString(rc)); } else { TEST_INT_EQ(0u, str.len); - TEST_PTR_NE(NULL, str.ptr); - XTESTS_TEST_STRING_EQUAL_(CSTRING_T_(""), str.ptr); - TEST_INT_GT(0u, str.capacity); - TEST_INT_GE(str.len, str.capacity); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); cstring_destroy(&str); @@ -267,15 +274,14 @@ static void test_1_3() if (CSTRING_RC_SUCCESS != rc) { - XTESTS_TEST_FAIL_WITH_QUALIFIER("could not assign", cstring_getStatusCodeString(rc)); + TEST_FAIL_WITH_QUALIFIER("could not assign", cstring_getStatusCodeString(rc)); } else { TEST_INT_EQ(0u, str.len); - TEST_PTR_NE(NULL, str.ptr); - XTESTS_TEST_STRING_EQUAL_(CSTRING_T_(""), str.ptr); - TEST_INT_GT(0u, str.capacity); - TEST_INT_GE(str.len, str.capacity); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); cstring_destroy(&str); @@ -292,15 +298,14 @@ static void test_1_3() if (CSTRING_RC_SUCCESS != rc) { - XTESTS_TEST_FAIL_WITH_QUALIFIER("could not create", cstring_getStatusCodeString(rc)); + TEST_FAIL_WITH_QUALIFIER("could not create", cstring_getStatusCodeString(rc)); } else { TEST_INT_EQ(0u, str.len); - TEST_PTR_NE(NULL, str.ptr); - XTESTS_TEST_STRING_EQUAL_(CSTRING_T_(""), str.ptr); - TEST_INT_GT(0u, str.capacity); - TEST_INT_GE(str.len, str.capacity); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); cstring_destroy(&str); @@ -317,15 +322,14 @@ static void test_1_3() if (CSTRING_RC_SUCCESS != rc) { - XTESTS_TEST_FAIL_WITH_QUALIFIER("could not create", cstring_getStatusCodeString(rc)); + TEST_FAIL_WITH_QUALIFIER("could not create", cstring_getStatusCodeString(rc)); } else { TEST_INT_EQ(0u, str.len); - TEST_PTR_NE(NULL, str.ptr); - XTESTS_TEST_STRING_EQUAL_(CSTRING_T_(""), str.ptr); - TEST_INT_GT(0u, str.capacity); - TEST_INT_GE(str.len, str.capacity); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); cstring_destroy(&str); @@ -342,15 +346,14 @@ static void test_1_3() if (CSTRING_RC_SUCCESS != rc) { - XTESTS_TEST_FAIL_WITH_QUALIFIER("could not create (with length)", cstring_getStatusCodeString(rc)); + TEST_FAIL_WITH_QUALIFIER("could not create (with length)", cstring_getStatusCodeString(rc)); } else { TEST_INT_EQ(0u, str.len); - TEST_PTR_NE(NULL, str.ptr); - XTESTS_TEST_STRING_EQUAL_(CSTRING_T_(""), str.ptr); - TEST_INT_GT(0u, str.capacity); - TEST_INT_GE(str.len, str.capacity); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); cstring_destroy(&str); @@ -367,15 +370,14 @@ static void test_1_3() if (CSTRING_RC_SUCCESS != rc) { - XTESTS_TEST_FAIL_WITH_QUALIFIER("could not create (with length)", cstring_getStatusCodeString(rc)); + TEST_FAIL_WITH_QUALIFIER("could not create (with length)", cstring_getStatusCodeString(rc)); } else { TEST_INT_EQ(0u, str.len); - TEST_PTR_NE(NULL, str.ptr); - XTESTS_TEST_STRING_EQUAL_(CSTRING_T_(""), str.ptr); - TEST_INT_GT(0u, str.capacity); - TEST_INT_GE(str.len, str.capacity); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); cstring_destroy(&str); @@ -392,19 +394,19 @@ static void test_1_3() if (CSTRING_RC_SUCCESS != rc) { - XTESTS_TEST_FAIL_WITH_QUALIFIER("could not create (with length)", cstring_getStatusCodeString(rc)); + TEST_FAIL_WITH_QUALIFIER("could not create (with length)", cstring_getStatusCodeString(rc)); } else { TEST_INT_EQ(5u, str.len); TEST_PTR_NE(NULL, str.ptr); - XTESTS_TEST_STRING_EQUAL_(CSTRING_T_(""), str.ptr); - XTESTS_TEST_CHARACTER_EQUAL('\0', str.ptr[0]); - XTESTS_TEST_CHARACTER_EQUAL('\0', str.ptr[1]); - XTESTS_TEST_CHARACTER_EQUAL('\0', str.ptr[2]); - XTESTS_TEST_CHARACTER_EQUAL('\0', str.ptr[3]); - XTESTS_TEST_CHARACTER_EQUAL('\0', str.ptr[4]); - XTESTS_TEST_CHARACTER_EQUAL('\0', str.ptr[5]); + TEST_STR_EQ_(CSTRING_T_(""), str.ptr); + TEST_CHAR_EQ('\0', str.ptr[0]); + TEST_CHAR_EQ('\0', str.ptr[1]); + TEST_CHAR_EQ('\0', str.ptr[2]); + TEST_CHAR_EQ('\0', str.ptr[3]); + TEST_CHAR_EQ('\0', str.ptr[4]); + TEST_CHAR_EQ('\0', str.ptr[5]); TEST_INT_GE(5u, str.capacity); TEST_INT_GE(str.len, str.capacity); @@ -423,19 +425,21 @@ static void test_1_3() if (CSTRING_RC_SUCCESS != rc) { - XTESTS_TEST_FAIL_WITH_QUALIFIER("could not create (with length)", cstring_getStatusCodeString(rc)); + TEST_FAIL_WITH_QUALIFIER("could not create (with length)", cstring_getStatusCodeString(rc)); } else { TEST_INT_EQ(5u, str.len); TEST_PTR_NE(NULL, str.ptr); - XTESTS_TEST_STRING_EQUAL_(CSTRING_T_(""), str.ptr); - XTESTS_TEST_CHARACTER_EQUAL('\0', str.ptr[0]); - XTESTS_TEST_CHARACTER_EQUAL('\0', str.ptr[1]); - XTESTS_TEST_CHARACTER_EQUAL('\0', str.ptr[2]); - XTESTS_TEST_CHARACTER_EQUAL('\0', str.ptr[3]); - XTESTS_TEST_CHARACTER_EQUAL('\0', str.ptr[4]); - XTESTS_TEST_CHARACTER_EQUAL('\0', str.ptr[5]); + TEST_STR_EQ_(CSTRING_T_(""), str.ptr); + TEST_CHAR_EQ('\0', str.ptr[0]); + /* + TEST_CHAR_EQ('\0', str.ptr[1]); + TEST_CHAR_EQ('\0', str.ptr[2]); + TEST_CHAR_EQ('\0', str.ptr[3]); + TEST_CHAR_EQ('\0', str.ptr[4]); + */ + TEST_CHAR_EQ('\0', str.ptr[5]); TEST_INT_GE(5u, str.capacity); TEST_INT_GE(str.len, str.capacity); @@ -449,7 +453,7 @@ static void test_1_3() } } -static void test_1_4() +static void TEST_cstring_assign_AND_cstring_create() { { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) { @@ -460,13 +464,13 @@ static void test_1_4() if (CSTRING_RC_SUCCESS != rc) { - XTESTS_TEST_FAIL_WITH_QUALIFIER("could not assign", cstring_getStatusCodeString(rc)); + TEST_FAIL_WITH_QUALIFIER("could not assign", cstring_getStatusCodeString(rc)); } else { TEST_INT_EQ(cch, str.len); TEST_PTR_NE(NULL, str.ptr); - XTESTS_TEST_STRING_EQUAL_(s, stlsoft::apply_const_ptr(str.ptr)); + TEST_STR_EQ_(s, stlsoft::apply_const_ptr(str.ptr)); TEST_INT_GE(cch, str.capacity); TEST_INT_GE(str.len, str.capacity); @@ -490,13 +494,13 @@ static void test_1_4() if (CSTRING_RC_SUCCESS != rc) { - XTESTS_TEST_FAIL_WITH_QUALIFIER("could not assign", cstring_getStatusCodeString(rc)); + TEST_FAIL_WITH_QUALIFIER("could not assign", cstring_getStatusCodeString(rc)); } else { TEST_INT_EQ(cch, str.len); TEST_PTR_NE(NULL, str.ptr); - XTESTS_TEST_STRING_EQUAL_(s, stlsoft::apply_const_ptr(str.ptr)); + TEST_STR_EQ_(s, stlsoft::apply_const_ptr(str.ptr)); TEST_INT_GT(cch, str.capacity); TEST_INT_GE(str.len, str.capacity); } @@ -519,13 +523,13 @@ static void test_1_4() if (CSTRING_RC_SUCCESS != rc) { - XTESTS_TEST_FAIL_WITH_QUALIFIER("could not create", cstring_getStatusCodeString(rc)); + TEST_FAIL_WITH_QUALIFIER("could not create", cstring_getStatusCodeString(rc)); } else { TEST_INT_EQ(cch, str.len); TEST_PTR_NE(NULL, str.ptr); - XTESTS_TEST_STRING_EQUAL_(s, stlsoft::apply_const_ptr(str.ptr)); + TEST_STR_EQ_(s, stlsoft::apply_const_ptr(str.ptr)); TEST_INT_GE(cch, str.capacity); TEST_INT_GE(str.len, str.capacity); @@ -539,7 +543,7 @@ static void test_1_4() }} } -static void test_1_5() +static void TEST_cstring_assignLen_AND_cstring_createLen() { { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) { @@ -550,15 +554,23 @@ static void test_1_5() if (CSTRING_RC_SUCCESS != rc) { - XTESTS_TEST_FAIL_WITH_QUALIFIER("could not assign (with length)", cstring_getStatusCodeString(rc)); + TEST_FAIL_WITH_QUALIFIER("could not assign (with length)", cstring_getStatusCodeString(rc)); } else { TEST_INT_EQ(cch, str.len); - TEST_PTR_NE(NULL, str.ptr); - XTESTS_TEST_STRING_EQUAL_N_(s, stlsoft::apply_const_ptr(str.ptr), int(i)); - TEST_INT_GE(cch, str.capacity); - TEST_INT_GE(str.len, str.capacity); + if (0u == cch) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_N_(s, stlsoft::apply_const_ptr(str.ptr), int(i)); + TEST_INT_GE(cch, str.capacity); + TEST_INT_GE(str.len, str.capacity); + } cstring_destroy(&str); @@ -580,15 +592,23 @@ static void test_1_5() if (CSTRING_RC_SUCCESS != rc) { - XTESTS_TEST_FAIL_WITH_QUALIFIER("could not assign (with length)", cstring_getStatusCodeString(rc)); + TEST_FAIL_WITH_QUALIFIER("could not assign (with length)", cstring_getStatusCodeString(rc)); } else { TEST_INT_EQ(cch, str.len); - TEST_PTR_NE(NULL, str.ptr); - XTESTS_TEST_STRING_EQUAL_N_(s, stlsoft::apply_const_ptr(str.ptr), int(i)); - TEST_INT_GE(cch, str.capacity); - TEST_INT_GE(str.len, str.capacity); + if (0u == cch) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_N_(s, stlsoft::apply_const_ptr(str.ptr), int(i)); + TEST_INT_GE(cch, str.capacity); + TEST_INT_GE(str.len, str.capacity); + } } }} @@ -609,15 +629,23 @@ static void test_1_5() if (CSTRING_RC_SUCCESS != rc) { - XTESTS_TEST_FAIL_WITH_QUALIFIER("could not create (with length)", cstring_getStatusCodeString(rc)); + TEST_FAIL_WITH_QUALIFIER("could not create (with length)", cstring_getStatusCodeString(rc)); } else { TEST_INT_EQ(cch, str.len); - TEST_PTR_NE(NULL, str.ptr); - XTESTS_TEST_STRING_EQUAL_N_(s, stlsoft::apply_const_ptr(str.ptr), int(i)); - TEST_INT_GE(cch, str.capacity); - TEST_INT_GE(str.len, str.capacity); + if (0u == cch) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_N_(s, stlsoft::apply_const_ptr(str.ptr), int(i)); + TEST_INT_GE(cch, str.capacity); + TEST_INT_GE(str.len, str.capacity); + } cstring_destroy(&str); @@ -629,7 +657,7 @@ static void test_1_5() }} } -static void test_1_6() +static void TEST_cstring_append_AND_cstring_appendLen() { { cstring_t str = cstring_t_DEFAULT; @@ -641,13 +669,13 @@ static void test_1_6() if (CSTRING_RC_SUCCESS != rc) { - XTESTS_TEST_FAIL_WITH_QUALIFIER("could not append", cstring_getStatusCodeString(rc)); + TEST_FAIL_WITH_QUALIFIER("could not append", cstring_getStatusCodeString(rc)); } else { TEST_INT_EQ(i + 1, str.len); TEST_PTR_NE(NULL, str.ptr); - XTESTS_TEST_STRING_EQUAL_N_(alphabet, str.ptr, int(i)); + TEST_STR_EQ_N_(alphabet, str.ptr, int(i)); TEST_INT_GE(i + 1, str.capacity); TEST_INT_GE(str.len, str.capacity); } @@ -670,13 +698,13 @@ static void test_1_6() if (CSTRING_RC_SUCCESS != rc) { - XTESTS_TEST_FAIL_WITH_QUALIFIER("could not append (with length)", cstring_getStatusCodeString(rc)); + TEST_FAIL_WITH_QUALIFIER("could not append (with length)", cstring_getStatusCodeString(rc)); } else { TEST_INT_EQ(i + 1, str.len); TEST_PTR_NE(NULL, str.ptr); - XTESTS_TEST_STRING_EQUAL_N_(alphabet, str.ptr, int(i)); + TEST_STR_EQ_N_(alphabet, str.ptr, int(i)); TEST_INT_GT(i, str.capacity); TEST_INT_GE(str.len, str.capacity); } @@ -691,24 +719,24 @@ static void test_1_6() } } -static void test_1_7() +static void TEST_cstring_truncate() { { cstring_t str = cstring_t_DEFAULT; - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, cstring_truncate(&str, 0u)); + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, cstring_truncate(&str, 0u)); TEST_INT_EQ(0u, str.len); TEST_INT_EQ(0u, str.capacity); - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, cstring_truncate(&str, 1u)); + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, cstring_truncate(&str, 1u)); TEST_INT_EQ(0u, str.len); TEST_INT_EQ(0u, str.capacity); - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, cstring_truncate(&str, 100u)); + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, cstring_truncate(&str, 100u)); TEST_INT_EQ(0u, str.len); TEST_INT_EQ(0u, str.capacity); - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, cstring_truncate(&str, 100000u)); + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, cstring_truncate(&str, 100000u)); TEST_INT_EQ(0u, str.len); TEST_INT_EQ(0u, str.capacity); @@ -729,7 +757,7 @@ static void test_1_7() { cstring_truncate(&str, i); - XTESTS_TEST_INTEGER_LESS_OR_EQUAL(26u, str.len); + TEST_INT_LE(26u, str.len); if (i < 26u) { @@ -752,7 +780,7 @@ static void test_1_7() } } -static void test_1_8() +static void TEST_cstring_appendLen_KEEPS_CAPACITY() { cstring_t str = cstring_t_DEFAULT; @@ -782,13 +810,8 @@ static void test_1_8() TEST_INT_EQ(0, str.flags); } -static void test_1_9() -{ -} - - -static void test_2_0() +static void TEST_cstring_init_AND_cstring_destroy() { cstring_t str; @@ -804,7 +827,7 @@ static void test_2_0() TEST_INT_EQ(0, str.flags); } -static void test_2_1() +static void TEST_cstring_create() { { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) { @@ -815,8 +838,17 @@ static void test_2_1() cstring_create(&str, s); TEST_INT_GE(i, str.len); - XTESTS_TEST_STRING_EQUAL_(s, str.ptr); - TEST_INT_GE(str.len, str.capacity); + if (0u == i) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + } cstring_destroy(&str); @@ -827,7 +859,7 @@ static void test_2_1() }} } -static void test_2_2() +static void TEST_cstring_createLen() { { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) { @@ -837,9 +869,18 @@ static void test_2_2() cstring_createLen(&str, s, i); - TEST_INT_GE(i, str.len); - XTESTS_TEST_STRING_EQUAL_(s, str.ptr); - TEST_INT_GE(str.len, str.capacity); + TEST_INT_EQ(i, str.len); + if (0u == i) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + } cstring_destroy(&str); @@ -850,7 +891,7 @@ static void test_2_2() }} } -static void test_2_3() +static void TEST_cstring_createEx() { { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) { @@ -862,8 +903,17 @@ static void test_2_3() cstring_createEx(&str, s, flags, NULL, 0); TEST_INT_GE(i, str.len); - XTESTS_TEST_STRING_EQUAL_(s, str.ptr); - TEST_INT_GE(str.len, str.capacity); + if (0u == str.len) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + } cstring_destroy(&str); @@ -884,16 +934,16 @@ static void test_2_3() rc = cstring_createEx(&str, s, flags, &buff[0] + 1, STLSOFT_NUM_ELEMENTS(buff) - 3); - XTESTS_TEST_CHARACTER_EQUAL(CSTRING_T_('['), buff[0]); - XTESTS_TEST_CHARACTER_EQUAL(CSTRING_T_(']'), buff[STLSOFT_NUM_ELEMENTS(buff) - 2]); - XTESTS_TEST_CHARACTER_EQUAL(CSTRING_T_('\0'), buff[STLSOFT_NUM_ELEMENTS(buff) - 1]); + TEST_CHAR_EQ(CSTRING_T_('['), buff[0]); + TEST_CHAR_EQ(CSTRING_T_(']'), buff[STLSOFT_NUM_ELEMENTS(buff) - 2]); + TEST_CHAR_EQ(CSTRING_T_('\0'), buff[STLSOFT_NUM_ELEMENTS(buff) - 1]); if (i < STLSOFT_NUM_ELEMENTS(buff) - 3) { - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc); + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc); TEST_INT_GE(i, str.len); - XTESTS_TEST_STRING_EQUAL_(s, str.ptr); + TEST_STR_EQ_(s, str.ptr); TEST_INT_GE(str.len, str.capacity); cstring_destroy(&str); @@ -905,7 +955,7 @@ static void test_2_3() } else { - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_EXCEEDBORROWEDCAPACITY, rc); + TEST_ENUM_EQ(CSTRING_RC_EXCEEDBORROWEDCAPACITY, rc); TEST_INT_EQ(0u, str.len); TEST_PTR_EQ(NULL, str.ptr); @@ -926,9 +976,18 @@ static void test_2_3() cstring_createEx(&str, s, flags, NULL, 0); TEST_INT_GE(i, str.len); - XTESTS_TEST_STRING_EQUAL_(s, str.ptr); - TEST_INT_GE(str.len, str.capacity); - TEST_INT_GE(str.capacity, ::GlobalSize(str.ptr)); + if (0u == str.len) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + TEST_INT_GE(str.capacity, ::GlobalSize(str.ptr)); + } cstring_destroy(&str); @@ -948,9 +1007,18 @@ static void test_2_3() cstring_createEx(&str, s, flags, NULL, 0); TEST_INT_GE(i, str.len); - XTESTS_TEST_STRING_EQUAL_(s, str.ptr); - TEST_INT_GE(str.len, str.capacity); - TEST_INT_GE(str.capacity, ::HeapSize(::GetProcessHeap(), 0, str.ptr)); + if (0u == str.len) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + TEST_INT_GE(str.capacity, ::HeapSize(::GetProcessHeap(), 0, str.ptr)); + } cstring_destroy(&str); @@ -970,9 +1038,18 @@ static void test_2_3() cstring_createEx(&str, s, flags, NULL, 0); TEST_INT_GE(i, str.len); - XTESTS_TEST_STRING_EQUAL_(s, str.ptr); - TEST_INT_GE(str.len, str.capacity); - TEST_INT_GE(str.capacity, comstl::CoTaskMemGetSize(str.ptr)); + if (0u == str.len) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + TEST_INT_GE(str.capacity, comstl::CoTaskMemGetSize(str.ptr)); + } cstring_destroy(&str); @@ -984,7 +1061,7 @@ static void test_2_3() #endif /* WIN32 */ } -static void test_2_4() +static void TEST_cstring_createLenEx() { { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) { @@ -995,9 +1072,18 @@ static void test_2_4() cstring_createLenEx(&str, s, i, flags, NULL, 0); - TEST_INT_GE(i, str.len); - XTESTS_TEST_STRING_EQUAL_(s, str.ptr); - TEST_INT_GE(str.len, str.capacity); + TEST_INT_EQ(i, str.len); + if (0u == i) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + } cstring_destroy(&str); @@ -1018,16 +1104,16 @@ static void test_2_4() rc = cstring_createLenEx(&str, s, i, flags, &buff[0] + 1, STLSOFT_NUM_ELEMENTS(buff) - 3); - XTESTS_TEST_CHARACTER_EQUAL(CSTRING_T_('['), buff[0]); - XTESTS_TEST_CHARACTER_EQUAL(CSTRING_T_(']'), buff[STLSOFT_NUM_ELEMENTS(buff) - 2]); - XTESTS_TEST_CHARACTER_EQUAL(CSTRING_T_('\0'), buff[STLSOFT_NUM_ELEMENTS(buff) - 1]); + TEST_CHAR_EQ(CSTRING_T_('['), buff[0]); + TEST_CHAR_EQ(CSTRING_T_(']'), buff[STLSOFT_NUM_ELEMENTS(buff) - 2]); + TEST_CHAR_EQ(CSTRING_T_('\0'), buff[STLSOFT_NUM_ELEMENTS(buff) - 1]); if (i < STLSOFT_NUM_ELEMENTS(buff) - 3) { - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc); + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc); TEST_INT_GE(i, str.len); - XTESTS_TEST_STRING_EQUAL_(s, str.ptr); + TEST_STR_EQ_(s, str.ptr); TEST_INT_GE(str.len, str.capacity); cstring_destroy(&str); @@ -1039,7 +1125,7 @@ static void test_2_4() } else { - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_EXCEEDBORROWEDCAPACITY, rc); + TEST_ENUM_EQ(CSTRING_RC_EXCEEDBORROWEDCAPACITY, rc); TEST_INT_EQ(0u, str.len); TEST_PTR_EQ(NULL, str.ptr); @@ -1059,10 +1145,19 @@ static void test_2_4() cstring_createLenEx(&str, s, i, flags, NULL, 0); - TEST_INT_GE(i, str.len); - XTESTS_TEST_STRING_EQUAL_(s, str.ptr); - TEST_INT_GE(str.len, str.capacity); - TEST_INT_GE(str.capacity, ::GlobalSize(str.ptr)); + TEST_INT_EQ(i, str.len); + if (0u == i) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + TEST_INT_GE(str.capacity, ::GlobalSize(str.ptr)); + } cstring_destroy(&str); @@ -1081,10 +1176,19 @@ static void test_2_4() cstring_createLenEx(&str, s, i, flags, NULL, 0); - TEST_INT_GE(i, str.len); - XTESTS_TEST_STRING_EQUAL_(s, str.ptr); - TEST_INT_GE(str.len, str.capacity); - TEST_INT_GE(str.capacity, ::HeapSize(::GetProcessHeap(), 0, str.ptr)); + TEST_INT_EQ(i, str.len); + if (0u == i) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + TEST_INT_GE(str.capacity, ::HeapSize(::GetProcessHeap(), 0, str.ptr)); + } cstring_destroy(&str); @@ -1103,10 +1207,19 @@ static void test_2_4() cstring_createLenEx(&str, s, i, flags, NULL, 0); - TEST_INT_GE(i, str.len); - XTESTS_TEST_STRING_EQUAL_(s, str.ptr); - TEST_INT_GE(str.len, str.capacity); - TEST_INT_GE(str.capacity, comstl::CoTaskMemGetSize(str.ptr)); + TEST_INT_EQ(i, str.len); + if (0u == i) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + TEST_INT_GE(str.capacity, comstl::CoTaskMemGetSize(str.ptr)); + } cstring_destroy(&str); @@ -1118,7 +1231,7 @@ static void test_2_4() #endif /* WIN32 */ } -static void test_2_5() +static void TEST_cstring_assignLen_KEEPS_CAPACITY() { cstring_t str = cstring_t_DEFAULT; @@ -1142,7 +1255,7 @@ static void test_2_5() TEST_INT_EQ(0u, str.len); TEST_PTR_NE(NULL, str.ptr); - XTESTS_TEST_INTEGER_NOT_EQUAL(0u, str.capacity); + TEST_INT_NE(0u, str.capacity); TEST_INT_EQ(0, str.flags); }} @@ -1154,7 +1267,7 @@ static void test_2_5() TEST_INT_EQ(0, str.flags); } -static void test_2_6() +static void TEST_cstring_assign_KEEPS_CAPACITY() { cstring_t str = cstring_t_DEFAULT; @@ -1178,7 +1291,7 @@ static void test_2_6() TEST_INT_EQ(0u, str.len); TEST_PTR_NE(NULL, str.ptr); - XTESTS_TEST_INTEGER_NOT_EQUAL(0u, str.capacity); + TEST_INT_NE(0u, str.capacity); TEST_INT_EQ(0, str.flags); }} @@ -1190,7 +1303,7 @@ static void test_2_6() TEST_INT_EQ(0, str.flags); } -static void test_2_7() +static void TEST_cstring_copy() { { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) { @@ -1200,19 +1313,19 @@ static void test_2_7() cstring_t str2 = cstring_t_DEFAULT; CSTRING_RC rc = cstring_assign(&str1, s); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(cch, str1.len); TEST_PTR_NE(NULL, str1.ptr); - XTESTS_TEST_STRING_EQUAL_(s, stlsoft::apply_const_ptr(str1.ptr)); + TEST_STR_EQ_(s, stlsoft::apply_const_ptr(str1.ptr)); TEST_INT_GE(cch, str1.capacity); TEST_INT_GE(str1.len, str1.capacity); rc = cstring_copy(&str2, &str1); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(cch, str2.len); TEST_PTR_NE(NULL, str2.ptr); - XTESTS_TEST_STRING_EQUAL_(s, stlsoft::apply_const_ptr(str2.ptr)); + TEST_STR_EQ_(s, stlsoft::apply_const_ptr(str2.ptr)); TEST_INT_GE(cch, str2.capacity); TEST_INT_GE(str2.len, str2.capacity); @@ -1233,14 +1346,14 @@ static void test_2_7() }} } -static void test_2_8() +static void TEST_cstring_yield() { { cstring_t str = cstring_t_DEFAULT; cstring_char_t* p; CSTRING_RC rc = cstring_yield(&str, &p); - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc); + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc); TEST_PTR_EQ(NULL, stlsoft::apply_const_ptr(stlsoft::apply_const_ptr(str.ptr))); TEST_INT_EQ(0u, str.capacity); @@ -1264,10 +1377,10 @@ static void test_2_8() CSTRING_RC rc = cstring_assign(&str, s); cstring_char_t* p; - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(cch, str.len); TEST_PTR_NE(NULL, str.ptr); - XTESTS_TEST_STRING_EQUAL_(s, stlsoft::apply_const_ptr(str.ptr)); + TEST_STR_EQ_(s, stlsoft::apply_const_ptr(str.ptr)); TEST_INT_GE(cch, str.capacity); TEST_INT_GE(str.len, str.capacity); @@ -1275,13 +1388,13 @@ static void test_2_8() stlsoft::scoped_handle scoper(p, ::free); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_PTR_EQ(NULL, str.ptr); TEST_INT_EQ(0u, str.capacity); TEST_INT_EQ(0, str.flags); TEST_PTR_NE(NULL, p); - XTESTS_TEST_STRING_EQUAL_(s, stlsoft::apply_const_ptr(p)); + TEST_STR_EQ_(s, stlsoft::apply_const_ptr(p)); cstring_destroy(&str); @@ -1292,7 +1405,7 @@ static void test_2_8() }} } -static void test_2_9() +static void TEST_cstring_yield2() { { cstring_t str = cstring_t_DEFAULT; @@ -1300,7 +1413,7 @@ static void test_2_9() void* raw; CSTRING_RC rc = cstring_yield2(&str, &p, &raw); - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc); + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc); TEST_PTR_EQ(NULL, str.ptr); TEST_INT_EQ(0u, str.capacity); @@ -1325,10 +1438,10 @@ static void test_2_9() cstring_char_t* p; void* raw; - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(cch, str.len); TEST_PTR_NE(NULL, str.ptr); - XTESTS_TEST_STRING_EQUAL_(s, stlsoft::apply_const_ptr(str.ptr)); + TEST_STR_EQ_(s, stlsoft::apply_const_ptr(str.ptr)); TEST_INT_GE(cch, str.capacity); TEST_INT_GE(str.len, str.capacity); @@ -1336,13 +1449,13 @@ static void test_2_9() stlsoft::scoped_handle scoper(raw, ::free); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_PTR_EQ(NULL, str.ptr); TEST_INT_EQ(0u, str.capacity); TEST_INT_EQ(0, str.flags); TEST_PTR_NE(NULL, p); - XTESTS_TEST_STRING_EQUAL_(s, stlsoft::apply_const_ptr(p)); + TEST_STR_EQ_(s, stlsoft::apply_const_ptr(p)); cstring_destroy(&str); @@ -1353,7 +1466,7 @@ static void test_2_9() }} } -static void test_2_10() +static void TEST_cstring_swap() { { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) { @@ -1363,10 +1476,10 @@ static void test_2_10() cstring_t str2 = cstring_t_DEFAULT; CSTRING_RC rc = cstring_assign(&str1, s); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(cch, str1.len); TEST_PTR_NE(NULL, str1.ptr); - XTESTS_TEST_STRING_EQUAL_(s, stlsoft::apply_const_ptr(str1.ptr)); + TEST_STR_EQ_(s, stlsoft::apply_const_ptr(str1.ptr)); TEST_INT_GE(cch, str1.capacity); TEST_INT_GE(str1.len, str1.capacity); @@ -1377,7 +1490,7 @@ static void test_2_10() rc = cstring_swap(&str2, &str1); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(0u, str1.len); TEST_PTR_EQ(NULL, str1.ptr); TEST_INT_EQ(0u, str1.capacity); @@ -1385,7 +1498,7 @@ static void test_2_10() TEST_INT_EQ(cch, str2.len); TEST_PTR_NE(NULL, str2.ptr); - XTESTS_TEST_STRING_EQUAL_(s, stlsoft::apply_const_ptr(str2.ptr)); + TEST_STR_EQ_(s, stlsoft::apply_const_ptr(str2.ptr)); TEST_INT_GE(cch, str2.capacity); TEST_INT_GE(str2.len, str2.capacity); @@ -1405,10 +1518,6 @@ static void test_2_10() TEST_INT_EQ(0, str2.flags); }} } - -static void test_2_11() -{ -} } // anonymous namespace diff --git a/examples/c/example.c.auto_buffer/CMakeLists.txt b/test/unit/cstring/CMakeLists.txt similarity index 62% rename from examples/c/example.c.auto_buffer/CMakeLists.txt rename to test/unit/cstring/CMakeLists.txt index 8b26151..f622ed8 100644 --- a/examples/c/example.c.auto_buffer/CMakeLists.txt +++ b/test/unit/cstring/CMakeLists.txt @@ -1,2 +1,2 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten -define_example_program(example.c.auto_buffer main.c) +define_automated_test_program(test.unit.cstring entry.c) diff --git a/test/unit/cstring/entry.c b/test/unit/cstring/entry.c new file mode 100644 index 0000000..290dcba --- /dev/null +++ b/test/unit/cstring/entry.c @@ -0,0 +1,1544 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: test/unit/cstring/entry.c + * + * Purpose: Unit-tests for cstring instance general functionality. + * + * Created: 23rd May 2009 + * Updated: 4th October 2026 + * + * ////////////////////////////////////////////////////////////////////// */ + + +/* ///////////////////////////////////////////////////////////////////////// + * compatibility + */ + +#define CSTRING_OBSOLETE + + +/* ///////////////////////////////////////////////////////////////////////// + * includes + */ + +/* ///////////////////////////////////// + * test component header file include(s) + */ + +#include + +/* ///////////////////////////////////// + * general includes + */ + +/* xTests header files */ +#include + +/* STLSoft header files */ +#include +#ifdef _WIN32 +# include +# include +#endif /* _WIN32 */ + +/* Standard C header files */ +#include + + +/* ///////////////////////////////////////////////////////////////////////// + * forward declarations + */ + +static void TEST_cstring_t_DEFAULT(void); +static void TEST_cstring_init(void); +static void TEST_cstring_setCapacity(void); +static void TEST_cstring_assign_AND_cstring_create_AND_cstring_createLen_NULL_AND_EMPTY(void); +static void TEST_cstring_assign_AND_cstring_create(void); +static void TEST_cstring_assignLen_AND_cstring_createLen(void); +static void TEST_cstring_append_AND_cstring_appendLen(void); +static void TEST_cstring_truncate(void); +static void TEST_cstring_appendLen_KEEPS_CAPACITY(void); + +static void TEST_cstring_init_AND_cstring_destroy(void); +static void TEST_cstring_create(void); +static void TEST_cstring_createLen(void); +static void TEST_cstring_createEx(void); +static void TEST_cstring_createLenEx(void); +static void TEST_cstring_assignLen_KEEPS_CAPACITY(void); +static void TEST_cstring_assign_KEEPS_CAPACITY(void); +static void TEST_cstring_copy(void); +static void TEST_cstring_yield(void); +static void TEST_cstring_yield2(void); +static void TEST_cstring_swap(void); + + +/* ///////////////////////////////////////////////////////////////////////// + * constants & definitions + */ + +static size_t const APPEND_ITERATIONS = 1000000u; +static size_t const ASSIGN_ITERATIONS = 100000u; + + +/* ///////////////////////////////////////////////////////////////////////// + * compiler compatibility + */ + +#if defined(STLSOFT_COMPILER_IS_MSVC) +# if _MSC_VER >= 1200 +# pragma warning(push) +# endif /* compiler */ +# pragma warning(disable : 4702) +#endif /* compiler */ + + +/* ///////////////////////////////////////////////////////////////////////// + * main + */ + +int main(int argc, char* argv[]) +{ + int retCode = EXIT_SUCCESS; + int verbosity = 2; + + XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); + + if (XTESTS_START_RUNNER("test.unit.cstring", verbosity)) + { + XTESTS_RUN_CASE(TEST_cstring_t_DEFAULT); + XTESTS_RUN_CASE(TEST_cstring_init); + XTESTS_RUN_CASE(TEST_cstring_setCapacity); + XTESTS_RUN_CASE(TEST_cstring_assign_AND_cstring_create_AND_cstring_createLen_NULL_AND_EMPTY); + XTESTS_RUN_CASE(TEST_cstring_assign_AND_cstring_create); + XTESTS_RUN_CASE(TEST_cstring_assignLen_AND_cstring_createLen); + XTESTS_RUN_CASE(TEST_cstring_append_AND_cstring_appendLen); + XTESTS_RUN_CASE(TEST_cstring_truncate); + XTESTS_RUN_CASE(TEST_cstring_appendLen_KEEPS_CAPACITY); + + XTESTS_RUN_CASE(TEST_cstring_init_AND_cstring_destroy); + XTESTS_RUN_CASE(TEST_cstring_create); + XTESTS_RUN_CASE(TEST_cstring_createLen); + XTESTS_RUN_CASE(TEST_cstring_createEx); + XTESTS_RUN_CASE(TEST_cstring_createLenEx); + XTESTS_RUN_CASE(TEST_cstring_assignLen_KEEPS_CAPACITY); + XTESTS_RUN_CASE(TEST_cstring_assign_KEEPS_CAPACITY); + XTESTS_RUN_CASE(TEST_cstring_copy); + XTESTS_RUN_CASE(TEST_cstring_yield); + XTESTS_RUN_CASE(TEST_cstring_yield2); + XTESTS_RUN_CASE(TEST_cstring_swap); + + XTESTS_PRINT_RESULTS(); + + XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode); + } + + return retCode; +} + + +/* ///////////////////////////////////////////////////////////////////////// + * compiler compatibility + */ + +#if defined(STLSOFT_COMPILER_IS_MSVC) +# if _MSC_VER >= 1200 +# pragma warning(pop) +# endif /* compiler */ +#endif /* compiler */ + + +/* ///////////////////////////////////////////////////////////////////////// + * compatibility + */ + +#ifdef CSTRING_USE_WIDE_STRINGS + +# define CSTRING_T_(x) L ## x +# define TEST_STR_EQ_ TEST_WS_EQ +# define TEST_STR_EQ_N_ TEST_WS_EQ_N +#else /* ? CSTRING_USE_WIDE_STRINGS */ + +# define CSTRING_T_(x) x +# define TEST_STR_EQ_ TEST_MS_EQ +# define TEST_STR_EQ_N_ TEST_MS_EQ_N +#endif /* CSTRING_USE_WIDE_STRINGS */ + + +/* ///////////////////////////////////////////////////////////////////////// + * helper functions + */ + + +/* ///////////////////////////////////////////////////////////////////////// + * test function implementations + */ + + +static cstring_char_t* make_tilde_string_(size_t n) +{ + cstring_char_t* s = (cstring_char_t*)malloc((n + 1u) * sizeof(cstring_char_t)); + size_t i; + + if (NULL == s) + { + return NULL; + } + + { for (i = 0; i != n; ++i) + { + s[i] = CSTRING_T_('~'); + }} + s[n] = CSTRING_T_('\0'); + + return s; +} + + +static cstring_char_t const alphabet[] = CSTRING_T_("abcdefghijklmnopqrstuvwxyz"); + + +static void TEST_cstring_t_DEFAULT(void) +{ + cstring_t str = cstring_t_DEFAULT; + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_GE(str.len, str.capacity); +} + +static void TEST_cstring_init(void) +{ + cstring_t str; + + cstring_init(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_GE(str.len, str.capacity); +} + +static void TEST_cstring_setCapacity(void) +{ + cstring_t str = cstring_t_DEFAULT; + CSTRING_RC rc = cstring_setCapacity(&str, 10); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_FAIL_WITH_QUALIFIER("could not set capacity", cstring_getStatusCodeString(rc)); + } + else + { + TEST_INT_EQ(0u, str.len); + TEST_PTR_NE(NULL, str.ptr); + TEST_INT_GE(10u, str.capacity); + TEST_INT_GE(str.len, str.capacity); + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } +} + +static void TEST_cstring_assign_AND_cstring_create_AND_cstring_createLen_NULL_AND_EMPTY(void) +{ + { + cstring_t str = cstring_t_DEFAULT; + CSTRING_RC rc = cstring_assign(&str, NULL); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_FAIL_WITH_QUALIFIER("could not assign", cstring_getStatusCodeString(rc)); + } + else + { + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + } + + { + cstring_t str = cstring_t_DEFAULT; + CSTRING_RC rc = cstring_assign(&str, CSTRING_T_("")); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_FAIL_WITH_QUALIFIER("could not assign", cstring_getStatusCodeString(rc)); + } + else + { + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + } + + { + cstring_t str; + CSTRING_RC rc = cstring_create(&str, NULL); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_FAIL_WITH_QUALIFIER("could not create", cstring_getStatusCodeString(rc)); + } + else + { + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + } + + { + cstring_t str; + CSTRING_RC rc = cstring_create(&str, CSTRING_T_("")); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_FAIL_WITH_QUALIFIER("could not create", cstring_getStatusCodeString(rc)); + } + else + { + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + } + + { + cstring_t str; + CSTRING_RC rc = cstring_createLen(&str, NULL, 0u); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_FAIL_WITH_QUALIFIER("could not create (with length)", cstring_getStatusCodeString(rc)); + } + else + { + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + } + + { + cstring_t str; + CSTRING_RC rc = cstring_createLen(&str, CSTRING_T_(""), 0u); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_FAIL_WITH_QUALIFIER("could not create (with length)", cstring_getStatusCodeString(rc)); + } + else + { + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + } + + { + cstring_t str; + CSTRING_RC rc = cstring_createLen(&str, NULL, 5u); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_FAIL_WITH_QUALIFIER("could not create (with length)", cstring_getStatusCodeString(rc)); + } + else + { + TEST_INT_EQ(5u, str.len); + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(CSTRING_T_(""), str.ptr); + TEST_CHAR_EQ('\0', str.ptr[0]); + TEST_CHAR_EQ('\0', str.ptr[1]); + TEST_CHAR_EQ('\0', str.ptr[2]); + TEST_CHAR_EQ('\0', str.ptr[3]); + TEST_CHAR_EQ('\0', str.ptr[4]); + TEST_CHAR_EQ('\0', str.ptr[5]); + TEST_INT_GE(5u, str.capacity); + TEST_INT_GE(str.len, str.capacity); + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + } + + { + cstring_t str; + CSTRING_RC rc = cstring_createLen(&str, CSTRING_T_(""), 5u); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_FAIL_WITH_QUALIFIER("could not create (with length)", cstring_getStatusCodeString(rc)); + } + else + { + TEST_INT_EQ(5u, str.len); + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(CSTRING_T_(""), str.ptr); + TEST_CHAR_EQ('\0', str.ptr[0]); + /* + TEST_CHAR_EQ('\0', str.ptr[1]); + TEST_CHAR_EQ('\0', str.ptr[2]); + TEST_CHAR_EQ('\0', str.ptr[3]); + TEST_CHAR_EQ('\0', str.ptr[4]); + */ + TEST_CHAR_EQ('\0', str.ptr[5]); + TEST_INT_GE(5u, str.capacity); + TEST_INT_GE(str.len, str.capacity); + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + } +} + +static void TEST_cstring_assign_AND_cstring_create(void) +{ + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_char_t const* s = alphabet + i; + const size_t cch = (STLSOFT_NUM_ELEMENTS(alphabet) - 1) - i; + cstring_t str = cstring_t_DEFAULT; + CSTRING_RC rc = cstring_assign(&str, s); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_FAIL_WITH_QUALIFIER("could not assign", cstring_getStatusCodeString(rc)); + } + else + { + TEST_INT_EQ(cch, str.len); + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(cch, str.capacity); + TEST_INT_GE(str.len, str.capacity); + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + }} + + { + cstring_t str = cstring_t_DEFAULT; + + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_char_t const* s = alphabet + i; + const size_t cch = (STLSOFT_NUM_ELEMENTS(alphabet) - 1) - i; + CSTRING_RC rc = cstring_assign(&str, s); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_FAIL_WITH_QUALIFIER("could not assign", cstring_getStatusCodeString(rc)); + } + else + { + TEST_INT_EQ(cch, str.len); + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GT(cch, str.capacity); + TEST_INT_GE(str.len, str.capacity); + } + }} + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_char_t const* s = alphabet + i; + const size_t cch = (STLSOFT_NUM_ELEMENTS(alphabet) - 1) - i; + cstring_t str; + CSTRING_RC rc = cstring_create(&str, s); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_FAIL_WITH_QUALIFIER("could not create", cstring_getStatusCodeString(rc)); + } + else + { + TEST_INT_EQ(cch, str.len); + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(cch, str.capacity); + TEST_INT_GE(str.len, str.capacity); + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + }} +} + +static void TEST_cstring_assignLen_AND_cstring_createLen(void) +{ + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_char_t const* s = alphabet; + const size_t cch = i; + cstring_t str = cstring_t_DEFAULT; + CSTRING_RC rc = cstring_assignLen(&str, s, i); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_FAIL_WITH_QUALIFIER("could not assign (with length)", cstring_getStatusCodeString(rc)); + } + else + { + TEST_INT_EQ(cch, str.len); + if (0u == cch) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_N_(s, str.ptr, (int)i); + TEST_INT_GE(cch, str.capacity); + TEST_INT_GE(str.len, str.capacity); + } + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + }} + + { + cstring_t str = cstring_t_DEFAULT; + + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_char_t const* s = alphabet; + const size_t cch = i; + CSTRING_RC rc = cstring_assignLen(&str, s, i); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_FAIL_WITH_QUALIFIER("could not assign (with length)", cstring_getStatusCodeString(rc)); + } + else + { + TEST_INT_EQ(cch, str.len); + if (0u == cch) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_N_(s, str.ptr, (int)i); + TEST_INT_GE(cch, str.capacity); + TEST_INT_GE(str.len, str.capacity); + } + } + }} + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_char_t const* s = alphabet; + const size_t cch = i; + cstring_t str; + CSTRING_RC rc = cstring_createLen(&str, s, i); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_FAIL_WITH_QUALIFIER("could not create (with length)", cstring_getStatusCodeString(rc)); + } + else + { + TEST_INT_EQ(cch, str.len); + if (0u == cch) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_N_(s, str.ptr, (int)i); + TEST_INT_GE(cch, str.capacity); + TEST_INT_GE(str.len, str.capacity); + } + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + }} +} + +static void TEST_cstring_append_AND_cstring_appendLen(void) +{ + { + cstring_t str = cstring_t_DEFAULT; + + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_char_t s[2] = { alphabet[i], '\0' }; + CSTRING_RC rc = cstring_append(&str, s); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_FAIL_WITH_QUALIFIER("could not append", cstring_getStatusCodeString(rc)); + } + else + { + TEST_INT_EQ(i + 1, str.len); + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_N_(alphabet, str.ptr, (int)i); + TEST_INT_GE(i + 1, str.capacity); + TEST_INT_GE(str.len, str.capacity); + } + }} + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + + { + cstring_t str = cstring_t_DEFAULT; + + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + CSTRING_RC rc = cstring_appendLen(&str, &alphabet[i], 1); + + if (CSTRING_RC_SUCCESS != rc) + { + TEST_FAIL_WITH_QUALIFIER("could not append (with length)", cstring_getStatusCodeString(rc)); + } + else + { + TEST_INT_EQ(i + 1, str.len); + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_N_(alphabet, str.ptr, (int)i); + TEST_INT_GT(i, str.capacity); + TEST_INT_GE(str.len, str.capacity); + } + }} + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } +} + +static void TEST_cstring_truncate(void) +{ + { + cstring_t str = cstring_t_DEFAULT; + + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, cstring_truncate(&str, 0u)); + TEST_INT_EQ(0u, str.len); + TEST_INT_EQ(0u, str.capacity); + + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, cstring_truncate(&str, 1u)); + TEST_INT_EQ(0u, str.len); + TEST_INT_EQ(0u, str.capacity); + + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, cstring_truncate(&str, 100u)); + TEST_INT_EQ(0u, str.len); + TEST_INT_EQ(0u, str.capacity); + + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, cstring_truncate(&str, 100000u)); + TEST_INT_EQ(0u, str.len); + TEST_INT_EQ(0u, str.capacity); + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + + { + cstring_t str; + + if (CSTRING_RC_SUCCESS == cstring_create(&str, alphabet)) + { + { for (size_t i = 1000; ; --i) + { + cstring_truncate(&str, i); + + TEST_INT_LE(26u, str.len); + + if (i < 26u) + { + TEST_INT_EQ(i, str.len); + } + + if (0u == i) + { + break; + } + }} + } + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } +} + +static void TEST_cstring_appendLen_KEEPS_CAPACITY(void) +{ + cstring_t str = cstring_t_DEFAULT; + + { for (size_t c = 1u; c != APPEND_ITERATIONS; c *= 10u) + { + cstring_setCapacity(&str, c); + + cstring_char_t const* const p = str.ptr; + + { for (size_t i = 0; i != str.capacity; ++i) + { + cstring_appendLen(&str, CSTRING_T_("~"), 1); + + TEST_INT_EQ(i + 1, str.len); + TEST_PTR_EQ(p, str.ptr); + TEST_INT_GE(c, str.capacity); + }} + + cstring_truncate(&str, 0u); + }} + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); +} + + +static void TEST_cstring_init_AND_cstring_destroy(void) +{ + cstring_t str; + + cstring_init(&str); + + TEST_INT_GE(str.len, str.capacity); + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); +} + +static void TEST_cstring_create(void) +{ + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_t str; + const size_t n = (STLSOFT_NUM_ELEMENTS(alphabet) - 1) - i; + cstring_char_t const* const s = alphabet + n; + + cstring_create(&str, s); + + TEST_INT_GE(i, str.len); + if (0u == i) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + } + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + }} +} + +static void TEST_cstring_createLen(void) +{ + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_t str; + const size_t n = (STLSOFT_NUM_ELEMENTS(alphabet) - 1) - i; + cstring_char_t const* const s = alphabet + n; + + cstring_createLen(&str, s, i); + + TEST_INT_EQ(i, str.len); + if (0u == i) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + } + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + }} +} + +static void TEST_cstring_createEx(void) +{ + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_t str; + const size_t n = (STLSOFT_NUM_ELEMENTS(alphabet) - 1) - i; + cstring_char_t const* const s = alphabet + n; + cstring_flags_t flags = 0; + + cstring_createEx(&str, s, flags, NULL, 0); + + TEST_INT_GE(i, str.len); + if (0u == str.len) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + } + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + }} + + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_t str; + const size_t n = (STLSOFT_NUM_ELEMENTS(alphabet) - 1) - i; + cstring_char_t const* const s = alphabet + n; + cstring_flags_t flags = CSTRING_F_MEMORY_IS_BORROWED; + cstring_char_t buff[] = CSTRING_T_("[01234567890123456789]"); + CSTRING_RC rc; + + rc = cstring_createEx(&str, s, flags, &buff[0] + 1, STLSOFT_NUM_ELEMENTS(buff) - 3); + + TEST_CHAR_EQ(CSTRING_T_('['), buff[0]); + TEST_CHAR_EQ(CSTRING_T_(']'), buff[STLSOFT_NUM_ELEMENTS(buff) - 2]); + TEST_CHAR_EQ(CSTRING_T_('\0'), buff[STLSOFT_NUM_ELEMENTS(buff) - 1]); + + if (i < STLSOFT_NUM_ELEMENTS(buff) - 3) + { + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc); + + TEST_INT_GE(i, str.len); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + else + { + TEST_ENUM_EQ(CSTRING_RC_EXCEEDBORROWEDCAPACITY, rc); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + }} + +#ifdef _WIN32 + + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_t str; + const size_t n = (STLSOFT_NUM_ELEMENTS(alphabet) - 1) - i; + cstring_char_t const* const s = alphabet + n; + cstring_flags_t flags = CSTRING_F_USE_WINDOWS_GLOBAL_MEMORY; + + cstring_createEx(&str, s, flags, NULL, 0); + + TEST_INT_GE(i, str.len); + if (0u == str.len) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + TEST_INT_GE(str.capacity, GlobalSize(str.ptr)); + } + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + }} + + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_t str; + const size_t n = (STLSOFT_NUM_ELEMENTS(alphabet) - 1) - i; + cstring_char_t const* const s = alphabet + n; + cstring_flags_t flags = CSTRING_F_USE_WINDOWS_PROCESSHEAP_MEMORY; + + cstring_createEx(&str, s, flags, NULL, 0); + + TEST_INT_GE(i, str.len); + if (0u == str.len) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + TEST_INT_GE(str.capacity, HeapSize(GetProcessHeap(), 0, str.ptr)); + } + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + }} + + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_t str; + const size_t n = (STLSOFT_NUM_ELEMENTS(alphabet) - 1) - i; + cstring_char_t const* const s = alphabet + n; + cstring_flags_t flags = CSTRING_F_USE_WINDOWS_COM_TASK_MEMORY; + + cstring_createEx(&str, s, flags, NULL, 0); + + TEST_INT_GE(i, str.len); + if (0u == str.len) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + TEST_INT_GE(str.capacity, comstl__CoTaskMemGetSize(str.ptr)); + } + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + }} +#endif /* _WIN32 */ +} + +static void TEST_cstring_createLenEx(void) +{ + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_t str; + const size_t n = (STLSOFT_NUM_ELEMENTS(alphabet) - 1) - i; + cstring_char_t const* const s = alphabet + n; + cstring_flags_t flags = 0; + + cstring_createLenEx(&str, s, i, flags, NULL, 0); + + TEST_INT_EQ(i, str.len); + if (0u == i) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + } + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + }} + + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_t str; + const size_t n = (STLSOFT_NUM_ELEMENTS(alphabet) - 1) - i; + cstring_char_t const* const s = alphabet + n; + cstring_flags_t flags = CSTRING_F_MEMORY_IS_BORROWED; + cstring_char_t buff[] = CSTRING_T_("[01234567890123456789]"); + CSTRING_RC rc; + + rc = cstring_createLenEx(&str, s, i, flags, &buff[0] + 1, STLSOFT_NUM_ELEMENTS(buff) - 3); + + TEST_CHAR_EQ(CSTRING_T_('['), buff[0]); + TEST_CHAR_EQ(CSTRING_T_(']'), buff[STLSOFT_NUM_ELEMENTS(buff) - 2]); + TEST_CHAR_EQ(CSTRING_T_('\0'), buff[STLSOFT_NUM_ELEMENTS(buff) - 1]); + + if (i < STLSOFT_NUM_ELEMENTS(buff) - 3) + { + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc); + + TEST_INT_GE(i, str.len); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + else + { + TEST_ENUM_EQ(CSTRING_RC_EXCEEDBORROWEDCAPACITY, rc); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + }} + +#ifdef _WIN32 + + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_t str; + const size_t n = (STLSOFT_NUM_ELEMENTS(alphabet) - 1) - i; + cstring_char_t const* const s = alphabet + n; + cstring_flags_t flags = CSTRING_F_USE_WINDOWS_GLOBAL_MEMORY; + + cstring_createLenEx(&str, s, i, flags, NULL, 0); + + TEST_INT_EQ(i, str.len); + if (0u == i) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + TEST_INT_GE(str.capacity, GlobalSize(str.ptr)); + } + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + }} + + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_t str; + const size_t n = (STLSOFT_NUM_ELEMENTS(alphabet) - 1) - i; + cstring_char_t const* const s = alphabet + n; + cstring_flags_t flags = CSTRING_F_USE_WINDOWS_PROCESSHEAP_MEMORY; + + cstring_createLenEx(&str, s, i, flags, NULL, 0); + + TEST_INT_EQ(i, str.len); + if (0u == i) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + TEST_INT_GE(str.capacity, HeapSize(GetProcessHeap(), 0, str.ptr)); + } + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + }} + + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_t str; + const size_t n = (STLSOFT_NUM_ELEMENTS(alphabet) - 1) - i; + cstring_char_t const* const s = alphabet + n; + cstring_flags_t flags = CSTRING_F_USE_WINDOWS_COM_TASK_MEMORY; + + cstring_createLenEx(&str, s, i, flags, NULL, 0); + + TEST_INT_EQ(i, str.len); + if (0u == i) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(str.len, str.capacity); + TEST_INT_GE(str.capacity, comstl__CoTaskMemGetSize(str.ptr)); + } + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + }} +#endif /* _WIN32 */ +} + +static void TEST_cstring_assignLen_KEEPS_CAPACITY(void) +{ + cstring_t str = cstring_t_DEFAULT; + + { for (size_t c = 1u; c != ASSIGN_ITERATIONS; c *= 10u) + { + cstring_setCapacity(&str, c); + + cstring_char_t* const s = make_tilde_string_(str.capacity); + cstring_char_t const* const p = str.ptr; + + if (NULL == s) + { + TEST_FAIL_WITH_QUALIFIER("could not allocate comparison string", NULL); + } + else + { + { for (size_t i = 0; i != str.capacity + 1; ++i) + { + cstring_assignLen(&str, s, i); + + TEST_INT_EQ(i, str.len); + TEST_PTR_EQ(p, str.ptr); + TEST_INT_GE(c, str.capacity); + }} + + free(s); + } + + cstring_truncate(&str, 0u); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_NE(NULL, str.ptr); + TEST_INT_NE(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + }} + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); +} + +static void TEST_cstring_assign_KEEPS_CAPACITY(void) +{ + cstring_t str = cstring_t_DEFAULT; + + { for (size_t c = 1u; c != ASSIGN_ITERATIONS; c *= 10u) + { + cstring_setCapacity(&str, c); + + size_t const n = str.capacity; + cstring_char_t* const s = make_tilde_string_(n); + cstring_char_t const* const p = str.ptr; + + if (NULL == s) + { + TEST_FAIL_WITH_QUALIFIER("could not allocate comparison string", NULL); + } + else + { + { for (size_t i = 0; i != str.capacity + 1; ++i) + { + cstring_assign(&str, s + i); + + TEST_INT_EQ(n - i, str.len); + TEST_PTR_EQ(p, str.ptr); + TEST_INT_GE(c, str.capacity); + }} + + free(s); + } + + cstring_truncate(&str, 0u); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_NE(NULL, str.ptr); + TEST_INT_NE(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + }} + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); +} + +static void TEST_cstring_copy(void) +{ + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_char_t const* s = alphabet + i; + const size_t cch = (STLSOFT_NUM_ELEMENTS(alphabet) - 1) - i; + cstring_t str1 = cstring_t_DEFAULT; + cstring_t str2 = cstring_t_DEFAULT; + CSTRING_RC rc = cstring_assign(&str1, s); + + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(cch, str1.len); + TEST_PTR_NE(NULL, str1.ptr); + TEST_STR_EQ_(s, str1.ptr); + TEST_INT_GE(cch, str1.capacity); + TEST_INT_GE(str1.len, str1.capacity); + + rc = cstring_copy(&str2, &str1); + + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(cch, str2.len); + TEST_PTR_NE(NULL, str2.ptr); + TEST_STR_EQ_(s, str2.ptr); + TEST_INT_GE(cch, str2.capacity); + TEST_INT_GE(str2.len, str2.capacity); + + TEST_PTR_NE(str1.ptr, str2.ptr); + + cstring_destroy(&str1); + cstring_destroy(&str2); + + TEST_INT_EQ(0u, str1.len); + TEST_PTR_EQ(NULL, str1.ptr); + TEST_INT_EQ(0u, str1.capacity); + TEST_INT_EQ(0, str1.flags); + + TEST_INT_EQ(0u, str2.len); + TEST_PTR_EQ(NULL, str2.ptr); + TEST_INT_EQ(0u, str2.capacity); + TEST_INT_EQ(0, str2.flags); + }} +} + +static void TEST_cstring_yield(void) +{ + { + cstring_t str = cstring_t_DEFAULT; + cstring_char_t* p; + CSTRING_RC rc = cstring_yield(&str, &p); + + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc); + + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + + TEST_PTR_EQ(NULL, p); + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_char_t const* s = alphabet + i; + const size_t cch = (STLSOFT_NUM_ELEMENTS(alphabet) - 1) - i; + cstring_t str = cstring_t_DEFAULT; + CSTRING_RC rc = cstring_assign(&str, s); + cstring_char_t* p; + + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(cch, str.len); + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(cch, str.capacity); + TEST_INT_GE(str.len, str.capacity); + + rc = cstring_yield(&str, &p); + + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + + TEST_PTR_NE(NULL, p); + TEST_STR_EQ_(s, p); + + free(p); + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + }} +} + +static void TEST_cstring_yield2(void) +{ + { + cstring_t str = cstring_t_DEFAULT; + cstring_char_t* p; + void* raw; + CSTRING_RC rc = cstring_yield2(&str, &p, &raw); + + TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc); + + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + + TEST_PTR_EQ(NULL, p); + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + } + + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_char_t const* s = alphabet + i; + const size_t cch = (STLSOFT_NUM_ELEMENTS(alphabet) - 1) - i; + cstring_t str = cstring_t_DEFAULT; + CSTRING_RC rc = cstring_assign(&str, s); + cstring_char_t* p; + void* raw; + + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(cch, str.len); + TEST_PTR_NE(NULL, str.ptr); + TEST_STR_EQ_(s, str.ptr); + TEST_INT_GE(cch, str.capacity); + TEST_INT_GE(str.len, str.capacity); + + rc = cstring_yield2(&str, &p, &raw); + + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + + TEST_PTR_NE(NULL, p); + TEST_STR_EQ_(s, p); + + free(raw); + + cstring_destroy(&str); + + TEST_INT_EQ(0u, str.len); + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + TEST_INT_EQ(0, str.flags); + }} +} + +static void TEST_cstring_swap(void) +{ + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(alphabet) - 1; ++i) + { + cstring_char_t const* s = alphabet + i; + const size_t cch = (STLSOFT_NUM_ELEMENTS(alphabet) - 1) - i; + cstring_t str1 = cstring_t_DEFAULT; + cstring_t str2 = cstring_t_DEFAULT; + CSTRING_RC rc = cstring_assign(&str1, s); + + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(cch, str1.len); + TEST_PTR_NE(NULL, str1.ptr); + TEST_STR_EQ_(s, str1.ptr); + TEST_INT_GE(cch, str1.capacity); + TEST_INT_GE(str1.len, str1.capacity); + + TEST_INT_EQ(0u, str2.len); + TEST_PTR_EQ(NULL, str2.ptr); + TEST_INT_EQ(0u, str2.capacity); + TEST_INT_EQ(0, str2.flags); + + rc = cstring_swap(&str2, &str1); + + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); + TEST_INT_EQ(0u, str1.len); + TEST_PTR_EQ(NULL, str1.ptr); + TEST_INT_EQ(0u, str1.capacity); + TEST_INT_EQ(0, str1.flags); + + TEST_INT_EQ(cch, str2.len); + TEST_PTR_NE(NULL, str2.ptr); + TEST_STR_EQ_(s, str2.ptr); + TEST_INT_GE(cch, str2.capacity); + TEST_INT_GE(str2.len, str2.capacity); + + TEST_PTR_NE(str1.ptr, str2.ptr); + + cstring_destroy(&str1); + cstring_destroy(&str2); + + TEST_INT_EQ(0u, str1.len); + TEST_PTR_EQ(NULL, str1.ptr); + TEST_INT_EQ(0u, str1.capacity); + TEST_INT_EQ(0, str1.flags); + + TEST_INT_EQ(0u, str2.len); + TEST_PTR_EQ(NULL, str2.ptr); + TEST_INT_EQ(0u, str2.capacity); + TEST_INT_EQ(0, str2.flags); + }} +} + + +/* ///////////////////////////// end of file //////////////////////////// */ + diff --git a/test/unit/cstring_vector/CMakeLists.txt b/test/unit/cstring_vector/CMakeLists.txt new file mode 100644 index 0000000..9ccfe97 --- /dev/null +++ b/test/unit/cstring_vector/CMakeLists.txt @@ -0,0 +1,2 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +define_automated_test_program(test.unit.cstring_vector entry.c) diff --git a/test/unit/cstring_vector/entry.c b/test/unit/cstring_vector/entry.c new file mode 100644 index 0000000..4a5cf2a --- /dev/null +++ b/test/unit/cstring_vector/entry.c @@ -0,0 +1,318 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: test/unit/cstring_vector/entry.c + * + * Purpose: Tests `cstring_vector_t` functionality. + * + * Created: 12th January 2024 + * Updated: 3rd October 2026 + * + * ////////////////////////////////////////////////////////////////////// */ + + + +#include + +#ifdef USE_PANTHEIOS_EXTRAS_DIAGUTIL_MAIN + +# include +#endif + +#include + +#include +#include + + +/* ///////////////////////////////////////////////////////////////////////// + * forward declarations + */ + +static void TEST_cstring_vector_t_DEFAULT(void); +static void TEST_cstring_vector_init_ZERO(void); +static void TEST_cstring_vector_init_WITH_CAPACITY(void); +static void TEST_cstring_vector_create_ZERO(void); +static void TEST_cstring_vector_create_N(void); +static void TEST_cstring_vector_insertAt_ONE(void); +static void TEST_cstring_vector_insertAt_LIST(void); + + +/* ///////////////////////////////////////////////////////////////////////// + * constants & definitions + */ + + +/* ///////////////////////////////////////////////////////////////////////// + * main() + */ + +static +int main0(int argc, char **argv) +{ + int retCode = EXIT_SUCCESS; + int verbosity = 2; + + XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); + + if (XTESTS_START_RUNNER("test.unit.cstring_vector", verbosity)) + { + XTESTS_RUN_CASE(TEST_cstring_vector_t_DEFAULT); + XTESTS_RUN_CASE(TEST_cstring_vector_init_ZERO); + XTESTS_RUN_CASE(TEST_cstring_vector_init_WITH_CAPACITY); + XTESTS_RUN_CASE(TEST_cstring_vector_create_ZERO); + XTESTS_RUN_CASE(TEST_cstring_vector_create_N); + XTESTS_RUN_CASE(TEST_cstring_vector_insertAt_ONE); + XTESTS_RUN_CASE(TEST_cstring_vector_insertAt_LIST); + + XTESTS_PRINT_RESULTS(); + + XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode); + } + + return retCode; +} + +int main(int argc, char **argv) +{ +#ifdef USE_PANTHEIOS_EXTRAS_DIAGUTIL_MAIN + + return pantheios_extras_diagutil_main_leak_trace_invoke(argc, argv, main0); +#else + + return main0(argc, argv); +#endif +} + + +/* ///////////////////////////////////////////////////////////////////////// + * test function implementations + */ + +static void TEST_cstring_vector_t_DEFAULT(void) +{ + cstring_vector_t csv = cstring_vector_t_DEFAULT; + + TEST_INT_EQ(0, csv.len); + TEST_PTR_EQ(NULL, csv.ptr); + TEST_INT_EQ(0, csv.capacity); + + cstring_vector_destroy(&csv); +} + +static void TEST_cstring_vector_init_ZERO(void) +{ + cstring_vector_t csv; + CSTRING_RC rc = cstring_vector_init(&csv, 0); + + if (CSTRING_RC_OUTOFMEMORY == rc) + { + /*XTESTS_TEST_WARN(*/ + } + else + if (CSTRING_RC_SUCCESS == rc) + { + TEST_INT_EQ(0, csv.len); + TEST_PTR_EQ(NULL, csv.ptr); + TEST_INT_EQ(0, csv.capacity); + + cstring_vector_destroy(&csv); + } + else + { + TEST_FAIL("should only fail cstring_vector_init() with CSTRING_RC_OUTOFMEMORY"); + } +} + +static void TEST_cstring_vector_init_WITH_CAPACITY(void) +{ + cstring_vector_t csv; + CSTRING_RC rc = cstring_vector_init(&csv, 10); + + if (CSTRING_RC_OUTOFMEMORY == rc) + { + /*XTESTS_TEST_WARN(*/ + } + else + if (CSTRING_RC_SUCCESS == rc) + { + TEST_INT_EQ(0, csv.len); + TEST_PTR_NE(NULL, csv.ptr); + TEST_INT_GE(10, csv.capacity); + + cstring_vector_destroy(&csv); + } + else + { + TEST_FAIL("should only fail cstring_vector_init() with CSTRING_RC_OUTOFMEMORY"); + } +} + +static void TEST_cstring_vector_create_ZERO(void) +{ + cstring_vector_t csv; + CSTRING_RC rc = cstring_vector_create(&csv, 0); + + if (CSTRING_RC_OUTOFMEMORY == rc) + { + /*XTESTS_TEST_WARN(*/ + } + else + if (CSTRING_RC_SUCCESS == rc) + { + TEST_INT_EQ(0, csv.len); + TEST_PTR_EQ(NULL, csv.ptr); + TEST_INT_EQ(0, csv.capacity); + + cstring_vector_destroy(&csv); + } + else + { + TEST_FAIL("should only fail cstring_vector_create() with CSTRING_RC_OUTOFMEMORY"); + } +} + +static void TEST_cstring_vector_create_N(void) +{ + cstring_vector_t csv; + CSTRING_RC rc = cstring_vector_create(&csv, 10); + + if (CSTRING_RC_OUTOFMEMORY == rc) + { + /*XTESTS_TEST_WARN(*/ + } + else + if (CSTRING_RC_SUCCESS == rc) + { + TEST_INT_EQ(10, csv.len); + TEST_PTR_NE(NULL, csv.ptr); + TEST_INT_GE(10, csv.capacity); + + cstring_vector_destroy(&csv); + } + else + { + TEST_FAIL("should only fail cstring_vector_create() with CSTRING_RC_OUTOFMEMORY"); + } +} + +static void TEST_cstring_vector_insertAt_ONE(void) +{ + cstring_vector_t csv; + CSTRING_RC rc = cstring_vector_create(&csv, 0); + + if (CSTRING_RC_OUTOFMEMORY == rc) + { + /*XTESTS_TEST_WARN(*/ + } + else + if (CSTRING_RC_SUCCESS == rc) + { + cstring_t cs = cstring_t_DEFAULT; + + TEST_INT_EQ(0, csv.len); + TEST_PTR_EQ(NULL, csv.ptr); + TEST_INT_EQ(0, csv.capacity); + + rc = cstring_vector_insertAt(&csv, 0, &cs, 1); + + if (CSTRING_RC_OUTOFMEMORY == rc) + { + /*XTESTS_TEST_WARN(*/ + } + else + if (CSTRING_RC_SUCCESS == rc) + { + TEST_INT_EQ(1, csv.len); + TEST_PTR_NE(NULL, csv.ptr); + TEST_INT_GE(1, csv.capacity); + } + else + { + TEST_FAIL("should only fail cstring_vector_insertAt() with CSTRING_RC_OUTOFMEMORY"); + } + + cstring_vector_destroy(&csv); + } + else + { + TEST_FAIL("should only fail cstring_vector_create() with CSTRING_RC_OUTOFMEMORY"); + } +} + +static void TEST_cstring_vector_insertAt_LIST(void) +{ + char const* const strings[] = + { + "blah", + "", + "abcdefghijklmnopqrstuvwxyz", + "My Ever Changing Moods", + "I Can Give You Everything", + "Newborn Friend", + "Escape Velocity", + "Weapon Of Choice", + "Ain't No Doubt", + "Eye Know", + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + }; + + cstring_vector_t csv; + CSTRING_RC rc = cstring_vector_create(&csv, 0); + + if (CSTRING_RC_OUTOFMEMORY == rc) + { + /*XTESTS_TEST_WARN(*/ + } + else + if (CSTRING_RC_SUCCESS == rc) + { + cstring_t cs = cstring_t_DEFAULT; + size_t i; + + TEST_INT_EQ(0, csv.len); + TEST_PTR_EQ(NULL, csv.ptr); + TEST_INT_EQ(0, csv.capacity); + + for (i = 0; i != STLSOFT_NUM_ELEMENTS(strings) && CSTRING_RC_SUCCESS == rc; ++i) + { + rc = cstring_assign(&cs, strings[i]); + + if (CSTRING_RC_SUCCESS == rc) + { + rc = cstring_vector_insertAt(&csv, i, &cs, 1); + } + } + + cstring_destroy(&cs); + + if (CSTRING_RC_OUTOFMEMORY == rc) + { + /*XTESTS_TEST_WARN(*/ + } + else + if (CSTRING_RC_SUCCESS == rc) + { + XTESTS_REQUIRE(TEST_INT_EQ(STLSOFT_NUM_ELEMENTS(strings), csv.len)); + TEST_PTR_NE(NULL, csv.ptr); + TEST_INT_GE(STLSOFT_NUM_ELEMENTS(strings), csv.capacity); + + for (i = 0; i != STLSOFT_NUM_ELEMENTS(strings); ++i) + { + TEST_INT_EQ(strlen(strings[i]), csv.ptr[i].len); + TEST_MS_EQ(strings[i], csv.ptr[i].ptr); + } + } + else + { + TEST_FAIL("should only fail cstring_vector_insertAt() with CSTRING_RC_OUTOFMEMORY"); + } + + cstring_vector_destroy(&csv); + } + else + { + TEST_FAIL("should only fail cstring_vector_create() with CSTRING_RC_OUTOFMEMORY"); + } +} + +/* ///////////////////////////// end of file //////////////////////////// */ + diff --git a/test/unit/insert-replace/CMakeLists.txt b/test/unit/insert-replace/CMakeLists.txt new file mode 100644 index 0000000..76417de --- /dev/null +++ b/test/unit/insert-replace/CMakeLists.txt @@ -0,0 +1,2 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +define_automated_test_program(test.unit.insert-replace entry.c) diff --git a/test/unit/test.unit.cstring.2/entry.c b/test/unit/insert-replace/entry.c similarity index 79% rename from test/unit/test.unit.cstring.2/entry.c rename to test/unit/insert-replace/entry.c index 4603462..9b7a67e 100644 --- a/test/unit/test.unit.cstring.2/entry.c +++ b/test/unit/insert-replace/entry.c @@ -1,10 +1,10 @@ /* ///////////////////////////////////////////////////////////////////////// - * File: test.unit.cstring.2/entry.c + * File: test/unit/insert-replace/entry.c * - * Purpose: Unit-tests for general functionality. + * Purpose: Unit-tests for cstring insert and replace functionality. * * Created: 4th June 2009 - * Updated: 2nd August 2026 + * Updated: 3rd October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -45,22 +45,20 @@ * forward declarations */ -static void test_createN(void); -static void test_insert(void); -static void test_insertLen(void); -static void test_replace(void); -static void test_replaceLen(void); -static void test_replaceAll_1(void); -static void test_replaceAll_2(void); -static void test_replaceAll_3(void); +static void TEST_cstring_createN(void); +static void TEST_cstring_insert(void); +static void TEST_cstring_insertLen(void); +static void TEST_cstring_replace(void); +static void TEST_cstring_replaceLen(void); +static void TEST_cstring_replaceAll_EMPTY(void); +static void TEST_cstring_replaceAll_SAME_LENGTH(void); +static void TEST_cstring_replaceAll_CHAINED(void); /* ///////////////////////////////////////////////////////////////////////// * constants & definitions */ -static char const TEST_FILE_NAME[] = "test.unit.cstring.2.txt"; - static char const alphabet[] = "abcdefghijklmnopqrstuvwxyz"; @@ -87,24 +85,22 @@ int main(int argc, char **argv) XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); - if (XTESTS_START_RUNNER("test.unit.cstring.2", verbosity)) + if (XTESTS_START_RUNNER("test.unit.insert-replace", verbosity)) { - XTESTS_RUN_CASE(test_createN); - XTESTS_RUN_CASE(test_insert); - XTESTS_RUN_CASE(test_insertLen); - XTESTS_RUN_CASE(test_replace); - XTESTS_RUN_CASE(test_replaceLen); - XTESTS_RUN_CASE(test_replaceAll_1); - XTESTS_RUN_CASE(test_replaceAll_2); - XTESTS_RUN_CASE(test_replaceAll_3); + XTESTS_RUN_CASE(TEST_cstring_createN); + XTESTS_RUN_CASE(TEST_cstring_insert); + XTESTS_RUN_CASE(TEST_cstring_insertLen); + XTESTS_RUN_CASE(TEST_cstring_replace); + XTESTS_RUN_CASE(TEST_cstring_replaceLen); + XTESTS_RUN_CASE(TEST_cstring_replaceAll_EMPTY); + XTESTS_RUN_CASE(TEST_cstring_replaceAll_SAME_LENGTH); + XTESTS_RUN_CASE(TEST_cstring_replaceAll_CHAINED); XTESTS_PRINT_RESULTS(); XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode); } - STLSOFT_SUPPRESS_UNUSED(TEST_FILE_NAME); - return retCode; } @@ -120,11 +116,16 @@ int main(int argc, char **argv) #endif /* compiler */ +/* ///////////////////////////////////////////////////////////////////////// + * helper functions + */ + + /* ///////////////////////////////////////////////////////////////////////// * test function implementations */ -static void test_createN(void) +static void TEST_cstring_createN(void) { { for (size_t volatile i = 0; i != 1000000u; i = (0u == i) ? 1u : i * 10u) { @@ -135,18 +136,26 @@ static void test_createN(void) if (CSTRING_RC_SUCCESS == rc) { TEST_INT_EQ((size_t)i, str.len); - TEST_PTR_NE(NULL, str.ptr); - TEST_INT_GE(str.len, str.capacity); + if (0u == i) + { + TEST_PTR_EQ(NULL, str.ptr); + TEST_INT_EQ(0u, str.capacity); + } + else + { + TEST_PTR_NE(NULL, str.ptr); + TEST_INT_GE(str.len, str.capacity); #ifdef XTESTS_HAS_SHWILD - /* created string must be entirely '~' */ - TEST_MS_DOES_NOT_MATCH("*[a-zA-Z0-9]*", str.ptr); - TEST_MS_DOES_NOT_MATCH("*[ ,.<>/?'\";:[{]}`!@#$%^&*()=_+\\\\|-]*", str.ptr); + /* created string must be entirely '~' */ + TEST_MS_DOES_NOT_MATCH("*[a-zA-Z0-9]*", str.ptr); + TEST_MS_DOES_NOT_MATCH("*[ ,.<>/?'\";:[{]}`!@#$%^&*()=_+\\\\|-]*", str.ptr); #else /* ? XTESTS_HAS_SHWILD */ - TEST_PTR_EQ(NULL, strpbrk(str.ptr, "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890,<.>/?'\";:[{]}`!@#$%^&*()-_=+\\|")); + TEST_PTR_EQ(NULL, strpbrk(str.ptr, "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890,<.>/?'\";:[{]}`!@#$%^&*()-_=+\\|")); #endif /* XTESTS_HAS_SHWILD */ + } cstring_destroy(&str); @@ -157,12 +166,12 @@ static void test_createN(void) } else { - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_OUTOFMEMORY, rc); + TEST_ENUM_EQ(CSTRING_RC_OUTOFMEMORY, rc); } }} } -static void test_insert(void) +static void TEST_cstring_insert(void) { { /* Forwards */ @@ -178,7 +187,7 @@ static void test_insert(void) char const sz[2] = { alphabet[i], '\0' }; cstring_insert(&str, (int)i, sz); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL_N(alphabet, str.ptr, (int)i); + TEST_MS_EQ_N(alphabet, str.ptr, (int)i); }} cstring_destroy(&str); @@ -203,7 +212,7 @@ static void test_insert(void) char const sz[2] = { alphabet[(STLSOFT_NUM_ELEMENTS(alphabet) - 1) - (1 + i)], '\0' }; cstring_insert(&str, 0, sz); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL_N(alphabet + ((STLSOFT_NUM_ELEMENTS(alphabet) - 1) - (1 + i)), (char const*)str.ptr, (int)i); + TEST_MS_EQ_N(alphabet + ((STLSOFT_NUM_ELEMENTS(alphabet) - 1) - (1 + i)), (char const*)str.ptr, (int)i); }} cstring_destroy(&str); @@ -215,7 +224,7 @@ static void test_insert(void) } } -static void test_insertLen(void) +static void TEST_cstring_insertLen(void) { cstring_t str = cstring_t_DEFAULT; @@ -287,7 +296,7 @@ static void test_insertLen(void) TEST_INT_EQ(0, str.flags); } -static void test_replace(void) +static void TEST_cstring_replace(void) { cstring_t str = cstring_t_DEFAULT; @@ -317,14 +326,14 @@ static void test_replace(void) TEST_INT_EQ(0, str.flags); } -static void test_replaceLen(void) +static void TEST_cstring_replaceLen(void) { cstring_t str; CSTRING_RC rc = cstring_create(&str, "abcdefghijklmnopqrstuvwxyz"); if (CSTRING_RC_SUCCESS != rc) { - XTESTS_TEST_FAIL_WITH_QUALIFIER("could not create string", cstring_getStatusCodeString(rc)); + TEST_FAIL_WITH_QUALIFIER("could not create string", cstring_getStatusCodeString(rc)); } else { @@ -354,7 +363,7 @@ static void test_replaceLen(void) } } -static void test_replaceAll_1(void) +static void TEST_cstring_replaceAll_EMPTY(void) { { cstring_t str = cstring_t_DEFAULT; @@ -363,7 +372,7 @@ static void test_replaceAll_1(void) rc = cstring_replaceAll(&str, NULL, NULL, NULL); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(0u, str.len); TEST_PTR_EQ(NULL, str.ptr); TEST_INT_EQ(0u, str.capacity); @@ -371,7 +380,7 @@ static void test_replaceAll_1(void) rc = cstring_replaceAll(&str, NULL, NULL, &n); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(0u, str.len); TEST_PTR_EQ(NULL, str.ptr); TEST_INT_EQ(0u, str.capacity); @@ -380,7 +389,7 @@ static void test_replaceAll_1(void) rc = cstring_replaceAll(&str, "abc", "def", &n); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(0u, str.len); TEST_PTR_EQ(NULL, str.ptr); TEST_INT_EQ(0u, str.capacity); @@ -396,18 +405,18 @@ static void test_replaceAll_1(void) } } -static void test_replaceAll_2(void) +static void TEST_cstring_replaceAll_SAME_LENGTH(void) { { cstring_t str; CSTRING_RC rc = cstring_create(&str, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); size_t n; - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); rc = cstring_replaceAll(&str, NULL, NULL, NULL); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(52u, str.len); TEST_PTR_NE(NULL, str.ptr); TEST_MS_EQ("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", str.ptr); @@ -416,7 +425,7 @@ static void test_replaceAll_2(void) rc = cstring_replaceAll(&str, NULL, NULL, &n); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(52u, str.len); TEST_PTR_NE(NULL, str.ptr); TEST_MS_EQ("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", str.ptr); @@ -426,7 +435,7 @@ static void test_replaceAll_2(void) rc = cstring_replaceAll(&str, "abc", "def", &n); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(52u, str.len); TEST_PTR_NE(NULL, str.ptr); TEST_MS_EQ("defdefghijklmnopqrstuvwxyzdefdefghijklmnopqrstuvwxyz", str.ptr); @@ -443,18 +452,18 @@ static void test_replaceAll_2(void) } } -static void test_replaceAll_3(void) +static void TEST_cstring_replaceAll_CHAINED(void) { { cstring_t str; CSTRING_RC rc = cstring_create(&str, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); size_t n; - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); rc = cstring_replaceAll(&str, NULL, NULL, NULL); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(52u, str.len); TEST_PTR_NE(NULL, str.ptr); TEST_MS_EQ("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", str.ptr); @@ -463,7 +472,7 @@ static void test_replaceAll_3(void) rc = cstring_replaceAll(&str, NULL, NULL, &n); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(52u, str.len); TEST_PTR_NE(NULL, str.ptr); TEST_MS_EQ("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", str.ptr); @@ -473,7 +482,7 @@ static void test_replaceAll_3(void) rc = cstring_replaceAll(&str, "abc", "de", &n); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(50u, str.len); TEST_PTR_NE(NULL, str.ptr); TEST_MS_EQ("dedefghijklmnopqrstuvwxyzdedefghijklmnopqrstuvwxyz", str.ptr); @@ -483,7 +492,7 @@ static void test_replaceAll_3(void) rc = cstring_replaceAll(&str, "de", "g", &n); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(46u, str.len); TEST_PTR_NE(NULL, str.ptr); TEST_MS_EQ("ggfghijklmnopqrstuvwxyzggfghijklmnopqrstuvwxyz", str.ptr); @@ -493,7 +502,7 @@ static void test_replaceAll_3(void) rc = cstring_replaceAll(&str, "fg", "", &n); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(42u, str.len); TEST_PTR_NE(NULL, str.ptr); TEST_MS_EQ("gghijklmnopqrstuvwxyzgghijklmnopqrstuvwxyz", str.ptr); @@ -503,7 +512,7 @@ static void test_replaceAll_3(void) rc = cstring_replaceAll(&str, "g", "", &n); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(38u, str.len); TEST_PTR_NE(NULL, str.ptr); TEST_MS_EQ("hijklmnopqrstuvwxyzhijklmnopqrstuvwxyz", str.ptr); @@ -513,7 +522,7 @@ static void test_replaceAll_3(void) rc = cstring_replaceAll(&str, "xyzhij", "", &n); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(32u, str.len); TEST_PTR_NE(NULL, str.ptr); TEST_MS_EQ("hijklmnopqrstuvwklmnopqrstuvwxyz", str.ptr); @@ -523,7 +532,7 @@ static void test_replaceAll_3(void) rc = cstring_replaceAll(&str, "mno", "MNO", &n); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(32u, str.len); TEST_PTR_NE(NULL, str.ptr); TEST_MS_EQ("hijklMNOpqrstuvwklMNOpqrstuvwxyz", str.ptr); @@ -533,7 +542,7 @@ static void test_replaceAll_3(void) rc = cstring_replaceAll(&str, "MNO", "<>", &n); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(40u, str.len); TEST_PTR_NE(NULL, str.ptr); TEST_MS_EQ("hijkl<>pqrstuvwkl<>pqrstuvwxyz", str.ptr); @@ -543,7 +552,7 @@ static void test_replaceAll_3(void) rc = cstring_replaceAll(&str, ">>", ">>>", &n); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(42u, str.len); TEST_PTR_NE(NULL, str.ptr); TEST_MS_EQ("hijkl<>>pqrstuvwkl<>>pqrstuvwxyz", str.ptr); @@ -553,7 +562,7 @@ static void test_replaceAll_3(void) rc = cstring_replaceAll(&str, "<<", "<< <<", &n); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(50u, str.len); TEST_PTR_NE(NULL, str.ptr); TEST_MS_EQ("hijkl<< <>>pqrstuvwkl<< <>>pqrstuvwxyz", str.ptr); @@ -563,7 +572,7 @@ static void test_replaceAll_3(void) rc = cstring_replaceAll(&str, "hijkl<< <>>", NULL, &n); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(33u, str.len); TEST_PTR_NE(NULL, str.ptr); TEST_MS_EQ("pqrstuvwkl<< <>>pqrstuvwxyz", str.ptr); @@ -573,7 +582,7 @@ static void test_replaceAll_3(void) rc = cstring_replaceAll(&str, "<< <>>", NULL, &n); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(21u, str.len); TEST_PTR_NE(NULL, str.ptr); TEST_MS_EQ("pqrstuvwklpqrstuvwxyz", str.ptr); @@ -583,7 +592,7 @@ static void test_replaceAll_3(void) rc = cstring_replaceAll(&str, "kl", "xyz", &n); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(22u, str.len); TEST_PTR_NE(NULL, str.ptr); TEST_MS_EQ("pqrstuvwxyzpqrstuvwxyz", str.ptr); @@ -597,7 +606,7 @@ static void test_replaceAll_3(void) rc = cstring_replaceAll(&str, sz, NULL, &n); - XTESTS_REQUIRE(XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc)); + XTESTS_REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_INT_EQ(22u - 2 * (1 + i), str.len); TEST_PTR_NE(NULL, str.ptr); /* TEST_MS_EQ("pqrstuvwklpqrstuvwxyz", str.ptr); */ diff --git a/test/unit/status-codes/CMakeLists.txt b/test/unit/status-codes/CMakeLists.txt new file mode 100644 index 0000000..6a57f74 --- /dev/null +++ b/test/unit/status-codes/CMakeLists.txt @@ -0,0 +1,5 @@ +list(APPEND X_MSVC_CUSTOM_WARNINGS_TO_BE_SUPPRESSED + 4996 +) + +define_automated_test_program(test.unit.status-codes entry.c) diff --git a/test/unit/status-codes/entry.c b/test/unit/status-codes/entry.c new file mode 100644 index 0000000..bb457be --- /dev/null +++ b/test/unit/status-codes/entry.c @@ -0,0 +1,232 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: test/unit/status-codes/entry.c + * + * Purpose: Unit-tests `cstring_error()`, `cstring_getStatusCodeString()`. + * + * Created: 28th July 2011 + * Updated: 4th October 2026 + * + * ////////////////////////////////////////////////////////////////////// */ + + +/* ///////////////////////////////////////////////////////////////////////// + * includes + */ + +/* ///////////////////////////////////// + * test component header file include(s) + */ + +#include + +/* ///////////////////////////////////// + * general includes + */ + +#ifdef XTESTS_HAS_SHWILD + /* shwild header files */ +# include +#endif /* XTESTS_HAS_SHWILD */ + +/* xTests header files */ +#include + +/* STLSoft header files */ +#include + +/* Standard C header files */ +#include + + +/* ///////////////////////////////////////////////////////////////////////// + * forward declarations + */ + +static void TEST_cstring_getStatusCodeString_SUCCESS(void); +static void TEST_cstring_getStatusCodeString_KNOWN_CODES(void); +static void TEST_cstring_getStatusCodeString_UNKNOWN_CODES(void); + +static void TEST_cstring_error_SUCCESS(void); +static void TEST_cstring_error_KNOWN_CODES(void); +static void TEST_cstring_error_UNKNOWN_CODES(void); + + +/* ///////////////////////////////////////////////////////////////////////// + * constants & definitions + */ + +static CSTRING_RC const knownCodes[] = +{ + CSTRING_RC_SUCCESS + , CSTRING_RC_OUTOFMEMORY + , CSTRING_RC_FIXED + , CSTRING_RC_BORROWED + , CSTRING_RC_READONLY + , CSTRING_RC_INVALIDARENA + , CSTRING_RC_CUSTOMARENANOTSUPPORTED + , CSTRING_RC_EXCEEDFIXEDCAPACITY + , CSTRING_RC_EXCEEDBORROWEDCAPACITY + , CSTRING_RC_CANNOTYIELDFROMSO + , CSTRING_RC_ARENAOVERLOADED + /* cstring 3.5+ */ + , CSTRING_RC_INVALIDSTREAM + , CSTRING_RC_EOF + , CSTRING_RC_INVALIDSECTION + , CSTRING_RC_IOERROR + /* cstring 3.6.2+ */ + , CSTRING_RC_SYSTEMSPECIFICFAILURE + /* cstring 4.0+ */ + , CSTRING_RC_REQUESTTOOLARGE +}; + + +/* ///////////////////////////////////////////////////////////////////////// + * compiler compatibility + */ + +#if defined(STLSOFT_COMPILER_IS_MSVC) +# if _MSC_VER >= 1200 +# pragma warning(push) +# endif /* compiler */ +# pragma warning(disable : 4702) +#endif /* compiler */ + + +/* ///////////////////////////////////////////////////////////////////////// + * main + */ + +int main(int argc, char **argv) +{ + int retCode = EXIT_SUCCESS; + int verbosity = 2; + + XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); + + if (XTESTS_START_RUNNER("test.unit.status-codes", verbosity)) + { + XTESTS_RUN_CASE(TEST_cstring_getStatusCodeString_SUCCESS); + XTESTS_RUN_CASE(TEST_cstring_getStatusCodeString_KNOWN_CODES); + XTESTS_RUN_CASE(TEST_cstring_getStatusCodeString_UNKNOWN_CODES); + + XTESTS_RUN_CASE(TEST_cstring_error_SUCCESS); + XTESTS_RUN_CASE(TEST_cstring_error_KNOWN_CODES); + XTESTS_RUN_CASE(TEST_cstring_error_UNKNOWN_CODES); + + XTESTS_PRINT_RESULTS(); + + XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode); + } + + return retCode; +} + + +/* ///////////////////////////////////////////////////////////////////////// + * compiler compatibility + */ + +#if defined(STLSOFT_COMPILER_IS_MSVC) +# if _MSC_VER >= 1200 +# pragma warning(pop) +# endif /* compiler */ +#endif /* compiler */ + + +/* ///////////////////////////////////////////////////////////////////////// + * test function implementations + */ + +static void TEST_cstring_getStatusCodeString_SUCCESS(void) +{ + TEST_MS_EQ("operation completed successfully", cstring_getStatusCodeString((CSTRING_RC)0)); +} + +static void TEST_cstring_getStatusCodeString_KNOWN_CODES(void) +{ + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(knownCodes); ++i) + { + TEST_MS_NE("<>", cstring_getStatusCodeString(knownCodes[i])); + }} + +#ifdef XTESTS_HAS_SHWILD + + TEST_MS_MATCHES("operation completed successfully", cstring_getStatusCodeString(CSTRING_RC_SUCCESS)); + TEST_MS_MATCHES("out of memory", cstring_getStatusCodeString(CSTRING_RC_OUTOFMEMORY)); + TEST_MS_MATCHES("operation cannot procede because the cstring *", cstring_getStatusCodeString(CSTRING_RC_FIXED)); + TEST_MS_MATCHES("operation cannot procede because the cstring *", cstring_getStatusCodeString(CSTRING_RC_BORROWED)); + TEST_MS_MATCHES("operation cannot procede because the cstring *", cstring_getStatusCodeString(CSTRING_RC_READONLY)); + TEST_MS_MATCHES("*invalid*", cstring_getStatusCodeString(CSTRING_RC_INVALIDARENA)); + TEST_MS_MATCHES("custom arena functionality not currently supported", cstring_getStatusCodeString(CSTRING_RC_CUSTOMARENANOTSUPPORTED)); + TEST_MS_MATCHES("operation cannot procede because the current capacity would be exceeded*", cstring_getStatusCodeString(CSTRING_RC_EXCEEDFIXEDCAPACITY)); + TEST_MS_MATCHES("operation cannot procede because the current capacity would be exceeded*", cstring_getStatusCodeString(CSTRING_RC_EXCEEDBORROWEDCAPACITY)); + TEST_MS_MATCHES("yield operation from a dynamic-library version of cstring was not allowed", cstring_getStatusCodeString(CSTRING_RC_CANNOTYIELDFROMSO)); + TEST_MS_MATCHES("cannot use arena parameter for both borrowed memory and custom arena", cstring_getStatusCodeString(CSTRING_RC_ARENAOVERLOADED)); + TEST_MS_MATCHES("*invalid*", cstring_getStatusCodeString(CSTRING_RC_INVALIDSTREAM)); + TEST_MS_MATCHES("reached the end of stream", cstring_getStatusCodeString(CSTRING_RC_EOF)); + TEST_MS_MATCHES("*invalid*", cstring_getStatusCodeString(CSTRING_RC_INVALIDSECTION)); + TEST_MS_MATCHES("an I/O error occured", cstring_getStatusCodeString(CSTRING_RC_IOERROR)); + TEST_MS_MATCHES("system-specific failure occurred", cstring_getStatusCodeString(CSTRING_RC_SYSTEMSPECIFICFAILURE)); + TEST_MS_MATCHES("request exceeded inherent or runtime limit", cstring_getStatusCodeString(CSTRING_RC_REQUESTTOOLARGE)); +#endif /* XTESTS_HAS_SHWILD */ +} + +static void TEST_cstring_getStatusCodeString_UNKNOWN_CODES(void) +{ + { for (size_t i = 0; i != 100000; ++i) + { + int r = rand(); + CSTRING_RC rc = (CSTRING_RC)(STLSOFT_NUM_ELEMENTS(knownCodes) + r + 1); + + TEST_MS_EQ("<>", cstring_getStatusCodeString(rc)); + }} +} + + +static void TEST_cstring_error_SUCCESS(void) +{ + TEST_MS_EQ("operation completed successfully", cstring_error((CSTRING_RC)0)); +} + +static void TEST_cstring_error_KNOWN_CODES(void) +{ + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(knownCodes); ++i) + { + TEST_MS_NE("<>", cstring_error(knownCodes[i])); + }} + +#ifdef XTESTS_HAS_SHWILD + + TEST_MS_MATCHES("operation completed successfully", cstring_error(CSTRING_RC_SUCCESS)); + TEST_MS_MATCHES("out of memory", cstring_error(CSTRING_RC_OUTOFMEMORY)); + TEST_MS_MATCHES("operation cannot procede because the cstring *", cstring_error(CSTRING_RC_FIXED)); + TEST_MS_MATCHES("operation cannot procede because the cstring *", cstring_error(CSTRING_RC_BORROWED)); + TEST_MS_MATCHES("operation cannot procede because the cstring *", cstring_error(CSTRING_RC_READONLY)); + TEST_MS_MATCHES("*invalid*", cstring_error(CSTRING_RC_INVALIDARENA)); + TEST_MS_MATCHES("custom arena functionality not currently supported", cstring_error(CSTRING_RC_CUSTOMARENANOTSUPPORTED)); + TEST_MS_MATCHES("operation cannot procede because the current capacity would be exceeded*", cstring_error(CSTRING_RC_EXCEEDFIXEDCAPACITY)); + TEST_MS_MATCHES("operation cannot procede because the current capacity would be exceeded*", cstring_error(CSTRING_RC_EXCEEDBORROWEDCAPACITY)); + TEST_MS_MATCHES("yield operation from a dynamic-library version of cstring was not allowed", cstring_error(CSTRING_RC_CANNOTYIELDFROMSO)); + TEST_MS_MATCHES("cannot use arena parameter for both borrowed memory and custom arena", cstring_error(CSTRING_RC_ARENAOVERLOADED)); + TEST_MS_MATCHES("*invalid*", cstring_error(CSTRING_RC_INVALIDSTREAM)); + TEST_MS_MATCHES("reached the end of stream", cstring_error(CSTRING_RC_EOF)); + TEST_MS_MATCHES("*invalid*", cstring_error(CSTRING_RC_INVALIDSECTION)); + TEST_MS_MATCHES("an I/O error occured", cstring_error(CSTRING_RC_IOERROR)); + TEST_MS_MATCHES("system-specific failure occurred", cstring_error(CSTRING_RC_SYSTEMSPECIFICFAILURE)); + TEST_MS_MATCHES("request exceeded inherent or runtime limit", cstring_error(CSTRING_RC_REQUESTTOOLARGE)); +#endif /* XTESTS_HAS_SHWILD */ +} + +static void TEST_cstring_error_UNKNOWN_CODES(void) +{ + { for (size_t i = 0; i != 100000; ++i) + { + int r = rand(); + CSTRING_RC rc = (CSTRING_RC)(STLSOFT_NUM_ELEMENTS(knownCodes) + r + 1); + + TEST_MS_EQ("<>", cstring_error(rc)); + }} +} + + +/* ///////////////////////////// end of file //////////////////////////// */ diff --git a/test/unit/test.unit.cstring.auto_buffer/CMakeLists.txt b/test/unit/test.unit.cstring.auto_buffer/CMakeLists.txt deleted file mode 100644 index 6bfad14..0000000 --- a/test/unit/test.unit.cstring.auto_buffer/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten -define_automated_test_program(test_unit_cstring_auto_buffer entry.c) diff --git a/test/unit/test.unit.cstring.auto_buffer/entry.c b/test/unit/test.unit.cstring.auto_buffer/entry.c deleted file mode 100644 index 5bd10b2..0000000 --- a/test/unit/test.unit.cstring.auto_buffer/entry.c +++ /dev/null @@ -1,508 +0,0 @@ -/* ///////////////////////////////////////////////////////////////////////// - * File: test.unit.cstring.auto_buffer/entry.c - * - * Purpose: Implementation file for the test.unit.cstring.auto_buffer - * project. - * - * Created: 28th July 2011 - * Updated: 2nd August 2026 - * - * ////////////////////////////////////////////////////////////////////// */ - - -/* ///////////////////////////////////////////////////////////////////////// - * test component header file include(s) - */ - -#include - - -/* ///////////////////////////////////////////////////////////////////////// - * includes - */ - -/* xTests header files */ -#include - -/* STLSoft header files */ -#include - -/* Standard C header files */ -#include - - -/* ///////////////////////////////////////////////////////////////////////// - * forward declarations - */ - -static void test_badFlags(void); -static void test_createEx(void); -static void test_createLenEx(void); -static void test_setCapacity(void); -static void test_assign(void); -static void test_assignLen(void); -static void test_append(void); -static void test_appendLen(void); - - -/* ///////////////////////////////////////////////////////////////////////// - * constants & definitions - */ - -static char const TEST_FILE_NAME[] = "test.unit.cstring.auto_buffer.txt"; - - -/* ///////////////////////////////////////////////////////////////////////// - * compiler compatibility - */ - -#if defined(STLSOFT_COMPILER_IS_MSVC) -# if _MSC_VER >= 1200 -# pragma warning(push) -# endif /* compiler */ -# pragma warning(disable : 4702) -#endif /* compiler */ - - -/* ///////////////////////////////////////////////////////////////////////// - * main - */ - -int main(int argc, char **argv) -{ - int retCode = EXIT_SUCCESS; - int verbosity = 2; - - XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); - - if (XTESTS_START_RUNNER("test.unit.cstring.auto_buffer", verbosity)) - { - XTESTS_RUN_CASE(test_badFlags); - XTESTS_RUN_CASE(test_createEx); - XTESTS_RUN_CASE(test_createLenEx); - XTESTS_RUN_CASE(test_setCapacity); - XTESTS_RUN_CASE(test_assign); - XTESTS_RUN_CASE(test_assignLen); - XTESTS_RUN_CASE(test_append); - XTESTS_RUN_CASE(test_appendLen); - - XTESTS_PRINT_RESULTS(); - - XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode); - } - - STLSOFT_SUPPRESS_UNUSED(TEST_FILE_NAME); - - return retCode; -} - - -/* ///////////////////////////////////////////////////////////////////////// - * compiler compatibility - */ - -#if defined(STLSOFT_COMPILER_IS_MSVC) -# if _MSC_VER >= 1200 -# pragma warning(pop) -# endif /* compiler */ -#endif /* compiler */ - - -/* ///////////////////////////////////////////////////////////////////////// - * test function implementations - */ - -static void test_badFlags(void) -{ - static int const goods[] = - { - CSTRING_F_MEMORY_IS_BORROWED - , CSTRING_F_MEMORY_IS_BORROWED | CSTRING_F_MEMORY_IS_FIXED - , CSTRING_F_MEMORY_IS_BORROWED | CSTRING_F_MEMORY_CAN_GROW_TO_HEAP - }; - - static int const bads[] = - { - 0 - , CSTRING_F_MEMORY_IS_FIXED - , CSTRING_F_MEMORY_IS_OFFSET - }; - - { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(goods); ++i) - { - int const flags = 0 - | goods[i] - ; - char buff[10]; - cstring_t cs; - CSTRING_RC rc; - - rc = cstring_createEx(&cs, "string: ", flags, buff, sizeof(buff)); - - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc); - }} - - { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(bads); ++i) - { - int const flags = 0 - | bads[i] - ; - char buff[10]; - cstring_t cs; - CSTRING_RC rc; - - rc = cstring_createEx(&cs, "string: ", flags, buff, sizeof(buff)); - - XTESTS_TEST_ENUM_NOT_EQUAL(CSTRING_RC_SUCCESS, rc); - }} -} - -static void test_createEx(void) -{ - int const flags = 0 - | CSTRING_F_MEMORY_IS_BORROWED - | CSTRING_F_MEMORY_CAN_GROW_TO_HEAP - ; - char buff[8]; - cstring_t cs; - CSTRING_RC rc; - - - /* must succeed in stack buffer. */ - - rc = cstring_createEx(&cs, "small", flags, buff, sizeof(buff)); - - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc); - XTESTS_TEST_INTEGER_EQUAL(5u, cs.len); - XTESTS_TEST_INTEGER_EQUAL(sizeof(buff) - 1u, cs.capacity); - - cstring_destroy(&cs); - - - /* try to resize. */ - - rc = cstring_createEx(&cs, "oversized", flags, buff, sizeof(buff)); - - if (CSTRING_RC_SUCCESS != rc) - { - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_OUTOFMEMORY, rc); - XTESTS_TEST_INTEGER_EQUAL(flags, cs.flags); - XTESTS_TEST_INTEGER_EQUAL(5u, cs.len); - XTESTS_TEST_POINTER_EQUAL(buff, cs.ptr); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("small", cs.ptr); - XTESTS_TEST_INTEGER_EQUAL(sizeof(buff) - 1u, cs.capacity); - } - else - { - XTESTS_TEST_INTEGER_EQUAL(0, cs.flags); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("oversized", cs.ptr); - XTESTS_TEST_POINTER_NOT_EQUAL(buff, cs.ptr); - - cstring_destroy(&cs); - } -} - -static void test_createLenEx(void) -{ - int const flags = 0 - | CSTRING_F_MEMORY_IS_BORROWED - | CSTRING_F_MEMORY_CAN_GROW_TO_HEAP - ; - char buff[8]; - cstring_t cs; - CSTRING_RC rc; - - - /* must succeed in stack buffer. */ - - rc = cstring_createEx(&cs, "small", flags, buff, sizeof(buff)); - - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc); - XTESTS_TEST_INTEGER_EQUAL(5u, cs.len); - XTESTS_TEST_INTEGER_EQUAL(sizeof(buff) - 1u, cs.capacity); - - cstring_destroy(&cs); - - - /* try to resize. */ - - rc = cstring_createLenEx(&cs, "oversized string", 9, flags, buff, sizeof(buff)); - - if (CSTRING_RC_SUCCESS != rc) - { - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_OUTOFMEMORY, rc); - XTESTS_TEST_INTEGER_EQUAL(flags, cs.flags); - XTESTS_TEST_INTEGER_EQUAL(5u, cs.len); - XTESTS_TEST_POINTER_EQUAL(buff, cs.ptr); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("small", cs.ptr); - XTESTS_TEST_INTEGER_EQUAL(sizeof(buff) - 1u, cs.capacity); - } - else - { - XTESTS_TEST_INTEGER_EQUAL(0, cs.flags); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("oversized", cs.ptr); - XTESTS_TEST_POINTER_NOT_EQUAL(buff, cs.ptr); - - cstring_destroy(&cs); - } -} - -static void test_setCapacity(void) -{ - int const flags = 0 - | CSTRING_F_MEMORY_IS_BORROWED - | CSTRING_F_MEMORY_CAN_GROW_TO_HEAP - ; - char buff[8]; - cstring_t cs; - CSTRING_RC rc; - - - /* must succeed in stack buffer. */ - - rc = cstring_createEx(&cs, "small", flags, buff, sizeof(buff)); - - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc); - XTESTS_TEST_INTEGER_EQUAL(5u, cs.len); - XTESTS_TEST_INTEGER_EQUAL(sizeof(buff) - 1u, cs.capacity); - - - /* try to resize. */ - - rc = cstring_setCapacity(&cs, 9); - - if (CSTRING_RC_SUCCESS != rc) - { - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_OUTOFMEMORY, rc); - XTESTS_TEST_INTEGER_EQUAL(flags, cs.flags); - XTESTS_TEST_INTEGER_EQUAL(5u, cs.len); - XTESTS_TEST_POINTER_EQUAL(buff, cs.ptr); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("small", cs.ptr); - XTESTS_TEST_INTEGER_EQUAL(sizeof(buff) - 1u, cs.capacity); - } - else - { - XTESTS_TEST_INTEGER_EQUAL(0, cs.flags); - XTESTS_TEST_INTEGER_EQUAL(5u, cs.len); - XTESTS_TEST_POINTER_NOT_EQUAL(buff, cs.ptr); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("small", cs.ptr); - XTESTS_TEST_INTEGER_GREATER_OR_EQUAL(9u - 1u, cs.capacity); - } - - cstring_destroy(&cs); -} - -static void test_assign(void) -{ - int const flags = 0 - | CSTRING_F_MEMORY_IS_BORROWED - | CSTRING_F_MEMORY_CAN_GROW_TO_HEAP - ; - char buff[8]; - cstring_t cs; - CSTRING_RC rc; - - - /* must succeed in stack buffer. */ - - rc = cstring_createEx(&cs, "small", flags, buff, sizeof(buff)); - - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc); - XTESTS_TEST_INTEGER_EQUAL(5u, cs.len); - XTESTS_TEST_INTEGER_EQUAL(sizeof(buff) - 1u, cs.capacity); - - - /* try to resize. */ - - rc = cstring_assign(&cs, "oversized"); - - if (CSTRING_RC_SUCCESS != rc) - { - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_OUTOFMEMORY, rc); - XTESTS_TEST_INTEGER_EQUAL(flags, cs.flags); - XTESTS_TEST_INTEGER_EQUAL(5u, cs.len); - XTESTS_TEST_POINTER_EQUAL(buff, cs.ptr); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("small", cs.ptr); - XTESTS_TEST_INTEGER_EQUAL(sizeof(buff) - 1u, cs.capacity); - } - else - { - XTESTS_TEST_INTEGER_EQUAL(0, cs.flags); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("oversized", cs.ptr); - XTESTS_TEST_POINTER_NOT_EQUAL(buff, cs.ptr); - - - /* shrink again */ - rc = cstring_assign(&cs, "small"); - - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc); - XTESTS_TEST_INTEGER_EQUAL(0, cs.flags); - XTESTS_TEST_INTEGER_EQUAL(5u, cs.len); - XTESTS_TEST_POINTER_NOT_EQUAL(buff, cs.ptr); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("small", cs.ptr); - } - - cstring_destroy(&cs); -} - -static void test_assignLen(void) -{ - int const flags = 0 - | CSTRING_F_MEMORY_IS_BORROWED - | CSTRING_F_MEMORY_CAN_GROW_TO_HEAP - ; - char buff[8]; - cstring_t cs; - CSTRING_RC rc; - - - /* must succeed in stack buffer. */ - - rc = cstring_createEx(&cs, "small", flags, buff, sizeof(buff)); - - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc); - XTESTS_TEST_INTEGER_EQUAL(5u, cs.len); - XTESTS_TEST_INTEGER_EQUAL(sizeof(buff) - 1u, cs.capacity); - - - /* try to resize. */ - - rc = cstring_assignLen(&cs, "oversized", 9); - - if (CSTRING_RC_SUCCESS != rc) - { - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_OUTOFMEMORY, rc); - XTESTS_TEST_INTEGER_EQUAL(flags, cs.flags); - XTESTS_TEST_INTEGER_EQUAL(5u, cs.len); - XTESTS_TEST_POINTER_EQUAL(buff, cs.ptr); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("small", cs.ptr); - XTESTS_TEST_INTEGER_EQUAL(sizeof(buff) - 1u, cs.capacity); - } - else - { - XTESTS_TEST_INTEGER_EQUAL(0, cs.flags); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("oversized", cs.ptr); - XTESTS_TEST_POINTER_NOT_EQUAL(buff, cs.ptr); - - - /* shrink again */ - rc = cstring_assign(&cs, "small"); - - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc); - XTESTS_TEST_INTEGER_EQUAL(0, cs.flags); - XTESTS_TEST_INTEGER_EQUAL(5u, cs.len); - XTESTS_TEST_POINTER_NOT_EQUAL(buff, cs.ptr); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("small", cs.ptr); - } - - cstring_destroy(&cs); -} - -static void test_append(void) -{ - int const flags = 0 - | CSTRING_F_MEMORY_IS_BORROWED - | CSTRING_F_MEMORY_CAN_GROW_TO_HEAP - ; - char buff[8]; - cstring_t cs; - CSTRING_RC rc; - - - /* must succeed in stack buffer. */ - - rc = cstring_createEx(&cs, "small", flags, buff, sizeof(buff)); - - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc); - XTESTS_TEST_INTEGER_EQUAL(5u, cs.len); - XTESTS_TEST_INTEGER_EQUAL(sizeof(buff) - 1u, cs.capacity); - - - /* try to resize. */ - - rc = cstring_append(&cs, "ish, but big enough"); - - if (CSTRING_RC_SUCCESS != rc) - { - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_OUTOFMEMORY, rc); - XTESTS_TEST_INTEGER_EQUAL(flags, cs.flags); - XTESTS_TEST_INTEGER_EQUAL(5u, cs.len); - XTESTS_TEST_POINTER_EQUAL(buff, cs.ptr); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("small", cs.ptr); - XTESTS_TEST_INTEGER_EQUAL(sizeof(buff) - 1u, cs.capacity); - } - else - { - XTESTS_TEST_INTEGER_EQUAL(0, cs.flags); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("smallish, but big enough", cs.ptr); - XTESTS_TEST_POINTER_NOT_EQUAL(buff, cs.ptr); - - - /* shrink again */ - rc = cstring_assign(&cs, "small"); - - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc); - XTESTS_TEST_INTEGER_EQUAL(0, cs.flags); - XTESTS_TEST_INTEGER_EQUAL(5u, cs.len); - XTESTS_TEST_POINTER_NOT_EQUAL(buff, cs.ptr); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("small", cs.ptr); - } - - cstring_destroy(&cs); -} - -static void test_appendLen(void) -{ - int const flags = 0 - | CSTRING_F_MEMORY_IS_BORROWED - | CSTRING_F_MEMORY_CAN_GROW_TO_HEAP - ; - char buff[8]; - cstring_t cs; - CSTRING_RC rc; - - - /* must succeed in stack buffer. */ - - rc = cstring_createEx(&cs, "small", flags, buff, sizeof(buff)); - - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc); - XTESTS_TEST_INTEGER_EQUAL(5u, cs.len); - XTESTS_TEST_INTEGER_EQUAL(sizeof(buff) - 1u, cs.capacity); - - - /* try to resize. */ - - rc = cstring_appendLen(&cs, "ish, but big enough", 19); - - if (CSTRING_RC_SUCCESS != rc) - { - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_OUTOFMEMORY, rc); - XTESTS_TEST_INTEGER_EQUAL(flags, cs.flags); - XTESTS_TEST_INTEGER_EQUAL(5u, cs.len); - XTESTS_TEST_POINTER_EQUAL(buff, cs.ptr); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("small", cs.ptr); - XTESTS_TEST_INTEGER_EQUAL(sizeof(buff) - 1u, cs.capacity); - } - else - { - XTESTS_TEST_INTEGER_EQUAL(0, cs.flags); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("smallish, but big enough", cs.ptr); - XTESTS_TEST_POINTER_NOT_EQUAL(buff, cs.ptr); - - - /* shrink again */ - rc = cstring_assign(&cs, "small"); - - XTESTS_TEST_ENUM_EQUAL(CSTRING_RC_SUCCESS, rc); - XTESTS_TEST_INTEGER_EQUAL(0, cs.flags); - XTESTS_TEST_INTEGER_EQUAL(5u, cs.len); - XTESTS_TEST_POINTER_NOT_EQUAL(buff, cs.ptr); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("small", cs.ptr); - } - - cstring_destroy(&cs); -} - - -/* ///////////////////////////// end of file //////////////////////////// */ diff --git a/test/unit/test.unit.cstring.cstring_getStatusCodeString/CMakeLists.txt b/test/unit/test.unit.cstring.cstring_getStatusCodeString/CMakeLists.txt deleted file mode 100644 index d34b3dd..0000000 --- a/test/unit/test.unit.cstring.cstring_getStatusCodeString/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -list(APPEND X_MSVC_CUSTOM_WARNINGS_TO_BE_SUPPRESSED - 4996 -) - -define_automated_test_program(test_unit_cstring_cstring_getStatusCodeString entry.c) diff --git a/test/unit/test.unit.cstring.cstring_getStatusCodeString/entry.c b/test/unit/test.unit.cstring.cstring_getStatusCodeString/entry.c deleted file mode 100644 index 9b6347c..0000000 --- a/test/unit/test.unit.cstring.cstring_getStatusCodeString/entry.c +++ /dev/null @@ -1,233 +0,0 @@ -/* ///////////////////////////////////////////////////////////////////////// - * File: test.unit.cstring.cstring_getStatusCodeString/entry.c - * - * Purpose: Unit-tests `cstring_error()`, `cstring_getStatusCodeString()`. - * - * Created: 28th July 2011 - * Updated: 2nd August 2026 - * - * ////////////////////////////////////////////////////////////////////// */ - - -/* ///////////////////////////////////////////////////////////////////////// - * test component header file include(s) - */ - -#include - - -/* ///////////////////////////////////////////////////////////////////////// - * includes - */ - -#ifdef XTESTS_HAS_SHWILD - /* shwild header files */ -# include -#endif /* XTESTS_HAS_SHWILD */ - -/* xTests header files */ -#include - -/* STLSoft header files */ -#include - -/* Standard C header files */ -#include - - -/* ///////////////////////////////////////////////////////////////////////// - * forward declarations - */ - -static void test_0(void); -static void test_known(void); -static void test_random(void); - -static void test_0_deprecated(void); -static void test_known_deprecated(void); -static void test_random_deprecated(void); - - -/* ///////////////////////////////////////////////////////////////////////// - * constants & definitions - */ - -static char const TEST_FILE_NAME[] = "test.unit.cstring.cstring_getStatusCodeString.txt"; - -static CSTRING_RC const knownCodes[] = -{ - CSTRING_RC_SUCCESS - , CSTRING_RC_OUTOFMEMORY - , CSTRING_RC_FIXED - , CSTRING_RC_BORROWED - , CSTRING_RC_READONLY - , CSTRING_RC_INVALIDARENA - , CSTRING_RC_CUSTOMARENANOTSUPPORTED - , CSTRING_RC_EXCEEDFIXEDCAPACITY - , CSTRING_RC_EXCEEDBORROWEDCAPACITY - , CSTRING_RC_CANNOTYIELDFROMSO - , CSTRING_RC_ARENAOVERLOADED - /* cstring 3.5+ */ - , CSTRING_RC_INVALIDSTREAM - , CSTRING_RC_EOF - , CSTRING_RC_INVALIDSECTION - , CSTRING_RC_IOERROR - /* cstring 3.6.2+ */ - , CSTRING_RC_SYSTEMSPECIFICFAILURE - /* cstring 4.0+ */ - , CSTRING_RC_REQUESTTOOLARGE -}; - - -/* ///////////////////////////////////////////////////////////////////////// - * compiler compatibility - */ - -#if defined(STLSOFT_COMPILER_IS_MSVC) -# if _MSC_VER >= 1200 -# pragma warning(push) -# endif /* compiler */ -# pragma warning(disable : 4702) -#endif /* compiler */ - - -/* ///////////////////////////////////////////////////////////////////////// - * main - */ - -int main(int argc, char **argv) -{ - int retCode = EXIT_SUCCESS; - int verbosity = 2; - - XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); - - if (XTESTS_START_RUNNER("test.unit.cstring.cstring_getStatusCodeString", verbosity)) - { - XTESTS_RUN_CASE(test_0); - XTESTS_RUN_CASE(test_known); - XTESTS_RUN_CASE(test_random); - - XTESTS_RUN_CASE(test_0_deprecated); - XTESTS_RUN_CASE(test_known_deprecated); - XTESTS_RUN_CASE(test_random_deprecated); - - XTESTS_PRINT_RESULTS(); - - XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode); - } - - STLSOFT_SUPPRESS_UNUSED(TEST_FILE_NAME); - - return retCode; -} - - -/* ///////////////////////////////////////////////////////////////////////// - * compiler compatibility - */ - -#if defined(STLSOFT_COMPILER_IS_MSVC) -# if _MSC_VER >= 1200 -# pragma warning(pop) -# endif /* compiler */ -#endif /* compiler */ - - -/* ///////////////////////////////////////////////////////////////////////// - * test function implementations - */ - -static void test_0(void) -{ - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("operation completed successfully", cstring_getStatusCodeString((CSTRING_RC)0)); -} - -static void test_known(void) -{ - { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(knownCodes); ++i) - { - XTESTS_TEST_MULTIBYTE_STRING_NOT_EQUAL("<>", cstring_getStatusCodeString(knownCodes[i])); - }} - -#ifdef XTESTS_HAS_SHWILD - - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("operation completed successfully", cstring_getStatusCodeString(CSTRING_RC_SUCCESS)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("out of memory", cstring_getStatusCodeString(CSTRING_RC_OUTOFMEMORY)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("operation cannot procede because the cstring *", cstring_getStatusCodeString(CSTRING_RC_FIXED)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("operation cannot procede because the cstring *", cstring_getStatusCodeString(CSTRING_RC_BORROWED)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("operation cannot procede because the cstring *", cstring_getStatusCodeString(CSTRING_RC_READONLY)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("*invalid*", cstring_getStatusCodeString(CSTRING_RC_INVALIDARENA)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("custom arena functionality not currently supported", cstring_getStatusCodeString(CSTRING_RC_CUSTOMARENANOTSUPPORTED)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("operation cannot procede because the current capacity would be exceeded*", cstring_getStatusCodeString(CSTRING_RC_EXCEEDFIXEDCAPACITY)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("operation cannot procede because the current capacity would be exceeded*", cstring_getStatusCodeString(CSTRING_RC_EXCEEDBORROWEDCAPACITY)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("yield operation from a dynamic-library version of cstring was not allowed", cstring_getStatusCodeString(CSTRING_RC_CANNOTYIELDFROMSO)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("cannot use arena parameter for both borrowed memory and custom arena", cstring_getStatusCodeString(CSTRING_RC_ARENAOVERLOADED)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("*invalid*", cstring_getStatusCodeString(CSTRING_RC_INVALIDSTREAM)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("reached the end of stream", cstring_getStatusCodeString(CSTRING_RC_EOF)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("*invalid*", cstring_getStatusCodeString(CSTRING_RC_INVALIDSECTION)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("an I/O error occured", cstring_getStatusCodeString(CSTRING_RC_IOERROR)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("system-specific failure occurred", cstring_getStatusCodeString(CSTRING_RC_SYSTEMSPECIFICFAILURE)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("request exceeded inherent or runtime limit", cstring_getStatusCodeString(CSTRING_RC_REQUESTTOOLARGE)); -#endif /* XTESTS_HAS_SHWILD */ -} - -static void test_random(void) -{ - { for (size_t i = 0; i != 100000; ++i) - { - int r = rand(); - CSTRING_RC rc = (CSTRING_RC)(STLSOFT_NUM_ELEMENTS(knownCodes) + r + 1); - - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("<>", cstring_getStatusCodeString(rc)); - }} -} - - -static void test_0_deprecated(void) -{ - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("operation completed successfully", cstring_error((CSTRING_RC)0)); -} - -static void test_known_deprecated(void) -{ - { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(knownCodes); ++i) - { - XTESTS_TEST_MULTIBYTE_STRING_NOT_EQUAL("<>", cstring_error(knownCodes[i])); - }} - -#ifdef XTESTS_HAS_SHWILD - - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("operation completed successfully", cstring_error(CSTRING_RC_SUCCESS)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("out of memory", cstring_error(CSTRING_RC_OUTOFMEMORY)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("operation cannot procede because the cstring *", cstring_error(CSTRING_RC_FIXED)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("operation cannot procede because the cstring *", cstring_error(CSTRING_RC_BORROWED)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("operation cannot procede because the cstring *", cstring_error(CSTRING_RC_READONLY)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("*invalid*", cstring_error(CSTRING_RC_INVALIDARENA)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("custom arena functionality not currently supported", cstring_error(CSTRING_RC_CUSTOMARENANOTSUPPORTED)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("operation cannot procede because the current capacity would be exceeded*", cstring_error(CSTRING_RC_EXCEEDFIXEDCAPACITY)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("operation cannot procede because the current capacity would be exceeded*", cstring_error(CSTRING_RC_EXCEEDBORROWEDCAPACITY)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("yield operation from a dynamic-library version of cstring was not allowed", cstring_error(CSTRING_RC_CANNOTYIELDFROMSO)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("cannot use arena parameter for both borrowed memory and custom arena", cstring_error(CSTRING_RC_ARENAOVERLOADED)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("*invalid*", cstring_error(CSTRING_RC_INVALIDSTREAM)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("reached the end of stream", cstring_error(CSTRING_RC_EOF)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("*invalid*", cstring_error(CSTRING_RC_INVALIDSECTION)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("an I/O error occured", cstring_error(CSTRING_RC_IOERROR)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("system-specific failure occurred", cstring_error(CSTRING_RC_SYSTEMSPECIFICFAILURE)); - XTESTS_TEST_MULTIBYTE_STRING_MATCHES("request exceeded inherent or runtime limit", cstring_error(CSTRING_RC_REQUESTTOOLARGE)); -#endif /* XTESTS_HAS_SHWILD */ -} - -static void test_random_deprecated(void) -{ - { for (size_t i = 0; i != 100000; ++i) - { - int r = rand(); - CSTRING_RC rc = (CSTRING_RC)(STLSOFT_NUM_ELEMENTS(knownCodes) + r + 1); - - XTESTS_TEST_MULTIBYTE_STRING_EQUAL("<>", cstring_error(rc)); - }} -} - - -/* ///////////////////////////// end of file //////////////////////////// */ diff --git a/test/unit/test.unit.cstring.cstring_vector/CMakeLists.txt b/test/unit/test.unit.cstring.cstring_vector/CMakeLists.txt deleted file mode 100644 index e1d68cc..0000000 --- a/test/unit/test.unit.cstring.cstring_vector/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten -define_automated_test_program(test_unit_cstring_cstring_vector entry.c) diff --git a/test/unit/test.unit.cstring.cstring_vector/entry.c b/test/unit/test.unit.cstring.cstring_vector/entry.c deleted file mode 100644 index b986cb0..0000000 --- a/test/unit/test.unit.cstring.cstring_vector/entry.c +++ /dev/null @@ -1,378 +0,0 @@ - - -#include - -#ifdef USE_PANTHEIOS_EXTRAS_DIAGUTIL_MAIN - -# include -#endif - -#include - -#include -#include - -/* ///////////////////////////////////////////////////////////////////////// - * forward declarations - */ - -static void test_1_00(void); -static void test_1_01(void); -static void test_1_02(void); -static void test_1_03(void); -static void test_1_04(void); -static void test_1_05(void); -static void test_1_06(void); -static void test_1_07(void); -static void test_1_08(void); -static void test_1_09(void); -static void test_1_10(void); -static void test_1_11(void); -static void test_1_12(void); -static void test_1_13(void); -static void test_1_14(void); -static void test_1_15(void); -static void test_1_16(void); -static void test_1_17(void); -static void test_1_18(void); -static void test_1_19(void); - -/* ///////////////////////////////////////////////////////////////////////// - * constants & definitions - */ - -/* ///////////////////////////////////////////////////////////////////////// - * main() - */ - -static -int main0(int argc, char **argv) -{ - int retCode = EXIT_SUCCESS; - int verbosity = 2; - - XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); - - if (XTESTS_START_RUNNER("test.unit.cstring.cstring_vector", verbosity)) - { - XTESTS_RUN_CASE(test_1_00); - XTESTS_RUN_CASE(test_1_01); - XTESTS_RUN_CASE(test_1_02); - XTESTS_RUN_CASE(test_1_03); - XTESTS_RUN_CASE(test_1_04); - XTESTS_RUN_CASE(test_1_05); - XTESTS_RUN_CASE(test_1_06); - XTESTS_RUN_CASE(test_1_07); - XTESTS_RUN_CASE(test_1_08); - XTESTS_RUN_CASE(test_1_09); - XTESTS_RUN_CASE(test_1_10); - XTESTS_RUN_CASE(test_1_11); - XTESTS_RUN_CASE(test_1_12); - XTESTS_RUN_CASE(test_1_13); - XTESTS_RUN_CASE(test_1_14); - XTESTS_RUN_CASE(test_1_15); - XTESTS_RUN_CASE(test_1_16); - XTESTS_RUN_CASE(test_1_17); - XTESTS_RUN_CASE(test_1_18); - XTESTS_RUN_CASE(test_1_19); - - XTESTS_PRINT_RESULTS(); - - XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode); - } - - return retCode; -} - -int main(int argc, char **argv) -{ -#ifdef USE_PANTHEIOS_EXTRAS_DIAGUTIL_MAIN - - return pantheios_extras_diagutil_main_leak_trace_invoke(argc, argv, main0); -#else - - return main0(argc, argv); -#endif -} - -static void test_1_00(void) -{ - cstring_vector_t csv = cstring_vector_t_DEFAULT; - - XTESTS_TEST_INTEGER_EQUAL(0, csv.len); - XTESTS_TEST_POINTER_EQUAL(NULL, csv.ptr); - XTESTS_TEST_INTEGER_EQUAL(0, csv.capacity); - - cstring_vector_destroy(&csv); -} - -static void test_1_01(void) -{ - cstring_vector_t csv; - CSTRING_RC rc = cstring_vector_init(&csv, 0); - - if (CSTRING_RC_OUTOFMEMORY == rc) - { - /*XTESTS_TEST_WARN(*/ - } - else - if (CSTRING_RC_SUCCESS == rc) - { - XTESTS_TEST_INTEGER_EQUAL(0, csv.len); - XTESTS_TEST_POINTER_EQUAL(NULL, csv.ptr); - XTESTS_TEST_INTEGER_EQUAL(0, csv.capacity); - - cstring_vector_destroy(&csv); - } - else - { - XTESTS_TEST_FAIL("should only fail cstring_vector_init() with CSTRING_RC_OUTOFMEMORY"); - } -} - -static void test_1_02(void) -{ - cstring_vector_t csv; - CSTRING_RC rc = cstring_vector_init(&csv, 10); - - if (CSTRING_RC_OUTOFMEMORY == rc) - { - /*XTESTS_TEST_WARN(*/ - } - else - if (CSTRING_RC_SUCCESS == rc) - { - XTESTS_TEST_INTEGER_EQUAL(0, csv.len); - XTESTS_TEST_POINTER_NOT_EQUAL(NULL, csv.ptr); - XTESTS_TEST_INTEGER_GREATER_OR_EQUAL(10, csv.capacity); - - cstring_vector_destroy(&csv); - } - else - { - XTESTS_TEST_FAIL("should only fail cstring_vector_init() with CSTRING_RC_OUTOFMEMORY"); - } -} - -static void test_1_03(void) -{ - cstring_vector_t csv; - CSTRING_RC rc = cstring_vector_create(&csv, 0); - - if (CSTRING_RC_OUTOFMEMORY == rc) - { - /*XTESTS_TEST_WARN(*/ - } - else - if (CSTRING_RC_SUCCESS == rc) - { - XTESTS_TEST_INTEGER_EQUAL(0, csv.len); - XTESTS_TEST_POINTER_EQUAL(NULL, csv.ptr); - XTESTS_TEST_INTEGER_EQUAL(0, csv.capacity); - - cstring_vector_destroy(&csv); - } - else - { - XTESTS_TEST_FAIL("should only fail cstring_vector_create() with CSTRING_RC_OUTOFMEMORY"); - } -} - -static void test_1_04(void) -{ - cstring_vector_t csv; - CSTRING_RC rc = cstring_vector_create(&csv, 10); - - if (CSTRING_RC_OUTOFMEMORY == rc) - { - /*XTESTS_TEST_WARN(*/ - } - else - if (CSTRING_RC_SUCCESS == rc) - { - XTESTS_TEST_INTEGER_EQUAL(10, csv.len); - XTESTS_TEST_POINTER_NOT_EQUAL(NULL, csv.ptr); - XTESTS_TEST_INTEGER_GREATER_OR_EQUAL(10, csv.capacity); - - cstring_vector_destroy(&csv); - } - else - { - XTESTS_TEST_FAIL("should only fail cstring_vector_create() with CSTRING_RC_OUTOFMEMORY"); - } -} - -static void test_1_05(void) -{ - cstring_vector_t csv; - CSTRING_RC rc = cstring_vector_create(&csv, 0); - - if (CSTRING_RC_OUTOFMEMORY == rc) - { - /*XTESTS_TEST_WARN(*/ - } - else - if (CSTRING_RC_SUCCESS == rc) - { - cstring_t cs = cstring_t_DEFAULT; - - XTESTS_TEST_INTEGER_EQUAL(0, csv.len); - XTESTS_TEST_POINTER_EQUAL(NULL, csv.ptr); - XTESTS_TEST_INTEGER_EQUAL(0, csv.capacity); - - rc = cstring_vector_insertAt(&csv, 0, &cs, 1); - - if (CSTRING_RC_OUTOFMEMORY == rc) - { - /*XTESTS_TEST_WARN(*/ - } - else - if (CSTRING_RC_SUCCESS == rc) - { - XTESTS_TEST_INTEGER_EQUAL(1, csv.len); - XTESTS_TEST_POINTER_NOT_EQUAL(NULL, csv.ptr); - XTESTS_TEST_INTEGER_GREATER_OR_EQUAL(1, csv.capacity); - } - else - { - XTESTS_TEST_FAIL("should only fail cstring_vector_insertAt() with CSTRING_RC_OUTOFMEMORY"); - } - - cstring_vector_destroy(&csv); - } - else - { - XTESTS_TEST_FAIL("should only fail cstring_vector_create() with CSTRING_RC_OUTOFMEMORY"); - } -} - -static void test_1_06(void) -{ - char const* const strings[] = - { - "blah", - "", - "abcdefghijklmnopqrstuvwxyz", - "My Ever Changing Moods", - "I Can Give You Everything", - "Newborn Friend", - "Escape Velocity", - "Weapon Of Choice", - "Ain't No Doubt", - "Eye Know", - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", - }; - - cstring_vector_t csv; - CSTRING_RC rc = cstring_vector_create(&csv, 0); - - if (CSTRING_RC_OUTOFMEMORY == rc) - { - /*XTESTS_TEST_WARN(*/ - } - else - if (CSTRING_RC_SUCCESS == rc) - { - cstring_t cs = cstring_t_DEFAULT; - size_t i; - - XTESTS_TEST_INTEGER_EQUAL(0, csv.len); - XTESTS_TEST_POINTER_EQUAL(NULL, csv.ptr); - XTESTS_TEST_INTEGER_EQUAL(0, csv.capacity); - - for (i = 0; i != STLSOFT_NUM_ELEMENTS(strings) && CSTRING_RC_SUCCESS == rc; ++i) - { - rc = cstring_assign(&cs, strings[i]); - - if (CSTRING_RC_SUCCESS == rc) - { - rc = cstring_vector_insertAt(&csv, i, &cs, 1); - } - } - - cstring_destroy(&cs); - - if (CSTRING_RC_OUTOFMEMORY == rc) - { - /*XTESTS_TEST_WARN(*/ - } - else - if (CSTRING_RC_SUCCESS == rc) - { - XTESTS_REQUIRE(XTESTS_TEST_INTEGER_EQUAL(STLSOFT_NUM_ELEMENTS(strings), csv.len)); - XTESTS_TEST_POINTER_NOT_EQUAL(NULL, csv.ptr); - XTESTS_TEST_INTEGER_GREATER_OR_EQUAL(STLSOFT_NUM_ELEMENTS(strings), csv.capacity); - - for (i = 0; i != STLSOFT_NUM_ELEMENTS(strings); ++i) - { - XTESTS_TEST_INTEGER_EQUAL(strlen(strings[i]), csv.ptr[i].len); - XTESTS_TEST_MULTIBYTE_STRING_EQUAL(strings[i], csv.ptr[i].ptr); - } - } - else - { - XTESTS_TEST_FAIL("should only fail cstring_vector_insertAt() with CSTRING_RC_OUTOFMEMORY"); - } - - cstring_vector_destroy(&csv); - } - else - { - XTESTS_TEST_FAIL("should only fail cstring_vector_create() with CSTRING_RC_OUTOFMEMORY"); - } -} - -static void test_1_07(void) -{ -} - -static void test_1_08(void) -{ -} - -static void test_1_09(void) -{ -} - -static void test_1_10(void) -{ -} - -static void test_1_11(void) -{ -} - -static void test_1_12(void) -{ -} - -static void test_1_13(void) -{ -} - -static void test_1_14(void) -{ -} - -static void test_1_15(void) -{ -} - -static void test_1_16(void) -{ -} - -static void test_1_17(void) -{ -} - -static void test_1_18(void) -{ -} - -static void test_1_19(void) -{ -} - -/* ///////////////////////////// end of file //////////////////////////// */ -