diff --git a/AI.md b/AI.md index caeb1f8..7d8ea7a 100644 --- a/AI.md +++ b/AI.md @@ -47,7 +47,7 @@ unfinished work out of `TODO.md` merely because its architecture is documented. Latest patch marker expected in an up-to-date bundle: -- `0459 test: isolate FLAIM smoke test work directories` +- `0460 test: add core NSS helper unit coverage` When a later chat receives a new `mars-nwe-master` bundle, compare `git log -1` with this marker. If the uploaded bundle already contains this commit subject, @@ -67,17 +67,16 @@ The active line is expected to include: Last generated patch: -- `0459 test: isolate FLAIM smoke test work directories` +- `0460 test: add core NSS helper unit coverage` Purpose of that patch: -- Fix FLAIM smoke tests that can fail after stale or root-owned build-tree - work directories are left behind. The mars-nwe FLAIM API smoke now creates a - unique temporary work directory derived from the configured base path instead - of reusing `tests/flaim/flaim-api` directly. Apply the matching mars-flaim - submodule patch for the imported `nwflaim.database.create-and-check` CTest - script, which similarly avoids the fixed `third_party/flaim/ctest/nwflaim-db` - directory. +- Add focused CTest coverage for the imported low-level NSS helper line in + `libnwcore`: bit operations, bitmap allocation/search, CRC/hash behavior, + xCtype classification/case conversion, xString compatibility helpers, and + UTF-8/Unicode round-trips. Keep the tests in subsystem directories under + `tests/core/` so later codepage, byte-converter, GUID/ID, UTC, and Unicode + table tests can be added without flattening the test tree. Rejected or superseded patches that must not be reused as-is: diff --git a/tests/README.md b/tests/README.md index da9475d..4c9c82b 100644 --- a/tests/README.md +++ b/tests/README.md @@ -9,8 +9,11 @@ exercise outside the normal install flow. `tests/core` contains normal CTest unit tests for `libnwcore` facilities. Keep them grouped by core subsystem: `tests/core/ini` covers the shared `nw_ini_*` reader/writer facade, `tests/core/log` covers the `nwlog_*` -level-mask facade, and future Unicode/codepage/UTF-8/byte/bitmap tests should -get their own sibling subdirectories instead of being added as a flat list. +level-mask facade, and imported NSS low-level helpers are covered by sibling +subdirectories such as `tests/core/bit`, `tests/core/bitmap`, +`tests/core/crc`, `tests/core/xctype`, `tests/core/xstring`, and +`tests/core/utf8`. Future Unicode/codepage/byte coverage should keep using +new focused sibling subdirectories instead of being added as a flat list. `tests/tui` contains non-interactive `libnwtui` smoke tests. These tests must not require a real terminal or compare full-screen terminal output; test widget diff --git a/tests/core/CMakeLists.txt b/tests/core/CMakeLists.txt index 976af5f..ff87d3b 100644 --- a/tests/core/CMakeLists.txt +++ b/tests/core/CMakeLists.txt @@ -4,3 +4,9 @@ add_subdirectory(ini) add_subdirectory(log) +add_subdirectory(bit) +add_subdirectory(bitmap) +add_subdirectory(crc) +add_subdirectory(xctype) +add_subdirectory(xstring) +add_subdirectory(utf8) diff --git a/tests/core/bit/CMakeLists.txt b/tests/core/bit/CMakeLists.txt new file mode 100644 index 0000000..1fd04cc --- /dev/null +++ b/tests/core/bit/CMakeLists.txt @@ -0,0 +1,9 @@ +add_executable(test_nwcore_bit + test_nwcore_bit.c) + +target_compile_features(test_nwcore_bit PRIVATE c_std_99) +target_include_directories(test_nwcore_bit PRIVATE + "${CMAKE_SOURCE_DIR}/include/core") +target_link_libraries(test_nwcore_bit PRIVATE mars_nwe::core) + +add_test(NAME nwcore_bit COMMAND test_nwcore_bit) diff --git a/tests/core/bit/test_nwcore_bit.c b/tests/core/bit/test_nwcore_bit.c new file mode 100644 index 0000000..be01338 --- /dev/null +++ b/tests/core/bit/test_nwcore_bit.c @@ -0,0 +1,36 @@ +#include "bit.h" + +#include +#include + +#define CHECK(expr) \ + do { \ + if (!(expr)) { \ + fprintf(stderr, "CHECK failed at %s:%d: %s\n", __FILE__, __LINE__, #expr); \ + return 1; \ + } \ + } while (0) + +int main(void) +{ + CHECK(LB_CountBits(0) == 0); + CHECK(LB_CountBits(1) == 1); + CHECK(LB_CountBits((NINT)0x0000f0f0U) == 8); + CHECK(LB_CountBits((NINT)0xffffffffU) == 32); + + CHECK(LB_findHighBit(0) == (NINT)-1); + CHECK(LB_findHighBit(1) == 0); + CHECK(LB_findHighBit(0x10) == 4); + CHECK(LB_findHighBit((NINT)0x80000000U) == 31); + + CHECK(LB_findLowBit(0) == (NINT)-1); + CHECK(LB_findLowBit(1) == 0); + CHECK(LB_findLowBit(0x10) == 4); + CHECK(LB_findLowBit((NINT)0x80000000U) == 31); + + CHECK((uint32_t)LB_RotateLeft((NINT)0x12345678U, 8) == 0x34567812U); + CHECK((uint32_t)LB_RotateRight((NINT)0x12345678U, 8) == 0x78123456U); + CHECK((uint32_t)LB_RotateLeft((NINT)0x12345678U, 0) == 0x12345678U); + CHECK((uint32_t)LB_RotateRight((NINT)0x12345678U, 32) == 0x12345678U); + return 0; +} diff --git a/tests/core/bitmap/CMakeLists.txt b/tests/core/bitmap/CMakeLists.txt new file mode 100644 index 0000000..59b8728 --- /dev/null +++ b/tests/core/bitmap/CMakeLists.txt @@ -0,0 +1,9 @@ +add_executable(test_nwcore_bitmap + test_nwcore_bitmap.c) + +target_compile_features(test_nwcore_bitmap PRIVATE c_std_99) +target_include_directories(test_nwcore_bitmap PRIVATE + "${CMAKE_SOURCE_DIR}/include/core") +target_link_libraries(test_nwcore_bitmap PRIVATE mars_nwe::core) + +add_test(NAME nwcore_bitmap COMMAND test_nwcore_bitmap) diff --git a/tests/core/bitmap/test_nwcore_bitmap.c b/tests/core/bitmap/test_nwcore_bitmap.c new file mode 100644 index 0000000..45eb1bc --- /dev/null +++ b/tests/core/bitmap/test_nwcore_bitmap.c @@ -0,0 +1,48 @@ +#include "bitmap.h" + +#include + +#define CHECK(expr) \ + do { \ + if (!(expr)) { \ + fprintf(stderr, "CHECK failed at %s:%d: %s\n", __FILE__, __LINE__, #expr); \ + return 1; \ + } \ + } while (0) + +int main(void) +{ + BitMap_s map; + SNINT start; + + CHECK(newBitMap(&map, 20) == &map); + CHECK(getMaxBits(&map) == 20); + CHECK(countBits(&map) == 20); + CHECK(testABit(&map, 0)); + CHECK(testABit(&map, 19)); + + start = findBits(&map, 4); + CHECK(start == 0); + CHECK(countBits(&map) == 16); + CHECK(!testABit(&map, 0)); + CHECK(!testABit(&map, 3)); + CHECK(testABit(&map, 4)); + + setAbit(&map, 1); + CHECK(testABit(&map, 1)); + clearAbit(&map, 1); + CHECK(!testABit(&map, 1)); + + clearBits(&map, 8, 4); + CHECK(!testABit(&map, 8)); + CHECK(!testABit(&map, 11)); + setBits(&map, 8, 2); + CHECK(testABit(&map, 8)); + CHECK(testABit(&map, 9)); + CHECK(!testABit(&map, 10)); + + start = findBits(&map, 4); + CHECK(start == 4); + freeBitMap(&map); + return 0; +} diff --git a/tests/core/crc/CMakeLists.txt b/tests/core/crc/CMakeLists.txt new file mode 100644 index 0000000..6025c64 --- /dev/null +++ b/tests/core/crc/CMakeLists.txt @@ -0,0 +1,9 @@ +add_executable(test_nwcore_crc + test_nwcore_crc.c) + +target_compile_features(test_nwcore_crc PRIVATE c_std_99) +target_include_directories(test_nwcore_crc PRIVATE + "${CMAKE_SOURCE_DIR}/include/core") +target_link_libraries(test_nwcore_crc PRIVATE mars_nwe::core) + +add_test(NAME nwcore_crc COMMAND test_nwcore_crc) diff --git a/tests/core/crc/test_nwcore_crc.c b/tests/core/crc/test_nwcore_crc.c new file mode 100644 index 0000000..1cecf47 --- /dev/null +++ b/tests/core/crc/test_nwcore_crc.c @@ -0,0 +1,31 @@ +#include "crc.h" + +#include +#include + +#define CHECK(expr) \ + do { \ + if (!(expr)) { \ + fprintf(stderr, "CHECK failed at %s:%d: %s\n", __FILE__, __LINE__, #expr); \ + return 1; \ + } \ + } while (0) + +int main(void) +{ + BYTE input[] = "123456789"; + char mixed[] = "Mars-NWE"; + char lower[] = "mars-nwe"; + crc_t running; + + CHECK((uint32_t)crc(input, 9) == 0xcbf43926U); + + running = updateCRC((crc_t)~0L, input, 4); + running = updateCRC(running, input + 4, 5); + CHECK((uint32_t)~running == 0xcbf43926U); + + CHECK(hashLowerString(mixed) == hashLowerString(lower)); + CHECK(hashString(mixed) != hashLowerString(mixed)); + CHECK(lowerStringCRC(mixed) == lowerStringCRC(lower)); + return 0; +} diff --git a/tests/core/utf8/CMakeLists.txt b/tests/core/utf8/CMakeLists.txt new file mode 100644 index 0000000..260bf27 --- /dev/null +++ b/tests/core/utf8/CMakeLists.txt @@ -0,0 +1,9 @@ +add_executable(test_nwcore_utf8 + test_nwcore_utf8.c) + +target_compile_features(test_nwcore_utf8 PRIVATE c_std_99) +target_include_directories(test_nwcore_utf8 PRIVATE + "${CMAKE_SOURCE_DIR}/include/core") +target_link_libraries(test_nwcore_utf8 PRIVATE mars_nwe::core) + +add_test(NAME nwcore_utf8 COMMAND test_nwcore_utf8) diff --git a/tests/core/utf8/test_nwcore_utf8.c b/tests/core/utf8/test_nwcore_utf8.c new file mode 100644 index 0000000..8fe8a5d --- /dev/null +++ b/tests/core/utf8/test_nwcore_utf8.c @@ -0,0 +1,43 @@ +#include "xUnicode.h" + +#include +#include + +#define CHECK(expr) \ + do { \ + if (!(expr)) { \ + fprintf(stderr, "CHECK failed at %s:%d: %s\n", __FILE__, __LINE__, #expr); \ + return 1; \ + } \ + } while (0) + +int main(void) +{ + utf8_t ascii[] = "Mars"; + utf8_t utf8_word[] = { 'M', 0xc3, 0xa4, 'r', 'z', 0 }; + unicode_t unicode[16]; + utf8_t out[16]; + NINT n; + + n = utf2uni(ascii, unicode, sizeof(unicode)); + CHECK(n == 4); + CHECK(unicode[0] == 'M'); + CHECK(unicode[3] == 's'); + CHECK(unicode[4] == 0); + + n = uni2utf(unicode, out, sizeof(out)); + CHECK(n == 4); + CHECK(strcmp((char *)out, "Mars") == 0); + + n = utf2uni(utf8_word, unicode, sizeof(unicode)); + CHECK(n == 4); + CHECK(unicode[0] == 'M'); + CHECK(unicode[1] == 0x00e4); + CHECK(unicode[2] == 'r'); + CHECK(unicode[3] == 'z'); + + n = uni2utf(unicode, out, sizeof(out)); + CHECK(n == 5); + CHECK(memcmp(out, utf8_word, 6) == 0); + return 0; +} diff --git a/tests/core/xctype/CMakeLists.txt b/tests/core/xctype/CMakeLists.txt new file mode 100644 index 0000000..2523e54 --- /dev/null +++ b/tests/core/xctype/CMakeLists.txt @@ -0,0 +1,9 @@ +add_executable(test_nwcore_xctype + test_nwcore_xctype.c) + +target_compile_features(test_nwcore_xctype PRIVATE c_std_99) +target_include_directories(test_nwcore_xctype PRIVATE + "${CMAKE_SOURCE_DIR}/include/core") +target_link_libraries(test_nwcore_xctype PRIVATE mars_nwe::core) + +add_test(NAME nwcore_xctype COMMAND test_nwcore_xctype) diff --git a/tests/core/xctype/test_nwcore_xctype.c b/tests/core/xctype/test_nwcore_xctype.c new file mode 100644 index 0000000..8153f83 --- /dev/null +++ b/tests/core/xctype/test_nwcore_xctype.c @@ -0,0 +1,33 @@ +#include "xCtype.h" + +#include + +#define CHECK(expr) \ + do { \ + if (!(expr)) { \ + fprintf(stderr, "CHECK failed at %s:%d: %s\n", __FILE__, __LINE__, #expr); \ + return 1; \ + } \ + } while (0) + +int main(void) +{ + CHECK(LB_tolower('A') == 'a'); + CHECK(LB_tolower('z') == 'z'); + CHECK(LB_toupper('a') == 'A'); + CHECK(LB_toupper('Z') == 'Z'); + + CHECK(LB_isalpha('A')); + CHECK(LB_isalpha('z')); + CHECK(LB_isdigit('7')); + CHECK(LB_isxdigit('f')); + CHECK(LB_isxdigit('F')); + CHECK(LB_isspace(' ')); + CHECK(LB_isspace('\n')); + CHECK(LB_ispunct('.')); + CHECK(LB_isprint('~')); + CHECK(!LB_isprint('\n')); + CHECK(LB_isascii(0x7f)); + CHECK(!LB_isascii(0x80)); + return 0; +} diff --git a/tests/core/xstring/CMakeLists.txt b/tests/core/xstring/CMakeLists.txt new file mode 100644 index 0000000..3190ff4 --- /dev/null +++ b/tests/core/xstring/CMakeLists.txt @@ -0,0 +1,9 @@ +add_executable(test_nwcore_xstring + test_nwcore_xstring.c) + +target_compile_features(test_nwcore_xstring PRIVATE c_std_99) +target_include_directories(test_nwcore_xstring PRIVATE + "${CMAKE_SOURCE_DIR}/include/core") +target_link_libraries(test_nwcore_xstring PRIVATE mars_nwe::core) + +add_test(NAME nwcore_xstring COMMAND test_nwcore_xstring) diff --git a/tests/core/xstring/test_nwcore_xstring.c b/tests/core/xstring/test_nwcore_xstring.c new file mode 100644 index 0000000..d56b4f3 --- /dev/null +++ b/tests/core/xstring/test_nwcore_xstring.c @@ -0,0 +1,39 @@ +#include "xString.h" + +#include +#include + +#define CHECK(expr) \ + do { \ + if (!(expr)) { \ + fprintf(stderr, "CHECK failed at %s:%d: %s\n", __FILE__, __LINE__, #expr); \ + return 1; \ + } \ + } while (0) + +int main(void) +{ + char buffer[32]; + char overlap[16] = "abcdef"; + + CHECK(LB_stricmp("Mars", "mArS") == 0); + CHECK(LB_strnicmp("NetWare", "net", 3) == 0); + CHECK(LB_memicmp("AbCd", "aBcD", 4) == 0); + + CHECK(LB_strmcpy(buffer, "abcdef", 4) == buffer); + CHECK(strcmp(buffer, "abc") == 0); + + strcpy(buffer, "Mars"); + CHECK(LB_strlwr(buffer) == buffer); + CHECK(strcmp(buffer, "mars") == 0); + CHECK(LB_strupr(buffer) == buffer); + CHECK(strcmp(buffer, "MARS") == 0); + CHECK(LB_strrev(buffer) == buffer); + CHECK(strcmp(buffer, "SRAM") == 0); + CHECK(LB_strset(buffer, 'x') == buffer); + CHECK(strcmp(buffer, "xxxx") == 0); + + CHECK(LB_memmove(overlap + 2, overlap, 4) == overlap + 2); + CHECK(memcmp(overlap, "ababcd", 6) == 0); + return 0; +}