From f3400902e860279d1682ddb33c8462b109f4dfda Mon Sep 17 00:00:00 2001 From: Denys Bielyshev Date: Thu, 21 Aug 2025 15:07:52 +0300 Subject: [PATCH 1/2] implement tests for UStringBuilder --- test/CMakeLists.txt | 1 + test/UStringBuilderTest.cpp | 140 ++++++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 test/UStringBuilderTest.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6dccd52..fd98226 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -59,6 +59,7 @@ wdk_add_driver(kf-test WINVER NTDDI_WIN10 STL AutoSpinLockTest.cpp EResourceSharedLockTest.cpp RecursiveAutoSpinLockTest.cpp + UStringBuilderTest.cpp ) target_link_libraries(kf-test kf::kf kmtest::kmtest) diff --git a/test/UStringBuilderTest.cpp b/test/UStringBuilderTest.cpp new file mode 100644 index 0000000..a3c5ed1 --- /dev/null +++ b/test/UStringBuilderTest.cpp @@ -0,0 +1,140 @@ +#include "pch.h" +#include +#include + +using namespace kf; + +SCENARIO("UStringBuilder") +{ + GIVEN("A default constructed builder") + { + UStringBuilder sb; + + THEN("It holds an empty string") + { + REQUIRE(sb.string().isEmpty()); + REQUIRE(sb.string().charLength() == 0); + REQUIRE(sb.string().maxCharLength() == 0); + } + + WHEN("reserve(0) is called") + { + NTSTATUS status = sb.reserve(0); + + THEN("It succeeds and remains empty") + { + REQUIRE_NT_SUCCESS(status); + REQUIRE(sb.string().isEmpty()); + REQUIRE(sb.string().maxCharLength() == 0); + } + } + + WHEN("reserve(10) is called") + { + NTSTATUS status = sb.reserve(10); + + THEN("Capacity grows and length stays 0") + { + REQUIRE_NT_SUCCESS(status); + REQUIRE(sb.string().charLength() == 0); + REQUIRE(sb.string().maxCharLength() == 10); + } + } + } + + GIVEN("An empty builder") + { + UStringBuilder sb; + + WHEN("A single PCWSTR is appended") + { + NTSTATUS status = sb.append(L"abc"); + + THEN("The resulting string equals 'abc'") + { + REQUIRE_NT_SUCCESS(status); + REQUIRE(sb.string().charLength() == 3); + REQUIRE(sb.string().equals(L"abc")); + } + } + + WHEN("USimpleString and PCWSTR are appended in sequence") + { + NTSTATUS status1 = sb.append(L"ab"); + USimpleString part(L"cd"); + NTSTATUS status2 = sb.append(part); + NTSTATUS status3 = sb.append(L""); + + THEN("They concatenate to 'abcd'") + { + REQUIRE_NT_SUCCESS(status1); + REQUIRE_NT_SUCCESS(status2); + REQUIRE_NT_SUCCESS(status3); + REQUIRE(sb.string().charLength() == 4); + REQUIRE(sb.string().equals(L"abcd")); + } + } + + WHEN("Variadic append with multiple arguments is used") + { + USimpleString a(L"ab"); + USimpleString c(L"cd"); + NTSTATUS status = sb.append(a, c, L"ef"); + + THEN("All parts are appended in order") + { + REQUIRE_NT_SUCCESS(status); + REQUIRE(sb.string().charLength() == 6); + REQUIRE(sb.string().equals(L"abcdef")); + } + } + + WHEN("Multiple append calls trigger internal reallocation and preserve content") + { + REQUIRE_NT_SUCCESS(sb.append(L"abc")); + // Force growth by appending a longer chunk + REQUIRE_NT_SUCCESS(sb.append(L"defghij")); + + THEN("Final content is the concatenation of all parts") + { + REQUIRE(sb.string().charLength() == 10); + REQUIRE(sb.string().equals(L"abcdefghij")); + } + } + } + + GIVEN("A builder with content 'Hello'") + { + UStringBuilder sb; + REQUIRE_NT_SUCCESS(sb.append(L"Hello")); + + WHEN("copyTo is used with a small destination buffer") + { + WCHAR buf[4] = { 0 }; + int copied = sb.string().copyTo(buf); + THEN("It copies as many chars as fits including null-terminator in destination") + { + REQUIRE(copied == 3); // min(4-1, 5) = 3 + REQUIRE(buf[0] == L'H'); + REQUIRE(buf[1] == L'e'); + REQUIRE(buf[2] == L'l'); + REQUIRE(buf[3] == L'\0'); + } + } + + WHEN("reserve is called with a smaller size than current length") + { + // Note: current charLength is 5, reserve(3) will reallocate to 3 and truncate + NTSTATUS status = sb.reserve(3); + + THEN("It truncates the current content") + { + REQUIRE_NT_SUCCESS(status); + REQUIRE(sb.string().charLength() == 3); + REQUIRE(sb.string().equals(L"Hel")); + REQUIRE(sb.string().maxCharLength() == 3); + } + } + } +} + From f8d93506aac43500f6c8f34f0ecadf11e143db69 Mon Sep 17 00:00:00 2001 From: Sergey Podobry Date: Tue, 6 Jan 2026 14:52:22 +0200 Subject: [PATCH 2/2] Update tests --- test/UStringBuilderTest.cpp | 106 +++++++++++++++++++++++------------- test/pch.h | 1 + 2 files changed, 69 insertions(+), 38 deletions(-) diff --git a/test/UStringBuilderTest.cpp b/test/UStringBuilderTest.cpp index a3c5ed1..0d9d991 100644 --- a/test/UStringBuilderTest.cpp +++ b/test/UStringBuilderTest.cpp @@ -12,7 +12,6 @@ SCENARIO("UStringBuilder") THEN("It holds an empty string") { - REQUIRE(sb.string().isEmpty()); REQUIRE(sb.string().charLength() == 0); REQUIRE(sb.string().maxCharLength() == 0); } @@ -24,7 +23,7 @@ SCENARIO("UStringBuilder") THEN("It succeeds and remains empty") { REQUIRE_NT_SUCCESS(status); - REQUIRE(sb.string().isEmpty()); + REQUIRE(sb.string().charLength() == 0); REQUIRE(sb.string().maxCharLength() == 0); } } @@ -53,16 +52,78 @@ SCENARIO("UStringBuilder") THEN("The resulting string equals 'abc'") { REQUIRE_NT_SUCCESS(status); - REQUIRE(sb.string().charLength() == 3); REQUIRE(sb.string().equals(L"abc")); } } + WHEN("A single USimpleString is appended") + { + NTSTATUS status = sb.append(USimpleString(L"def")); + THEN("The resulting string equals 'def'") + { + REQUIRE_NT_SUCCESS(status); + REQUIRE(sb.string().equals(L"def")); + } + } + + WHEN("A single UNICODE_STRING is appended") + { + UNICODE_STRING us = RTL_CONSTANT_STRING(L"ghi"); + NTSTATUS status = sb.append(us); + THEN("The resulting string equals 'ghi'") + { + REQUIRE_NT_SUCCESS(status); + REQUIRE(sb.string().equals(L"ghi")); + } + } + + WHEN("A single UString is appended") + { + UString us; + REQUIRE_NT_SUCCESS(us.init(L"xyz")); + + NTSTATUS status = sb.append(us); + THEN("The resulting string equals 'xyz'") + { + REQUIRE_NT_SUCCESS(status); + REQUIRE(sb.string().equals(L"xyz")); + } + } + + WHEN("A single span is appended") + { + const wchar_t data[] = { L'm', L'n', L'o' }; + NTSTATUS status = sb.append(span{ data }); + + THEN("The resulting string equals 'mno'") + { + REQUIRE_NT_SUCCESS(status); + REQUIRE(sb.string().equals(L"mno")); + } + } + + WHEN("A single span is appended") + { + // UTF-16LE bytes for "uvw": u=0x0075, v=0x0076, w=0x0077 + const std::byte bytes[] = + { + std::byte{0x75}, std::byte{0x00}, + std::byte{0x76}, std::byte{0x00}, + std::byte{0x77}, std::byte{0x00} + }; + NTSTATUS status = sb.append(span { bytes }); + + THEN("The resulting string equals 'uvw'") + { + REQUIRE_NT_SUCCESS(status); + REQUIRE(sb.string().equals(L"uvw")); + } + } + WHEN("USimpleString and PCWSTR are appended in sequence") { NTSTATUS status1 = sb.append(L"ab"); - USimpleString part(L"cd"); - NTSTATUS status2 = sb.append(part); + NTSTATUS status2 = sb.append(USimpleString(L"cd")); NTSTATUS status3 = sb.append(L""); THEN("They concatenate to 'abcd'") @@ -70,37 +131,20 @@ SCENARIO("UStringBuilder") REQUIRE_NT_SUCCESS(status1); REQUIRE_NT_SUCCESS(status2); REQUIRE_NT_SUCCESS(status3); - REQUIRE(sb.string().charLength() == 4); REQUIRE(sb.string().equals(L"abcd")); } } WHEN("Variadic append with multiple arguments is used") { - USimpleString a(L"ab"); - USimpleString c(L"cd"); - NTSTATUS status = sb.append(a, c, L"ef"); + NTSTATUS status = sb.append(USimpleString(L"ab"), USimpleString(L"cd"), L"ef"); THEN("All parts are appended in order") { REQUIRE_NT_SUCCESS(status); - REQUIRE(sb.string().charLength() == 6); REQUIRE(sb.string().equals(L"abcdef")); } } - - WHEN("Multiple append calls trigger internal reallocation and preserve content") - { - REQUIRE_NT_SUCCESS(sb.append(L"abc")); - // Force growth by appending a longer chunk - REQUIRE_NT_SUCCESS(sb.append(L"defghij")); - - THEN("Final content is the concatenation of all parts") - { - REQUIRE(sb.string().charLength() == 10); - REQUIRE(sb.string().equals(L"abcdefghij")); - } - } } GIVEN("A builder with content 'Hello'") @@ -108,20 +152,6 @@ SCENARIO("UStringBuilder") UStringBuilder sb; REQUIRE_NT_SUCCESS(sb.append(L"Hello")); - WHEN("copyTo is used with a small destination buffer") - { - WCHAR buf[4] = { 0 }; - int copied = sb.string().copyTo(buf); - THEN("It copies as many chars as fits including null-terminator in destination") - { - REQUIRE(copied == 3); // min(4-1, 5) = 3 - REQUIRE(buf[0] == L'H'); - REQUIRE(buf[1] == L'e'); - REQUIRE(buf[2] == L'l'); - REQUIRE(buf[3] == L'\0'); - } - } - WHEN("reserve is called with a smaller size than current length") { // Note: current charLength is 5, reserve(3) will reallocate to 3 and truncate @@ -131,8 +161,8 @@ SCENARIO("UStringBuilder") { REQUIRE_NT_SUCCESS(status); REQUIRE(sb.string().charLength() == 3); - REQUIRE(sb.string().equals(L"Hel")); REQUIRE(sb.string().maxCharLength() == 3); + REQUIRE(sb.string().equals(L"Hel")); } } } diff --git a/test/pch.h b/test/pch.h index b5c57a3..b0bdf5f 100644 --- a/test/pch.h +++ b/test/pch.h @@ -2,6 +2,7 @@ #include #include #include +#include #include // TODO: move this default implementation to kf