#include "c_utf8.h" #include #include /* --- 单元测试模块集成 --- */ #define RUN_TEST(test_case, name) \ do { \ printf("[RUN] %s... ", name); \ if (test_case) { \ printf("\033[32mPASSED\033[0m\n"); \ } else { \ printf("\033[31mFAILED\033[0m (%s:%d)\n", __FILE__, __LINE__); \ return C_ERR_FAIL; \ } \ } while(0) c_err_t c_Utf8String_UnitTest(void) { printf("==================================================\n"); printf(" STARTING C_UTF8STRING UNIT TESTING \n"); printf("==================================================\n"); const char* sample = "NLP大模型_2026"; // 包含 3个大写英文、3个汉字、1个下划线、4个数字 = 11个字符 /* 1. c_utf8_strlen 测试 */ // 传统的 strlen(sample) 会返回 3 + 3*3 + 1 + 4 = 17 字节 RUN_TEST(c_utf8_strlen(sample) == 11, "c_utf8_strlen correctly counts character points"); RUN_TEST(c_utf8_strlen("") == 0, "c_utf8_strlen handles empty strings"); /* 2. c_utf8_strchr 测试 */ const char* find_eng = c_utf8_strchr(sample, "P"); const char* find_chn = c_utf8_strchr(sample, "模"); const char* find_none = c_utf8_strchr(sample, "国"); RUN_TEST(find_eng != NULL && *find_eng == 'P', "c_utf8_strchr locate ASCII element"); // "模" 在 "模型_2026" 头部,其后紧跟 "型" RUN_TEST(find_chn != NULL && strncmp(find_chn, "模型", 6) == 0, "c_utf8_strchr locate multi-byte Chinese word"); RUN_TEST(find_none == NULL, "c_utf8_strchr returns NULL for non-existing chars"); /* 3. c_utf8_strncpy 安全截断测试 */ char dest_buf[64]; // 截断前 5 个字符 -> "NLP大模" (绝不会出现半个汉字或乱码断裂) c_utf8_strncpy(dest_buf, sample, 5); RUN_TEST(c_utf8_strlen(dest_buf) == 5, "c_utf8_strncpy slices correct character width"); RUN_TEST(strcmp(dest_buf, "NLP大模") == 0, "c_utf8_strncpy safe boundary isolation checked"); /* 4. c_utf8_strncmp 字符匹配测试 */ RUN_TEST(c_utf8_strncmp("自然语言", "自然选择", 2) == 0, "c_utf8_strncmp matches first 2 shared Chinese words"); RUN_TEST(c_utf8_strncmp("自然语言", "自然选择", 3) != 0, "c_utf8_strncmp detects variance at 3rd word slot"); /* ------------------------------------------------------------------------------------------------------------------ */ /* */ /* 5. Lowercase Transform Verification (c_utf8_tolower) */ char case_buf[64] = "NLP大模型_2026_Go!"; c_utf8_tolower(case_buf); RUN_TEST(strcmp(case_buf, "nlp大模型_2026_go!") == 0, "c_utf8_tolower translates ASCII while isolating Chinese layout characters"); // Cyrillic multi-byte letter test ("П" -> 0xD0 0x9F converted down to "п" -> 0xD0 0xBF) char cyrillic_buf[8] = { (char)0xD0, (char)0x9F, '\0' }; c_utf8_tolower(cyrillic_buf); RUN_TEST((unsigned char)cyrillic_buf[1] == 0xBF, "c_utf8_tolower successfully transforms multi-byte Cyrillic characters"); /* 6. Concatenation Verification (c_utf8_strcat) */ char cat_dest[32] = "自然"; c_utf8_strcat(cat_dest, "语言"); RUN_TEST(strcmp(cat_dest, "自然语言") == 0, "c_utf8_strcat appends string tokens cleanly"); RUN_TEST(c_utf8_strlen(cat_dest) == 4, "Post-concatenation size checks out at 4 characters total"); /* ------------------------------------------------------------------------------------------------------------------ */ /* */ /* 24. Uppercase Transform Verification (c_utf8_toupper) */ char upper_buf[] = "nlp大模型_2026_go!"; c_utf8_toupper(upper_buf); RUN_TEST(strcmp(upper_buf, "NLP大模型_2026_GO!") == 0, "c_utf8_toupper translates ASCII while isolating Chinese layout characters"); // Cyrillic multi-byte lowercase letter test ("п" -> 0xD0 0xBF converted up to "П" -> 0xD0 0x9F) char cyr_upper_buf[] = { (char)0xD0, (char)0xBF, '\0' }; c_utf8_toupper(cyr_upper_buf); RUN_TEST((unsigned char)cyr_upper_buf[1] == 0x9F, "c_utf8_toupper successfully transforms multi-byte Cyrillic lowercase characters"); /* ------------------------------------------------------------------------------------------------------------------ */ /* */ /* 7. Character Scanning Verification (c_utf8_strchr) */ const char* scan_base = "NLP大模型_2026"; /* 8. Substring Scanning Verification (c_utf8_strstr) */ const char* match_str = c_utf8_strstr(scan_base, "大模型"); const char* miss_str = c_utf8_strstr(scan_base, "小模型"); const char* empty_str = c_utf8_strstr(scan_base, ""); RUN_TEST(match_str != NULL && strncmp(match_str, "大模型_2026", 14) == 0, "c_utf8_strstr fetches multi-byte string locations"); RUN_TEST(miss_str == NULL, "c_utf8_strstr returns NULL cleanly on substring mismatch"); RUN_TEST(empty_str == scan_base, "c_utf8_strstr returns parent head context given empty needle input"); /* ------------------------------------------------------------------------------------------------------------------ */ /* */ /* 16. Reverse Scanning Verification (c_utf8_strrchr) */ const char* r_base = "自然_模型_自然_2026"; const char* last_match = c_utf8_strrchr(r_base, "自然"); const char* first_match = c_utf8_strchr(r_base, "自然"); RUN_TEST(last_match != NULL && last_match != first_match, "c_utf8_strrchr skips the first match to pull the last occurrence"); RUN_TEST(strncmp(last_match, "自然_2026", 11) == 0, "c_utf8_strrchr locates the correct trailing block address"); /* 17. Reentrant Tokenization Verification (c_utf8_strtok) */ char token_source[] = ",NLP,,大模型,2026,"; // Multi-byte Chinese comma delimiters const char* delimiters = ","; char* save_context = NULL; // First Call char* token = c_utf8_strtok(token_source, delimiters, &save_context); RUN_TEST(token != NULL && strcmp(token, "NLP") == 0, "c_utf8_strtok parses the first token and skips leading delims"); // Second Call (Pass NULL to proceed) token = c_utf8_strtok(NULL, delimiters, &save_context); RUN_TEST(token != NULL && strcmp(token, "大模型") == 0, "c_utf8_strtok correctly extracts multi-byte Chinese '大模型'"); // Third Call token = c_utf8_strtok(NULL, delimiters, &save_context); RUN_TEST(token != NULL && strcmp(token, "2026") == 0, "c_utf8_strtok extracts '2026'"); // Fourth Call - Termination token = c_utf8_strtok(NULL, delimiters, &save_context); RUN_TEST(token == NULL, "c_utf8_strtok returns NULL cleanly when parsing is finished"); /* ------------------------------------------------------------------------------------------------------------------ */ /* */ /* 25. Case-Insensitive Bounded Comparison Verification (c_utf8_strncasecmp) */ // Test Case 1: Simple matching mixed-case strings RUN_TEST(c_utf8_strncasecmp("Nlp大模型", "NLP大模型", 6) == 0, "c_utf8_strncasecmp matches mixed cases up to 6 characters"); // Test Case 2: Verification of prefix character restrictions bounding RUN_TEST(c_utf8_strncasecmp("NLP大模型_v2", "nlp大模型_v3", 6) == 0, "c_utf8_strncasecmp returns 0 if differences fall past the character count limit"); RUN_TEST(c_utf8_strncasecmp("NLP大模型_v2", "nlp大模型_v3", 9) != 0, "c_utf8_strncasecmp registers structural variance when character limits cover differences"); // Test Case 3: Mixed language case sorting behavior RUN_TEST(c_utf8_strncasecmp("自然语言NLP", "自然语言nlp", 7) == 0, "c_utf8_strncasecmp handles matching trailing ASCII case differences after multi-byte blocks"); /* ------------------------------------------------------------------------------------------------------------------ */ /* */ /* 18. UTF-8 to Unicode Codepoint Decoding Verification */ const char* utf8_src = "中!🚀"; // "中" (3 bytes), "!" (1 byte), "🚀" Emoji (4 bytes) c_ucs4_t cp = 0; c_size_t bytes_step = 0; // Decode first character ("中" -> Expected Codepoint: U+4E2D) c_err_t err = c_utf8_to_unicode(utf8_src, &cp, &bytes_step); RUN_TEST(err == C_ERR_OK && bytes_step == 3, "c_utf8_to_unicode processes 3-byte Chinese characters"); RUN_TEST(cp == 0x4E2D, "Decoded codepoint matches U+4E2D ('中') accurately"); // Decode next sequence ("!" -> Expected Codepoint: U+0021) err = c_utf8_to_unicode(utf8_src + bytes_step, &cp, &bytes_step); RUN_TEST(err == C_ERR_OK && bytes_step == 1, "c_utf8_to_unicode processes 1-byte ASCII markers"); RUN_TEST(cp == 0x0021, "Decoded codepoint matches U+0021 ('!')"); // Decode next sequence (Rocket Emoji "🚀" -> Expected Codepoint: U+1F680) err = c_utf8_to_unicode(utf8_src + 4, &cp, &bytes_step); // Skip forward 4 bytes total RUN_TEST(err == C_ERR_OK && bytes_step == 4, "c_utf8_to_unicode decodes 4-byte astral plane emojis"); RUN_TEST(cp == 0x1F680, "Decoded codepoint matches U+1F680 ('🚀')"); /* 19. Unicode Codepoint to UTF-8 Encoding Verification */ char encode_buf[8]; c_size_t written_len = 0; // Encode U+4E2D back to UTF-8 err = c_utf8_from_unicode(0x4E2D, encode_buf, &written_len); RUN_TEST(err == C_ERR_OK && written_len == 3, "c_utf8_from_unicode encodes U+4E2D back into 3 bytes"); RUN_TEST(strcmp(encode_buf, "中") == 0, "Encoded string content matches '中' flawlessly"); // Encode U+1F680 back to UTF-8 err = c_utf8_from_unicode(0x1F680, encode_buf, &written_len); RUN_TEST(err == C_ERR_OK && written_len == 4, "c_utf8_from_unicode encodes U+1F680 back into 4 bytes"); /* ------------------------------------------------------------------------------------------------------------------ */ /* */ /* 20. UTF-8 String Stream <-> Unicode Array Conversions Verification */ const char* mixed_sentence = "NLP大模型!🚀"; // Length: 3 ASCII, 3 Chinese (9B), 1 ASCII, 1 Emoji (4B) = 8 characters total c_ucs4_t uni_array[16]; c_size_t total_chars = 0; // Test Point 1: Decode stream into codepoint container array err = c_utf8_to_unicode_array(mixed_sentence, uni_array, 16, &total_chars); RUN_TEST(err == C_ERR_OK && total_chars == 8, "c_utf8_to_unicode_array maps complex string streams into separate integer points"); RUN_TEST(uni_array[0] == 'N' && uni_array[3] == 0x5927 && uni_array[7] == 0x1F680, "Decoded array positions hold proper character points ('N', '大', '🚀')"); // Test Point 2: Trigger array capacity guard protection c_ucs4_t tight_array[4]; err = c_utf8_to_unicode_array(mixed_sentence, tight_array, 4, &total_chars); RUN_TEST(err == C_ERR_PARAM && total_chars == 4, "c_utf8_to_unicode_array safely blocks operations and returns current progress on buffer limit hits"); // Test Point 3: Reverse operation - Encode codepoint array back into native UTF-8 string layout char reconstructed_str[64]; c_size_t written_bytes = 0; err = c_utf8_from_unicode_array(uni_array, 8, reconstructed_str, sizeof(reconstructed_str), &written_bytes); RUN_TEST(err == C_ERR_OK && written_bytes == 17, "c_utf8_from_unicode_array successfully packs codepoints back into 17 raw bytes"); RUN_TEST(strcmp(reconstructed_str, mixed_sentence) == 0, "Reconstructed stream data matches original expression perfectly"); /* ------------------------------------------------------------------------------------------------------------------ */ /* */ /* 21. UTF-8 <-> UTF-16 Array Conversions Verification */ const char* utf8_sentence = "NLP大模型!🚀"; // Contains ASCII, 3-byte Chinese, and a 4-byte Astral Plane Emoji c_uint16_t utf16_array[32]; c_size_t units_written = 0; // Test Point 1: Convert UTF-8 stream to UTF-16 code units // "NLP" (3 units) + "大模型" (3 units) + "!" (1 unit) + "🚀" (Surrogate pair = 2 units) = 9 units total err = c_utf8_to_utf16(utf8_sentence, utf16_array, 32, &units_written); RUN_TEST(err == C_ERR_OK && units_written == 9, "c_utf8_to_utf16 successfully packs characters including astral planes"); RUN_TEST(utf16_array[0] == 'N' && utf16_array[3] == 0x5927, "Verify standard BMP mapping inside UTF-16 array structure"); // Verify high and low surrogate points for the Rocket Emoji 🚀 RUN_TEST(utf16_array[7] == 0xD83D && utf16_array[8] == 0xDE80, "Verify surrogate pair matching (0xD83D 0xDE80) for U+1F680"); // Test Point 2: Convert UTF-16 array back to native UTF-8 string layout char reconstructed_utf8[64]; c_size_t bytes_written_utf8 = 0; err = c_utf8_from_utf16(utf16_array, units_written, reconstructed_utf8, sizeof(reconstructed_utf8), &bytes_written_utf8); RUN_TEST(err == C_ERR_OK && bytes_written_utf8 == 17, "c_utf8_from_utf16 unpacks units back into 17 raw bytes"); RUN_TEST(strcmp(reconstructed_utf8, utf8_sentence) == 0, "Reconstructed UTF-8 matches the original string precisely"); // Test Point 3: Malformed Surrogate Pair Detection c_uint16_t malformed_utf16[] = { 0xD83D, 'A' }; // High surrogate followed by a literal letter (invalid) char error_buf[16]; c_size_t err_bytes = 0; err = c_utf8_from_utf16(malformed_utf16, 2, error_buf, sizeof(error_buf), &err_bytes); RUN_TEST(err == C_ERR_PARAM, "c_utf8_from_utf16 successfully flags and rejects malformed/orphaned surrogate code units"); /* ------------------------------------------------------------------------------------------------------------------ */ /* */ /* 22. UTF-16 Endianness Byte-Swapping Verification (c_utf16_swap_endian) */ c_uint16_t sample_utf16[] = { 0xD83D, 0xDE80, 0x5927 }; // 🚀 and 大 in standard host endianness c_size_t array_len = sizeof(sample_utf16) / sizeof(sample_utf16[0]); // Test Point 1: Guard against NULL parameters RUN_TEST(c_utf16_swap_endian(NULL, array_len) == C_ERR_PARAM, "c_utf16_swap_endian safely rejects NULL pointer arrays"); // Test Point 2: Perform initial byte-swap transformation err = c_utf16_swap_endian(sample_utf16, array_len); RUN_TEST(err == C_ERR_OK, "c_utf16_swap_endian executes successfully"); // 0xD83D -> 0x3DD8, 0xDE80 -> 0x80DE, 0x5927 -> 0x2759 RUN_TEST(sample_utf16[0] == 0x3DD8, "First code unit correctly bit-swapped (0xD83D -> 0x3DD8)"); RUN_TEST(sample_utf16[1] == 0x80DE, "Second code unit correctly bit-swapped (0xDE80 -> 0x80DE)"); RUN_TEST(sample_utf16[2] == 0x2759, "Third code unit correctly bit-swapped (0x5927 -> 0x2759)"); // Test Point 3: Swap back to restore the original host endianness values err = c_utf16_swap_endian(sample_utf16, array_len); RUN_TEST(err == C_ERR_OK, "c_utf16_swap_endian reverts state back on secondary execution pass"); RUN_TEST(sample_utf16[0] == 0xD83D && sample_utf16[1] == 0xDE80 && sample_utf16[2] == 0x5927, "Original internal value contexts perfectly preserved"); /* ------------------------------------------------------------------------------------------------------------------ */ /* */ /* 23. UTF-16 Single-Char Unicode Conversions Verification */ c_ucs4_t decoded_cp = 0; c_size_t units_moved = 0; // Test Point 1: Decode Standard BMP Character ('大' -> U+5927) c_uint16_t bmp_sample[] = { 0x5927 }; err = c_utf16_to_unicode(bmp_sample, 1, &decoded_cp, &units_moved); RUN_TEST(err == C_ERR_OK && units_moved == 1, "c_utf16_to_unicode processes BMP code units"); RUN_TEST(decoded_cp == 0x5927, "Decoded BMP matches expected U+5927 successfully"); // Test Point 2: Decode Surrogate Pair Character (Rocket Emoji '🚀' -> High: 0xD83D, Low: 0xDE80) c_uint16_t astral_sample[] = { 0xD83D, 0xDE80 }; err = c_utf16_to_unicode(astral_sample, 2, &decoded_cp, &units_moved); RUN_TEST(err == C_ERR_OK && units_moved == 2, "c_utf16_to_unicode correctly handles surrogate pairs"); RUN_TEST(decoded_cp == 0x1F680, "Decoded Astral match returns U+1F680 ('🚀')"); // Test Point 3: Error validation protection against malformed surrogate chains c_uint16_t isolated_high[] = { 0xD83D }; // Lacks matching trailing block RUN_TEST(c_utf16_to_unicode(isolated_high, 1, &decoded_cp, &units_moved) == C_ERR_PARAM, "c_utf16_to_unicode rejects truncated surrogate sequences"); // Test Point 4: Encode Astral Plane back to UTF-16 code units c_uint16_t encode_units[2]; units_written = 0; err = c_utf16_from_unicode(0x1F680, encode_units, 2, &units_written); RUN_TEST(err == C_ERR_OK && units_written == 2, "c_utf16_from_unicode builds surrogate pairs for plane codepoints"); RUN_TEST(encode_units[0] == 0xD83D && encode_units[1] == 0xDE80, "Generated high and low values match standard encoding targets"); /* ------------------------------------------------------------------------------------------------------------------ */ /* */ printf("==================================================\n"); printf("\033[32mSUCCESS: ALL UTF-8 COMPATIBLE INTERFACES PASSED!\033[0m\n"); printf("==================================================\n"); return C_ERR_OK; } int main(int argc, char** argv){ return c_Utf8String_UnitTest(); }