mirror of
https://github.com/LHY0125/PathEditor.git
synced 2026-05-10 02:09:46 +08:00
feat(test): 添加单元测试框架并完善国际化支持
- 新增 CMake 测试框架配置,支持 safe_string、string_ext 和 path_manager 模块的单元测试 - 实现 Windows API Mock 机制,便于测试编码转换函数 - 添加 error_code 模块的字符串表示函数,支持英文错误日志 - 在 UI 回调函数中集成国际化翻译,覆盖新建、编辑、导入导出等操作提示 - 扩展 string_list 功能,新增重复路径检查函数 - 更新翻译文件,同步所有用户界面的中英文文本
This commit is contained in:
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* string_ext.c 单元测试
|
||||
* 测试字符串扩展函数和 StringList 操作
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "utils/string_ext.h"
|
||||
#include "mock_windows.h"
|
||||
|
||||
/* ==================== Mock 计数器 ==================== */
|
||||
int mock_MultiByteToWideChar_call_count = 0;
|
||||
int mock_WideCharToMultiByte_call_count = 0;
|
||||
static int mock_MB2WC_return = 0;
|
||||
static int mock_WC2MB_return = 0;
|
||||
|
||||
void mock_set_MultiByteToWideChar_return(int ret) {
|
||||
mock_MB2WC_return = ret;
|
||||
}
|
||||
|
||||
void mock_set_WideCharToMultiByte_return(int ret) {
|
||||
mock_WC2MB_return = ret;
|
||||
}
|
||||
|
||||
/* ==================== Mock 实现 ==================== */
|
||||
|
||||
int mock_MultiByteToWideChar(
|
||||
UINT CodePage,
|
||||
DWORD dwFlags,
|
||||
LPCSTR lpMultiByteStr,
|
||||
int cbMultiByte,
|
||||
LPWSTR lpWideCharStr,
|
||||
int cchWideChar)
|
||||
{
|
||||
(void)CodePage;
|
||||
(void)dwFlags;
|
||||
mock_MultiByteToWideChar_call_count++;
|
||||
|
||||
if (!lpMultiByteStr || cbMultiByte == 0)
|
||||
return 0;
|
||||
|
||||
int len = (cbMultiByte == -1) ? strlen(lpMultiByteStr) : cbMultiByte;
|
||||
|
||||
/* 简单 ASCII 转宽字符 */
|
||||
if (lpWideCharStr && cchWideChar > 0) {
|
||||
int copy_len = (cchWideChar < len + 1) ? cchWideChar - 1 : len;
|
||||
for (int i = 0; i < copy_len; i++) {
|
||||
lpWideCharStr[i] = (wchar_t)lpMultiByteStr[i];
|
||||
}
|
||||
lpWideCharStr[copy_len] = L'\0';
|
||||
}
|
||||
|
||||
return mock_MB2WC_return > 0 ? mock_MB2WC_return : (len + 1);
|
||||
}
|
||||
|
||||
int mock_WideCharToMultiByte(
|
||||
UINT CodePage,
|
||||
DWORD dwFlags,
|
||||
LPCWSTR lpWideCharStr,
|
||||
int cchWideChar,
|
||||
LPSTR lpMultiByteStr,
|
||||
int cbMultiByte,
|
||||
LPCSTR lpDefaultChar,
|
||||
LPBOOL lpUsedDefaultChar)
|
||||
{
|
||||
(void)CodePage;
|
||||
(void)dwFlags;
|
||||
(void)lpDefaultChar;
|
||||
(void)lpUsedDefaultChar;
|
||||
mock_WideCharToMultiByte_call_count++;
|
||||
|
||||
if (!lpWideCharStr || cchWideChar == 0)
|
||||
return 0;
|
||||
|
||||
int len = (cchWideChar == -1) ? wcslen(lpWideCharStr) : cchWideChar;
|
||||
|
||||
if (lpMultiByteStr && cbMultiByte > 0) {
|
||||
int copy_len = (cbMultiByte < len + 1) ? cbMultiByte - 1 : len;
|
||||
for (int i = 0; i < copy_len; i++) {
|
||||
lpMultiByteStr[i] = (char)lpWideCharStr[i];
|
||||
}
|
||||
lpMultiByteStr[copy_len] = '\0';
|
||||
}
|
||||
|
||||
return mock_WC2MB_return > 0 ? mock_WC2MB_return : (len + 1);
|
||||
}
|
||||
|
||||
/* ==================== StringList 测试 ==================== */
|
||||
|
||||
static void test_init_string_list(void **state)
|
||||
{
|
||||
(void)state;
|
||||
StringList list;
|
||||
list.items = (void *)0x1234; /* 初始化前设置垃圾值 */
|
||||
list.count = 999;
|
||||
list.capacity = 999;
|
||||
|
||||
init_string_list(&list);
|
||||
|
||||
/* init_string_list 将 items 设置为 NULL,count 和 capacity 设为 0 */
|
||||
assert_null(list.items);
|
||||
assert_int_equal(list.count, 0);
|
||||
assert_int_equal(list.capacity, 0);
|
||||
|
||||
/* clear_string_list 对 NULL items 应该安全处理 */
|
||||
clear_string_list(&list);
|
||||
}
|
||||
|
||||
static void test_add_string_list_single(void **state)
|
||||
{
|
||||
(void)state;
|
||||
StringList list;
|
||||
init_string_list(&list);
|
||||
|
||||
add_string_list(&list, "C:\\Windows");
|
||||
|
||||
assert_int_equal(list.count, 1);
|
||||
assert_string_equal(string_list_get(&list, 0), "C:\\Windows");
|
||||
|
||||
clear_string_list(&list);
|
||||
}
|
||||
|
||||
static void test_add_string_list_multiple(void **state)
|
||||
{
|
||||
(void)state;
|
||||
StringList list;
|
||||
init_string_list(&list);
|
||||
|
||||
add_string_list(&list, "C:\\Windows");
|
||||
add_string_list(&list, "C:\\Program Files");
|
||||
add_string_list(&list, "D:\\Tools");
|
||||
|
||||
assert_int_equal(list.count, 3);
|
||||
assert_string_equal(string_list_get(&list, 0), "C:\\Windows");
|
||||
assert_string_equal(string_list_get(&list, 1), "C:\\Program Files");
|
||||
assert_string_equal(string_list_get(&list, 2), "D:\\Tools");
|
||||
|
||||
clear_string_list(&list);
|
||||
}
|
||||
|
||||
static void test_string_list_get_out_of_bounds(void **state)
|
||||
{
|
||||
(void)state;
|
||||
StringList list;
|
||||
init_string_list(&list);
|
||||
|
||||
add_string_list(&list, "test");
|
||||
|
||||
assert_null(string_list_get(&list, -1));
|
||||
assert_null(string_list_get(&list, 1));
|
||||
assert_null(string_list_get(&list, 100));
|
||||
|
||||
clear_string_list(&list);
|
||||
}
|
||||
|
||||
static void test_string_list_get_null_list(void **state)
|
||||
{
|
||||
(void)state;
|
||||
assert_null(string_list_get(NULL, 0));
|
||||
}
|
||||
|
||||
static void test_string_list_set_normal(void **state)
|
||||
{
|
||||
(void)state;
|
||||
StringList list;
|
||||
init_string_list(&list);
|
||||
|
||||
add_string_list(&list, "original");
|
||||
int result = string_list_set(&list, 0, "modified");
|
||||
|
||||
assert_int_equal(result, 0);
|
||||
assert_string_equal(string_list_get(&list, 0), "modified");
|
||||
|
||||
clear_string_list(&list);
|
||||
}
|
||||
|
||||
static void test_string_list_set_out_of_bounds(void **state)
|
||||
{
|
||||
(void)state;
|
||||
StringList list;
|
||||
init_string_list(&list);
|
||||
|
||||
add_string_list(&list, "test");
|
||||
int result = string_list_set(&list, 5, "modified");
|
||||
|
||||
assert_int_equal(result, -1);
|
||||
|
||||
clear_string_list(&list);
|
||||
}
|
||||
|
||||
static void test_string_list_set_null_list(void **state)
|
||||
{
|
||||
(void)state;
|
||||
int result = string_list_set(NULL, 0, "test");
|
||||
assert_int_equal(result, -1);
|
||||
}
|
||||
|
||||
static void test_clear_string_list(void **state)
|
||||
{
|
||||
(void)state;
|
||||
StringList list;
|
||||
init_string_list(&list);
|
||||
|
||||
add_string_list(&list, "item1");
|
||||
add_string_list(&list, "item2");
|
||||
add_string_list(&list, "item3");
|
||||
|
||||
clear_string_list(&list);
|
||||
|
||||
assert_int_equal(list.count, 0);
|
||||
assert_null(list.items);
|
||||
}
|
||||
|
||||
/* ==================== 编码转换测试 ==================== */
|
||||
|
||||
static void test_utf8_to_wide_normal(void **state)
|
||||
{
|
||||
(void)state;
|
||||
const char *utf8_str = "Hello";
|
||||
|
||||
wchar_t *result = utf8_to_wide(utf8_str);
|
||||
|
||||
if (result) {
|
||||
assert_true(wcscmp(result, L"Hello") == 0);
|
||||
free(result);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_utf8_to_wide_null(void **state)
|
||||
{
|
||||
(void)state;
|
||||
wchar_t *result = utf8_to_wide(NULL);
|
||||
assert_null(result);
|
||||
}
|
||||
|
||||
static void test_wide_to_utf8_normal(void **state)
|
||||
{
|
||||
(void)state;
|
||||
const wchar_t *wide_str = L"World";
|
||||
|
||||
char *result = wide_to_utf8(wide_str);
|
||||
|
||||
if (result) {
|
||||
assert_string_equal(result, "World");
|
||||
free(result);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_wide_to_utf8_null(void **state)
|
||||
{
|
||||
(void)state;
|
||||
char *result = wide_to_utf8(NULL);
|
||||
assert_null(result);
|
||||
}
|
||||
|
||||
/* ==================== stristr 测试 ==================== */
|
||||
|
||||
static void test_stristr_found(void **state)
|
||||
{
|
||||
(void)state;
|
||||
const char *haystack = "The quick brown fox";
|
||||
const char *needle = "quick";
|
||||
|
||||
char *result = stristr(haystack, needle);
|
||||
|
||||
assert_non_null(result);
|
||||
assert_ptr_equal(result, haystack + 4); /* "quick" 在 "The " 之后 */
|
||||
}
|
||||
|
||||
static void test_stristr_not_found(void **state)
|
||||
{
|
||||
(void)state;
|
||||
const char *haystack = "The quick brown fox";
|
||||
const char *needle = "jumps";
|
||||
|
||||
char *result = stristr(haystack, needle);
|
||||
|
||||
assert_null(result);
|
||||
}
|
||||
|
||||
static void test_stristr_case_insensitive(void **state)
|
||||
{
|
||||
(void)state;
|
||||
const char *haystack = "The QUICK brown fox";
|
||||
const char *needle = "quick";
|
||||
|
||||
char *result = stristr(haystack, needle);
|
||||
|
||||
assert_non_null(result);
|
||||
}
|
||||
|
||||
static void test_stristr_null_haystack(void **state)
|
||||
{
|
||||
(void)state;
|
||||
char *result = stristr(NULL, "test");
|
||||
assert_null(result);
|
||||
}
|
||||
|
||||
static void test_stristr_null_needle(void **state)
|
||||
{
|
||||
(void)state;
|
||||
char *result = stristr("test", NULL);
|
||||
assert_null(result);
|
||||
}
|
||||
|
||||
static void test_stristr_empty_needle(void **state)
|
||||
{
|
||||
(void)state;
|
||||
const char *haystack = "The quick brown fox";
|
||||
const char *needle = "";
|
||||
|
||||
char *result = stristr(haystack, needle);
|
||||
|
||||
/* 空字符串应该返回原字符串首地址 */
|
||||
assert_non_null(result);
|
||||
assert_ptr_equal(result, haystack);
|
||||
}
|
||||
|
||||
/* ==================== 主函数 ==================== */
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const struct CMUnitTest tests[] = {
|
||||
/* StringList 测试 */
|
||||
cmocka_unit_test(test_init_string_list),
|
||||
cmocka_unit_test(test_add_string_list_single),
|
||||
cmocka_unit_test(test_add_string_list_multiple),
|
||||
cmocka_unit_test(test_string_list_get_out_of_bounds),
|
||||
cmocka_unit_test(test_string_list_get_null_list),
|
||||
cmocka_unit_test(test_string_list_set_normal),
|
||||
cmocka_unit_test(test_string_list_set_out_of_bounds),
|
||||
cmocka_unit_test(test_string_list_set_null_list),
|
||||
cmocka_unit_test(test_clear_string_list),
|
||||
|
||||
/* 编码转换测试 */
|
||||
cmocka_unit_test(test_utf8_to_wide_normal),
|
||||
cmocka_unit_test(test_utf8_to_wide_null),
|
||||
cmocka_unit_test(test_wide_to_utf8_normal),
|
||||
cmocka_unit_test(test_wide_to_utf8_null),
|
||||
|
||||
/* stristr 测试 */
|
||||
cmocka_unit_test(test_stristr_found),
|
||||
cmocka_unit_test(test_stristr_not_found),
|
||||
cmocka_unit_test(test_stristr_case_insensitive),
|
||||
cmocka_unit_test(test_stristr_null_haystack),
|
||||
cmocka_unit_test(test_stristr_null_needle),
|
||||
cmocka_unit_test(test_stristr_empty_needle),
|
||||
};
|
||||
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
Reference in New Issue
Block a user