测试用例
This commit is contained in:
+155
-122
@@ -2,144 +2,177 @@
|
||||
#define INCLUDED_C_TEST_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#include <time.h>
|
||||
#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*/
|
||||
|
||||
+47
-24
@@ -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;
|
||||
}
|
||||
|
||||
+8
-8
@@ -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
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
Reference in New Issue
Block a user