From e460f9fe0044e764f0f0bdb3da113e5a0f0cf820 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Sun, 27 Sep 2026 17:51:17 +1000 Subject: [PATCH 01/15] squash-commit (test.performance.cstring_readline) --- .github/workflows/ci.yml | 1 - include/cstring/cstring.h | 2 +- src/cstring.core.c | 9 +- test/performance/CMakeLists.txt | 1 + .../CMakeLists.txt | 5 + .../main.cpp | 609 ++++++++++++++++++ 6 files changed, 621 insertions(+), 6 deletions(-) create mode 100644 test/performance/test.performance.cstring_readline/CMakeLists.txt create mode 100644 test/performance/test.performance.cstring_readline/main.cpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a6e40f3..758f139 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,6 @@ on: - rc1 - rc2 - rc3 - - perf-tests-win pull_request: concurrency: diff --git a/include/cstring/cstring.h b/include/cstring/cstring.h index edd5026..0db2dee 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: 28th September 2026 * * Home: http://synesis.com.au/software/ * diff --git a/src/cstring.core.c b/src/cstring.core.c index 00f77c3..b7668dd 100644 --- a/src/cstring.core.c +++ b/src/cstring.core.c @@ -291,17 +291,18 @@ 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); return NULL; } -#endif /* _DEBUG */ + pvNew = realloc(pv, cb); break; -#ifdef CSTRING_USE_WINAPI_ +#if defined(CSTRING_USE_WINAPI_) case CSTRING_F_USE_WINDOWS_GLOBAL_MEMORY: pvNew = win32_global_realloc(pv, cb); diff --git a/test/performance/CMakeLists.txt b/test/performance/CMakeLists.txt index 15d78ed..6c8958d 100644 --- a/test/performance/CMakeLists.txt +++ b/test/performance/CMakeLists.txt @@ -4,6 +4,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}) add_subdirectory(test.performance.cstring) +add_subdirectory(test.performance.cstring_readline) add_subdirectory(test.performance.cstring_vector) diff --git a/test/performance/test.performance.cstring_readline/CMakeLists.txt b/test/performance/test.performance.cstring_readline/CMakeLists.txt new file mode 100644 index 0000000..7588498 --- /dev/null +++ b/test/performance/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/test.performance.cstring_readline/main.cpp b/test/performance/test.performance.cstring_readline/main.cpp new file mode 100644 index 0000000..8c40189 --- /dev/null +++ b/test/performance/test.performance.cstring_readline/main.cpp @@ -0,0 +1,609 @@ +/* ///////////////////////////////////////////////////////////////////////// + * 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: 27th September 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"; + +enum line_ending_t +{ + LINE_ENDING_LF = 0, + LINE_ENDING_CRLF, + LINE_ENDING_NONE, +}; + +enum instance_mode_t +{ + INSTANCE_REUSE = 0, + INSTANCE_FRESH, + INSTANCE_PRERESERVED, +}; + +struct readline_case +{ + char const* scenario; + std::size_t num_lines; + std::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; + +struct run_result +{ + interval_t tm_ns; + std::uint64_t anchor; + p99::histogram hist; +}; + +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; + result.hist.clear(); + + interval_t tm_ns = 0; + + 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)); + } + + 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 +) +{ + double const ratio = + (0 == ::strcmp(impl, IMPL_READLINE)) + ? 1.0 + : ratio_or_dash(r.tm_ns, cstring_tm_ns) + ; + + display_results( + scenario + , size + , impl + , num_iterations + , num_actions + , r.tm_ns + , ratio + , r.anchor + , &r.hist + ); +} + +std::uint64_t +line_anchor( + std::size_t len +, unsigned char head +) +{ + return static_cast(len) + 1u + static_cast(head); +} + +unsigned char +content_head( + char const* p +, std::size_t n +) +{ + if (0 == n || NULL == p) + { + return 0; + } + + return static_cast(p[0]); +} + +bool +write_lines_file( + char const* path +, std::size_t num_lines +, std::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 (std::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); +} + +std::uint64_t +read_all_cstring( + char const* path +, std::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 +, std::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 +, std::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)) + { + std::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 +, std::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 +, std::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 +, std::size_t num_trials +, std::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; + std::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 + + std::size_t const num_trials = cstring_perf::default_file_trials(); + std::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 (std::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 //////////////////////////// */ + From 0de2177a122b234e4ae2f7a6ca7ce4dbaf945857 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Tue, 29 Sep 2026 12:03:37 +1000 Subject: [PATCH 02/15] date --- include/cstring/cstring.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/cstring/cstring.h b/include/cstring/cstring.h index 0db2dee..edd5026 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: 28th September 2026 + * Updated: 29th September 2026 * * Home: http://synesis.com.au/software/ * From 218afc47fd1d6299367668cb703bcea39ed0720f Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Tue, 29 Sep 2026 12:22:23 +1000 Subject: [PATCH 03/15] portability --- .../test.performance.cstring/main.cpp | 129 +++++++++--------- 1 file changed, 64 insertions(+), 65 deletions(-) diff --git a/test/performance/test.performance.cstring/main.cpp b/test/performance/test.performance.cstring/main.cpp index f7b8c48..fb76447 100644 --- a/test/performance/test.performance.cstring/main.cpp +++ b/test/performance/test.performance.cstring/main.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -39,7 +40,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 +59,7 @@ cstring_arena const WINDOWS_CSTRING_ARENAS[] = }; #endif /* _WIN32 */ -std::size_t const SIZES[] = +size_t const SIZES[] = { 16u, 64u, @@ -67,7 +67,6 @@ std::size_t const SIZES[] = 4096u, 65536u, }; - } // anonymous namespace @@ -92,15 +91,15 @@ struct run_result template run_result time_iterations( - std::size_t num_iterations -, std::size_t num_warm_loops -, F fn + size_t num_iterations +, size_t num_warm_loops +, F fn ) { run_result result = {}; stopwatch_t sw; - for (std::size_t w = num_warm_loops; 0 != w; --w) + for (size_t w = num_warm_loops; 0 != w; --w) { result.anchor = 0; #ifdef HAS_P99 @@ -110,7 +109,7 @@ time_iterations( interval_t tm_ns = 0; #ifdef HAS_P99 - for (std::size_t i = 0; num_iterations != i; ++i) + for (size_t i = 0; num_iterations != i; ++i) { sw.start(); result.anchor += fn(); @@ -124,7 +123,7 @@ time_iterations( #else /* ? HAS_P99 */ sw.start(); - for (std::size_t i = 0; num_iterations != i; ++i) + for (size_t i = 0; num_iterations != i; ++i) { result.anchor += fn(); } @@ -143,13 +142,13 @@ time_iterations( 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 = @@ -180,18 +179,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 +235,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 +288,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 +347,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 +407,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 +419,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 +430,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 +440,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 +466,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); } @@ -485,21 +484,21 @@ scenario_append_len_growth( 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 +510,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 +521,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 +545,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 +563,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 +628,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); @@ -702,9 +701,9 @@ scenario_copy( void scenario_borrowed_fixed( - 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); @@ -752,11 +751,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,10 +777,10 @@ 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); From 36603b0ef9e56eec4106babbf05bc84511845832 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Tue, 29 Sep 2026 12:32:46 +1000 Subject: [PATCH 04/15] test(cstring): use auto_buffer in borrowed_fixed_assign harness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace per-iteration std::vector arenas with stlsoft::auto_buffer (512 stack elements) so sizes ≤256 avoid a heap alloc in the timed path; rename scenario_borrowed_fixed to scenario_borrowed_fixed_assign to match the measured operation. --- test/performance/test.performance.cstring/main.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/test/performance/test.performance.cstring/main.cpp b/test/performance/test.performance.cstring/main.cpp index fb76447..a08618d 100644 --- a/test/performance/test.performance.cstring/main.cpp +++ b/test/performance/test.performance.cstring/main.cpp @@ -20,8 +20,9 @@ #include "perf_harness.hpp" +#include + #include -#include #include #include @@ -67,6 +68,8 @@ size_t const SIZES[] = 4096u, 65536u, }; + +const size_t NUM_STACK_ELEMENTS = 512; } // anonymous namespace @@ -700,7 +703,7 @@ scenario_copy( } void -scenario_borrowed_fixed( +scenario_borrowed_fixed_assign( size_t n , size_t num_iterations , size_t num_warm_loops @@ -710,7 +713,7 @@ scenario_borrowed_fixed( 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_createEx( &s @@ -732,7 +735,7 @@ scenario_borrowed_fixed( }); 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]); @@ -786,7 +789,7 @@ int main(int /*argc*/, char* /*argv*/[]) 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_assign(n, iters, num_warm_loops); if (n <= 4096u) { From 1e0bc91f17b647646b0024b80305dc6ce958bebb Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Tue, 29 Sep 2026 12:48:47 +1000 Subject: [PATCH 05/15] (fix(performance tests): `scenario_borrowed_fixed_assign()` all anchor values calculated consistently --- test/performance/test.performance.cstring/main.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/performance/test.performance.cstring/main.cpp b/test/performance/test.performance.cstring/main.cpp index a08618d..b8f99ad 100644 --- a/test/performance/test.performance.cstring/main.cpp +++ b/test/performance/test.performance.cstring/main.cpp @@ -723,22 +723,25 @@ scenario_borrowed_fixed_assign( , 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 { 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); From e1ff75e186c039d3bd171ae9f87b19c425d8e966 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Tue, 29 Sep 2026 13:01:44 +1000 Subject: [PATCH 06/15] feature(performance tests): added `scenario_borrowed_fixed_construct()` --- .../test.performance.cstring/main.cpp | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/test/performance/test.performance.cstring/main.cpp b/test/performance/test.performance.cstring/main.cpp index b8f99ad..3690c93 100644 --- a/test/performance/test.performance.cstring/main.cpp +++ b/test/performance/test.performance.cstring/main.cpp @@ -702,6 +702,53 @@ scenario_copy( emit_row("copy", n, IMPL_RAW, num_iterations, 1, raw, cs.tm_ns); } +void +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 { + 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 @@ -792,6 +839,7 @@ int main(int /*argc*/, char* /*argv*/[]) 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_construct(n, iters, num_warm_loops); scenario_borrowed_fixed_assign(n, iters, num_warm_loops); if (n <= 4096u) From a467c4a0aa40899b1ba6c4460c9c5b2484c2c358 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Tue, 29 Sep 2026 14:08:30 +1000 Subject: [PATCH 07/15] fix(cstring): silence GCC free-nonheap-object in the perf harness GCC 15 with -Werror flags an inlined stlsoft::auto_buffer destructor as freeing its internal buffer. Suppress -Wfree-nonheap-object on test.performance.cstring; the destructor deallocates external storage only. --- test/performance/test.performance.cstring/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/performance/test.performance.cstring/CMakeLists.txt b/test/performance/test.performance.cstring/CMakeLists.txt index 96b4531..348b004 100644 --- a/test/performance/test.performance.cstring/CMakeLists.txt +++ b/test/performance/test.performance.cstring/CMakeLists.txt @@ -1,3 +1,8 @@ +# 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 ) From e0575361fa7d1098df7186f161aaafc907e28adb Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Tue, 29 Sep 2026 14:20:18 +1000 Subject: [PATCH 08/15] misc --- CMakeLists.txt | 8 ++++---- prepare_cmake.sh | 2 +- test/performance/perf_harness.hpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 82e1af4..aca4361 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 @@ -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/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/test/performance/perf_harness.hpp b/test/performance/perf_harness.hpp index 5b91e6f..24a6eca 100644 --- a/test/performance/perf_harness.hpp +++ b/test/performance/perf_harness.hpp @@ -186,7 +186,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." From 06111acfe9bd30e209597f60b1a1f52a76378045 Mon Sep 17 00:00:00 2001 From: synesissoftware Date: Tue, 29 Sep 2026 15:55:53 +1000 Subject: [PATCH 09/15] Internal refac (#35) * misc * chore(internal): removed `CSTRING_USE_WINAPI_` and now discriminate on `_WIN32` * chore(internal): simplified includes * refactor(cstring): centralise CSTRING_ASSERT in internal.h Define CSTRING_ASSERT once in src/internal.h, with , and drop the copies in cstring.core.c and cstring.vector.c. Vector call sites use that macro in place of CSTRING_VECTOR_ASSERT. dllmain.c includes internal.h. * fix --- TODO.md | 2 +- src/cstring.core.c | 12 ++---------- src/cstring.vector.c | 20 ++++++++++---------- src/dllmain.c | 4 +++- src/internal.h | 35 ++++++++++++++++++++++++++++------- src/win.c | 1 + 6 files changed, 45 insertions(+), 29 deletions(-) diff --git a/TODO.md b/TODO.md index a6c3bf2..4f1564b 100644 --- a/TODO.md +++ b/TODO.md @@ -15,7 +15,7 @@ * [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); diff --git a/src/cstring.core.c b/src/cstring.core.c index b7668dd..b00bdec 100644 --- a/src/cstring.core.c +++ b/src/cstring.core.c @@ -59,7 +59,6 @@ /* Standard C header files */ -#include #include #include #include @@ -78,13 +77,6 @@ #define CSTRING_OFFSET_SIZE (16) -/* ///////////////////////////////////////////////////////////////////////// - * debugging - */ - -#define CSTRING_ASSERT(expr) assert(expr) - - /* ///////////////////////////////////////////////////////////////////////// * character encoding */ @@ -302,7 +294,7 @@ cstring_realloc_2_( pvNew = realloc(pv, cb); break; -#if defined(CSTRING_USE_WINAPI_) +#ifdef _WIN32 case CSTRING_F_USE_WINDOWS_GLOBAL_MEMORY: pvNew = win32_global_realloc(pv, cb); @@ -315,7 +307,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 */ diff --git a/src/cstring.vector.c b/src/cstring.vector.c index 59152c8..81f43ed 100644 --- a/src/cstring.vector.c +++ b/src/cstring.vector.c @@ -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 @@ -114,7 +114,7 @@ cstring_vector_init( , size_t minCapacity ) { - CSTRING_VECTOR_ASSERT(NULL != pcsv); + CSTRING_ASSERT(NULL != pcsv); if (cstring_vector_DEFAULT_CAPACITY == minCapacity) { @@ -158,7 +158,7 @@ cstring_vector_destroy( CSTRING_RC rc = CSTRING_RC_SUCCESS; size_t i; - CSTRING_VECTOR_ASSERT(NULL != pcsv); + CSTRING_ASSERT(NULL != pcsv); for (i = 0; i != pcsv->len; ++i) { @@ -190,8 +190,8 @@ cstring_vector_truncate( CSTRING_RC rc = CSTRING_RC_SUCCESS; size_t i; - CSTRING_VECTOR_ASSERT(NULL != pcsv); - CSTRING_VECTOR_ASSERT(len <= pcsv->len); + CSTRING_ASSERT(NULL != pcsv); + CSTRING_ASSERT(len <= pcsv->len); for (i = len; i != pcsv->len; ++i) { @@ -215,7 +215,7 @@ cstring_vector_create( , size_t initialSize ) { - CSTRING_VECTOR_ASSERT(NULL != pcsv); + CSTRING_ASSERT(NULL != pcsv); if (cstring_vector_DEFAULT_CAPACITY == initialSize) { @@ -229,7 +229,7 @@ cstring_vector_create( { size_t i; - CSTRING_VECTOR_ASSERT(pcsv->capacity >= initialSize); + CSTRING_ASSERT(pcsv->capacity >= initialSize); for (i = 0; i != initialSize; ++i) { @@ -272,8 +272,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) { @@ -373,7 +373,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" From d25a6546b2a57a10ebfc469699e9316961179fe6 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Tue, 29 Sep 2026 17:35:25 +1000 Subject: [PATCH 10/15] misc --- src/cstring.core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cstring.core.c b/src/cstring.core.c index b00bdec..5bee299 100644 --- a/src/cstring.core.c +++ b/src/cstring.core.c @@ -1364,13 +1364,13 @@ cstring_readline( ) { int previous = '\0'; - size_t numRead_; + size_t dummy; CSTRING_ASSERT(NULL != pcs); if (NULL == numRead) { - numRead = &numRead_; + numRead = &dummy; } if (NULL == stm || @@ -1431,13 +1431,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 || @@ -1650,13 +1650,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; From 13a628da5a702526a3b7d88e6f6fada61fdc63ca Mon Sep 17 00:00:00 2001 From: synesissoftware Date: Sat, 3 Oct 2026 11:52:48 +1000 Subject: [PATCH 11/15] `cstring_readline()` fix for readonly string (#38) * dependencies * misc * test(cstring): show readline ignoring a failed readonly clear Add a component case that passes a non-empty readonly cstring to cstring_readline() for an empty line and for immediate EOF. The clear must return CSTRING_RC_READONLY, leave the payload in place, and leave the stream unconsumed. * docs(cstring): record the 4.0.19 readonly readline clear Describe the cstring_readline() truncate check in CHANGES.md and NEWS.md, set Doxyfile PROJECT_NUMBER to 4.0.19, and date the cstring.core.c banner. --- CHANGES.md | 6 ++ CMakeLists.txt | 2 +- Doxyfile | 2 +- NEWS.md | 1 + TODO.md | 2 + include/cstring/cstring.h | 9 ++- src/cstring.core.c | 19 ++++- .../test.component.cstring_readline/entry.cpp | 79 ++++++++++++++++++- 8 files changed, 109 insertions(+), 11 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ad45af0..1b2a41c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,12 @@ # cstring - Changes +## 4.0.19 - 3rd October 2026 + +* `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; +* Added **test.component.cstring_readline** case `TEST_cstring_readline_READONLY_RETAINS_PAYLOAD`; + + ## 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 aca4361..330b3e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/Doxyfile b/Doxyfile index 361b9a9..b549b16 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.0.19 # 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/NEWS.md b/NEWS.md index b1106c3..02d9c20 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,7 @@ | Date | News Item | Details | | ------------------- | -------------------------------- | ------- | | Available from [**cstring** project on GitHub](https://synesissoftware.com/cstring): | +| 3rd October 2026 | Release of [cstring 4.0.19](https://github.com/synesissoftware/cstring/releases/tag/4.0.19) | `cstring_readline()` reports a failed readonly clear | | 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/TODO.md b/TODO.md index 4f1564b..cf57dcf 100644 --- a/TODO.md +++ b/TODO.md @@ -17,6 +17,8 @@ * [x] ~~~discriminate on `_WIN32` in implementation (and maybe also in API)~~~ - ✅; * [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()`; ## Performance improvements diff --git a/include/cstring/cstring.h b/include/cstring/cstring.h index edd5026..663ba1c 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: 3rd 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 91 #endif /* !CSTRING_DOCUMENTATION_SKIP_SECTION */ @@ -103,6 +103,7 @@ # 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 #endif /* !CSTRING_DOCUMENTATION_SKIP_SECTION */ /** \def CSTRING_VER_MAJOR @@ -123,7 +124,7 @@ #define CSTRING_VER_MAJOR 4 #define CSTRING_VER_MINOR 0 -#define CSTRING_VER_PATCH 18 +#define CSTRING_VER_PATCH 19 #define CSTRING_VER_ALPHABETA 0xFF #define CSTRING_VER \ diff --git a/src/cstring.core.c b/src/cstring.core.c index 5bee299..7fbfa3f 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/ * @@ -1363,8 +1363,9 @@ cstring_readline( , size_t* numRead /* = NULL */ ) { - int previous = '\0'; - size_t dummy; + int previous = '\0'; + size_t dummy; + CSTRING_RC rc0; CSTRING_ASSERT(NULL != pcs); @@ -1381,7 +1382,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 (;;) { diff --git a/test/component/test.component.cstring_readline/entry.cpp b/test/component/test.component.cstring_readline/entry.cpp index 7bb3c7c..ad5d765 100644 --- a/test/component/test.component.cstring_readline/entry.cpp +++ b/test/component/test.component.cstring_readline/entry.cpp @@ -4,7 +4,7 @@ * Purpose: Component-tests `cstring_readline()`. * * Created: 23rd May 2009 - * Updated: 27th September 2026 + * Updated: 3rd October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -56,6 +56,7 @@ namespace 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); + static void TEST_cstring_readline_READONLY_RETAINS_PAYLOAD(void); int setup(void*); int teardown(void*); @@ -104,6 +105,7 @@ int main(int argc, char* argv[]) 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(); @@ -578,6 +580,81 @@ static void TEST_cstring_readline_REUSE_AFTER_LONG_LINE() 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(0u, 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 From 87803635e6a5c8ba7e6f6d09c061a814aa5f42ab Mon Sep 17 00:00:00 2001 From: synesissoftware Date: Sat, 3 Oct 2026 12:06:15 +1000 Subject: [PATCH 12/15] fix: `cstring_readline()` (#37) * dependencies * chore(tests): canonicalised form * refactoring(tests): canonicalised form * tests(readline): more test cases * refactor(readline): canonicalising tests * fix(scripts): drop --verbosity from non-automated runners Example, performance, and scratch runners do not take --verbosity and do not pass it to the programs they launch. Each program is invoked with no extra arguments. Unit and component runners still accept the flag and forward it. Apply the same change to the cstring runners and to the cmake-helpers templates they are copied from. * fix(cstring): count the newline in cstring_readline() numRead Count every character read from the stream in numRead, including the terminating newline. A CRLF's CR is still counted and still stripped from the string. Previously that newline was omitted, so a line containing only LF reported zero. Add single-line LF and CRLF component cases and align the existing numRead expectations. Record the correction as 4.0.19. * chore(tests): simplified form(s) * fix(cstring): treat CR, LF, and CRLF as cstring_readline terminators Count every character read for the line in numRead, including the terminator, and do not store CR or LF. A lone CR, including CR at end of stream, ends the line with CSTRING_RC_SUCCESS and pushes the following character back. Add single-line LF, CRLF, and CR component cases. An embedded CR now splits a cstring_vector_readLines line. Record the correction as 4.0.19. * tests(readline): more test cases * tests(readline): more test cases * test(cstring): close readline temp files before reopen xtests::temp_file creates its Windows file with an exclusive handle and leaves that handle open. The readline component tests then fopen the same path, which Windows rejects as a sharing violation. Pass CloseOnOpen so the creating handle is closed before fopen. Drop the local argc == 0 guard, which skipped the suite on every normal invocation. * fix * fix --- CHANGES.md | 4 + NEWS.md | 2 +- include/cstring/cstring.h | 12 +- run_all_component_tests.sh | 7 +- run_all_examples.sh | 24 +- run_all_performance_tests.sh | 24 +- run_all_scratch_tests.sh | 24 +- run_all_unit_tests.sh | 7 +- src/cstring.core.c | 36 +- .../test.component.cstring_readline/entry.cpp | 382 +++++++++++++----- .../entry.cpp | 31 +- .../entry.cpp | 16 +- .../test.performance.cstring_vector/main.cpp | 3 +- test/unit/test.unit.cstring.1/entry.cpp | 48 +-- 14 files changed, 381 insertions(+), 239 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 1b2a41c..c8cb447 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,10 @@ ## 4.0.19 - 3rd 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; +* 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; +* 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; * `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; * Added **test.component.cstring_readline** case `TEST_cstring_readline_READONLY_RETAINS_PAYLOAD`; diff --git a/NEWS.md b/NEWS.md index 02d9c20..b9c2f48 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,7 +4,7 @@ | Date | News Item | Details | | ------------------- | -------------------------------- | ------- | | Available from [**cstring** project on GitHub](https://synesissoftware.com/cstring): | -| 3rd October 2026 | Release of [cstring 4.0.19](https://github.com/synesissoftware/cstring/releases/tag/4.0.19) | `cstring_readline()` reports a failed readonly clear | +| 3rd October 2026 | Release of [cstring 4.0.19](https://github.com/synesissoftware/cstring/releases/tag/4.0.19) | lone CR ends a line; `cstring_readline()` `numRead` includes EOL; `cstring_readline()` reports failed readonly | | 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/include/cstring/cstring.h b/include/cstring/cstring.h index 663ba1c..7c3f9ed 100644 --- a/include/cstring/cstring.h +++ b/include/cstring/cstring.h @@ -56,7 +56,7 @@ # define CSTRING_VER_CSTRING_H_CSTRING_MAJOR 3 # define CSTRING_VER_CSTRING_H_CSTRING_MINOR 12 # define CSTRING_VER_CSTRING_H_CSTRING_REVISION 2 -# define CSTRING_VER_CSTRING_H_CSTRING_EDIT 91 +# define CSTRING_VER_CSTRING_H_CSTRING_EDIT 93 #endif /* !CSTRING_DOCUMENTATION_SKIP_SECTION */ @@ -827,17 +827,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/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/cstring.core.c b/src/cstring.core.c index 7fbfa3f..bbd6245 100644 --- a/src/cstring.core.c +++ b/src/cstring.core.c @@ -62,7 +62,6 @@ #include #include #include - #ifdef CSTRING_USE_WIDE_STRINGS # include #endif /* CSTRING_USE_WIDE_STRINGS */ @@ -1400,10 +1399,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) @@ -1415,19 +1423,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; + 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); - previous = ch; + if (CSTRING_RC_SUCCESS != rc) + { + return rc; + } + } } + previous = ch; } } } diff --git a/test/component/test.component.cstring_readline/entry.cpp b/test/component/test.component.cstring_readline/entry.cpp index ad5d765..c1b5484 100644 --- a/test/component/test.component.cstring_readline/entry.cpp +++ b/test/component/test.component.cstring_readline/entry.cpp @@ -23,6 +23,7 @@ /* xTests header files */ #include #include +#include /* STLSoft header files */ #include @@ -46,17 +47,24 @@ 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); - static void TEST_cstring_readline_READONLY_RETAINS_PAYLOAD(void); + 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(); int setup(void*); int teardown(void*); @@ -97,11 +105,18 @@ int main(int argc, char* argv[]) { 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_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); + 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); @@ -196,6 +211,8 @@ namespace namespace { + using ::xtests::cpp::util::temp_file; + static void TEST_cstring_readline_CALLABILITY() { @@ -215,6 +232,121 @@ static void TEST_cstring_readline_INVALID_STREAM() 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() { { @@ -246,48 +378,48 @@ static void TEST_cstring_readline_SHORT_MULTILINE() cstring_t_DEFAULT, cstring_t_DEFAULT, }; - size_t lengths[7]; + size_t readCounts[7]; - rc = cstring_readline(f, &strings[0], &lengths[0]); + rc = cstring_readline(f, &strings[0], &readCounts[0]); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(0u, lengths[0]); + TEST_INT_EQ(1u, readCounts[0]); TEST_MS_EQ("", strings[0]); - rc = cstring_readline(f, &strings[1], &lengths[1]); + rc = cstring_readline(f, &strings[1], &readCounts[1]); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(3u, lengths[1]); + TEST_INT_EQ(4u, readCounts[1]); TEST_MS_EQ("abc", strings[1]); - rc = cstring_readline(f, &strings[2], &lengths[2]); + rc = cstring_readline(f, &strings[2], &readCounts[2]); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(6u, lengths[2]); + TEST_INT_EQ(7u, readCounts[2]); TEST_MS_EQ("abcdef", strings[2]); - rc = cstring_readline(f, &strings[3], &lengths[3]); + rc = cstring_readline(f, &strings[3], &readCounts[3]); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(12u, lengths[3]); + TEST_INT_EQ(13u, readCounts[3]); TEST_MS_EQ("abcdefghijkl", strings[3]); - rc = cstring_readline(f, &strings[4], &lengths[4]); + rc = cstring_readline(f, &strings[4], &readCounts[4]); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(26u, lengths[4]); + TEST_INT_EQ(27u, readCounts[4]); TEST_MS_EQ("abcdefghijklmnopqrstuvwxyz", strings[4]); - rc = cstring_readline(f, &strings[5], &lengths[5]); + rc = cstring_readline(f, &strings[5], &readCounts[5]); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(62u, lengths[5]); + TEST_INT_EQ(63u, readCounts[5]); TEST_MS_EQ("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", strings[5]); - rc = cstring_readline(f, &strings[6], &lengths[6]); + rc = cstring_readline(f, &strings[6], &readCounts[6]); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); - TEST_INT_EQ(3u, lengths[6]); + TEST_INT_EQ(3u, readCounts[6]); TEST_MS_EQ("xyz", strings[6]); { for (size_t i = 0; i != 7; ++i) @@ -297,27 +429,6 @@ static void TEST_cstring_readline_SHORT_MULTILINE() } } -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) */ @@ -338,37 +449,43 @@ static void TEST_cstring_readline_CRLF_AND_MIXED_EOL() stlsoft::scoped_handle scoper(f, ::fclose); cstring_t cs = cstring_t_DEFAULT; - size_t n; + size_t numRead; CSTRING_RC rc; - rc = cstring_readline(f, &cs, &n); + rc = cstring_readline(f, &cs, &numRead); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - /* CRLF: CR is appended then stripped; numRead still counts the CR */ - TEST_INT_EQ(4u, n); + /* 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, &n); + rc = cstring_readline(f, &cs, &numRead); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(3u, n); + TEST_INT_EQ(4u, numRead); TEST_INT_EQ(3u, cs.len); TEST_MS_EQ("two", cs); - rc = cstring_readline(f, &cs, &n); + rc = cstring_readline(f, &cs, &numRead); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(6u, n); + TEST_INT_EQ(7u, numRead); TEST_INT_EQ(5u, cs.len); TEST_MS_EQ("three", cs); - rc = cstring_readline(f, &cs, &n); + 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(10u, n); - TEST_INT_EQ(9u, cs.len); - TEST_MS_EQ("has\rembed", cs); + TEST_INT_EQ(7u, numRead); + TEST_INT_EQ(5u, cs.len); + TEST_MS_EQ("embed", cs); - rc = cstring_readline(f, &cs, &n); + rc = cstring_readline(f, &cs, &numRead); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); - TEST_INT_EQ(3u, n); + TEST_INT_EQ(3u, numRead); TEST_INT_EQ(3u, cs.len); TEST_MS_EQ("end", cs); @@ -378,84 +495,145 @@ static void TEST_cstring_readline_CRLF_AND_MIXED_EOL() 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); + { + 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 n; + size_t numRead; CSTRING_RC rc; - rc = cstring_readline(f, &cs, &n); + rc = cstring_readline(f, &cs, &numRead); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_MS_EQ("alpha", cs); - rc = cstring_readline(f, &cs, &n); + rc = cstring_readline(f, &cs, &numRead); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_MS_EQ("beta", cs); - rc = cstring_readline(f, &cs, &n); + rc = cstring_readline(f, &cs, &numRead); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); - TEST_INT_EQ(0u, n); + TEST_INT_EQ(0u, numRead); 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); + 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 n; + size_t numRead; CSTRING_RC rc; - rc = cstring_readline(f, &cs, &n); + rc = cstring_readline(f, &cs, &numRead); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); TEST_MS_EQ("alpha", cs); - rc = cstring_readline(f, &cs, &n); + rc = cstring_readline(f, &cs, &numRead); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); - TEST_INT_EQ(4u, n); + TEST_INT_EQ(4u, numRead); TEST_MS_EQ("beta", cs); cstring_destroy(&cs); } } -static void TEST_cstring_readline_CONSECUTIVE_EMPTY_LINES() +static void TEST_cstring_readline_CONSECUTIVE_EMPTY_LINES_BY_LF() { /* consecutive empty lines */ - write_string_bytes(TEST_FILE_NAME, std::string("\n\n\n")); - FILE* f = fopen_or_throw(TEST_FILE_NAME, "rb"); + 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); - 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 n; + size_t numRead; CSTRING_RC rc; { for (int i = 0; i != 3; ++i) { - rc = cstring_readline(f, &cs, &n); + rc = cstring_readline(f, &cs, &numRead); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(0u, n); + 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, &n); + rc = cstring_readline(f, &cs, &numRead); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); - TEST_INT_EQ(0u, n); + TEST_INT_EQ(0u, numRead); cstring_destroy(&cs); } @@ -486,18 +664,18 @@ static void TEST_cstring_readline_LONG_LINES() stlsoft::scoped_handle scoper(f, ::fclose); cstring_t cs = cstring_t_DEFAULT; - size_t n; + size_t numRead; CSTRING_RC rc; - rc = cstring_readline(f, &cs, &n); + rc = cstring_readline(f, &cs, &numRead); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(len, n); + 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, &n); + rc = cstring_readline(f, &cs, &numRead); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); - TEST_INT_EQ(len / 2u == 0 ? 1u : len / 2u, n); + TEST_INT_EQ(len / 2u == 0 ? 1u : len / 2u, numRead); cstring_destroy(&cs); }} @@ -530,22 +708,22 @@ static void TEST_cstring_readline_MANY_SHORT_LINES() stlsoft::scoped_handle scoper(f, ::fclose); cstring_t cs = cstring_t_DEFAULT; - size_t n; + size_t numRead; CSTRING_RC rc; { for (size_t i = 0; i != num_lines; ++i) { - rc = cstring_readline(f, &cs, &n); + rc = cstring_readline(f, &cs, &numRead); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(expected[i].size(), n); + TEST_INT_EQ(expected[i].size() + 1, numRead); TEST_MS_EQ(expected[i].c_str(), cs); }} - rc = cstring_readline(f, &cs, &n); + rc = cstring_readline(f, &cs, &numRead); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_EOF, rc)); - TEST_INT_EQ(0u, n); + TEST_INT_EQ(0u, numRead); cstring_destroy(&cs); } @@ -614,7 +792,7 @@ static void TEST_cstring_readline_READONLY_RETAINS_PAYLOAD() rc = cstring_readline(f, &fresh, &n); REQUIRE(TEST_ENUM_EQ(CSTRING_RC_SUCCESS, rc)); - TEST_INT_EQ(0u, n); + TEST_INT_EQ(1u, n); TEST_MS_EQ("", fresh); cstring_destroy(&fresh); diff --git a/test/component/test.component.cstring_vector_readLines/entry.cpp b/test/component/test.component.cstring_vector_readLines/entry.cpp index 5e6810b..be3b18c 100644 --- a/test/component/test.component.cstring_vector_readLines/entry.cpp +++ b/test/component/test.component.cstring_vector_readLines/entry.cpp @@ -4,7 +4,7 @@ * Purpose: Component-tests `cstring_vector_readLines()`. * * Created: 27th September 2026 - * Updated: 27th September 2026 + * Updated: 3rd October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -46,16 +46,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); + 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(); int setup(void*); int teardown(void*); @@ -281,13 +281,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/test.component.cstring_writeline/entry.cpp b/test/component/test.component.cstring_writeline/entry.cpp index fa52b71..05017c3 100644 --- a/test/component/test.component.cstring_writeline/entry.cpp +++ b/test/component/test.component.cstring_writeline/entry.cpp @@ -4,7 +4,7 @@ * Purpose: Unit-tests of `cstring_write()` and `cstring_writeline()`. * * Created: 10th August 2020 - * Updated: 27th September 2026 + * Updated: 2nd October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -41,16 +41,16 @@ 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); + 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(); int setup(void*); int teardown(void*); - } // anonymous namespace + /* ///////////////////////////////////////////////////////////////////////// * constants & definitions */ @@ -259,10 +259,8 @@ static void TEST_cstring_write_CONCATENATED() TEST_MS_EQ(expected, out_lines[0]); } } - - - } // anonymous namespace + /* ///////////////////////////// end of file //////////////////////////// */ diff --git a/test/performance/test.performance.cstring_vector/main.cpp b/test/performance/test.performance.cstring_vector/main.cpp index f783477..5de587b 100644 --- a/test/performance/test.performance.cstring_vector/main.cpp +++ b/test/performance/test.performance.cstring_vector/main.cpp @@ -8,7 +8,7 @@ * getline, platformstl::file_lines). * * Created: 23rd September 2026 - * Updated: 23rd September 2026 + * Updated: 2nd October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -169,7 +169,6 @@ make_cstring_payload( return cs; } - } // anonymous namespace diff --git a/test/unit/test.unit.cstring.1/entry.cpp b/test/unit/test.unit.cstring.1/entry.cpp index 214a071..ac49435 100644 --- a/test/unit/test.unit.cstring.1/entry.cpp +++ b/test/unit/test.unit.cstring.1/entry.cpp @@ -4,7 +4,7 @@ * Purpose: Unit-tests for general functionality. * * Created: 23rd May 2009 - * Updated: 29th September 2026 + * Updated: 2nd October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -51,29 +51,29 @@ 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_1_0(); + static void test_1_1(); + static void test_1_2(); + static void test_1_3(); + static void test_1_4(); + static void test_1_5(); + static void test_1_6(); + static void test_1_7(); + static void test_1_8(); + static void test_1_9(); + + static void test_2_0(); + static void test_2_1(); + static void test_2_2(); + static void test_2_3(); + static void test_2_4(); + static void test_2_5(); + static void test_2_6(); + static void test_2_7(); + static void test_2_8(); + static void test_2_9(); + static void test_2_10(); + static void test_2_11(); } // anonymous namespace From 178d40a9b0cc626667e402141b97013d6405f45b Mon Sep 17 00:00:00 2001 From: synesissoftware Date: Sun, 4 Oct 2026 07:02:26 +1100 Subject: [PATCH 13/15] test(cstring): shorten test directories and name cases in `TEST_` form (#39) * test(cstring): shorten test directories and name cases in TEST_ form Shorten test directories to the subject under test//, and name each CMake target test... Spell every case TEST_ in shouting snake case, keeping each API or type in its real spelling, and switch assertions to the terse xTests API. - Drop empty cases and unused temp-file names; - Rename **auto_buffer** to **cstring.borrowed**; - Use `size_t` in the performance programs; - Run CI on the **directories** branch. * docs(cstring): complete the 4.0.19 release notes Record post-4.0.18 behaviour and tests in **CHANGES.md** and **NEWS.md**; Cover zero-size realloc frees and the readline performance suite; borrowed-fixed scenarios, and shortened test directories with `TEST_` names; * chore(tests): refactored --- .github/workflows/ci.yml | 1 + CHANGES.md | 9 + CMakeLists.txt | 2 +- NEWS.md | 2 +- examples/c/example.c.cstring/main.c | 1 - examples/c/example.c.cstring_create/main.c | 5 +- examples/c/example.c.cstring_vector/main.c | 4 +- .../cpp/example.cpp.cstring.dynload/main.cpp | 1 + src/CMakeLists.txt | 4 +- test/component/CMakeLists.txt | 14 +- test/component/component_fixture.hpp | 152 ++++++ .../CMakeLists.txt | 2 +- .../entry.cpp | 84 +-- .../cstring_vector_readLines/CMakeLists.txt | 2 + .../entry.cpp | 86 +-- .../cstring_writeline/CMakeLists.txt | 5 + .../entry.cpp | 43 +- .../CMakeLists.txt | 2 - .../CMakeLists.txt | 5 - test/performance/CMakeLists.txt | 6 +- .../CMakeLists.txt | 0 .../main.cpp | 86 +-- .../CMakeLists.txt | 0 .../main.cpp | 162 ++---- .../CMakeLists.txt | 0 .../main.cpp | 187 ++----- test/performance/perf_harness.hpp | 174 +++++- test/unit/CMakeLists.txt | 10 +- .../CMakeLists.txt | 2 +- test/unit/cstring.borrowed/entry.c | 506 +++++++++++++++++ .../CMakeLists.txt | 2 +- .../entry.c | 117 ++-- .../CMakeLists.txt | 2 +- .../entry.cpp | 350 ++++++------ .../CMakeLists.txt | 5 + test/unit/cstring_getStatusCodeString/entry.c | 232 ++++++++ .../CMakeLists.txt | 0 test/unit/cstring_vector/entry.c | 318 +++++++++++ .../test.unit.cstring.auto_buffer/entry.c | 508 ------------------ .../CMakeLists.txt | 5 - .../entry.c | 233 -------- .../test.unit.cstring.cstring_vector/entry.c | 378 ------------- 42 files changed, 1805 insertions(+), 1902 deletions(-) create mode 100644 test/component/component_fixture.hpp rename test/component/{test.component.cstring_readline => cstring_readline}/CMakeLists.txt (53%) rename test/component/{test.component.cstring_readline => cstring_readline}/entry.cpp (92%) create mode 100644 test/component/cstring_vector_readLines/CMakeLists.txt rename test/component/{test.component.cstring_vector_readLines => cstring_vector_readLines}/entry.cpp (88%) create mode 100644 test/component/cstring_writeline/CMakeLists.txt rename test/component/{test.component.cstring_writeline => cstring_writeline}/entry.cpp (91%) delete mode 100644 test/component/test.component.cstring_vector_readLines/CMakeLists.txt delete mode 100644 test/component/test.component.cstring_writeline/CMakeLists.txt rename test/performance/{test.performance.cstring => cstring}/CMakeLists.txt (100%) rename test/performance/{test.performance.cstring => cstring}/main.cpp (93%) rename test/performance/{test.performance.cstring_readline => cstring_readline}/CMakeLists.txt (100%) rename test/performance/{test.performance.cstring_readline => cstring_readline}/main.cpp (78%) rename test/performance/{test.performance.cstring_vector => cstring_vector}/CMakeLists.txt (100%) rename test/performance/{test.performance.cstring_vector => cstring_vector}/main.cpp (77%) rename test/unit/{test.unit.cstring.2 => cstring.borrowed}/CMakeLists.txt (58%) create mode 100644 test/unit/cstring.borrowed/entry.c rename test/unit/{test.unit.cstring.auto_buffer => cstring.insert-replace}/CMakeLists.txt (56%) rename test/unit/{test.unit.cstring.2 => cstring.insert-replace}/entry.c (82%) rename test/unit/{test.unit.cstring.1 => cstring.instance}/CMakeLists.txt (58%) rename test/unit/{test.unit.cstring.1 => cstring.instance}/entry.cpp (78%) create mode 100644 test/unit/cstring_getStatusCodeString/CMakeLists.txt create mode 100644 test/unit/cstring_getStatusCodeString/entry.c rename test/unit/{test.unit.cstring.cstring_vector => cstring_vector}/CMakeLists.txt (100%) create mode 100644 test/unit/cstring_vector/entry.c delete mode 100644 test/unit/test.unit.cstring.auto_buffer/entry.c delete mode 100644 test/unit/test.unit.cstring.cstring_getStatusCodeString/CMakeLists.txt delete mode 100644 test/unit/test.unit.cstring.cstring_getStatusCodeString/entry.c delete mode 100644 test/unit/test.unit.cstring.cstring_vector/entry.c diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 758f139..9967548 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,7 @@ on: - rc1 - rc2 - rc3 + - directories pull_request: concurrency: diff --git a/CHANGES.md b/CHANGES.md index c8cb447..9412878 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,15 @@ * 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; * `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; * Added **test.component.cstring_readline** case `TEST_cstring_readline_READONLY_RETAINS_PAYLOAD`; +* Freed a zero-size realloc-arena block on every platform (`realloc(pv, 0)` allocates on some), and left a NULL pointer unchanged; +* Added **test.performance.cstring_readline** (filesystem timings gated on **p99**); +* Added borrowed-fixed construct and assign scenarios to **test.performance.cstring**, using `stlsoft::auto_buffer` for the borrowed anchor; +* Suppressed the GCC 15 `-Wfree-nonheap-object` false positive on inlined `stlsoft::auto_buffer` destruction in **test.performance.cstring**; +* Shortened test directories under **test/** to the subject, and renamed the unit suites to **test.unit.cstring.instance**, **test.unit.cstring.insert-replace**, and **test.unit.cstring.borrowed** (formerly **test.unit.cstring.1**, **test.unit.cstring.2**, and **test.unit.cstring.auto_buffer**); +* 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; +* Removed empty unit cases and unused temporary-file names; +* Used `size_t` in the performance programs; +* 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**; ## 4.0.18 - 29th September 2026 diff --git a/CMakeLists.txt b/CMakeLists.txt index 330b3e0..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 # # ######################################################################## # diff --git a/NEWS.md b/NEWS.md index b9c2f48..b6e87cf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,7 +4,7 @@ | Date | News Item | Details | | ------------------- | -------------------------------- | ------- | | Available from [**cstring** project on GitHub](https://synesissoftware.com/cstring): | -| 3rd October 2026 | Release of [cstring 4.0.19](https://github.com/synesissoftware/cstring/releases/tag/4.0.19) | lone CR ends a line; `cstring_readline()` `numRead` includes EOL; `cstring_readline()` reports failed readonly | +| 3rd 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 directories | | 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/examples/c/example.c.cstring/main.c b/examples/c/example.c.cstring/main.c index daaf369..3df3645 100644 --- a/examples/c/example.c.cstring/main.c +++ b/examples/c/example.c.cstring/main.c @@ -154,7 +154,6 @@ int main(int argc, char *argv[]) } - /* ///////////////////////////////////////////////////////////////////////// * function implementations */ diff --git a/examples/c/example.c.cstring_create/main.c b/examples/c/example.c.cstring_create/main.c index fcca051..af630bc 100644 --- a/examples/c/example.c.cstring_create/main.c +++ b/examples/c/example.c.cstring_create/main.c @@ -3,8 +3,8 @@ * * Purpose: Minimal example of `cstring_create()` / `cstring_destroy()`. * - * Created: ... - * Updated: 2nd August 2026 + * Created: 12th January 2024 + * Updated: 3rd October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -15,6 +15,7 @@ /* Standard C header files */ #include + /* ///////////////////////////////////////////////////////////////////////// * main() */ diff --git a/examples/c/example.c.cstring_vector/main.c b/examples/c/example.c.cstring_vector/main.c index a460381..ce4b24b 100644 --- a/examples/c/example.c.cstring_vector/main.c +++ b/examples/c/example.c.cstring_vector/main.c @@ -4,8 +4,8 @@ * Purpose: Example reading lines into a `cstring_vector_t` and sorting * them. * - * Created: ... - * Updated: 23rd September 2026 + * Created: 12th January 2024 + * Updated: 3rd October 2026 * * ////////////////////////////////////////////////////////////////////// */ diff --git a/examples/cpp/example.cpp.cstring.dynload/main.cpp b/examples/cpp/example.cpp.cstring.dynload/main.cpp index e2b327c..11c17b4 100644 --- a/examples/cpp/example.cpp.cstring.dynload/main.cpp +++ b/examples/cpp/example.cpp.cstring.dynload/main.cpp @@ -38,6 +38,7 @@ namespace std /* Standard C header files */ #include + /* ///////////////////////////////////////////////////////////////////////// * forward declarations */ 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/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/test.component.cstring_readline/CMakeLists.txt b/test/component/cstring_readline/CMakeLists.txt similarity index 53% rename from test/component/test.component.cstring_readline/CMakeLists.txt rename to test/component/cstring_readline/CMakeLists.txt index af6840b..9d95889 100644 --- a/test/component/test.component.cstring_readline/CMakeLists.txt +++ b/test/component/cstring_readline/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_readline entry.cpp) diff --git a/test/component/test.component.cstring_readline/entry.cpp b/test/component/cstring_readline/entry.cpp similarity index 92% rename from test/component/test.component.cstring_readline/entry.cpp rename to test/component/cstring_readline/entry.cpp index c1b5484..0bcf330 100644 --- a/test/component/test.component.cstring_readline/entry.cpp +++ b/test/component/cstring_readline/entry.cpp @@ -10,24 +10,26 @@ /* ///////////////////////////////////////////////////////////////////////// + * 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 /* Standard C++ header files */ @@ -65,9 +67,6 @@ namespace 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(); - - int setup(void*); - int teardown(void*); } // anonymous namespace @@ -101,7 +100,7 @@ int main(int argc, char* argv[]) XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); - if (XTESTS_START_RUNNER_WITH_SETUP_FNS("test.component.cstring_readline", verbosity, setup, teardown, (void*)TEST_FILE_NAME)) + 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); @@ -142,75 +141,14 @@ int main(int argc, char* argv[]) #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 { + + using namespace cstring_component; using ::xtests::cpp::util::temp_file; diff --git a/test/component/cstring_vector_readLines/CMakeLists.txt b/test/component/cstring_vector_readLines/CMakeLists.txt new file mode 100644 index 0000000..f22eb90 --- /dev/null +++ b/test/component/cstring_vector_readLines/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_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 88% rename from test/component/test.component.cstring_vector_readLines/entry.cpp rename to test/component/cstring_vector_readLines/entry.cpp index be3b18c..f2ef821 100644 --- a/test/component/test.component.cstring_vector_readLines/entry.cpp +++ b/test/component/cstring_vector_readLines/entry.cpp @@ -10,23 +10,25 @@ /* ///////////////////////////////////////////////////////////////////////// + * 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 */ @@ -56,9 +58,6 @@ namespace 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(); - - int setup(void*); - int teardown(void*); } // 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); 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 91% rename from test/component/test.component.cstring_writeline/entry.cpp rename to test/component/cstring_writeline/entry.cpp index 05017c3..3ca1090 100644 --- a/test/component/test.component.cstring_writeline/entry.cpp +++ b/test/component/cstring_writeline/entry.cpp @@ -4,36 +4,39 @@ * Purpose: Unit-tests of `cstring_write()` and `cstring_writeline()`. * * Created: 10th August 2020 - * Updated: 2nd October 2026 + * Updated: 3rd 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 */ @@ -45,9 +48,6 @@ namespace static void TEST_cstring_writeline_INVALID_STREAM(); static void TEST_cstring_writeline_MULTIPLE_LINES(); static void TEST_cstring_write_CONCATENATED(); - - int setup(void*); - int teardown(void*); } // anonymous namespace @@ -57,6 +57,7 @@ namespace 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() 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 6c8958d..9b58e9a 100644 --- a/test/performance/CMakeLists.txt +++ b/test/performance/CMakeLists.txt @@ -3,9 +3,9 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -add_subdirectory(test.performance.cstring) -add_subdirectory(test.performance.cstring_readline) -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/test.performance.cstring/CMakeLists.txt b/test/performance/cstring/CMakeLists.txt similarity index 100% rename from test/performance/test.performance.cstring/CMakeLists.txt rename to test/performance/cstring/CMakeLists.txt diff --git a/test/performance/test.performance.cstring/main.cpp b/test/performance/cstring/main.cpp similarity index 93% rename from test/performance/test.performance.cstring/main.cpp rename to test/performance/cstring/main.cpp index 3690c93..af5fec3 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: 3rd October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -81,68 +81,6 @@ 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( - 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; -} - void emit_row( char const* scenario @@ -154,24 +92,22 @@ emit_row( , 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) ); } diff --git a/test/performance/test.performance.cstring_readline/CMakeLists.txt b/test/performance/cstring_readline/CMakeLists.txt similarity index 100% rename from test/performance/test.performance.cstring_readline/CMakeLists.txt rename to test/performance/cstring_readline/CMakeLists.txt diff --git a/test/performance/test.performance.cstring_readline/main.cpp b/test/performance/cstring_readline/main.cpp similarity index 78% rename from test/performance/test.performance.cstring_readline/main.cpp rename to test/performance/cstring_readline/main.cpp index 8c40189..fa16a88 100644 --- a/test/performance/test.performance.cstring_readline/main.cpp +++ b/test/performance/cstring_readline/main.cpp @@ -9,7 +9,7 @@ * require p99. * * Created: 27th September 2026 - * Updated: 27th September 2026 + * Updated: 3rd October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -48,12 +48,7 @@ char const* const IMPL_RAW = "raw_fgetc"; char const TEST_FILE_NAME[] = "test.performance.cstring_readline.lines.txt"; -enum line_ending_t -{ - LINE_ENDING_LF = 0, - LINE_ENDING_CRLF, - LINE_ENDING_NONE, -}; +using namespace cstring_perf; enum instance_mode_t { @@ -65,8 +60,8 @@ enum instance_mode_t struct readline_case { char const* scenario; - std::size_t num_lines; - std::size_t line_len; + size_t num_lines; + size_t line_len; line_ending_t ending; instance_mode_t mode; }; @@ -95,85 +90,38 @@ namespace { using namespace cstring_perf; -struct run_result -{ - interval_t tm_ns; - std::uint64_t anchor; - p99::histogram hist; -}; - -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; - result.hist.clear(); - - interval_t tm_ns = 0; - - 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)); - } - - 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_READLINE)) - ? 1.0 - : ratio_or_dash(r.tm_ns, cstring_tm_ns) - ; + char const* const baselines[] = + { + IMPL_READLINE, + }; - display_results( + cstring_perf::emit_row( scenario , size , impl , num_iterations , num_actions - , r.tm_ns - , ratio - , r.anchor - , &r.hist + , r + , cstring_tm_ns + , baselines + , STLSOFT_NUM_ELEMENTS(baselines) ); } std::uint64_t line_anchor( - std::size_t len + size_t len , unsigned char head ) { @@ -183,7 +131,7 @@ line_anchor( unsigned char content_head( char const* p -, std::size_t n +, size_t n ) { if (0 == n || NULL == p) @@ -194,45 +142,11 @@ content_head( return static_cast(p[0]); } -bool -write_lines_file( - char const* path -, std::size_t num_lines -, std::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 (std::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); -} - std::uint64_t read_all_cstring( - char const* path -, std::size_t line_len -, instance_mode_t mode + char const* path +, size_t line_len +, instance_mode_t mode ) { FILE* const f = std::fopen(path, "rb"); @@ -291,9 +205,9 @@ read_all_cstring( std::uint64_t read_all_getline( - char const* path -, std::size_t line_len -, instance_mode_t mode + char const* path +, size_t line_len +, instance_mode_t mode ) { std::ifstream in(path, std::ios::in | std::ios::binary); @@ -334,7 +248,7 @@ read_all_getline( std::uint64_t read_all_fgets( char const* path -, std::size_t line_len +, size_t line_len ) { FILE* const f = std::fopen(path, "rb"); @@ -350,7 +264,7 @@ read_all_fgets( while (NULL != std::fgets(&buf[0], static_cast(buf.size()), f)) { - std::size_t n = std::strlen(&buf[0]); + size_t n = std::strlen(&buf[0]); if (0 != n && '\n' == buf[n - 1u]) { @@ -372,9 +286,9 @@ read_all_fgets( std::uint64_t read_all_raw_fgetc( - char const* path -, std::size_t line_len -, instance_mode_t mode + char const* path +, size_t line_len +, instance_mode_t mode ) { FILE* const f = std::fopen(path, "rb"); @@ -462,7 +376,7 @@ read_all_raw_fgetc( bool anchors_agree( char const* scenario -, std::size_t line_len +, size_t line_len , run_result const& cs , run_result const& gl , run_result const& fg @@ -493,8 +407,8 @@ anchors_agree( bool scenario_readline( readline_case const& spec -, std::size_t num_trials -, std::size_t num_warm_loops +, size_t num_trials +, size_t num_warm_loops ) { if (LINE_ENDING_NONE == spec.ending && 1u != spec.num_lines) @@ -520,9 +434,9 @@ scenario_readline( return true; } - char const* const path = TEST_FILE_NAME; - std::size_t const line_len = spec.line_len; - instance_mode_t const mode = spec.mode; + 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); @@ -566,8 +480,8 @@ int main(int /*argc*/, char* /*argv*/[]) { #ifdef HAS_P99 - std::size_t const num_trials = cstring_perf::default_file_trials(); - std::size_t const num_warm_loops = cstring_perf::default_warmups(); + 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"); @@ -582,7 +496,7 @@ int main(int /*argc*/, char* /*argv*/[]) cstring_perf::display_results_title(); - for (std::size_t i = 0; STLSOFT_NUM_ELEMENTS(CASES) != i; ++i) + for (size_t i = 0; STLSOFT_NUM_ELEMENTS(CASES) != i; ++i) { if (!scenario_readline(CASES[i], num_trials, num_warm_loops)) { 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 5de587b..b30cfe1 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: 2nd October 2026 + * Updated: 3rd 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); @@ -180,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 { @@ -203,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)); } @@ -219,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); @@ -231,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); } @@ -242,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); } @@ -252,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); } @@ -270,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); @@ -306,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); } @@ -315,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]); } @@ -327,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); @@ -339,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); } @@ -350,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); } @@ -360,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); } @@ -377,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" @@ -482,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 ; @@ -503,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 24a6eca..8f24f17 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: 3rd 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 */ @@ -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/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 5fb1b6f..3ea2254 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -1,12 +1,12 @@ +add_subdirectory(cstring_getStatusCodeString) +add_subdirectory(cstring_vector) +add_subdirectory(cstring.borrowed) if(NOT NO_CSTRING_CPP_API) - add_subdirectory(test.unit.cstring.1) + add_subdirectory(cstring.instance) 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(cstring.insert-replace) # ############################## end of file ############################# # diff --git a/test/unit/test.unit.cstring.2/CMakeLists.txt b/test/unit/cstring.borrowed/CMakeLists.txt similarity index 58% rename from test/unit/test.unit.cstring.2/CMakeLists.txt rename to test/unit/cstring.borrowed/CMakeLists.txt index 1912881..ae01815 100644 --- a/test/unit/test.unit.cstring.2/CMakeLists.txt +++ b/test/unit/cstring.borrowed/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.cstring.borrowed entry.c) diff --git a/test/unit/cstring.borrowed/entry.c b/test/unit/cstring.borrowed/entry.c new file mode 100644 index 0000000..4e923da --- /dev/null +++ b/test/unit/cstring.borrowed/entry.c @@ -0,0 +1,506 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: test/unit/cstring.borrowed/entry.c + * + * Purpose: Tests borrowed string functionality. + * + * Created: 28th July 2011 + * Updated: 3rd 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("cstring.borrowed", 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.auto_buffer/CMakeLists.txt b/test/unit/cstring.insert-replace/CMakeLists.txt similarity index 56% rename from test/unit/test.unit.cstring.auto_buffer/CMakeLists.txt rename to test/unit/cstring.insert-replace/CMakeLists.txt index 6bfad14..12ea3b4 100644 --- a/test/unit/test.unit.cstring.auto_buffer/CMakeLists.txt +++ b/test/unit/cstring.insert-replace/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_auto_buffer entry.c) +define_automated_test_program(test.unit.cstring.insert-replace entry.c) diff --git a/test/unit/test.unit.cstring.2/entry.c b/test/unit/cstring.insert-replace/entry.c similarity index 82% rename from test/unit/test.unit.cstring.2/entry.c rename to test/unit/cstring.insert-replace/entry.c index 4603462..bcfbf2a 100644 --- a/test/unit/test.unit.cstring.2/entry.c +++ b/test/unit/cstring.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"; @@ -89,22 +87,20 @@ int main(int argc, char **argv) if (XTESTS_START_RUNNER("test.unit.cstring.2", 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) { @@ -157,12 +158,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 +179,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 +204,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 +216,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 +288,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 +318,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 +355,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 +364,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 +372,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 +381,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 +397,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 +417,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 +427,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 +444,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 +464,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 +474,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 +484,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 +494,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 +504,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 +514,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 +524,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 +534,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 +544,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 +554,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 +564,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 +574,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 +584,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 +598,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/test.unit.cstring.1/CMakeLists.txt b/test/unit/cstring.instance/CMakeLists.txt similarity index 58% rename from test/unit/test.unit.cstring.1/CMakeLists.txt rename to test/unit/cstring.instance/CMakeLists.txt index f8fb9c2..58f4b47 100644 --- a/test/unit/test.unit.cstring.1/CMakeLists.txt +++ b/test/unit/cstring.instance/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.instance entry.cpp) diff --git a/test/unit/test.unit.cstring.1/entry.cpp b/test/unit/cstring.instance/entry.cpp similarity index 78% rename from test/unit/test.unit.cstring.1/entry.cpp rename to test/unit/cstring.instance/entry.cpp index ac49435..1d2a1f9 100644 --- a/test/unit/test.unit.cstring.1/entry.cpp +++ b/test/unit/cstring.instance/entry.cpp @@ -1,10 +1,10 @@ /* ///////////////////////////////////////////////////////////////////////// - * File: test.unit.cstring.1/entry.cpp + * File: test/unit/cstring.instance/entry.cpp * - * Purpose: Unit-tests for general functionality. + * Purpose: Unit-tests for cstring instance general functionality. * * Created: 23rd May 2009 - * Updated: 2nd October 2026 + * Updated: 3rd October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -51,29 +51,27 @@ namespace { - static void test_1_0(); - static void test_1_1(); - static void test_1_2(); - static void test_1_3(); - static void test_1_4(); - static void test_1_5(); - static void test_1_6(); - static void test_1_7(); - static void test_1_8(); - static void test_1_9(); - - static void test_2_0(); - static void test_2_1(); - static void test_2_2(); - static void test_2_3(); - static void test_2_4(); - static void test_2_5(); - static void test_2_6(); - static void test_2_7(); - static void test_2_8(); - static void test_2_9(); - static void test_2_10(); - static void test_2_11(); + 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 +79,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; @@ -112,37 +108,33 @@ int main(int argc, char **argv) if (XTESTS_START_RUNNER("test.unit.cstring.1", 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 +151,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 +188,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 +198,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 +210,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 +235,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,13 +243,13 @@ 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_STR_EQ_(CSTRING_T_(""), str.ptr); TEST_INT_GT(0u, str.capacity); TEST_INT_GE(str.len, str.capacity); @@ -267,13 +268,13 @@ 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_STR_EQ_(CSTRING_T_(""), str.ptr); TEST_INT_GT(0u, str.capacity); TEST_INT_GE(str.len, str.capacity); @@ -292,13 +293,13 @@ 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_STR_EQ_(CSTRING_T_(""), str.ptr); TEST_INT_GT(0u, str.capacity); TEST_INT_GE(str.len, str.capacity); @@ -317,13 +318,13 @@ 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_STR_EQ_(CSTRING_T_(""), str.ptr); TEST_INT_GT(0u, str.capacity); TEST_INT_GE(str.len, str.capacity); @@ -342,13 +343,13 @@ 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_STR_EQ_(CSTRING_T_(""), str.ptr); TEST_INT_GT(0u, str.capacity); TEST_INT_GE(str.len, str.capacity); @@ -367,13 +368,13 @@ 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_STR_EQ_(CSTRING_T_(""), str.ptr); TEST_INT_GT(0u, str.capacity); TEST_INT_GE(str.len, str.capacity); @@ -392,19 +393,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 +424,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); @@ -449,7 +450,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 +461,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 +491,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 +520,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 +540,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,13 +551,13 @@ 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_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); @@ -580,13 +581,13 @@ 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_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,13 +610,13 @@ 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_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); @@ -629,7 +630,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 +642,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 +671,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 +692,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 +730,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 +753,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 +783,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 +800,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,7 +811,7 @@ static void test_2_1() cstring_create(&str, s); 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); @@ -827,7 +823,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) { @@ -838,7 +834,7 @@ static void test_2_2() cstring_createLen(&str, s, i); 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); @@ -850,7 +846,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,7 +858,7 @@ 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_STR_EQ_(s, str.ptr); TEST_INT_GE(str.len, str.capacity); cstring_destroy(&str); @@ -884,16 +880,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 +901,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,7 +922,7 @@ 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_STR_EQ_(s, str.ptr); TEST_INT_GE(str.len, str.capacity); TEST_INT_GE(str.capacity, ::GlobalSize(str.ptr)); @@ -948,7 +944,7 @@ 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_STR_EQ_(s, str.ptr); TEST_INT_GE(str.len, str.capacity); TEST_INT_GE(str.capacity, ::HeapSize(::GetProcessHeap(), 0, str.ptr)); @@ -970,7 +966,7 @@ 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_STR_EQ_(s, str.ptr); TEST_INT_GE(str.len, str.capacity); TEST_INT_GE(str.capacity, comstl::CoTaskMemGetSize(str.ptr)); @@ -984,7 +980,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) { @@ -996,7 +992,7 @@ 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_STR_EQ_(s, str.ptr); TEST_INT_GE(str.len, str.capacity); cstring_destroy(&str); @@ -1018,16 +1014,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 +1035,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); @@ -1060,7 +1056,7 @@ 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_STR_EQ_(s, str.ptr); TEST_INT_GE(str.len, str.capacity); TEST_INT_GE(str.capacity, ::GlobalSize(str.ptr)); @@ -1082,7 +1078,7 @@ 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_STR_EQ_(s, str.ptr); TEST_INT_GE(str.len, str.capacity); TEST_INT_GE(str.capacity, ::HeapSize(::GetProcessHeap(), 0, str.ptr)); @@ -1104,7 +1100,7 @@ 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_STR_EQ_(s, str.ptr); TEST_INT_GE(str.len, str.capacity); TEST_INT_GE(str.capacity, comstl::CoTaskMemGetSize(str.ptr)); @@ -1118,7 +1114,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 +1138,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 +1150,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 +1174,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 +1186,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 +1196,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 +1229,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 +1260,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 +1271,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 +1288,7 @@ static void test_2_8() }} } -static void test_2_9() +static void TEST_cstring_yield2() { { cstring_t str = cstring_t_DEFAULT; @@ -1300,7 +1296,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 +1321,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 +1332,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 +1349,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 +1359,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 +1373,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 +1381,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 +1401,6 @@ static void test_2_10() TEST_INT_EQ(0, str2.flags); }} } - -static void test_2_11() -{ -} } // anonymous namespace diff --git a/test/unit/cstring_getStatusCodeString/CMakeLists.txt b/test/unit/cstring_getStatusCodeString/CMakeLists.txt new file mode 100644 index 0000000..e949cff --- /dev/null +++ b/test/unit/cstring_getStatusCodeString/CMakeLists.txt @@ -0,0 +1,5 @@ +list(APPEND X_MSVC_CUSTOM_WARNINGS_TO_BE_SUPPRESSED + 4996 +) + +define_automated_test_program(test.unit.cstring_getStatusCodeString entry.c) diff --git a/test/unit/cstring_getStatusCodeString/entry.c b/test/unit/cstring_getStatusCodeString/entry.c new file mode 100644 index 0000000..1644575 --- /dev/null +++ b/test/unit/cstring_getStatusCodeString/entry.c @@ -0,0 +1,232 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: test/unit/cstring_getStatusCodeString/entry.c + * + * Purpose: Unit-tests `cstring_error()`, `cstring_getStatusCodeString()`. + * + * Created: 28th July 2011 + * Updated: 3rd 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.cstring_getStatusCodeString", 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.cstring_vector/CMakeLists.txt b/test/unit/cstring_vector/CMakeLists.txt similarity index 100% rename from test/unit/test.unit.cstring.cstring_vector/CMakeLists.txt rename to test/unit/cstring_vector/CMakeLists.txt 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/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/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 //////////////////////////// */ - From af193976c1c94563f21ef973e0b27f890574014e Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Sun, 4 Oct 2026 15:41:29 +1100 Subject: [PATCH 14/15] release --- CHANGES.md | 2 +- NEWS.md | 2 +- include/cstring/cstring.h | 4 ++-- test/performance/cstring/main.cpp | 2 +- test/performance/cstring_readline/main.cpp | 2 +- test/performance/cstring_vector/main.cpp | 2 +- test/performance/perf_harness.hpp | 4 ++-- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 9412878..b5a9ad0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,7 @@ # cstring - Changes -## 4.0.19 - 3rd October 2026 +## 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; diff --git a/NEWS.md b/NEWS.md index b6e87cf..239b42f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,7 +4,7 @@ | Date | News Item | Details | | ------------------- | -------------------------------- | ------- | | Available from [**cstring** project on GitHub](https://synesissoftware.com/cstring): | -| 3rd 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 directories | +| 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 directories | | 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/include/cstring/cstring.h b/include/cstring/cstring.h index 7c3f9ed..c2c5b90 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: 3rd October 2026 + * Updated: 4th October 2026 * * Home: http://synesis.com.au/software/ * @@ -56,7 +56,7 @@ # define CSTRING_VER_CSTRING_H_CSTRING_MAJOR 3 # define CSTRING_VER_CSTRING_H_CSTRING_MINOR 12 # define CSTRING_VER_CSTRING_H_CSTRING_REVISION 2 -# define CSTRING_VER_CSTRING_H_CSTRING_EDIT 93 +# define CSTRING_VER_CSTRING_H_CSTRING_EDIT 94 #endif /* !CSTRING_DOCUMENTATION_SKIP_SECTION */ diff --git a/test/performance/cstring/main.cpp b/test/performance/cstring/main.cpp index af5fec3..bf68ed1 100644 --- a/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: 3rd October 2026 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ diff --git a/test/performance/cstring_readline/main.cpp b/test/performance/cstring_readline/main.cpp index fa16a88..da1a1e0 100644 --- a/test/performance/cstring_readline/main.cpp +++ b/test/performance/cstring_readline/main.cpp @@ -9,7 +9,7 @@ * require p99. * * Created: 27th September 2026 - * Updated: 3rd October 2026 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ diff --git a/test/performance/cstring_vector/main.cpp b/test/performance/cstring_vector/main.cpp index b30cfe1..b0a066f 100644 --- a/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: 3rd October 2026 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ diff --git a/test/performance/perf_harness.hpp b/test/performance/perf_harness.hpp index 8f24f17..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: 3rd October 2026 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -392,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 || From 91d029ac62999c456536acbbed4121585d3df8e4 Mon Sep 17 00:00:00 2001 From: synesissoftware Date: Sun, 4 Oct 2026 18:28:24 +1100 Subject: [PATCH 15/15] test(cstring): add a C unit suite and shorten example and test names (#40) Add **test.unit.cstring** as a C port of **test.unit.cstring.cxx**, and shorten example and unit-test directories to the subject. Document memory contracts, allocators, and a default-use sketch in **README.md**, and note 5.x SSO, vector-owned payloads, and a macOS dylib in **TODO.md**. Record the split in **FAQ.md**, **NEWS.md**, and **CHANGES.md**. --- CHANGES.md | 18 +- FAQ.md | 6 +- NEWS.md | 2 +- README.md | 107 +- TODO.md | 3 + examples/c/CMakeLists.txt | 8 +- .../auto-buffer}/CMakeLists.txt | 2 +- .../README.md | 4 +- .../main.c | 4 +- .../CMakeLists.txt | 0 .../{example.c.cstring => cstring}/README.md | 18 +- .../c/{example.c.cstring => cstring}/main.c | 4 +- .../CMakeLists.txt | 0 .../README.md | 2 +- .../main.c | 4 +- .../CMakeLists.txt | 0 .../README.md | 6 +- .../main.c | 4 +- examples/cpp/CMakeLists.txt | 4 +- .../CMakeLists.txt | 0 .../README.md | 2 +- .../main.cpp | 4 +- .../cpp/cstring.global_memory}/CMakeLists.txt | 2 +- .../README.md | 4 +- .../main.cpp | 0 test/component/cstring_readline/entry.cpp | 4 +- .../cstring_vector_readLines/entry.cpp | 4 +- test/component/cstring_writeline/entry.cpp | 4 +- test/unit/CMakeLists.txt | 9 +- .../CMakeLists.txt | 2 +- .../{cstring.borrowed => auto-buffer}/entry.c | 6 +- test/unit/cstring.cxx/CMakeLists.txt | 2 + .../entry.cpp | 6 +- .../cstring.insert-replace/CMakeLists.txt | 2 - .../unit/cstring}/CMakeLists.txt | 2 +- test/unit/cstring/entry.c | 1434 +++++++++++++++++ .../CMakeLists.txt | 5 - test/unit/cstring_vector/CMakeLists.txt | 2 +- test/unit/insert-replace/CMakeLists.txt | 2 + .../entry.c | 2 +- test/unit/status-codes/CMakeLists.txt | 5 + .../entry.c | 6 +- 42 files changed, 1615 insertions(+), 90 deletions(-) rename examples/{cpp/example.cpp.HGLOBAL_on_x64 => c/auto-buffer}/CMakeLists.txt (60%) rename examples/c/{example.c.auto_buffer => auto-buffer}/README.md (83%) rename examples/c/{example.c.auto_buffer => auto-buffer}/main.c (96%) rename examples/c/{example.c.cstring => cstring}/CMakeLists.txt (100%) rename examples/c/{example.c.cstring => cstring}/README.md (86%) rename examples/c/{example.c.cstring => cstring}/main.c (99%) rename examples/c/{example.c.cstring_create => cstring_create}/CMakeLists.txt (100%) rename examples/c/{example.c.cstring_create => cstring_create}/README.md (85%) rename examples/c/{example.c.cstring_create => cstring_create}/main.c (93%) rename examples/c/{example.c.cstring_vector => cstring_vector}/CMakeLists.txt (100%) rename examples/c/{example.c.cstring_vector => cstring_vector}/README.md (79%) rename examples/c/{example.c.cstring_vector => cstring_vector}/main.c (98%) rename examples/cpp/{example.cpp.cstring.dynload => cstring.dynload}/CMakeLists.txt (100%) rename examples/cpp/{example.cpp.cstring.dynload => cstring.dynload}/README.md (90%) rename examples/cpp/{example.cpp.cstring.dynload => cstring.dynload}/main.cpp (97%) rename {test/unit/cstring.instance => examples/cpp/cstring.global_memory}/CMakeLists.txt (58%) rename examples/cpp/{example.cpp.HGLOBAL_on_x64 => cstring.global_memory}/README.md (75%) rename examples/cpp/{example.cpp.HGLOBAL_on_x64 => cstring.global_memory}/main.cpp (100%) rename test/unit/{cstring.borrowed => auto-buffer}/CMakeLists.txt (58%) rename test/unit/{cstring.borrowed => auto-buffer}/entry.c (98%) create mode 100644 test/unit/cstring.cxx/CMakeLists.txt rename test/unit/{cstring.instance => cstring.cxx}/entry.cpp (99%) delete mode 100644 test/unit/cstring.insert-replace/CMakeLists.txt rename {examples/c/example.c.auto_buffer => test/unit/cstring}/CMakeLists.txt (62%) create mode 100644 test/unit/cstring/entry.c delete mode 100644 test/unit/cstring_getStatusCodeString/CMakeLists.txt create mode 100644 test/unit/insert-replace/CMakeLists.txt rename test/unit/{cstring.insert-replace => insert-replace}/entry.c (99%) create mode 100644 test/unit/status-codes/CMakeLists.txt rename test/unit/{cstring_getStatusCodeString => status-codes}/entry.c (98%) diff --git a/CHANGES.md b/CHANGES.md index b5a9ad0..061e099 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,19 +5,21 @@ * 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; -* 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; -* 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; +* 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; -* Added **test.component.cstring_readline** case `TEST_cstring_readline_READONLY_RETAINS_PAYLOAD`; -* Freed a zero-size realloc-arena block on every platform (`realloc(pv, 0)` allocates on some), and left a NULL pointer 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; -* Suppressed the GCC 15 `-Wfree-nonheap-object` false positive on inlined `stlsoft::auto_buffer` destruction in **test.performance.cstring**; -* Shortened test directories under **test/** to the subject, and renamed the unit suites to **test.unit.cstring.instance**, **test.unit.cstring.insert-replace**, and **test.unit.cstring.borrowed** (formerly **test.unit.cstring.1**, **test.unit.cstring.2**, and **test.unit.cstring.auto_buffer**); +* 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; -* Used `size_t` in the performance programs; -* 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**; +* 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 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 239b42f..b5c9dde 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,7 +4,7 @@ | Date | News Item | Details | | ------------------- | -------------------------------- | ------- | | Available from [**cstring** project on GitHub](https://synesissoftware.com/cstring): | -| 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 directories | +| 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 cf57dcf..e936f3a 100644 --- a/TODO.md +++ b/TODO.md @@ -19,6 +19,8 @@ * [ ] 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 @@ -40,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 3df3645..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 * * ////////////////////////////////////////////////////////////////////// */ 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 93% rename from examples/c/example.c.cstring_create/main.c rename to examples/c/cstring_create/main.c index af630bc..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: 12th January 2024 - * Updated: 3rd October 2026 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ 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 ce4b24b..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: 12th January 2024 - * Updated: 3rd October 2026 + * 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 11c17b4..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 * * ////////////////////////////////////////////////////////////////////// */ diff --git a/test/unit/cstring.instance/CMakeLists.txt b/examples/cpp/cstring.global_memory/CMakeLists.txt similarity index 58% rename from test/unit/cstring.instance/CMakeLists.txt rename to examples/cpp/cstring.global_memory/CMakeLists.txt index 58f4b47..7011da3 100644 --- a/test/unit/cstring.instance/CMakeLists.txt +++ b/examples/cpp/cstring.global_memory/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.instance entry.cpp) +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/test/component/cstring_readline/entry.cpp b/test/component/cstring_readline/entry.cpp index 0bcf330..fa4ef14 100644 --- a/test/component/cstring_readline/entry.cpp +++ b/test/component/cstring_readline/entry.cpp @@ -1,10 +1,10 @@ /* ///////////////////////////////////////////////////////////////////////// - * File: test.component.cstring_readline/entry.cpp + * File: test/component/cstring_readline/entry.cpp * * Purpose: Component-tests `cstring_readline()`. * * Created: 23rd May 2009 - * Updated: 3rd October 2026 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ diff --git a/test/component/cstring_vector_readLines/entry.cpp b/test/component/cstring_vector_readLines/entry.cpp index f2ef821..e45cad6 100644 --- a/test/component/cstring_vector_readLines/entry.cpp +++ b/test/component/cstring_vector_readLines/entry.cpp @@ -1,10 +1,10 @@ /* ///////////////////////////////////////////////////////////////////////// - * 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: 3rd October 2026 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ diff --git a/test/component/cstring_writeline/entry.cpp b/test/component/cstring_writeline/entry.cpp index 3ca1090..8ef8166 100644 --- a/test/component/cstring_writeline/entry.cpp +++ b/test/component/cstring_writeline/entry.cpp @@ -1,10 +1,10 @@ /* ///////////////////////////////////////////////////////////////////////// - * 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: 3rd October 2026 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 3ea2254..ecb3f12 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -1,12 +1,13 @@ -add_subdirectory(cstring_getStatusCodeString) +add_subdirectory(auto-buffer) +add_subdirectory(cstring) add_subdirectory(cstring_vector) -add_subdirectory(cstring.borrowed) if(NOT NO_CSTRING_CPP_API) - add_subdirectory(cstring.instance) + add_subdirectory(cstring.cxx) endif() -add_subdirectory(cstring.insert-replace) +add_subdirectory(insert-replace) +add_subdirectory(status-codes) # ############################## end of file ############################# # diff --git a/test/unit/cstring.borrowed/CMakeLists.txt b/test/unit/auto-buffer/CMakeLists.txt similarity index 58% rename from test/unit/cstring.borrowed/CMakeLists.txt rename to test/unit/auto-buffer/CMakeLists.txt index ae01815..57a2d51 100644 --- a/test/unit/cstring.borrowed/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.borrowed entry.c) +define_automated_test_program(test.unit.auto-buffer entry.c) diff --git a/test/unit/cstring.borrowed/entry.c b/test/unit/auto-buffer/entry.c similarity index 98% rename from test/unit/cstring.borrowed/entry.c rename to test/unit/auto-buffer/entry.c index 4e923da..c301667 100644 --- a/test/unit/cstring.borrowed/entry.c +++ b/test/unit/auto-buffer/entry.c @@ -1,10 +1,10 @@ /* ///////////////////////////////////////////////////////////////////////// - * File: test/unit/cstring.borrowed/entry.c + * File: test/unit/auto-buffer/entry.c * * Purpose: Tests borrowed string functionality. * * Created: 28th July 2011 - * Updated: 3rd October 2026 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -75,7 +75,7 @@ int main(int argc, char **argv) XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); - if (XTESTS_START_RUNNER("cstring.borrowed", 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); diff --git a/test/unit/cstring.cxx/CMakeLists.txt b/test/unit/cstring.cxx/CMakeLists.txt new file mode 100644 index 0000000..0a5aa85 --- /dev/null +++ b/test/unit/cstring.cxx/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.cxx entry.cpp) diff --git a/test/unit/cstring.instance/entry.cpp b/test/unit/cstring.cxx/entry.cpp similarity index 99% rename from test/unit/cstring.instance/entry.cpp rename to test/unit/cstring.cxx/entry.cpp index 1d2a1f9..60a18fa 100644 --- a/test/unit/cstring.instance/entry.cpp +++ b/test/unit/cstring.cxx/entry.cpp @@ -1,10 +1,10 @@ /* ///////////////////////////////////////////////////////////////////////// - * File: test/unit/cstring.instance/entry.cpp + * File: test/unit/cstring.cxx/entry.cpp * * Purpose: Unit-tests for cstring instance general functionality. * * Created: 23rd May 2009 - * Updated: 3rd October 2026 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -106,7 +106,7 @@ int main(int argc, char **argv) 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_cstring_t_DEFAULT); XTESTS_RUN_CASE(TEST_cstring_init); diff --git a/test/unit/cstring.insert-replace/CMakeLists.txt b/test/unit/cstring.insert-replace/CMakeLists.txt deleted file mode 100644 index 12ea3b4..0000000 --- a/test/unit/cstring.insert-replace/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.insert-replace entry.c) 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..5c41fea --- /dev/null +++ b/test/unit/cstring/entry.c @@ -0,0 +1,1434 @@ +/* ///////////////////////////////////////////////////////////////////////// + * 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_NE(NULL, str.ptr); + TEST_STR_EQ_(CSTRING_T_(""), str.ptr); + TEST_INT_GT(0u, 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; + 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_NE(NULL, str.ptr); + TEST_STR_EQ_(CSTRING_T_(""), str.ptr); + TEST_INT_GT(0u, 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_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_NE(NULL, str.ptr); + TEST_STR_EQ_(CSTRING_T_(""), str.ptr); + TEST_INT_GT(0u, 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_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_NE(NULL, str.ptr); + TEST_STR_EQ_(CSTRING_T_(""), str.ptr); + TEST_INT_GT(0u, 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, 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_NE(NULL, str.ptr); + TEST_STR_EQ_(CSTRING_T_(""), str.ptr); + TEST_INT_GT(0u, 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_(""), 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_NE(NULL, str.ptr); + TEST_STR_EQ_(CSTRING_T_(""), str.ptr); + TEST_INT_GT(0u, 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, 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); + 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); + 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); + 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); + 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_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); + }} +} + +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); + 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); + 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); + 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); + 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_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); + }} + + { 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_GE(i, str.len); + 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_GE(i, str.len); + 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_GE(i, str.len); + 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_getStatusCodeString/CMakeLists.txt b/test/unit/cstring_getStatusCodeString/CMakeLists.txt deleted file mode 100644 index e949cff..0000000 --- a/test/unit/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_getStatusCodeString entry.c) diff --git a/test/unit/cstring_vector/CMakeLists.txt b/test/unit/cstring_vector/CMakeLists.txt index e1d68cc..9ccfe97 100644 --- a/test/unit/cstring_vector/CMakeLists.txt +++ b/test/unit/cstring_vector/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_cstring_vector entry.c) +define_automated_test_program(test.unit.cstring_vector entry.c) 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/cstring.insert-replace/entry.c b/test/unit/insert-replace/entry.c similarity index 99% rename from test/unit/cstring.insert-replace/entry.c rename to test/unit/insert-replace/entry.c index bcfbf2a..c752baf 100644 --- a/test/unit/cstring.insert-replace/entry.c +++ b/test/unit/insert-replace/entry.c @@ -85,7 +85,7 @@ 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_cstring_createN); XTESTS_RUN_CASE(TEST_cstring_insert); 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/cstring_getStatusCodeString/entry.c b/test/unit/status-codes/entry.c similarity index 98% rename from test/unit/cstring_getStatusCodeString/entry.c rename to test/unit/status-codes/entry.c index 1644575..bb457be 100644 --- a/test/unit/cstring_getStatusCodeString/entry.c +++ b/test/unit/status-codes/entry.c @@ -1,10 +1,10 @@ /* ///////////////////////////////////////////////////////////////////////// - * File: test/unit/cstring_getStatusCodeString/entry.c + * File: test/unit/status-codes/entry.c * * Purpose: Unit-tests `cstring_error()`, `cstring_getStatusCodeString()`. * * Created: 28th July 2011 - * Updated: 3rd October 2026 + * Updated: 4th October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -103,7 +103,7 @@ int main(int argc, char **argv) XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); - if (XTESTS_START_RUNNER("test.unit.cstring_getStatusCodeString", 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);