Files
2026-08-30 03:59:45 +08:00

303 lines
17 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "c_utf8.h"
#include <stdlib.h>
#include <stdio.h>
/* --- 单元测试模块集成 --- */
#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();
}