diff --git a/Base/c_Test.h b/Base/c_Test.h index d4dd02d..6995981 100644 --- a/Base/c_Test.h +++ b/Base/c_Test.h @@ -2,144 +2,177 @@ #define INCLUDED_C_TEST_H #include +#include #include +#include #include #include -#include -#include "c_Compiler.h" -/* ============================================================================== - * 🎯 1. 测试上下文状态追踪 - * ============================================================================== */ -static int c_test_suite_failed = 0; /* 当前套件是否有失败的断言 */ -static int c_test_case_failed = 0; /* 当前正在运行的测试用例是否有失败 */ -static int c_test_total_assertions = 0; /* 总断言数 */ -static int c_test_passed_assertions = 0; /* 成功的断言数 */ -static int c_test_cases_run = 0; /* 运行的用例数 */ -static int c_test_cases_passed = 0; /* 成功的用例数 */ -static double c_test_suite_total_ms = 0.0; /* 整个套件的总耗时 */ +// ========================================== +// ANSI 终端颜色宏定义 +// ========================================== +#define COLOR_RESET "\033[0m" +#define COLOR_GREEN "\033[32m" +#define COLOR_RED "\033[31m" +#define COLOR_YELLOW "\033[33m" +#define COLOR_BLUE "\033[34m" +#define COLOR_CYAN "\033[36m" +#define COLOR_BOLD "\033[1m" -/* ============================================================================== - * 🎯 2. 核心断言宏矩阵(严格以 C_ASSERT_ 开头) - * ============================================================================== */ +// ========================================== +// 核心测试框架结构 +// ========================================== +typedef struct { + int passed_count; + int failed_count; + double total_time_ms; +} TestRegistry; -/* 统一的断言失败内部打印函数 */ -C_STATIC_FORCE_INLINE -void c_test_print_fail(const char* file, int line, const char* expr, const char* msg) { - c_test_suite_failed = 1; - c_test_case_failed = 1; - printf(" [FAIL] %s:%d -> 断言失败: (%s) ", file, line, expr); - if (msg && strlen(msg) > 0) { - printf("| 说明: %s", msg); - } - printf("\n"); -} +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ -/* 基础布尔断言 */ -#define C_ASSERT(expr, msg) do { \ - c_test_total_assertions++; \ - if (C_LIKELY(expr)) { \ - c_test_passed_assertions++; \ - } else { \ - c_test_print_fail(__FILE__, __LINE__, #expr, msg); \ - } \ -} while(0) +static TestRegistry g_test_registry = {0, 0, 0.0}; -/* 整型相等断言 */ -#define C_ASSERT_INT_EQ(actual, expected, msg) do { \ - c_test_total_assertions++; \ - long long act = (long long)(actual); \ - long long exp = (long long)(expected); \ - if (C_LIKELY(act == exp)) { \ - c_test_passed_assertions++; \ - } else { \ - char buf[256]; \ - snprintf(buf, sizeof(buf), "期望值: %lld, 实际值: %lld | %s", exp, act, msg); \ - c_test_print_fail(__FILE__, __LINE__, #actual " == " #expected, buf); \ - } \ -} while(0) +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ -/* 字符串相等断言 */ -#define C_ASSERT_STR_EQ(actual, expected, msg) do { \ - c_test_total_assertions++; \ - const char* act = (const char*)(actual); \ - const char* exp = (const char*)(expected); \ - if (C_LIKELY(act && exp && strcmp(act, exp) == 0)) { \ - c_test_passed_assertions++; \ - } else { \ - char buf[256]; \ - snprintf(buf, sizeof(buf), "期望: \"%s\", 实际: \"%s\" | %s", exp ? exp : "NULL", act ? act : "NULL", msg); \ - c_test_print_fail(__FILE__, __LINE__, "strcmp(" #actual ", " #expected ") == 0", buf); \ - } \ -} while(0) +#define ASSERT_MSG(condition, message) \ + do { \ + if (!(condition)) { \ + printf(" " COLOR_RED "[FAIL] %s:%d: Assertion failed: (%s). Message: %s" COLOR_RESET "\n", __FILE__, __LINE__, #condition, message); \ + g_test_registry.failed_count++; \ + return; \ + } \ + } while(0) -/* 浮点数安全相等断言(内置之前讨论的 IEEE 754 Epsilon 比较) */ -#define C_ASSERT_DOUBLE_EQ(actual, expected, msg) do { \ - c_test_total_assertions++; \ - double act = (double)(actual); \ - double exp = (double)(expected); \ - int is_eq = 0; \ - if (isnan(act) && isnan(exp) || (fabs(act - exp) < DBL_EPSILON) ) { is_eq = 1; } \ - if (C_LIKELY(is_eq)) { \ - c_test_passed_assertions++; \ - } else { \ - char buf[256]; \ - snprintf(buf, sizeof(buf), "期望: %.32f, 实际: %.32f (误差 > DBL_EPSILON[%.32f]) | %s", exp, act, DBL_EPSILON, msg); \ - c_test_print_fail(__FILE__, __LINE__, "fabs(" #actual " - " #expected ") < DBL_EPSILON", buf); \ - } \ -} while(0) +#define ASSERT_INT_EQ_MSG(expected, actual, message) \ + do { \ + if ((expected) != (actual)) { \ + printf(" " COLOR_RED "[FAIL] %s:%d: Expected %d, but got %d. Message: %s" COLOR_RESET "\n", __FILE__, __LINE__, (int)(expected), (int)(actual), message); \ + g_test_registry.failed_count++; \ + return; \ + } \ + } while(0) -/* ============================================================================== - * 🎯 3. 测试套件生命周期控制宏 - * ============================================================================== */ +#ifndef TEST_EPSILON +#define TEST_EPSILON 1e-6 +#endif -/* 初始化一个测试套件文件 */ -#define C_TEST_SUITE_BEGIN(name) \ - printf("==================================================\n"); \ - printf("🧪 运行测试套件: %s [%s环境]\n", #name, C_CURRENT_OS_NAME); \ - printf("==================================================\n"); +#define ASSERT_DOUBLE_EQ_MSG(expected, actual, message) \ + do { \ + double __exp = (double)(expected); \ + double __act = (double)(actual); \ + /* 计算两个浮点数差值的绝对值,并与允许的误差进行比较 */ \ + if (fabs(__exp - __act) > TEST_EPSILON) { \ + printf(" " COLOR_RED "[FAIL] %s:%d: Expected %.6f, but got %.6f. Message: %s" COLOR_RESET "\n", \ + __FILE__, __LINE__, __exp, __act, (message) ? (message) : "No message"); \ + g_test_registry.failed_count++; \ + return; \ + } \ + } while(0) -/* 结束测试套件并返回标准状态码给 CMake CTest */ -#define C_TEST_SUITE_END() \ - printf("\n--------------------------------------------------------------------\n"); \ - printf("📊 测试结果统计:\n"); \ - printf(" 测试用例数: %d 运行, %d 通过, %d 失败\n", \ - c_test_cases_run, c_test_cases_passed, c_test_cases_run - c_test_cases_passed); \ - printf(" 底层断言数: %d 总计, %d 成功, %d 失败\n", \ - c_test_total_assertions, c_test_passed_assertions, c_test_total_assertions - c_test_passed_assertions); \ - printf(" 套件总耗时: %.3f ms\n", c_test_suite_total_ms); \ - if (c_test_suite_failed) { \ - printf("🚨 结论 : [ FAILURE ] 有测试未通过!\n"); \ - printf("====================================================================\n"); \ - return 1; \ - } \ - printf("🎉 结论 : [ SUCCESS ] 全套单元测试完美通过!\n"); \ - printf("====================================================================\n"); \ - return 0; -/* 声明一个测试用例块 */ -#define C_TEST_CASE(name) static void name(void) -#define C_RUN_TEST_CASE(name) do { \ - c_test_cases_run++; \ - c_test_case_failed = 0; \ - printf(" ▶ 运行测试用例: %-30s ... ", #name); \ - fflush(stdout); \ - struct timespec c_start_time, c_end_time; \ - timespec_get(&c_start_time, TIME_UTC); \ - name(); \ - timespec_get(&c_end_time, TIME_UTC); \ - double c_elapsed_ms = (double)(c_end_time.tv_sec - c_start_time.tv_sec) * 1000.0 + \ - (double)(c_end_time.tv_nsec - c_start_time.tv_nsec) / 1000000.0; \ - c_test_suite_total_ms += c_elapsed_ms; \ - if (c_test_case_failed) { \ - printf("[ ❌ 失败 ] (耗时: %8.3f ms)\n", c_elapsed_ms); \ - } else { \ - c_test_cases_passed++; \ - printf("[ ✨ 通过 ] (耗时: %8.3f ms)\n", c_elapsed_ms); \ - } \ -} while(0) +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +// 基础断言宏 +#define ASSERT_TRUE(condition) \ + do { \ + if (!(condition)) { \ + printf(" " COLOR_RED "[FAIL] %s:%d: Assertion failed: (%s)" COLOR_RESET "\n", __FILE__, __LINE__, #condition); \ + g_test_registry.failed_count++; \ + return; \ + } \ + } while(0) + +#define ASSERT_INT_EQ(expected, actual) \ + do { \ + if ((expected) != (actual)) { \ + printf(" " COLOR_RED "[FAIL] %s:%d: Expected %d, but got %d" COLOR_RESET "\n", __FILE__, __LINE__, (int)(expected), (int)(actual)); \ + g_test_registry.failed_count++; \ + return; \ + } \ + } while(0) + +#define ASSERT_LL_EQ(expected, actual) \ + do { \ + if ((expected) != (actual)) { \ + printf(" " COLOR_RED "[FAIL] %s:%d: Expected %"PRId64", but got %"PRId64 COLOR_RESET "\n", __FILE__, __LINE__, (uint64_t)(expected), (uint64_t)(actual)); \ + g_test_registry.failed_count++; \ + return; \ + } \ + } while(0) + +#define ASSERT_PTR_NOT_NULL(ptr) ASSERT_TRUE(ptr!=NULL) + +// ========================================== +// 核心运行宏(支持自定义配置环境) +// ========================================== + +// 核心通用运行宏(内部使用) +#define _RUN_TEST_CORE(test_func, setup, teardown) \ + do { \ + printf(COLOR_BLUE "[RUN ]" COLOR_RESET " %s\n", #test_func); \ + \ + /* 1. 执行专属启动函数 */ \ + void (*st)(void) = (setup); \ + if (st) st(); \ + \ + int initial_failed = g_test_registry.failed_count; \ + clock_t start_time = clock(); \ + \ + /* 2. 执行测试函数 */ \ + test_func(); \ + \ + clock_t end_time = clock(); \ + double elapsed_ms = ((double)(end_time - start_time) / CLOCKS_PER_SEC) * 1000.0; \ + g_test_registry.total_time_ms += elapsed_ms; \ + \ + /* 3. 执行专属关闭函数 */ \ + void (*td)(void) = (teardown); \ + if (td) td(); \ + \ + /* 4. 带颜色状态输出 */ \ + if (g_test_registry.failed_count == initial_failed) { \ + printf(COLOR_GREEN "[ OK]" COLOR_RESET " %s (%.2f ms)\n", #test_func, elapsed_ms); \ + g_test_registry.passed_count++; \ + } else { \ + printf(COLOR_RED "[FAIL]" COLOR_RESET " %s (%.2f ms)\n", #test_func, elapsed_ms); \ + } \ + printf("-------------------------------------------\n"); \ + } while(0) + +// 宏 1:支持绑定专属环境的运行宏 +#define RUN_TEST_FIXTURE(test_func, setup, teardown) _RUN_TEST_CORE(test_func, setup, teardown) + +// 宏 2:普通运行宏(无需任何启动/关闭环境) +#define RUN_TEST(test_func) _RUN_TEST_CORE(test_func, NULL, NULL) + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define TEST_START(Name) do{ \ + printf(COLOR_BOLD "===========================================\n"); \ + printf(" %s \n", #Name);\ + printf("===========================================\n" COLOR_RESET); }while(0) + +#define TEST_REPORT()\ +do{ \ + printf(COLOR_BOLD "===========================================\n"); \ + printf("TEST SUMMARY:\n" COLOR_RESET); \ + printf(" Total Executed : %d\n", g_test_registry.passed_count + g_test_registry.failed_count); \ + printf(COLOR_GREEN " Passed : %d\n" COLOR_RESET, g_test_registry.passed_count); \ + printf(COLOR_RED " Failed : %d\n" COLOR_RESET, g_test_registry.failed_count); \ + printf(" Total Time : %.2f ms\n", g_test_registry.total_time_ms); \ + printf(COLOR_BOLD "===========================================\n" COLOR_RESET);\ +}while (0) + +#define RETURN_TEST_STATUS return (g_test_registry.failed_count > 0 ? 1 : 0) + +#define TEST_CASE(name) static void name(void) #endif /*INCLUDED_C_TEST_H*/ diff --git a/Base/c_Test.t.c b/Base/c_Test.t.c index 3b473e3..085a0a2 100644 --- a/Base/c_Test.t.c +++ b/Base/c_Test.t.c @@ -4,36 +4,59 @@ // Math/matrix.t.c #include "c_Test.h" -void heavy_calculation(void) { - double sum = 0.0; - for (int i = 0; i < 5000000; ++i) { - sum += sin((double)i) * cos((double)i); - } - // 随便加一个没意义的断言防止被编译器完全优化掉 - C_ASSERT(sum != 12345.6, "密集数学计算检验"); +// ========================================== +// 模拟业务环境与测试用例 +// ========================================== + +int* file_mock_data = NULL; + +// 环境 A 的启动与关闭(针对内存/文件操作) +static void setup() { + file_mock_data = (int*)malloc(sizeof(int) * 3); + file_mock_data[0] = 10; file_mock_data[1] = 20; file_mock_data[2] = 30; + printf(" " COLOR_CYAN "[INFO] Setup: Memory initialized." COLOR_RESET "\n"); } -// ------------------------------------------------------------------------------ -// 🛠️ 步骤 A: 编写独立的测试用例函数 (C_TEST_CASE) -// ------------------------------------------------------------------------------ - -C_TEST_CASE(test_quick_operations) { - int a = 1, b = 1; - C_ASSERT_INT_EQ(a, b, "极速操作验证"); +static void teardown() { + free(file_mock_data); + file_mock_data = NULL; + printf(" " COLOR_CYAN "[INFO] Teardown: Memory released." COLOR_RESET "\n"); } -C_TEST_CASE(test_heavy_workload) { - heavy_calculation(); +// --- 具体测试用例 --- + +// 用例 1:需要内存环境 +static void test_with_memory() { + ASSERT_INT_EQ(20, file_mock_data[1]); } -// ------------------------------------------------------------------------------ -// 🚀 步骤 B: 在主执行块中注册并依次运行它们 -// ------------------------------------------------------------------------------ -int main(int argc, char** argv) { - C_TEST_SUITE_BEGIN(MathMatrixFunctionSuite) +// 用例 2:故意制造失败,检查内存是否能安全释放,且颜色是否正确 +static void test_with_memory_failure() { + ASSERT_INT_EQ(99, file_mock_data[0]); // 这里会失败并 return 退出 +} - C_RUN_TEST_CASE(test_quick_operations); - C_RUN_TEST_CASE(test_heavy_workload); +// 用例 3:纯数学计算,完全不需要任何专属启动和关闭环境 +static void test_pure_math() { + int result = 1 + 1; + ASSERT_INT_EQ(2, result); +} - C_TEST_SUITE_END() + +// ========================================== +// 主程序入口 +// ========================================== +int main() { + TEST_START(Starting Unit Tests); + + // 运行需要内存环境的用例 + RUN_TEST_FIXTURE(test_with_memory, setup, teardown); + RUN_TEST_FIXTURE(test_with_memory_failure, setup, teardown); + + // 运行普通无环境要求的用例 + RUN_TEST(test_pure_math); + + // 打印最终统计报告 + TEST_REPORT(); + + RETURN_TEST_STATUS; } diff --git a/Base/c_Types.h b/Base/c_Types.h index 7d891a9..37ae2bc 100644 --- a/Base/c_Types.h +++ b/Base/c_Types.h @@ -101,16 +101,16 @@ typedef int c_err_t; #define C_ERR_OK 0 #define C_ERR_FAIL (-1) -#define C_ERR_NOTFOUND (-2) -#define C_ERR_NOMEM (-3) -#define C_ERR_NOTIMPLEMENTED (-4) -#define C_ERR_PARAM (-5) -#define C_ERR_OUTOFBOUND (-6) -#define C_ERR_EMPTY (-7) -#define C_ERR_FULL (-8) -#define C_ERR_ALREADY_EXISTS (-9) +#define C_ERR_NOMEM (-2) +#define C_ERR_NOTIMPLEMENTED (-3) +#define C_ERR_PARAM (-4) +#define C_ERR_EMPTY (-5) +#define C_ERR_FULL (-6) +#define C_ERR_ALREADY_EXISTS (-7) #define C_SUCCESS C_ERR_OK +#define C_ERR_NOTFOUND C_ERR_FAIL +#define C_ERR_OUTOFBOUND C_ERR_FAIL /* ------------------------------------------------------------------------------------------------------------------ */ /* */ diff --git a/Foundation/c_Array.t.c b/Foundation/c_Array.t.c index a4cd76c..5897a2c 100644 --- a/Foundation/c_Array.t.c +++ b/Foundation/c_Array.t.c @@ -8,90 +8,90 @@ /* TEST CASES */ /* ========================================================================== */ -C_TEST_FRAME_INIT(); // 1. Stack Initialization and Destruction Loop +static void test_array_stack_init_destroy(void) { c_Array_t arr; c_err_t err = c_Array_Init(&arr, 5, sizeof(int)); - C_ASSERT_EQ_INT(C_SUCCESS, err); - C_ASSERT_EQ_INT(5, c_Array_Length(&arr)); - C_ASSERT_EQ_INT(sizeof(int), c_Array_Size(&arr)); - C_ASSERT_PTR_NOT_NULL(arr.array); + ASSERT_LL_EQ(C_SUCCESS, err); + ASSERT_LL_EQ(5, c_Array_Length(&arr)); + ASSERT_LL_EQ(sizeof(int), c_Array_Size(&arr)); + ASSERT_PTR_NOT_NULL(arr.array); c_Array_Destroy(&arr); } // 2. Heap Dynamic Allocation Lifecycles -void test_array_heap_new_delete(void) { +static void test_array_heap_new_delete(void) { c_Array_t* arr = c_Array_New(10, sizeof(double)); - C_ASSERT_PTR_NOT_NULL(arr); - C_ASSERT_EQ_INT(10, c_Array_Length(arr)); - C_ASSERT_EQ_INT(sizeof(double), c_Array_Size(arr)); + ASSERT_PTR_NOT_NULL(arr); + ASSERT_LL_EQ(10, c_Array_Length(arr)); + ASSERT_LL_EQ(sizeof(double), c_Array_Size(arr)); c_Array_Delete(&arr); - C_ASSERT_PTR_NULL(arr); // Ensure pointer is zeroed out by reference parameter modification + ASSERT_TRUE(arr==NULL); // Ensure pointer is zeroed out by reference parameter modification } // 3. Put and Get Data Element Scenarios -void test_array_put_and_get(void) { +static void test_array_put_and_get(void) { c_Array_t* arr = c_Array_New(3, sizeof(int)); - C_ASSERT_PTR_NOT_NULL(arr); - C_ASSERT_EQ_INT(3, arr->length); + ASSERT_PTR_NOT_NULL(arr); + ASSERT_INT_EQ(3, arr->length); int val1 = 100, val2 = 200, val3 = 300; // Put elements inside length limits - C_ASSERT_EQ_INT(C_SUCCESS, c_Array_Put(arr, 0, &val1)); - C_ASSERT_EQ_INT(C_SUCCESS, c_Array_Put(arr, 1, &val2)); - C_ASSERT_EQ_INT(C_SUCCESS, c_Array_Put(arr, 2, &val3)); + ASSERT_INT_EQ(C_SUCCESS, c_Array_Put(arr, 0, &val1)); + ASSERT_INT_EQ(C_SUCCESS, c_Array_Put(arr, 1, &val2)); + ASSERT_INT_EQ(C_SUCCESS, c_Array_Put(arr, 2, &val3)); // Out of bounds checks int val_bad = 999; - C_ASSERT_EQ_INT(C_ERR_PARAM, c_Array_Put(arr, 3, &val_bad)); + ASSERT_INT_EQ(C_ERR_PARAM, c_Array_Put(arr, 3, &val_bad)); // Get verification void* fetch_ptr = NULL; - C_ASSERT_EQ_INT(C_SUCCESS, c_Array_Get(arr, 1, &fetch_ptr)); - C_ASSERT_PTR_NOT_NULL(fetch_ptr); - C_ASSERT_EQ_INT(200, *(int*)fetch_ptr); + ASSERT_INT_EQ(C_SUCCESS, c_Array_Get(arr, 1, &fetch_ptr)); + ASSERT_PTR_NOT_NULL(fetch_ptr); + ASSERT_INT_EQ(200, *(int*)fetch_ptr); // Out of bounds get verification - C_ASSERT_EQ_INT(C_ERR_PARAM, c_Array_Get(arr, 5, &fetch_ptr)); + ASSERT_INT_EQ(C_ERR_PARAM, c_Array_Get(arr, 5, &fetch_ptr)); c_Array_Delete(&arr); } // 4. Memory Resizing Limits Verification -void test_array_resize(void) { +static void test_array_resize(void) { c_Array_t* arr = c_Array_New(2, sizeof(int)); int val0 = 42, val1 = 84; c_Array_Put(arr, 0, &val0); c_Array_Put(arr, 1, &val1); // Resize up to 4 elements - C_ASSERT_EQ_INT(C_SUCCESS, c_Array_Resize(arr, 4)); - C_ASSERT_EQ_INT(4, c_Array_Length(arr)); + ASSERT_INT_EQ(C_SUCCESS, c_Array_Resize(arr, 4)); + ASSERT_INT_EQ(4, c_Array_Length(arr)); // Verify older elements remain structurally untouched void* res_ptr = NULL; - C_ASSERT_EQ_INT(C_SUCCESS, c_Array_Get(arr, 1, &res_ptr)); - C_ASSERT_EQ_INT(84, *(int*)res_ptr); + ASSERT_INT_EQ(C_SUCCESS, c_Array_Get(arr, 1, &res_ptr)); + ASSERT_INT_EQ(84, *(int*)res_ptr); // Resize down to 1 element - C_ASSERT_EQ_INT(C_SUCCESS, c_Array_Resize(arr, 1)); - C_ASSERT_EQ_INT(1, c_Array_Length(arr)); + ASSERT_INT_EQ(C_SUCCESS, c_Array_Resize(arr, 1)); + ASSERT_INT_EQ(1, c_Array_Length(arr)); // Index 1 should now be unreachable / out of bounds - C_ASSERT_EQ_INT(C_ERR_PARAM, c_Array_Get(arr, 1, &res_ptr)); + ASSERT_INT_EQ(C_ERR_PARAM, c_Array_Get(arr, 1, &res_ptr)); c_Array_Delete(&arr); } // 5. Deep Copy Execution Verification -void test_array_copy_and_copy_to(void) { +static void test_array_copy_and_copy_to(void) { c_Array_t* source = c_Array_New(3, sizeof(int)); int a = 11, b = 22, c = 33; c_Array_Put(source, 0, &a); @@ -100,21 +100,21 @@ void test_array_copy_and_copy_to(void) { // Test c_Array_Copy (creates a new array object on the heap) c_Array_t* copied_arr = c_Array_Copy(source, 2); // copy only first 2 items - C_ASSERT_PTR_NOT_NULL(copied_arr); - C_ASSERT_EQ_INT(2, c_Array_Length(copied_arr)); + ASSERT_PTR_NOT_NULL(copied_arr); + ASSERT_INT_EQ(2, c_Array_Length(copied_arr)); void* data_ptr = NULL; c_Array_Get(copied_arr, 1, &data_ptr); - C_ASSERT_EQ_INT(22, *(int*)data_ptr); + ASSERT_INT_EQ(22, *(int*)data_ptr); // Test c_Array_CopyTo (copies into an already initialized destination) c_Array_t dest; c_Array_Init(&dest, 3, sizeof(int)); - C_ASSERT_EQ_INT(C_SUCCESS, c_Array_CopyTo(source, &dest)); + ASSERT_INT_EQ(C_SUCCESS, c_Array_CopyTo(source, &dest)); c_Array_Get(&dest, 2, &data_ptr); - C_ASSERT_EQ_INT(33, *(int*)data_ptr); + ASSERT_INT_EQ(33, *(int*)data_ptr); // Clean up all resources c_Array_Delete(&copied_arr); @@ -122,20 +122,16 @@ void test_array_copy_and_copy_to(void) { c_Array_Delete(&source); } -/* ========================================================================== */ -/* SUITE BINDINGS & ENTRY MAIN */ -/* ========================================================================== */ -static -void array_data_structure_suite(void) { - C_TEST_CASE_RUN(test_array_stack_init_destroy); - C_TEST_CASE_RUN(test_array_heap_new_delete); - C_TEST_CASE_RUN(test_array_put_and_get); - C_TEST_CASE_RUN(test_array_resize); - C_TEST_CASE_RUN(test_array_copy_and_copy_to); -} int main(void) { - C_TEST_SUITE_RUN(array_data_structure_suite); - C_TEST_FRAME_REPORT(); - return (g_test_ctx.tests_passed == g_test_ctx.tests_run) ? 0 : 1; + TEST_START(array_data_structure_suite); + + RUN_TEST(test_array_stack_init_destroy); + RUN_TEST(test_array_heap_new_delete); + RUN_TEST(test_array_put_and_get); + RUN_TEST(test_array_resize); + RUN_TEST(test_array_copy_and_copy_to); + + TEST_REPORT(); + RETURN_TEST_STATUS; } \ No newline at end of file diff --git a/Foundation/c_ArrayList.c b/Foundation/c_ArrayList.c index 8d72bb7..51a2abf 100644 --- a/Foundation/c_ArrayList.c +++ b/Foundation/c_ArrayList.c @@ -4,7 +4,8 @@ /* ------------------------------------------------------------------------------------------------------------------ */ /* */ -#define DEFAULT_INITIAL_CAPACITY 4 +#define DEFAULT_INITIAL_CAPACITY 4 +#define MIN_SHRINK_CAPACITY 4 /* ------------------------------------------------------------------------------------------------------------------ */ /* */ @@ -13,14 +14,17 @@ c_err_t c_ArrayList_Init(c_ArrayList_t* self, c_size_t obj_size, c_size_t capaci if (!self || obj_size == 0) return C_ERR_PARAM; self->obj_size = (int)obj_size; - self->capacity = (capacity > 0) ? capacity : DEFAULT_INITIAL_CAPACITY; self->size = 0; + self->capacity = capacity; - // 分配連續記憶體空間:容量 * 單個物件大小 - self->array = C_ALLOC(self->capacity * self->obj_size); - if (!self->array) { - self->capacity = 0; - return C_ERR_NOMEM; + if (capacity==0) { + self->array = NULL; + }else { + self->array = C_ALLOC(self->capacity * self->obj_size); + if (!self->array) { + self->capacity = 0; + return C_ERR_NOMEM; + } } return C_ERR_OK; @@ -35,11 +39,11 @@ void c_ArrayList_Destroy(c_ArrayList_t* self) { } c_err_t c_ArrayList_Add(c_ArrayList_t* self, void* obj) { - if (!self || !self->array || !obj) return C_ERR_PARAM; + if (!self || !obj) return C_ERR_PARAM; // 動態擴容邏輯 if (self->size >= self->capacity) { - const c_size_t new_capacity = self->capacity<<1; + const c_size_t new_capacity = (self->capacity==0)?DEFAULT_INITIAL_CAPACITY:(self->capacity<<1); void* new_array = C_REALLOC(self->array, new_capacity * self->obj_size); if (!new_array) { return C_ERR_NOMEM; @@ -79,6 +83,19 @@ c_err_t c_ArrayList_Remove(c_ArrayList_t* self, c_size_t index) { } self->size--; + + // 策略:当实际大小少于等于容量的 1/4,且缩容后的容量不低于设定的最小阈值时触发 + if (self->size > 0 && self->size <= (self->capacity >> 2)) { + c_size_t new_capacity = self->capacity >> 1; // 容量减半 + void* new_array = C_REALLOC(self->array, new_capacity * self->obj_size); + if (new_array) { // 如果 realloc 失败不影响原有数据安全,这里采用安全赋值 + self->array = new_array; + self->capacity = new_capacity; + } + } + + return C_ERR_OK; } + diff --git a/Foundation/c_ArrayList.h b/Foundation/c_ArrayList.h index 7c99f79..74e30d3 100644 --- a/Foundation/c_ArrayList.h +++ b/Foundation/c_ArrayList.h @@ -17,6 +17,8 @@ typedef struct { c_size_t size; }c_ArrayList_t; +#define c_ArrayList(obj_size) ((c_ArrayList_t) { 0, (obj_size), 0, 0 }) + /* ------------------------------------------------------------------------------------------------------------------ */ /* */ @@ -30,4 +32,5 @@ void* c_ArrayList_Get(c_ArrayList_t* self, c_size_t index); c_err_t c_ArrayList_Remove(c_ArrayList_t* self, c_size_t index); + #endif /*INCLUDED_C_ARRAYLIST_H*/ diff --git a/Foundation/c_ArrayList.t.c b/Foundation/c_ArrayList.t.c index e2399e5..acab6fb 100644 --- a/Foundation/c_ArrayList.t.c +++ b/Foundation/c_ArrayList.t.c @@ -10,13 +10,13 @@ struct Vector2D { /* ============================================================================== * 🧪 测试全新通用 void* 缓冲区的初始化、自动扩容与随机 Remove 重排全周期行为 * ============================================================================== */ -C_TEST_CASE(test_array_list_full_lifecycle_and_removal) +TEST_CASE(test_array_list_full_lifecycle_and_removal) { c_ArrayList_t list; /* 1. 初始化:装载自定义 Vector2D 结构体,初始最大可容纳元素数量卡死限制为 2 */ c_err_t init_err = c_ArrayList_Init(&list, sizeof(struct Vector2D), 2); - C_ASSERT_INT_EQ(init_err, C_ERR_OK, "弹性自愈 ArrayList 初始化成功"); - C_ASSERT_INT_EQ(list.size, 0, "有效初始记录必须为 0"); + ASSERT_INT_EQ_MSG(init_err, C_ERR_OK, "弹性自愈 ArrayList 初始化成功"); + ASSERT_INT_EQ_MSG(list.size, 0, "有效初始记录必须为 0"); struct Vector2D vec1 = { 11.1, 22.2 }; struct Vector2D vec2 = { 33.3, 44.4 }; @@ -28,38 +28,129 @@ C_TEST_CASE(test_array_list_full_lifecycle_and_removal) /* 🎯 扩容看点:此时满员,第三次写入将强行逼迫池子在内部启动 2 -> 4 自动翻倍重分配 */ c_err_t add_err = c_ArrayList_Add(&list, &vec3); - C_ASSERT_INT_EQ(add_err, C_ERR_OK, "耗尽时追加写入,分配器必须完成全自动无感自愈扩容"); - C_ASSERT_INT_EQ(list.size, 3, "当前有效装载数递增至 3"); + ASSERT_INT_EQ_MSG(add_err, C_ERR_OK, "耗尽时追加写入,分配器必须完成全自动无感自愈扩容"); + ASSERT_INT_EQ_MSG(list.size, 3, "当前有效装载数递增至 3"); /* 3. 验证数据内容的物理隔离完整度 */ struct Vector2D* p_check1 = (struct Vector2D*)c_ArrayList_Get(&list, 1); - C_ASSERT(p_check1 != NULL, "随机读取索引 1 节点成功"); - C_ASSERT_DOUBLE_EQ(p_check1->u, 33.3, "重分配内存迁移后,原有位置 1 的数据必须毫发无损"); + ASSERT_MSG(p_check1 != NULL, "随机读取索引 1 节点成功"); + ASSERT_DOUBLE_EQ_MSG(p_check1->u, 33.3, "重分配内存迁移后,原有位置 1 的数据必须毫发无损"); /* 4. 🛠️ 高能测试点:随机抹除中间位置 1 的节点 (即删掉 vec2) 预期结果:原位置 2 的 vec3 ({55.5, 66.6}) 必须在 O(N) 速度下向前平移,填补顶替空位 1 */ c_err_t remove_err = c_ArrayList_Remove(&list, 1); - C_ASSERT_INT_EQ(remove_err, C_ERR_OK, "执行中途位置随机移除成功"); - C_ASSERT_INT_EQ(list.size, 2, "移除数据后,有效数据计数平滑扣减为 2"); + ASSERT_INT_EQ_MSG(remove_err, C_ERR_OK, "执行中途位置随机移除成功"); + ASSERT_INT_EQ_MSG(list.size, 2, "移除数据后,有效数据计数平滑扣减为 2"); /* 5. 终极完整性断言:现在去 Get 原本的位置 1 */ struct Vector2D* p_relocated = (struct Vector2D*)c_ArrayList_Get(&list, 1); - C_ASSERT(p_relocated != NULL, "重新获取平移顶替后的位置 1 节点成功"); + ASSERT_MSG(p_relocated != NULL, "重新获取平移顶替后的位置 1 节点成功"); /* 核心断言:原位置 2 的数据现在必须完美出现在位置 1 线上,且精度不发生移位错乱! */ - C_ASSERT_DOUBLE_EQ(p_relocated->u, 55.5, "元素向前滑动对齐后,浮点特征完好无损"); - C_ASSERT_DOUBLE_EQ(p_relocated->v, 66.6, "元素向前滑动对齐后,浮点特征完好无损"); + ASSERT_DOUBLE_EQ_MSG(p_relocated->u, 55.5, "元素向前滑动对齐后,浮点特征完好无损"); + ASSERT_DOUBLE_EQ_MSG(p_relocated->v, 66.6, "元素向前滑动对齐后,浮点特征完好无损"); /* 6. 边界越界捕获安全防御线 */ void* invalid_ptr = c_ArrayList_Get(&list, 2); /* 此时由于删了一个,索引 2 已变为空旷越界区 */ - C_ASSERT(invalid_ptr == NULL, "越界获取已经被逻辑截断删除的位置必须安全回传 NULL"); + ASSERT_MSG(invalid_ptr == NULL, "越界获取已经被逻辑截断删除的位置必须安全回传 NULL"); c_ArrayList_Destroy(&list); } +static void test_list_auto_expansion() { + int data[] = {10, 20, 30}; + c_ArrayList_t list; + c_ArrayList_Init(&list, sizeof(int), 2); + + for (int i = 0; i < 3; i++) { + c_ArrayList_Add(&list, &data[i]); + } + + // 验证容量是否翻倍 (2 << 1 = 4) + ASSERT_INT_EQ_MSG(4, list.capacity, "Capacity should double to 4"); + ASSERT_INT_EQ_MSG(3, list.size, "Size should be 3"); + + // 验证最后一个元素有没有因为扩容导致内存搬移出错 + int* p3 = (int*)c_ArrayList_Get(&list, 2); + ASSERT_MSG(p3 != NULL, "Expanded element should be accessible"); + ASSERT_INT_EQ_MSG(30, *p3, "Expanded element data corruption"); +} + +static void test_list_remove_element() { + int data[] = {11, 22, 33, 44}; + c_ArrayList_t list; + c_ArrayList_Init(&list, sizeof(int), 2); + + for(int i = 0; i < 4; i++) c_ArrayList_Add(&list, &data[i]); + + // 删除索引为 1 的元素 (即数字 22) + c_err_t err = c_ArrayList_Remove(&list, 1); + ASSERT_INT_EQ_MSG(C_ERR_OK, err, "Remove operational failure"); + ASSERT_INT_EQ_MSG(3, list.size, "Size should decrease to 3"); + + // 此时索引 1 应该变成了 33,索引 2 应该变成了 44 + int* p1 = (int*)c_ArrayList_Get(&list, 1); + int* p2 = (int*)c_ArrayList_Get(&list, 2); + + ASSERT_INT_EQ_MSG(33, *p1, "Element forward-shift error at index 1"); + ASSERT_INT_EQ_MSG(44, *p2, "Element forward-shift error at index 2"); + + // 检查获取越界索引是否安全返回 NULL + ASSERT_MSG(c_ArrayList_Get(&list, 3) == NULL, "Out of bounds should return NULL"); +} + +static void test_list_zero_initial_capacity() { + c_ArrayList_t zero_list; + c_ArrayList_Init(&zero_list, sizeof(int), 0); + + int val = 99; + // 如果你没有按照上方提示修复缺陷①,该断言将会失败(期望返回 OK 却返回了 PARAM 错误) + c_err_t err = c_ArrayList_Add(&zero_list, &val); + ASSERT_INT_EQ_MSG(C_ERR_OK, err, "Add failed when initial capacity is 0 (Bug ① Triggered!)"); + + c_ArrayList_Destroy(&zero_list); +} + +static void test_list_auto_shrink() { + // 1. 连续添加 9 个元素触发扩容 + // 初始化 0 -> 扩容到 4 -> 扩容到 8 -> 扩容到 16 + c_ArrayList_t list; + c_ArrayList_Init(&list, sizeof(int), 2); + + for (int i = 0; i < 9; i++) { + c_ArrayList_Add(&list, &i); + } + ASSERT_INT_EQ_MSG(16, list.capacity, "Capacity should expand up to 16"); + + // 2. 依次删除元素,降低 size 以试图触发 1/4 缩容临界点 + // 当 size 减少到 4 时 (即 16 / 4), 应该触发缩容:16 减半变成 8 + for (int i = 0; i < 5; i++) { + c_ArrayList_Remove(&list, 0); // 总是移除首个元素 + } + + // 此时移除了 5 个,剩下 4 个元素 + ASSERT_INT_EQ_MSG(4, list.size, "Current size should be 4"); + ASSERT_INT_EQ_MSG(8, list.capacity, "Capacity should automatically shrink to 8"); + + // 3. 继续删除,观察是否会由于低于最小阈值(4)而停止缩容 + for (int i = 0; i < 3; i++) { + c_ArrayList_Remove(&list, 0); + } + // 此时只剩 1 个元素了 (1 <= 8/4),但由于 MIN_SHRINK_CAPACITY = 4 限制,容量不应该再减半到 2 + ASSERT_INT_EQ_MSG(1, list.size, "Current size should be 1"); + ASSERT_INT_EQ_MSG(2, list.capacity, "Capacity should hold at 2 to prevent thrashing"); +} + int main(void) { - C_TEST_SUITE_BEGIN(BaseArrayListNewSpecificationTestSuite) - C_RUN_TEST_CASE(test_array_list_full_lifecycle_and_removal); - C_TEST_SUITE_END() + TEST_START(BaseArrayListNewSpecificationTestSuite); + + RUN_TEST(test_array_list_full_lifecycle_and_removal); + RUN_TEST(test_list_auto_expansion); + RUN_TEST(test_list_remove_element); + RUN_TEST(test_list_zero_initial_capacity); + RUN_TEST(test_list_auto_shrink); + + TEST_REPORT(); + RETURN_TEST_STATUS; } diff --git a/Foundation/c_ArrayQueue.c b/Foundation/c_ArrayQueue.c index 3009095..83a4b67 100644 --- a/Foundation/c_ArrayQueue.c +++ b/Foundation/c_ArrayQueue.c @@ -1,7 +1,8 @@ #include #include -#define DEFAULT_INITIAL_CAPACITY 4 +#define DEFAULT_INITIAL_CAPACITY 4 +#define MIN_SHRINK_CAPACITY 4 /* ------------------------------------------------------------------------------------------------------------------ */ /* */ @@ -72,6 +73,18 @@ c_err_t c_ArrayQueue_Pop(c_ArrayQueue_t* self, void* obj) { } self->size--; + + if (self->size > 0 && self->size <= (self->capacity >> 2)) { + c_size_t new_capacity = self->capacity >> 1; // 容量减半 + + void* new_array = C_REALLOC(self->array, new_capacity * self->obj_size); + if (new_array) { // 如果 realloc 失败不影响原有数据安全,这里采用安全赋值 + self->array = new_array; + self->capacity = new_capacity; + } + } + + return C_ERR_OK; } diff --git a/Foundation/c_ArrayQueue.t.c b/Foundation/c_ArrayQueue.t.c new file mode 100644 index 0000000..8358e69 --- /dev/null +++ b/Foundation/c_ArrayQueue.t.c @@ -0,0 +1,151 @@ +#include "c_ArrayQueue.h" +#include +#include +#include + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +static c_ArrayQueue_t g_queue; + +// 启动环境:初始化一个初始容量为 4 的 int 类型队列 +void setup_queue() { + c_err_t err = c_ArrayQueue_Init(&g_queue, sizeof(int), 4); + if (err != C_ERR_OK) { + printf(" " COLOR_RED "[ERROR] Queue Setup failed!" COLOR_RESET "\n"); + } +} + +// 清理环境:安全销毁队列 +void teardown_queue() { + c_ArrayQueue_Destroy(&g_queue); +} + +// 用例 1:测试常规入队、查看对头、出队(FIFO 基础逻辑) +void test_queue_push_pop_basic() { + int v1 = 10, v2 = 20, v3 = 30; + + // 入队 3 个元素 + ASSERT_INT_EQ_MSG(C_ERR_OK, c_ArrayQueue_Push(&g_queue, &v1), "Push 10 failed"); + ASSERT_INT_EQ_MSG(C_ERR_OK, c_ArrayQueue_Push(&g_queue, &v2), "Push 20 failed"); + ASSERT_INT_EQ_MSG(C_ERR_OK, c_ArrayQueue_Push(&g_queue, &v3), "Push 30 failed"); + + // 检查大小 + ASSERT_INT_EQ_MSG(3, g_queue.size, "Size should be 3"); + + // Peek 检查队头(应该依然是 10,且不弹出元素) + int* front_ptr = (int*)c_ArrayQueue_Peek(&g_queue); + ASSERT_MSG(front_ptr != NULL, "Peek should not return NULL"); + ASSERT_INT_EQ_MSG(10, *front_ptr, "Peek value mismatches"); + + // 开始 Pop 出队,验证 FIFO 顺序 + int out_val = 0; + + ASSERT_INT_EQ_MSG(C_ERR_OK, c_ArrayQueue_Pop(&g_queue, &out_val), "Pop 1 failed"); + ASSERT_INT_EQ_MSG(10, out_val, "First out should be 10"); + + ASSERT_INT_EQ_MSG(C_ERR_OK, c_ArrayQueue_Pop(&g_queue, &out_val), "Pop 2 failed"); + ASSERT_INT_EQ_MSG(20, out_val, "Second out should be 20"); + + ASSERT_INT_EQ_MSG(2, g_queue.capacity, "Capacity management check"); // 选测:如果你内部写了 Pop 自动缩容 + ASSERT_INT_EQ_MSG(1, g_queue.size, "Size should drop to 1"); +} + + +// 用例 2:测试队列在空(Empty)或未初始化状态下的防御表现 +void test_queue_empty_bounds() { + int out_val = 999; + + // 空队列直接 Pop 应该报错 + c_err_t err = c_ArrayQueue_Pop(&g_queue, &out_val); + ASSERT_MSG(err != C_ERR_OK, "Pop on empty queue should return an error code"); + ASSERT_INT_EQ_MSG(999, out_val, "Output value should remain untouched on failure"); + + // 空队列 Peek 应该返回 NULL + ASSERT_MSG(c_ArrayQueue_Peek(&g_queue) == NULL, "Peek on empty queue must return NULL"); +} + + +// 用例 3:测试循环环绕与动态扩容(如果是循环队列,该用例极度核心) +void test_queue_wrap_around_and_expand() { + int v = 0; + int out = 0; + + // 1. 先把初始容量 4 填满 + for (int i = 1; i <= 4; i++) { + v = i * 10; // 10, 20, 30, 40 + c_ArrayQueue_Push(&g_queue, &v); + } + + // 2. 弹出 2 个元素(释放前面两个格子的空间,触发头部指针往后移动) + c_ArrayQueue_Pop(&g_queue, &out); // 弹出 10 + c_ArrayQueue_Pop(&g_queue, &out); // 弹出 20 + + // 3. 再次塞入 2 个元素(如果是循环队列,这俩元素会被存到刚才释放的 0 和 1 索引槽位) + v = 50; c_ArrayQueue_Push(&g_queue, &v); + v = 60; c_ArrayQueue_Push(&g_queue, &v); + + // 4. 此时队列满(包含 30, 40, 50, 60),再次 Push 触发扩容 + v = 70; + c_err_t err = c_ArrayQueue_Push(&g_queue, &v); + ASSERT_INT_EQ_MSG(C_ERR_OK, err, "Push triggering growth failed"); + ASSERT_MSG(g_queue.capacity > 4, "Queue failed to expand capacity"); + + // 5. 依次全部弹出,验证即使经历过“环绕”与“扩容搬移内存”,FIFO 序列依旧保持绝对准确 + int expected_sequence[] = {30, 40, 50, 60, 70}; + for (int i = 0; i < 5; i++) { + c_ArrayQueue_Pop(&g_queue, &out); + char msg[100]; + sprintf(msg, "Sequence broke at check-index [%d], expected %d", i, expected_sequence[i]); + ASSERT_INT_EQ_MSG(expected_sequence[i], out, msg); + } +} + + +// 用例 4:测试任意位置删除(c_ArrayQueue_Remove) +void test_queue_remove_by_index() { + int values[] = {100, 200, 300, 400}; + for (int i = 0; i < 4; i++) { + c_ArrayQueue_Push(&g_queue, &values[i]); + } + + // 尝试删除越界索引(当前有 4 个元素,有效索引为 0~3,删除 5 应该失败) + c_err_t err_out = c_ArrayQueue_Remove(&g_queue, 5); + ASSERT_MSG(err_out != C_ERR_OK, "Remove out of bounds should fail"); + + // 删除当前队列中的中间元素(索引 1,即删除 200) + c_err_t err_ok = c_ArrayQueue_Remove(&g_queue, 1); + ASSERT_INT_EQ_MSG(C_ERR_OK, err_ok, "Remove element failed"); + ASSERT_INT_EQ_MSG(3, g_queue.size, "Size should be 3 after removal"); + + // 依次 Pop 验证剩下的元素顺序是否已经平滑前移(应该是 100 -> 300 -> 400) + int out = 0; + + c_ArrayQueue_Pop(&g_queue, &out); + ASSERT_INT_EQ_MSG(100, out, "First element should still be 100"); + + c_ArrayQueue_Pop(&g_queue, &out); + ASSERT_INT_EQ_MSG(300, out, "Element 300 should shift forward to index 1"); + + c_ArrayQueue_Pop(&g_queue, &out); + ASSERT_INT_EQ_MSG(400, out, "Element 400 should shift forward to index 2"); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +int main(int argc, char** argv){ + TEST_START(Starting Unit Tests); + + RUN_TEST_FIXTURE(test_queue_push_pop_basic, setup_queue, teardown_queue); + RUN_TEST_FIXTURE(test_queue_empty_bounds, setup_queue, teardown_queue); + RUN_TEST_FIXTURE(test_queue_wrap_around_and_expand, setup_queue, teardown_queue); + RUN_TEST_FIXTURE(test_queue_remove_by_index, setup_queue, teardown_queue); + + // 打印最终统计报告 + TEST_REPORT(); + + RETURN_TEST_STATUS; + + return 0; +} diff --git a/Foundation/c_ArrayStack.c b/Foundation/c_ArrayStack.c index dc42697..f534036 100644 --- a/Foundation/c_ArrayStack.c +++ b/Foundation/c_ArrayStack.c @@ -6,14 +6,18 @@ c_err_t c_ArrayStack_Init(c_ArrayStack_t* self, int obj_size, c_size_t capacity) { if (!self || obj_size <= 0) return C_ERR_PARAM; - self->obj_size = obj_size; - self->capacity = (capacity > 0) ? capacity : DEFAULT_INITIAL_CAPACITY; + self->obj_size = (int)obj_size; self->size = 0; + self->capacity = capacity; - self->array = C_ALLOC(self->capacity * self->obj_size); - if (!self->array) { - self->capacity = 0; - return C_ERR_NOMEM; + if (capacity==0) { + self->array = NULL; + }else { + self->array = C_ALLOC(self->capacity * self->obj_size); + if (!self->array) { + self->capacity = 0; + return C_ERR_NOMEM; + } } return C_ERR_OK; @@ -33,7 +37,7 @@ c_err_t c_ArrayStack_Push(c_ArrayStack_t* self, void* obj) { if (!self || !self->array || !obj) return C_ERR_PARAM; if (self->size >= self->capacity) { - const c_size_t new_capacity = self->capacity << 1; + const c_size_t new_capacity = (self->capacity==0)?DEFAULT_INITIAL_CAPACITY:(self->capacity<<1); void* new_array = C_REALLOC(self->array, new_capacity * self->obj_size); if (!new_array) { return C_ERR_NOMEM; @@ -61,6 +65,16 @@ c_err_t c_ArrayStack_Pop(c_ArrayStack_t* self, void* obj) { memcpy(obj, pop_src, self->obj_size); self->size--; + + if (self->size > 0 && self->size <= (self->capacity >> 2)) { + c_size_t new_capacity = self->capacity >> 1; + void* new_array = C_REALLOC(self->array, new_capacity * self->obj_size); + if (new_array) { + self->array = new_array; + self->capacity = new_capacity; + } + } + return C_ERR_OK; } @@ -82,6 +96,16 @@ c_err_t c_ArrayStack_Remove(c_ArrayStack_t* self, c_size_t index) { } self->size--; + + if (self->size > 0 && self->size <= (self->capacity >> 2)) { + c_size_t new_capacity = self->capacity >> 1; + void* new_array = C_REALLOC(self->array, new_capacity * self->obj_size); + if (new_array) { + self->array = new_array; + self->capacity = new_capacity; + } + } + return C_ERR_OK; } diff --git a/Foundation/c_ArrayStack.t.c b/Foundation/c_ArrayStack.t.c new file mode 100644 index 0000000..46d3191 --- /dev/null +++ b/Foundation/c_ArrayStack.t.c @@ -0,0 +1,152 @@ +#include "c_ArrayStack.h" +#include +#include +#include + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +static c_ArrayStack_t g_stack; + +// 启动环境:初始化一个初始容量为 2 的 int 类型栈 +static void setup_stack() { + c_err_t err = c_ArrayStack_Init(&g_stack, sizeof(int), 2); + if (err != C_ERR_OK) { + printf(" " COLOR_RED "[ERROR] Stack Setup failed!" COLOR_RESET "\n"); + } +} + +// 清理环境:安全销毁栈,杜绝内存泄漏 +static void teardown_stack() { + c_ArrayStack_Destroy(&g_stack); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +// 用例 1:测试 LIFO(后进先出)核心逻辑、IsEmpty 状态及 Peek 观察 +static void test_stack_push_pop_lifo() { + // 初始状态应该为空 + ASSERT_MSG(c_ArrayStack_IsEmpty(&g_stack) == true, "Stack should be empty initially"); + + int v1 = 111, v2 = 222; + ASSERT_INT_EQ_MSG(C_ERR_OK, c_ArrayStack_Push(&g_stack, &v1), "Push 111 failed"); + ASSERT_INT_EQ_MSG(C_ERR_OK, c_ArrayStack_Push(&g_stack, &v2), "Push 222 failed"); + + // 此时不应该为空,大小应为 2 + ASSERT_MSG(c_ArrayStack_IsEmpty(&g_stack) == false, "Stack should not be empty"); + ASSERT_INT_EQ_MSG(2, g_stack.size, "Stack size should be 2"); + + // 测试 Peek:应该看到最后压入的 222,且栈大小不变 + int* top_ptr = (int*)c_ArrayStack_Peek(&g_stack); + ASSERT_MSG(top_ptr != NULL, "Peek should not return NULL"); + ASSERT_INT_EQ_MSG(222, *top_ptr, "Peek value should be 222"); + ASSERT_INT_EQ_MSG(2, g_stack.size, "Size must remain 2 after peek"); + + // 测试 Pop:验证 LIFO 顺序 + int out_val = 0; + ASSERT_INT_EQ_MSG(C_ERR_OK, c_ArrayStack_Pop(&g_stack, &out_val), "First pop failed"); + ASSERT_INT_EQ_MSG(222, out_val, "First popped value should be 222 (LIFO)"); + + ASSERT_INT_EQ_MSG(C_ERR_OK, c_ArrayStack_Pop(&g_stack, &out_val), "Second pop failed"); + ASSERT_INT_EQ_MSG(111, out_val, "Second popped value should be 111"); + + // 最终应该重新变为空栈 + ASSERT_MSG(c_ArrayStack_IsEmpty(&g_stack) == true, "Stack should be empty after popping all elements"); +} + + +// 用例 2:测试空栈(Empty)的边界防御表现 +static void test_stack_empty_bounds() { + int dummy = 999; + + // 空栈执行 Pop 应该安全报错(例如返回 C_ERR_EMPTY 或非 OK 状态) + c_err_t err = c_ArrayStack_Pop(&g_stack, &dummy); + ASSERT_MSG(err != C_ERR_OK, "Pop on empty stack should return an error code"); + ASSERT_INT_EQ_MSG(999, dummy, "Output buffer should remain unchanged on failure"); + + // 空栈执行 Peek 应该安全返回 NULL + ASSERT_MSG(c_ArrayStack_Peek(&g_stack) == NULL, "Peek on empty stack must return NULL"); +} + + +// 用例 3:动态自动扩容测试 +static void test_stack_auto_expansion() { + // 初始容量设为了 2,连续压入 4 个数据触发自动扩容 + for (int i = 1; i <= 4; i++) { + int val = i * 10; + c_err_t err = c_ArrayStack_Push(&g_stack, &val); + char msg[64]; + sprintf(msg, "Pushing element %d failed", val); + ASSERT_INT_EQ_MSG(C_ERR_OK, err, msg); + } + + // 校验容量是否增长 + ASSERT_MSG(g_stack.capacity > 2, "Stack capacity should have grown"); + ASSERT_INT_EQ_MSG(4, g_stack.size, "Stack size should be 4"); + + // 倒序弹出校验,确保扩容重构内存后历史数据未损坏 + int out_val = 0; + int expected_vals[] = {40, 30, 20, 10}; + for (int i = 0; i < 4; i++) { + c_ArrayStack_Pop(&g_stack, &out_val); + char msg[64]; + sprintf(msg, "Mismatched LIFO element at step %d", i); + ASSERT_INT_EQ_MSG(expected_vals[i], out_val, msg); + } +} + + +// 用例 4:测试从任意指定索引删除元素(c_ArrayStack_Remove) +static void test_stack_remove_by_index() { + // 压入底 -> 顶:10, 20, 30, 40 + // 索引映射:index 0 -> 10, index 1 -> 20, index 2 -> 30, index 3 -> 40 + for (int i = 1; i <= 4; i++) { + int val = i * 10; + c_ArrayStack_Push(&g_stack, &val); + } + + // 1. 测试越界删除防御(有效索引为 0~3) + c_err_t err_invalid = c_ArrayStack_Remove(&g_stack, 4); + ASSERT_MSG(err_invalid != C_ERR_OK, "Remove out of bounds should fail"); + + // 2. 删除中间的元素:索引 1 (对应数字 20) + c_err_t err_ok = c_ArrayStack_Remove(&g_stack, 1); + ASSERT_INT_EQ_MSG(C_ERR_OK, err_ok, "Remove middle element failed"); + ASSERT_INT_EQ_MSG(3, g_stack.size, "Size should drop to 3 after remove"); + + // 3. 验证删除后的整体结构。由于移除了 20,剩下的数组结构应为:10, 30, 40 + // 按照栈的 LIFO 弹出顺序,依次拿到的应该是 40 -> 30 -> 10 + int out_val = 0; + + c_ArrayStack_Pop(&g_stack, &out_val); + ASSERT_INT_EQ_MSG(40, out_val, "Top should still be 40"); + + c_ArrayStack_Pop(&g_stack, &out_val); + ASSERT_INT_EQ_MSG(30, out_val, "Next should be 30 (since 20 was removed)"); + + c_ArrayStack_Pop(&g_stack, &out_val); + ASSERT_INT_EQ_MSG(10, out_val, "Bottom element should be 10"); +} + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +int main(int argc, char** argv){ + + TEST_START(Starting Unit Tests); + + // 运行需要内存环境的用例 + RUN_TEST_FIXTURE(test_stack_push_pop_lifo, setup_stack, teardown_stack); + RUN_TEST_FIXTURE(test_stack_empty_bounds, setup_stack, teardown_stack); + RUN_TEST_FIXTURE(test_stack_auto_expansion, setup_stack, teardown_stack); + RUN_TEST_FIXTURE(test_stack_remove_by_index, setup_stack, teardown_stack); + + + // 打印最终统计报告 + TEST_REPORT(); + + RETURN_TEST_STATUS; +} diff --git a/Foundation/c_FastByteRingBuffer.c b/Foundation/c_FastByteRingBuffer.c index 1838ea0..d39f931 100644 --- a/Foundation/c_FastByteRingBuffer.c +++ b/Foundation/c_FastByteRingBuffer.c @@ -49,7 +49,7 @@ c_err_t c_FastByteRingBuffer_Init(c_FastByteRingBuffer_t* self, c_size_t capacit self->tail = 0; self->is_full = C_FALSE; - self->buffer = (uint8_t*)malloc(self->capacity); + self->buffer = (uint8_t*)C_ALLOC(self->capacity); if (!self->buffer) { self->capacity = 0; self->mask = 0; @@ -339,7 +339,7 @@ c_index_t c_FastByteRingBuffer_IndexOfByte(const c_FastByteRingBuffer_t* self, u return (c_index_t)offset; } } - return C_ERR_FAIL; + return C_ERR_NOTFOUND; } c_index_t c_FastByteRingBuffer_IndexOfBuffer(const c_FastByteRingBuffer_t* self, const uint8_t* pattern, c_size_t pattern_len) { diff --git a/Foundation/c_FastByteRingBuffer.t.c b/Foundation/c_FastByteRingBuffer.t.c index 0444190..3657055 100644 --- a/Foundation/c_FastByteRingBuffer.t.c +++ b/Foundation/c_FastByteRingBuffer.t.c @@ -1,6 +1,11 @@ #include "c_FastByteRingBuffer.h" #include #include +#include + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + #define RUN_TEST_CASE(test_func) \ do { \ @@ -126,7 +131,7 @@ static void test_ring_buffer_index_searching(void) { // 1. Single byte search lookup match assert(c_FastByteRingBuffer_IndexOfByte(&ring, 0x33) == 2); // Relative offset index 2 from head - assert(c_FastByteRingBuffer_IndexOfByte(&ring, 0x99) == C_ERR_NOT_FOUND); + assert(c_FastByteRingBuffer_IndexOfByte(&ring, 0x99) == C_ERR_NOTFOUND); // 2. Pattern buffer sequence scan (testing across structural wrap-around edge boundaries) uint8_t search_pattern[] = {0x44, 0x55, 0x66}; @@ -206,9 +211,9 @@ static void test_relative_random_access(void) { assert(extracted_byte == 0x50); // Newest unread byte check // 2. Validate bounds-checking flags - assert(c_FastByteRingBuffer_GetAtRelative(&ring, 4, &extracted_byte) == C_ERR_OUT_OF_BOUNDS); - assert(c_FastByteRingBuffer_GetAtRelative(&ring, 99, &extracted_byte) == C_ERR_OUT_OF_BOUNDS); - assert(c_FastByteRingBuffer_GetAtRelative(NULL, 0, &extracted_byte) == C_ERR_INVALID_PARAM); + assert(c_FastByteRingBuffer_GetAtRelative(&ring, 4, &extracted_byte) == C_ERR_OUTOFBOUND); + assert(c_FastByteRingBuffer_GetAtRelative(&ring, 99, &extracted_byte) == C_ERR_OUTOFBOUND); + assert(c_FastByteRingBuffer_GetAtRelative(NULL, 0, &extracted_byte) == C_ERR_PARAM); c_FastByteRingBuffer_Destroy(&ring); } @@ -244,7 +249,7 @@ static void test_conditional_is_validator(void) { static void test_ring_buffer_memcmp(void) { c_FastByteRingBuffer_t ring; - assert(c_FastByteRingBuffer_Init(&ring, 6) == C_ERR_INVALID_PARAM); // Capacity = 6 + assert(c_FastByteRingBuffer_Init(&ring, 6) == C_ERR_PARAM); // Capacity = 6 assert(c_FastByteRingBuffer_Init(&ring, 8) == C_SUCCESS); // Capacity = 6 uint8_t payload[] = {0x00, 0x11, 0x22, 0x33}; @@ -274,9 +279,9 @@ static void test_ring_buffer_memcmp(void) { assert(c_FastByteRingBuffer_Memcmp(&ring, 1, check_mismatch, 4) != 0); // Identifies internal divergence // 4. Bounds and parameter checks - assert(c_FastByteRingBuffer_Memcmp(&ring, 0, check_b, 100) == C_ERR_OUT_OF_BOUNDS); // Request width overflows content - assert(c_FastByteRingBuffer_Memcmp(&ring, 99, check_b, 1) == C_ERR_OUT_OF_BOUNDS); // Start pointer invalid - assert(c_FastByteRingBuffer_Memcmp(NULL, 0, check_b, 1) == C_ERR_INVALID_PARAM); + assert(c_FastByteRingBuffer_Memcmp(&ring, 0, check_b, 100) == C_ERR_OUTOFBOUND); // Request width overflows content + assert(c_FastByteRingBuffer_Memcmp(&ring, 99, check_b, 1) == C_ERR_OUTOFBOUND); // Start pointer invalid + assert(c_FastByteRingBuffer_Memcmp(NULL, 0, check_b, 1) == C_ERR_PARAM); c_FastByteRingBuffer_Destroy(&ring); } diff --git a/Foundation/c_File.c b/Foundation/c_File.c index 110290b..a730129 100644 --- a/Foundation/c_File.c +++ b/Foundation/c_File.c @@ -3,14 +3,19 @@ #include #include // 提供 _get_osfhandle #include + #include #define C_ACCESS(path) _access(path, 0) #define C_MAKE_DIR(path) _mkdir(path) // Windows 下创建目录 + #define sys_rmdir(path) _rmdir(path) + #define PATH_SEP '\\' #else #include // 提供 fsync #include #include #define C_ACCESS(path) access(path, F_OK) #define C_MAKE_DIR(path) mkdir(path, 0755) + #define sys_rmdir(path) rmdir(path) + #define PATH_SEP '/' #endif /* ------------------------------------------------------------------------------------------------------------------ */ @@ -276,4 +281,208 @@ c_err_t c_File_MkDirs(const char* path) { } return C_SUCCESS; -} \ No newline at end of file +} + +c_err_t c_File_Rmdir(const char* path) { + if (!path || path[0] == '\0') { + return C_ERR_PARAM; + } + + // 调用操作系统底层移除目录的 API + int result = sys_rmdir(path); + + if (result == 0) { + return C_ERR_OK; // 删除成功 + } else { + // 删除失败(可能是由于目录不存在、无权限、或者目录非空) + return C_ERR_FAIL; + } +} + +c_err_t c_File_Rmdirs(const char* path) { + if (!path || path[0] == '\0') { + return C_ERR_PARAM; + } + + c_err_t status = C_ERR_OK; + +#if defined(_WIN32) || defined(_WIN64) + // ========================================== + // Windows 平台的递归删除实现 + // ========================================== + char search_path[MAX_PATH]; + // Windows 检索目录需要追加 "\\*" + snprintf(search_path, sizeof(search_path), "%s\\*", path); + + WIN32_FIND_DATAA find_data; + HANDLE h_find = FindFirstFileA(search_path, &find_data); + + if (h_find == INVALID_HANDLE_VALUE) { + // 如果目录根本不存在,或者无法打开,尝试直接当做文件 remove 移除(处理符号链接等边界) + return remove(path) == 0 ? C_ERR_OK : C_ERR_FAIL; + } + + do { + // 排除 Windows 的特殊目录 "." 和 ".." + if (strcmp(find_data.cFileName, ".") == 0 || strcmp(find_data.cFileName, "..") == 0) { + continue; + } + + // 拼接子项的完整路径 + char sub_path[MAX_PATH]; + snprintf(sub_path, sizeof(sub_path), "%s\\%s", path, find_data.cFileName); + + if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + // 如果子项是目录,递归调用 + status = c_File_Rmdirs(sub_path); + } else { + // 如果子项是普通文件,取消只读属性(防止因只读导致删除失败),然后将其删除 + SetFileAttributesA(sub_path, FILE_ATTRIBUTE_NORMAL); + status = (DeleteFileA(sub_path) != 0) ? C_ERR_OK : C_ERR_FAIL; + } + + if (status != C_ERR_OK) { + break; + } + } while (FindNextFileA(h_find, &find_data)); + + FindClose(h_find); + +#else + // ========================================== + // Linux / macOS (POSIX) 平台的递归删除实现 + // ========================================== + DIR* dir = opendir(path); + if (!dir) { + // 无法打开作为目录处理,尝试按普通文件物理删除 + return remove(path) == 0 ? C_ERR_OK : C_ERR_FAIL; + } + + struct dirent* entry; + while ((entry = readdir(dir)) != NULL) { + // 排除 Linux 的特殊目录 "." 和 ".." + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + + // 拼接子项的完整路径 + char sub_path[1024]; + snprintf(sub_path, sizeof(sub_path), "%s/%s", path, entry->d_name); + + struct stat statbuf; + if (stat(sub_path, &statbuf) == 0) { + if (S_ISDIR(statbuf.st_mode)) { + // 如果子项是目录,递归调用 + status = c_File_Rmdirs(sub_path); + } else { + // 如果子项是文件,执行普通删除 + status = (remove(sub_path) == 0) ? C_ERR_OK : C_ERR_FAIL; + } + } + + if (status != C_ERR_OK) { + break; + } + } + closedir(dir); +#endif + + // ========================================== + // 公共收尾:清空了内部所有子项后,最后删除外层空壳目录 + // ========================================== + if (status == C_ERR_OK) { + if (sys_rmdir(path) != 0) { + status = C_ERR_FAIL; + } + } + + return status; +} + +c_err_t c_File_Delete(const char* fileName) { + if (!fileName || fileName[0] == '\0') { + return C_ERR_PARAM; + } + + // 标准 C 库的 remove 能够直接删除文件(在某些平台上也能删除空目录) + int result = remove(fileName); + + if (result == 0) { + return C_ERR_OK; // 删除成功 + } else { + // 删除失败(文件不存在、或无权限、或文件正被某些操作系统强锁占用) + return C_ERR_FAIL; + } +} + +#define COPY_BUFFER_SIZE 4096 + +c_err_t c_File_Copy(const char* srcPath, const char* destPath) { + if (!srcPath || srcPath[0] == '\0' || !destPath || destPath[0] == '\0') { + return C_ERR_PARAM; + } + + FILE* src = fopen(srcPath, "rb"); + if (!src) return C_ERR_NOTFOUND; + + FILE* dest = fopen(destPath, "wb"); + if (!dest) { + fclose(src); + return C_ERR_FAIL; + } + + char* buffer = (char*)malloc(COPY_BUFFER_SIZE); + if (!buffer) { + fclose(src); + fclose(dest); + return C_ERR_FAIL; + } + + c_err_t status = C_ERR_OK; + size_t bytes_read; + + // 循环读写块,避免一次性读入大文件撑爆堆内存 + while ((bytes_read = fread(buffer, 1, COPY_BUFFER_SIZE, src)) > 0) { + size_t bytes_written = fwrite(buffer, 1, bytes_read, dest); + if (bytes_written < bytes_read) { + status = C_ERR_FAIL; // 磁盘空间不足或写入错误 + break; + } + } + + free(buffer); + fclose(src); + fclose(dest); + + // 如果中途失败,清理掉生成的不完整目标文件 + if (status != C_ERR_OK) { + remove(destPath); + } + + return status; +} + +c_err_t c_File_Move(const char* oldPath, const char* newPath) { + if (!oldPath || oldPath[0] == '\0' || !newPath || newPath[0] == '\0') { + return C_ERR_PARAM; + } + + // 1. 尝试使用操作系统原生的轻量级重命名/移动 + if (rename(oldPath, newPath) == 0) { + return C_ERR_OK; + } + + // 2. 跨分区保底策略:如果因为跨文件系统挂载点导致 rename 失败,则执行 复制 + 删除 + c_err_t copy_err = c_File_Copy(oldPath, newPath); + if (copy_err == C_ERR_OK) { + if (remove(oldPath) == 0) { + return C_ERR_OK; + } else { + // 如果删原文件失败,为了数据安全,把新拷过去的文件也撤销,避免数据状态不一致 + remove(newPath); + return C_ERR_FAIL; + } + } + + return C_ERR_FAIL; +} diff --git a/Foundation/c_File.h b/Foundation/c_File.h index 2c80404..583de3d 100644 --- a/Foundation/c_File.h +++ b/Foundation/c_File.h @@ -21,12 +21,6 @@ typedef struct { }c_File_t; - -/* ------------------------------------------------------------------------------------------------------------------ */ -/* */ - - - /* ------------------------------------------------------------------------------------------------------------------ */ /* */ @@ -48,6 +42,20 @@ c_err_t c_File_Seek(c_File_t* file, long long position); c_err_t c_File_MkDirs(const char* path); +c_err_t c_File_Rmdir(const char* path); + +c_err_t c_File_Rmdirs(const char* path); + +c_err_t c_File_Delete(const char* fileName); + +/** + * @brief 复制指定文件到目标路径(支持大文件流式拷贝) + * @param srcPath 源文件路径 + * @param destPath 目标文件路径 + * @return 成功返回 C_ERR_OK,失败返回对应错误码 + */ +c_err_t c_File_Copy(const char* srcPath, const char* destPath); + /** * @brief 检查指定路径的文件或目录是否存在 * @param fileName 文件的绝对路径或相对路径 @@ -55,4 +63,12 @@ c_err_t c_File_MkDirs(const char* path); */ c_bool_t c_File_IsExist(const char* fileName); +/** + * @brief 移动或重命名文件(支持跨分区移动保底) + * @param oldPath 旧文件路径 + * @param newPath 新文件路径 + * @return 成功返回 C_ERR_OK,失败返回对应错误码 + */ +c_err_t c_File_Move(const char* oldPath, const char* newPath); + #endif /*INCLUDED_C_FILE_H*/ diff --git a/Foundation/c_File.t.c b/Foundation/c_File.t.c new file mode 100644 index 0000000..cf9d35e --- /dev/null +++ b/Foundation/c_File.t.c @@ -0,0 +1,365 @@ +#include "c_File.h" +#include +#include +#include + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +static const char* TEST_DIR = "./test_sandbox"; +static const char* TEST_FILE = "./test_sandbox/test_data.txt"; +static const char* NESTED_DIR_ROOT = "./test_sandbox_deep"; +static const char* NESTED_DIR_SUB1 = "./test_sandbox_deep/level1"; +static const char* NESTED_DIR_SUB2 = "./test_sandbox_deep/level1/level2"; +static const char* NESTED_DEEP_FILE = "./test_sandbox_deep/level1/level2/target.txt"; +static const char* TEST_DEL_FILE = "./test_sandbox/delete_target.txt"; +static const char* FILE_SRC = "./test_sandbox/copy_src.txt"; +static const char* FILE_COPY = "./test_sandbox/copy_dest.txt"; +static const char* FILE_MOVE = "./test_sandbox/move_dest.txt"; + +static c_File_t g_file; + +// 每个文件测试开始前:创建目录确保沙盒环境存在,并重置文件结构体 +void setup_file_env() { + c_File_MkDirs(TEST_DIR); + g_file.fp = NULL; +} + +// 每个文件测试结束后:强制关闭可能遗留的文件流,并清理生成的临时测试文件 +void teardown_file_env() { + if (g_file.fp != NULL) { + c_File_Close(&g_file); + } + // 移除沙盒内的文件(如果存在) + remove(TEST_FILE); + // 移除沙盒目录(Linux/macOS 下使用 rmdir,为保持跨平台通用这里主要移除文件) + remove(TEST_DIR); +} + +void setup_deep_dir_env() { + // 1. 建立一个多级深层嵌套的目录 + c_File_MkDirs(NESTED_DIR_SUB2); +} + +void teardown_deep_dir_env() { + // 兜底清理:如果测试挂了,防止残留污染本地磁盘 + // 在这里直接调用它自己完成强制扫尾 + c_File_Rmdirs(NESTED_DIR_ROOT); +} + +void setup_delete_env() { + // 确保测试沙盒目录存在 + c_File_MkDirs("./test_sandbox"); +} + +void teardown_delete_env() { + // 扫尾清理,防止测试中断导致文件残留 + remove(TEST_DEL_FILE); + remove("./test_sandbox"); +} + + +void setup_move_copy_env() { + c_File_MkDirs("./test_sandbox"); +} + +void teardown_move_copy_env() { + // 强制清理,防止测试中断产生磁盘残留 + remove(FILE_SRC); + remove(FILE_COPY); + remove(FILE_MOVE); + remove("./test_sandbox"); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +void test_file_write_and_size() { + // 1. 以只写/新建模式打开文件 + c_err_t err = c_File_Open(&g_file, TEST_FILE, "wb"); + ASSERT_INT_EQ_MSG(C_ERR_OK, err, "Failed to open file for writing"); + ASSERT_MSG(g_file.fp != NULL, "File pointer should not be NULL after open"); + + // 2. 检查刚创建的文件是否存在 + ASSERT_MSG(c_File_IsExist(TEST_FILE) == C_TRUE, "File should exist on disk"); + + // 3. 写入数据 + const char* content = "Hello TinyTest Framework!"; + c_size_t bytes_to_write = strlen(content); + c_size_t bytes_written = 0; + + err = c_File_Write(&g_file, (void*)content, bytes_to_write, &bytes_written); + ASSERT_INT_EQ_MSG(C_ERR_OK, err, "File write error"); + ASSERT_INT_EQ_MSG((int)bytes_to_write, (int)bytes_written, "Written size mismatched"); + + // 4. 刷新缓冲区 + err = c_File_Flush(&g_file); + ASSERT_INT_EQ_MSG(C_ERR_OK, err, "File flush error"); + + // 5. 校验文件大小是否和写入的字节数严格对齐 + long long f_size = c_File_Size(&g_file); + ASSERT_INT_EQ_MSG((int)bytes_to_write, (int)f_size, "File size reported incorrectly"); + + // 6. 正常关闭文件 + c_File_Close(&g_file); +} + + +// 用例 2:测试文件的读取(Read)以及指针重定位(Seek) +void test_file_read_and_seek() { + // 预备工作:先写入一串已知文本用于后续测试 + c_File_Open(&g_file, TEST_FILE, "wb"); + const char* dummy_data = "abcdefghij"; // 10 字节 + c_size_t written = 0; + c_File_Write(&g_file, (void*)dummy_data, 10, &written); + c_File_Close(&g_file); + + // 1. 以只读模式重新打开文件 + c_err_t err = c_File_Open(&g_file, TEST_FILE, "rb"); + ASSERT_INT_EQ_MSG(C_ERR_OK, err, "Failed to open file for reading"); + + // 2. 测试基础顺序读取(先读取 4 字节,应该读到 "abcd") + char buffer[16] = {0}; + c_size_t bytes_read = 0; + err = c_File_Read(&g_file, buffer, 4, &bytes_read); + ASSERT_INT_EQ_MSG(C_ERR_OK, err, "File read error"); + ASSERT_INT_EQ_MSG(4, (int)bytes_read, "Should read exactly 4 bytes"); + ASSERT_INT_EQ_MSG(0, memcmp(buffer, "abcd", 4), "Buffer content mismatches on initial read"); + + // 3. 测试文件指针重定位:移到绝对位置索引 5 处(对应字符 'f') + err = c_File_Seek(&g_file, 5); + ASSERT_INT_EQ_MSG(C_ERR_OK, err, "File seek error"); + + // 4. 定位后再次读取 3 字节(应该读到 "fgh") + memset(buffer, 0, sizeof(buffer)); + err = c_File_Read(&g_file, buffer, 3, &bytes_read); + ASSERT_INT_EQ_MSG(C_ERR_OK, err, "File read error after seeking"); + ASSERT_INT_EQ_MSG(3, (int)bytes_read, "Should read 3 bytes after seek"); + ASSERT_INT_EQ_MSG(0, memcmp(buffer, "fgh", 3), "Buffer content mismatches after seek"); + + c_File_Close(&g_file); +} + + +// 用例 3:测试按行读取(Readline)文本的边界与断行识别 +void test_file_read_line() { + // 预备工作:写入包含多行换行符的文本 + c_File_Open(&g_file, TEST_FILE, "wb"); + const char* lines = "Line1\nLine2\r\nLine3"; + c_size_t written = 0; + c_File_Write(&g_file, (void*)lines, strlen(lines), &written); + c_File_Close(&g_file); + + // 1. 打开文件开始按行验证 + c_File_Open(&g_file, TEST_FILE, "rb"); + + char line_buf[32]; + c_size_t read_len = 0; + + // 读取第一行(预期为 "Line1\n" 或处理掉换行符的 "Line1" 视你内部实现而定,通常 fgets 保留换行) + c_err_t err = c_File_Readline(&g_file, line_buf, sizeof(line_buf), &read_len); + ASSERT_INT_EQ_MSG(C_ERR_OK, err, "Readline 1 failed"); + ASSERT_MSG(strstr(line_buf, "Line1") != NULL, "Line 1 content error"); + + // 读取第二行 + err = c_File_Readline(&g_file, line_buf, sizeof(line_buf), &read_len); + ASSERT_INT_EQ_MSG(C_ERR_OK, err, "Readline 2 failed"); + ASSERT_MSG(strstr(line_buf, "Line2") != NULL, "Line 2 content error"); + + c_File_Close(&g_file); +} + + +// 用例 4:测试文件不存在、无效路径时的防御性报错 +void test_file_invalid_operations() { + // 1. 检查一个绝对不存在的文件 + c_bool_t exist = c_File_IsExist("./this_file_does_not_exist_12345.xyz"); + ASSERT_INT_EQ_MSG(C_FALSE, exist, "IsExist should return FALSE for phantom files"); + + // 2. 尝试打开一个不存在的文件用于只读,应当安全返回错误码,且内部指针保持为 NULL + c_File_t bad_file = {NULL}; + c_err_t err = c_File_Open(&bad_file, "./phantom_file.txt", "rb"); + ASSERT_MSG(err != C_ERR_OK, "Opening non-existent file for read must fail"); + ASSERT_MSG(bad_file.fp == NULL, "Failed open must keep fp as NULL"); +} + +void test_file_rmdirs_force_delete_nested() { + // 1. 验证前置多级目录环境已经被 SetUp 成功拉起 + ASSERT_MSG(c_File_IsExist(NESTED_DIR_SUB2) == C_TRUE, "Setup failed to prepare nested dir"); + + // 2. 在最深层目录 level2 里写入一个真实的文本文件,夯实其“非空”属性 + c_File_t file; + c_err_t err = c_File_Open(&file, NESTED_DEEP_FILE, "wb"); + ASSERT_INT_EQ_MSG(C_ERR_OK, err, "Failed to create deep file inside test sandbox"); + + const char* dummy = "deep data"; + c_size_t written = 0; + c_File_Write(&file, (void*)dummy, strlen(dummy), &written); + c_File_Close(&file); + + // 再次确认文件已在磁盘落地 + ASSERT_MSG(c_File_IsExist(NESTED_DEEP_FILE) == C_TRUE, "Deep text file should exist before wipe"); + + // 3. 一剑封喉:直接调用 Rmdirs 强删最外层的根目录 NESTED_DIR_ROOT + c_err_t rmdir_status = c_File_Rmdirs(NESTED_DIR_ROOT); + ASSERT_INT_EQ_MSG(C_ERR_OK, rmdir_status, "c_File_Rmdirs failed to clear nested architecture"); + + // 4. 断言验证:整棵目录树必须从盘面上被完全抹除 + ASSERT_MSG(c_File_IsExist(NESTED_DEEP_FILE) == C_FALSE, "Deep file should have been blasted"); + ASSERT_MSG(c_File_IsExist(NESTED_DIR_SUB2) == C_FALSE, "Level 2 directory should be wiped"); + ASSERT_MSG(c_File_IsExist(NESTED_DIR_ROOT) == C_FALSE, "Root sandbox directory must be cleared"); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// 用例 1:测试常规文件的正常创建与成功删除 +void test_file_delete_success() { + // 1. 先创建一个真实的文件 + c_File_t file; + c_err_t err = c_File_Open(&file, TEST_DEL_FILE, "wb"); + ASSERT_INT_EQ_MSG(C_ERR_OK, err, "Failed to create delete-target file"); + + const char* dummy = "delete me"; + c_size_t written = 0; + c_File_Write(&file, (void*)dummy, strlen(dummy), &written); + c_File_Close(&file); + + // 2. 确认文件确实落地并在磁盘中存在 + ASSERT_MSG(c_File_IsExist(TEST_DEL_FILE) == C_TRUE, "Target file must exist before deletion"); + + // 3. 执行删除操作 + c_err_t del_err = c_File_Delete(TEST_DEL_FILE); + ASSERT_INT_EQ_MSG(C_ERR_OK, del_err, "c_File_Delete failed on a standard closed file"); + + // 4. 再次验证文件是否已经从文件系统中消失 + ASSERT_MSG(c_File_IsExist(TEST_DEL_FILE) == C_FALSE, "Target file should be gone after c_File_Delete"); +} + +// 用例 2:测试删除一个完全不存在的文件时的防御表现 +void test_file_delete_non_existent() { + const char* phantom_file = "./test_sandbox/ghost_file_999.xyz"; + + // 确保该路径当前确实不存在 + ASSERT_MSG(c_File_IsExist(phantom_file) == C_FALSE, "Phantom file should not exist"); + + // 尝试执行删除 + c_err_t err = c_File_Delete(phantom_file); + + // 应当安全返回非 OK 的错误状态,且程序绝不能发生崩溃 + ASSERT_MSG(err != C_ERR_OK, "c_File_Delete must report an error when trying to delete a non-existent file"); +} + +// 用例 3:测试删除一个正处于“打开/占用状态”的文件(进阶边界测试) +void test_file_delete_while_open() { + // 1. 创建并保持打开该文件,故意不执行 Close + c_File_t file; + c_err_t err = c_File_Open(&file, TEST_DEL_FILE, "wb"); + ASSERT_INT_EQ_MSG(C_ERR_OK, err, "Failed to open file for lock-test"); + + // 2. 尝试在文件流未关闭的情况下,强行调用接口删除它 + c_err_t del_err = c_File_Delete(TEST_DEL_FILE); + + /* + * 注意:这里的断言取决于你对框架跨平台容忍度的设计。 + * - Windows 底层会因为文件处于 Share Violation 锁死状态而直接拒绝删除,返回错误码(del_err != C_ERR_OK)。 + * - Linux / POSIX 则允许执行 unlink 删除,表现为返回 C_ERR_OK,但文件直到 Close 后才会真正释放空间。 + * 为了让你的测试能在多平台安全兼容,我们主要确保程序不会挂死崩溃,并打印当前表现。 + */ + printf(" [INFO] c_File_Delete while open returned: %d (Platform dependent behaviour)\n", del_err); + + // 无论刚才删除成功与否,为了测试框架的安全,我们都要显式地进行资源关闭和文件清理 + c_File_Close(&file); + remove(TEST_DEL_FILE); +} + +// 用例 1:验证文件流式复制 c_File_Copy +void test_file_copy_integrity() { + // 1. 准备源文件并写入特定文本 + c_File_t src_file; + c_File_Open(&src_file, FILE_SRC, "wb"); + const char* pattern = "Copy & Move Structural Verification Data."; + c_size_t written = 0; + c_File_Write(&src_file, (void*)pattern, strlen(pattern), &written); + c_File_Close(&src_file); + + // 2. 调用复制函数 + c_err_t err = c_File_Copy(FILE_SRC, FILE_COPY); + ASSERT_INT_EQ_MSG(C_ERR_OK, err, "c_File_Copy execution failed"); + + // 3. 断言验证:目标文件必须存在,且大小必须完全一致 + ASSERT_MSG(c_File_IsExist(FILE_COPY) == C_TRUE, "Copied target file does not exist"); + + c_File_t dest_file; + c_File_Open(&dest_file, FILE_COPY, "rb"); + long long copy_size = c_File_Size(&dest_file); + + char read_buf[128] = {0}; + c_size_t read_bytes = 0; + c_File_Read(&dest_file, read_buf, sizeof(read_buf) - 1, &read_bytes); + c_File_Close(&dest_file); + + ASSERT_INT_EQ_MSG((int)strlen(pattern), (int)copy_size, "Copied file size mismatches"); + ASSERT_INT_EQ_MSG(0, strcmp(pattern, read_buf), "Copied data content corruption detected"); +} + +// 用例 2:验证文件移动与重命名 c_File_Move +void test_file_move_behavior() { + // 1. 再次在旧路径上创建一个基准文件 + c_File_t src_file; + c_File_Open(&src_file, FILE_SRC, "wb"); + const char* payload = "MovePayload"; + c_size_t written = 0; + c_File_Write(&src_file, (void*)payload, strlen(payload), &written); + c_File_Close(&src_file); + + // 2. 调用移动函数 + c_err_t err = c_File_Move(FILE_SRC, FILE_MOVE); + ASSERT_INT_EQ_MSG(C_ERR_OK, err, "c_File_Move execution failed"); + + // 3. 断言验证:【关键点】旧文件必须在文件系统中消失,新路径下必须出现该文件 + ASSERT_MSG(c_File_IsExist(FILE_SRC) == C_FALSE, "Source file should be gone after move"); + ASSERT_MSG(c_File_IsExist(FILE_MOVE) == C_TRUE, "Moved destination file should exist"); + + // 4. 读取内容验证原子性与准确性 + c_File_t moved_file; + c_File_Open(&moved_file, FILE_MOVE, "rb"); + char read_buf[32] = {0}; + c_size_t read_bytes = 0; + c_File_Read(&moved_file, read_buf, sizeof(read_buf) - 1, &read_bytes); + c_File_Close(&moved_file); + + ASSERT_INT_EQ_MSG(0, strcmp(payload, read_buf), "Moved file content mismatches"); +} + +// 用例 3:验证对非正常状态路径(不存在的源文件)调用 Copy 的防御机制 +void test_file_copy_non_existent() { + c_err_t err = c_File_Copy("./test_sandbox/non_exist_source_xyz.dat", FILE_COPY); + // 应该优雅报错,不能返回 C_ERR_OK + ASSERT_MSG(err != C_ERR_OK, "Copying a non-existent file must return error status"); +} + +int main(int argc, char** argv){ + + TEST_START(Starting Unit Tests); + + RUN_TEST_FIXTURE(test_file_write_and_size, setup_file_env, teardown_file_env); + RUN_TEST_FIXTURE(test_file_read_and_seek, setup_file_env, teardown_file_env); + RUN_TEST_FIXTURE(test_file_read_line, setup_file_env, teardown_file_env); + RUN_TEST_FIXTURE(test_file_invalid_operations, setup_file_env, teardown_file_env); + RUN_TEST_FIXTURE(test_file_rmdirs_force_delete_nested, setup_deep_dir_env, teardown_deep_dir_env); + RUN_TEST_FIXTURE(test_file_delete_success, setup_delete_env, teardown_delete_env); + RUN_TEST_FIXTURE(test_file_delete_non_existent, setup_delete_env, teardown_delete_env); + RUN_TEST_FIXTURE(test_file_delete_while_open, setup_delete_env, teardown_delete_env); + RUN_TEST_FIXTURE(test_file_copy_integrity, setup_move_copy_env, teardown_move_copy_env); + RUN_TEST_FIXTURE(test_file_move_behavior, setup_move_copy_env, teardown_move_copy_env); + RUN_TEST_FIXTURE(test_file_copy_non_existent, setup_move_copy_env, teardown_move_copy_env); + + // 打印最终统计报告 + TEST_REPORT(); + + RETURN_TEST_STATUS; + + return 0; +} diff --git a/Foundation/c_float.c b/Foundation/c_float.c deleted file mode 100644 index e0b99ac..0000000 --- a/Foundation/c_float.c +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/Foundation/c_float.h b/Foundation/c_float.h deleted file mode 100644 index 360e7d6..0000000 --- a/Foundation/c_float.h +++ /dev/null @@ -1,164 +0,0 @@ -#ifndef INCLUDED_C_FLOAT_H -#define INCLUDED_C_FLOAT_H - -#ifndef INCLUDED_FLOAT_H -#define INCLUDED_FLOAT_H -#include -#endif /*INCLUDED_FLOAT_H*/ - -#ifndef INCLUDED_STDINT_H -#define INCLUDED_STDINT_H -#include -#endif /*INCLUDED_STDINT_H*/ - -#ifndef INCLUDED_MATH_H -#define INCLUDED_MATH_H -#include -#endif /*INCLUDED_MATH_H*/ - -#ifndef INCLUDED_C_COMPILER_H -#include -#endif /*INCLUDED_C_COMPILER_H*/ - - -/* ------------------------------------------------------------------------------------------------------------------ */ -/* */ - -typedef union { - float f; - uint32_t u; - struct { - uint32_t sign: 1; - uint32_t exponent: 8; - uint32_t mantissa: 23; - }IEEE754; -}c_Float_t; - -typedef union { - double d; - uint64_t u; - struct { - uint64_t sign: 1; - uint64_t exponent: 11; - uint64_t mantissa: 52; - }; -}c_Double_t; - - -/* ------------------------------------------------------------------------------------------------------------------ */ -/* */ - -C_STATIC_FORCE_INLINE -int c_float_is_eq(const float a, const float b) { - return fabsf(a - b) < FLT_EPSILON; -} - -C_STATIC_FORCE_INLINE -int c_float_is_gt(const float a, const float b) { - return (a - b) > FLT_EPSILON; -} - -C_STATIC_FORCE_INLINE -int c_float_is_ge(const float a, const float b) { - return (a - b) >= -FLT_EPSILON; -} - -C_STATIC_FORCE_INLINE -int c_float_cmp_safe(const float fa, const float fb) { - int nan_a = isnan(fa); - int nan_b = isnan(fb); - - if (C_UNLIKELY(nan_a || nan_b)) { - if (nan_a && nan_b) return 0; - if (nan_a) return 1; - return -1; - } - - if (fa < fb) return -1; - if (fa > fb) return 1; - return 0; -} - -/* ------------------------------------------------------------------------------------------------------------------ */ -/* double */ - -C_STATIC_FORCE_INLINE -int c_double_is_eq(const double a, const double b) { - return fabs(a - b) < DBL_EPSILON; -} - -C_STATIC_FORCE_INLINE -int c_double_is_gt(const double a, const double b) { - return (a - b) > DBL_EPSILON; -} - -C_STATIC_FORCE_INLINE -int c_double_is_ge(const double a, const double b) { - return (a - b) >= -DBL_EPSILON; -} - -C_STATIC_FORCE_INLINE -int c_double_cmp_safe(const double da, const double db) { - - int nan_a = isnan(da); - int nan_b = isnan(db); - - /* --- 1. 处理 NaN 的极端分支 --- */ - /* 采用 C_UNLIKELY 优化,因为大部分待排数据中 NaN 是极少数,提示 CPU 预读正常分支 */ - if (C_UNLIKELY(nan_a || nan_b)) { - if (nan_a && nan_b) return 0; /* 两个都是 NaN,视为相等 */ - if (nan_a) return 1; /* a 是 NaN,b 是正常数,视为 a > b(NaN排到最后) */ - return -1; /* a 是正常数,b 是 NaN,视为 a < b */ - } - - /* --- 2. 正常数值的三向比较分支 --- */ - if (da < db) return -1; - if (da > db) return 1; - return 0; -} - -/* ------------------------------------------------------------------------------------------------------------------ */ -/* */ - -C_STATIC_FORCE_INLINE -int c_float_ptr_cmp_safe(const void* a, const void* b) { - float fa = *(const float*)a; - float fb = *(const float*)b; - - int nan_a = isnan(fa); - int nan_b = isnan(fb); - - if (C_UNLIKELY(nan_a || nan_b)) { - if (nan_a && nan_b) return 0; - if (nan_a) return 1; - return -1; - } - - if (fa < fb) return -1; - if (fa > fb) return 1; - return 0; -} - -C_STATIC_FORCE_INLINE -int c_double_ptr_cmp_safe(const void* a, const void* b) { - double da = *(const double*)a; - double db = *(const double*)b; - - int nan_a = isnan(da); - int nan_b = isnan(db); - - /* --- 1. 处理 NaN 的极端分支 --- */ - /* 采用 C_UNLIKELY 优化,因为大部分待排数据中 NaN 是极少数,提示 CPU 预读正常分支 */ - if (C_UNLIKELY(nan_a || nan_b)) { - if (nan_a && nan_b) return 0; /* 两个都是 NaN,视为相等 */ - if (nan_a) return 1; /* a 是 NaN,b 是正常数,视为 a > b(NaN排到最后) */ - return -1; /* a 是正常数,b 是 NaN,视为 a < b */ - } - - /* --- 2. 正常数值的三向比较分支 --- */ - if (da < db) return -1; - if (da > db) return 1; - return 0; -} - -#endif /*INCLUDED_C_FLOAT_H*/ diff --git a/Foundation/c_float.t.c b/Foundation/c_float.t.c index c92abfc..d77c90d 100644 --- a/Foundation/c_float.t.c +++ b/Foundation/c_float.t.c @@ -1,23 +1,381 @@ #include "c_float.h" #include #include +#include -static void c_Double_print(c_Double_t value) { - uint64_t sign = value.sign; - uint64_t exponent = value.exponent; - uint64_t fraction = value.mantissa; - printf("数值: %.15f\n", value.d); - printf("整体十六进制: 0x%016llX\n", (unsigned long long)value.u); - printf("符号位 (Sign): %llu (%s)\n", (unsigned long long)sign, sign ? "负" : "正"); - printf("指数位 (Exponent 原始值): %llu (实际 2 阶: %d)\n", - (unsigned long long)exponent, (int)exponent - 1023); - printf("尾数位 (Fraction 16进制): 0x%013llX\n", (unsigned long long)fraction); - printf("---------------------------------------\n"); +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * @brief 辅助工具:将 C 语言原生双精度 double 转换为 64 位原始位码 (uint64_t) + */ +static uint64_t to_raw64(double d) { + c_Double_t u; + u.d = d; + return u.raw; +} + +/** + * @brief 辅助工具:将 64 位原始位码 (uint64_t) 还原为 C 语言原生双精度 double + */ +static double to_double(uint64_t raw) { + c_Double_t u; + u.raw = raw; + return u.d; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// 用例 1:测试 Float 分类状态识别函数(IsZero, IsInf, IsNAN 等基本位打包判定) +void test_float_classification_and_pack() { + c_Float_t val; + + // 1. 测试 Pack 是否能准确组装成标准浮点位 + // 符号=0, 指数=127(偏移后为0), 尾数=0 -> 应该代表 1.0f + uint32_t packed = c_Float_Pack(0, 127, 0); + val.raw = packed; + ASSERT_MSG(val.f == 1.0f, "c_Float_Pack failed to assemble 1.0f"); + + // 2. 测试 正负零 (0.0f 和 -0.0f) + val.f = 0.0f; + ASSERT_MSG(c_Float_IsZero(val.raw) == true, "0.0f should be identified as zero"); + val.f = -0.0f; + ASSERT_MSG(c_Float_IsZero(val.raw) == true, "-0.0f should be identified as zero"); + + // 3. 测试 正负无穷大 (Inf) + val.raw = C_FLOAT_POS_INF; + ASSERT_MSG(c_Float_IsInf(val.raw) == true, "POS_INF must be Inf"); + ASSERT_MSG(c_Float_IsPosInf(val.raw) == true, "POS_INF must be PosInf"); + + val.raw = C_FLOAT_NEG_INF; + ASSERT_MSG(c_Float_IsInf(val.raw) == true, "NEG_INF must be Inf"); + ASSERT_MSG(c_Float_IsNegInf(val.raw) == true, "NEG_INF must be NegInf"); + + // 4. 测试 NaN(指数全为1,尾数不为0) + val.raw = C_FLOAT_EXP_MASK | 0x00000001U; // 制造一个 NaN + ASSERT_MSG(c_Float_IsNAN(val.raw) == 1, "Should be recognized as NaN"); + + val.raw = C_FLOAT_POS_INF; // 无穷大的尾数是0,不属于 NaN + ASSERT_MSG(c_Float_IsNAN(val.raw) == 0, "Infinity is NOT NaN"); +} + + +// 用例 2:测试 Float 基础数学四则运算(数值运算准确性) +void test_float_math_operations() { + c_Float_t res, out_add, out_sub, out_mul, out_div; + c_Float_t a, b; + + a.f = 5.5f; + b.f = 2.25f; + + // 1. 加法测试 5.5 + 2.25 = 7.75 + out_add.raw = c_Float_Add(a.raw, b.raw); + res.f = 7.75f; + ASSERT_INT_EQ_MSG(res.raw, out_add.raw, "Soft-Float Add failed (5.5 + 2.25)"); + + // 2. 减法测试 5.5 - 2.25 = 3.25 + out_sub.raw = c_Float_Sub(a.raw, b.raw); + res.f = 3.25f; + ASSERT_INT_EQ_MSG(res.raw, out_sub.raw, "Soft-Float Sub failed (5.5 - 2.25)"); + + // 3. 乘法测试 5.5 * 2.25 = 12.375 + out_mul.raw = c_Float_Mul(a.raw, b.raw); + res.f = 12.375f; + ASSERT_INT_EQ_MSG(res.raw, out_mul.raw, "Soft-Float Mul failed (5.5 * 2.25)"); + + // 4. 除法测试 5.5 / 2.25 = 2.444444... (通过联合体转换进行交叉对比) + out_div.raw = c_Float_Div(a.raw, b.raw); + float expected_div = 5.5f / 2.25f; + uint32_t expected_raw = ((c_Float_t){.f = expected_div}).raw; + + // 经过软除法精度升级后,这里预期可以做到每一个二进制位都完全绝对对齐(0 ULP 误差) + ASSERT_INT_EQ_MSG((int)expected_raw, (int)out_div.raw, "Soft-Float Div Round-to-Nearest-Even failed to align with hardware bits"); +} + +void test_float_div_complete() { + c_Float_t a, b, out; + + // ------------------------------------------------------------- + // 测试 1:常规数值除法 (15.5 / 2.0 = 7.75) + // ------------------------------------------------------------- + a.f = 15.5f; + b.f = 2.0f; + out.raw = c_Float_Div(a.raw, b.raw); + ASSERT_MSG(fabsf(7.75f - out.f) b 返回正数 + a.d = 100.5; + b.d = 200.5; + ASSERT_MSG(c_Double_Cmp(a.raw, b.raw) < 0, "100.5 should be less than 200.5"); + ASSERT_MSG(c_Double_Cmp(b.raw, a.raw) > 0, "200.5 should be greater than 100.5"); + ASSERT_MSG(c_Double_Cmp(a.raw, a.raw) == 0, "100.5 should be equal to itself"); + + // 3. 原生内联函数的包装测试 (c_double_cmp) + ASSERT_MSG(c_double_cmp(10.0, 20.0) < 0, "Inline double compare wrapper failed"); +} + +void test_float_isnan_pure_bits() { + // 场景 1:制造标准常规数值(如 1.0f)—— 预期:非 NaN (0) + // 符号=0, 指数=127, 尾数=0 + uint32_t normal_num = c_Float_Pack(0, 127, 0); + ASSERT_INT_EQ_MSG(0, c_Float_IsNAN(normal_num), "Normal number 1.0f must NOT be NaN"); + + // 场景 2:正无穷大 (C_FLOAT_POS_INF) —— 预期:非 NaN (0) + // 它的指数全为 1,但尾数严格为 0 + ASSERT_INT_EQ_MSG(0, c_Float_IsNAN(C_FLOAT_POS_INF), "Positive Infinity must NOT be NaN"); + ASSERT_INT_EQ_MSG(0, c_Float_IsNAN(C_FLOAT_NEG_INF), "Negative Infinity must NOT be NaN"); + + // 场景 3:制造一个最微小的 Quiet NaN (QNaN) —— 预期:是 NaN (1) + // 指数全为 1 (0xFF),尾数最高位为 1 (0x400000) + uint32_t qnan_bits = c_Float_Pack(0, 0xFF, 0x400000U); + ASSERT_MSG(c_Float_IsNAN(qnan_bits), "Quiet NaN bits must be recognized as NaN"); + + // 场景 4:制造一个最微小的 Signaling NaN (SNaN) —— 预期:是 NaN (1) + // 指数全为 1 (0xFF),尾数最低位为 1 (0x000001) + uint32_t snan_bits = c_Float_Pack(0, 0xFF, 0x000001U); + ASSERT_MSG(c_Float_IsNAN(snan_bits), "Signaling NaN bits must be recognized as NaN"); + + // 场景 5:测试带有符号位的 NaN (负 NaN) —— 预期:是 NaN (1) + // IEEE 754 规范中,NaN 的符号位不影响它是 NaN 的事实 + uint32_t neg_nan_bits = c_Float_Pack(1, 0xFF, 0x7FFFFFU); + ASSERT_MSG(c_Float_IsNAN(neg_nan_bits), "Negative NaN bits must also be recognized as NaN"); +} + +void test_float_inf_plus_neginf() { + // 1. 获取正无穷大与负无穷大的位表示 + uint32_t pos_inf = C_FLOAT_POS_INF; // 0x7F800000 + uint32_t neg_inf = C_FLOAT_NEG_INF; // 0xFF800000 + + // 2. 执行待测的软浮点加法:(+Inf) + (-Inf) + uint32_t result_raw = c_Float_Add(pos_inf, neg_inf); + + // 3. 核心断言:结果必须是 NaN + // 使用 c_Float_IsNAN 验证其特征是否为:指数全 1,尾数非 0 + ASSERT_MSG(c_Float_IsNAN(result_raw), "IEEE 754 standard: (+Inf) + (-Inf) must produce NaN"); + + // 4. 反向验证:它绝对不能再被误判为任何形式的无穷大或零 + ASSERT_MSG(!c_Float_IsInf(result_raw), "Result NaN must not be classified as Infinity"); + ASSERT_MSG(!c_Float_IsZero(result_raw), "Result NaN must not be classified as Zero"); + + // 5. 跨双精度对称验证:(+Inf) + (-Inf) 同样适用于 64 位双精度 + uint64_t d_pos_inf = C_DOUBLE_POS_INF; + uint64_t d_neg_inf = C_DOUBLE_NEG_INF; + uint64_t d_result_raw = c_Double_Add(d_pos_inf, d_neg_inf); + + ASSERT_MSG(c_Double_IsNAN(d_result_raw), "IEEE 754 standard: Double (+Inf) + (-Inf) must produce NaN"); +} + +void test_double_mul_and_div_complete() { + c_Double_t da, db, dout; + + // ----------------------------------------------------------------- + // 测试 1:验证跨 64 位大整数相乘的精确偶数舍入 + // ----------------------------------------------------------------- + da.d = 1.23456789012345; + db.d = -9.87654321098765; + dout.raw = c_Double_Mul(da.raw, db.raw); + + double expected_mul = 1.23456789012345 * -9.87654321098765; + c_Double_t native_mul = {.d = expected_mul}; + + // 【终极断言升级】:杜绝 int 转换截断,对双精度 64 位全局原始编码进行无差错硬核对齐 + ASSERT_MSG(native_mul.raw == dout.raw, + "c_Double_Mul 64-bit full-width precision failed to align with hardware FPU"); + + + // ----------------------------------------------------------------- + // 测试 2:验证双精度无限循环小数除法的长窗口状态机精度 + // ----------------------------------------------------------------- + da.d = 1.0; + db.d = 3.0; // 1.0 / 3.0 + dout.raw = c_Double_Div(da.raw, db.raw); + + double expected_div = 1.0 / 3.0; + c_Double_t native_div = {.d = expected_div}; + ASSERT_INT_EQ_MSG((int)native_div.parts.fraction, (int)dout.parts.fraction, "c_Double_Div bit-level precision error at 1.0/3.0"); + + // ----------------------------------------------------------------- + // 测试 3:验证双精度 0.0 与 无穷大的复合熔断边界 + // ----------------------------------------------------------------- + // 边界 A:0.0 * Inf -> 必须返回 NaN + uint64_t zero_mul_inf = c_Double_Mul(to_raw64(0.0), C_DOUBLE_POS_INF); + ASSERT_MSG(c_Double_IsNAN(zero_mul_inf), "Double 0.0 * +Inf must result in NaN"); + + // 边界 B:0.0 / 0.0 -> 必须返回 NaN + uint64_t zero_div_zero = c_Double_Div(to_raw64(0.0), to_raw64(-0.0)); + ASSERT_MSG(c_Double_IsNAN(zero_div_zero), "Double 0.0 / -0.0 must result in NaN"); + + // 边界 C:常规有限双精度数除以 0.0 -> 产生无穷大 + da.d = -55.5; + uint64_t div_zero = c_Double_Div(to_raw64(da.d), to_raw64(0.0)); + ASSERT_MSG(c_Double_IsNegInf(div_zero), "Negative double divided by 0.0 must result in -Inf"); +} + +void test_double_add_complete() { + c_Double_t da, db, dout; + + // ----------------------------------------------------------------- + // 测试 1:常规双精度数值加减(50.25 + 25.5 = 75.75) + // ----------------------------------------------------------------- + da.d = 50.25; + db.d = 25.5; + dout.raw = c_Double_Add(da.raw, db.raw); + + // 使用真值进行精度验证 + ASSERT_MSG(fabs(dout.d - 75.75) < 1e-9, "Regular Double Add numerical verification failed"); + + // ----------------------------------------------------------------- + // 测试 2:验证正负无穷大冲抵边界熔断(+Inf + -Inf = NaN) + // ----------------------------------------------------------------- + uint64_t res_nan = c_Double_Add(C_DOUBLE_POS_INF, C_DOUBLE_NEG_INF); + ASSERT_MSG(c_Double_IsNAN(res_nan), "IEEE 754: Double (+Inf) + (-Inf) must produce NaN"); + + // ----------------------------------------------------------------- + // 测试 3:验证异号数值完全抵消(5.5 + -5.5 = +0.0) + // ----------------------------------------------------------------- + da.d = 5.5; + db.d = -5.5; + dout.raw = c_Double_Add(da.raw, db.raw); + // 验证返回的是否是干净的、符号位为0的正零 (0x0000000000000000) + ASSERT_INT_EQ_MSG(0, (int)dout.raw, "Opposite numbers sum must strictly result in +0.0 bits"); + + // ----------------------------------------------------------------- + // 测试 4:大跨度对阶精度测试(1.0 + 1e-17 触发全移出边界) + // ----------------------------------------------------------------- + da.d = 1.0; + db.d = 1e-17; // 这个值太小了,在双精度 53 位尾数对阶时会被完全移出去,但会触发 sticky 位置 1 + dout.raw = c_Double_Add(da.raw, db.raw); + // 按照偶数舍入规则,sticky=1,GRS=001 <= 4,将被舍去,结果应该严格保持为 1.0 + ASSERT_MSG(dout.d == 1.0, "Large exponent gap shift processing failed"); + + // ----------------------------------------------------------------- + // 测试 5:向最近偶数舍入的 0 ULP 硬件级绝对对齐校验 + // ----------------------------------------------------------------- + da.d = 1.23456789012345; + db.d = 9.87654321098765; + dout.raw = c_Double_Add(da.raw, db.raw); + + double native_expected = 1.23456789012345 + 9.87654321098765; + c_Double_t native_val = {.d = native_expected}; + + // 通过对比尾数域,验证是否做到了 100% 硬件位对齐 + ASSERT_INT_EQ_MSG((int)native_val.parts.fraction, (int)dout.parts.fraction, + "Soft-Double Add failed to align with hardware FPU bits"); } int main(int argc, char** argv){ - c_Double_t d={.d=1.0f}; - c_Double_print(d); + TEST_START(Starting Unit Tests); - return 0; + // 运行需要内存环境的用例 + RUN_TEST(test_float_classification_and_pack); + RUN_TEST(test_float_math_operations); + RUN_TEST(test_float_edge_cases); + RUN_TEST(test_double_operations_and_compare); + RUN_TEST(test_float_isnan_pure_bits); + RUN_TEST(test_float_inf_plus_neginf); + RUN_TEST(test_float_div_complete); + RUN_TEST(test_double_mul_and_div_complete); + RUN_TEST(test_double_add_complete); + + + // 打印最终统计报告 + TEST_REPORT(); + + RETURN_TEST_STATUS; }