Code - LoneWolfL/LeetCode GitHub Wiki

链表结构的相关函数声明

// 链表节点的结构体
typedef struct ListNode {
    const void *value;      // 节点值
    int refcount;
    struct ListNode *next;  // 指向下一节点的链表指针
    struct ListNode *prev;  // 指向上一节点的链表指针
} ListNode;

// 链表的初始化函数
static ListNode* list_initialize(ListNode * const node);
// 将一个链表节点添加到指定的链表中
static ListNode* list_add(ListNode * const head, ListNode *new_node);
// 分配一个链表节点结构并将它添加到指定的链表中
static ListNode* list_add_value(ListNode * const head, const void *value, const int count);
// 从链表中移除指定的节点
static ListNode* list_remove(ListNode * const node, const CleanupListValue cleanup_value, void * const cleanup_value_data);
// 从链表中移除指定的节点并释放该节点
static void list_remove_free(ListNode * const node, const CleanupListValue cleanup_value, void * const cleanup_value_data);
// 判断链表是否为空
static int list_empty(const ListNode * const head);
// 使用传入的函数equal_func()在链表中查找指定值
static int list_find(ListNode * const head, const void *value, const EqualityFunction equal_func, ListNode **output);
// 返回链表中的第一个值
static int list_first(ListNode * const head, ListNode **output);
// 释放链表中除了head节点外所有节点
static ListNode* list_free(ListNode * const head, const CleanupListValue cleanup_value, void * const cleanup_value_data);

Map结构的相关函数声明

// 为指定符号添加对应值
static void add_symbol_value(ListNode * const symbol_map_head, const char * const symbol_names[], const size_t number_of_symbol_names, const void* value, const int count);
// 获取指定符号对应的值
static int get_symbol_value(ListNode * const symbol_map_head, const char * const symbol_names[], const size_t number_of_symbol_names, void **output);
static void free_value(const void *value, void *cleanup_value_data);
// 移除一个值
static void free_symbol_map_value(const void *value, void *cleanup_value_data);
// 移除持续返回的值
static void remove_always_return_values(ListNode * const map_head, const size_t number_of_symbol_names);

测试环境创建函数setup_test()的代码实现

void setup_test(const char *test_name) {
    // 初始化存储预期Mock值链表
    list_initialize(&global_function_result_map_head);
    initialize_source_location(&global_last_mock_value_location);
    // 初始化存储预期参数值链表
    list_initialize(&global_function_parameter_map_head);
    initialize_source_location(&global_last_parameter_location);
}

测试环境清除函数teardown_test()的代码实现

void teardown_test(const char *test_name) {
    // 释放存储预期Mock值链表内所有节点
    list_free(&global_function_result_map_head, free_symbol_map_value,(void*)0);
    initialize_source_location(&global_last_mock_value_location);
    // 释放存储预期参数值链表内所有节点
    list_free(&global_function_parameter_map_head, free_symbol_map_value,(void*)1);
    initialize_source_location(&global_last_parameter_location);
}

Mock值和参数值使用检查函数

void fail_if_leftover_values(const char *test_name) {
    int error_occurred = 0;
    // 移除持续返回的Mock值
    remove_always_return_values(&global_function_result_map_head, 1);
    // 检查是否有剩余Mock函数值
    if (check_for_leftover_values(&global_function_result_map_head, "%s() has remaining non-returned values.\n", 1)) {
        error_occurred = 1;
    }
    // 移除持续检查参数事件
    remove_always_return_values(&global_function_parameter_map_head, 2);
    // 检查是否有未使用参数检查事件
    if (check_for_leftover_values(&global_function_parameter_map_head, "%s parameter still has values that haven't been checked.\n", 2)) {
        error_occurred = 1;
    }
    if (error_occurred) {
        exit_test(1);
    }
}

内存泄漏检查函数

static void fail_if_blocks_allocated(const ListNode * const check_point, const char * const test_name) {
    const int allocated_blocks = display_allocated_blocks(check_point); // 检查存储内存块链表内节点数量
    if (allocated_blocks) {
        free_allocated_blocks(check_point);
        print_error("ERROR: %s leaked %d block(s)\n", test_name, allocated_blocks);
        exit_test(1);
    }
}

单元测试执行函数run_test()的关键代码

int run_test(
    const char * const function_name,           // 测试用例名
    const UnitTestFunction Function,            // 测试用例函数
    void ** const state,                        
    const UnitTestFunctionType function_type,
    const void* const heap_check_point
) {
    const ListNode * const check_point = heap_check_point ? heap_check_point : check_point_allocated_blocks();
    void *current_state = NULL;
    int rc = 1;                                                 // 测试结果标记
    ...
    setup_test(function_name);                                  // 创建测试环境
    global_running_test = 1;                                    // 测试开始
    if (setjmp(global_run_test_env) == 0) {                     // 设置跳转点
        Function(state ? state : &current_state);               // 运行测试用例,进行测试
        fail_if_leftover_values(function_name);                 // Mock值和参数值使用检查
        fail_if_blocks_allocated(check_point, function_name);   // 内存泄漏检查
        global_running_test = 0;                                // 测试结束
        print_message("%s: Test completed successfully.\n", function_name);
        rc = 0;                                                 // 测试成功
    }
    else {
        global_running_test = 0;                                // 测试结束
        print_message("%s: Test failed.\n", function_name);
    }
    teardown_test(function_name);                               // 清除测试环境
    ...
    return rc;
}

异常断言宏定义

#if UNIT_TESTING
extern void mock_assert(const int result, const char* const expression, const char * const file, const int line);
#undef assert
#define assert(expression) mock_assert((int)(expression), #expression, __FILE__, __LINE__);
#endif // UNIT_TESTING

mock_assert()函数的代码实现

void mock_assert(const int result, const char* const expression, const char* const file, const int line) {
    if (!result) {
        // 判断是否预期触发
        if (global_expecting_assert) {
            longjmp(global_expect_assert_env, (int)expression);
        } else {
            print_error("ASSERT: %s\n", expression);
            _fail(file, line);
        }
    }
}

异常断言函数集的函数声明

// 逻辑true断言
void assert_true(const int result, const char* const expression, const char * const file, const int line);
// int值相等断言
void assert_int_equal(const int a, const int b, const char * const file, const int line);
// int值不等断言
void assert_int_not_equal(const int a, const int b, const char * const file, const int line);
// string值相等断言
void assert_string_equal(const char * const a, const char * const b, const char * const file, const int line);
// string值不等断言
void assert_string_not_equal(const char * const a, const char * const b, const char *file, const int line);
// 内存块值相等断言
void assert_memory_equal(const void * const a, const void * const b, const size_t size, const char* const file, const int line);
// 内存块值不等断言
void assert_memory_not_equal(const void * const a, const void * const b, const size_t size, const char* const file, const int line);
// int值在值区间内
void assert_in_range(const int value, const int minimum, const int maximum, const char* const file, const int line);
// int值不在值区间内
void assert_not_in_range(const int value, const int minimum, const int maximum, const char* const file, const int line);
// int值在值集合内
void assert_in_set(const void * const value, const void *values[], const size_t number_of_values, const char* const file, const int line);
// int值不在值集合内
void assert_not_in_set(const void * const value, const void *values[], const size_t number_of_values, const char* const file, const int line);

内存操作监测宏定义

#if UNIT_TESTING
extern void* _test_malloc(const size_t size, const char* file, const int line);
extern void* _test_calloc(const size_t number_of_elements, const size_t size,  const char* file, const int line);
extern void _test_free(void* const ptr, const char* file, const int line);
#define malloc(size) _test_malloc(size, __FILE__, __LINE__)
#define calloc(num, size) _test_calloc(num, size, __FILE__, __LINE__)
#define free(ptr) _test_free(ptr, __FILE__, __LINE__)
#endif // UNIT_TESTING

test_malloc()函数的代码实现

#undef malloc
void* test_malloc(const size_t size, const char* file, const int line) {
    char* ptr;
    MallocBlockInfo *block_info;
    ListNode * const block_list = get_allocated_blocks_list();
    const size_t allocate_size = size + (MALLOC_GUARD_SIZE * 2) + sizeof(*block_info) + MALLOC_ALIGNMENT;
    char* const block = (char*)malloc(allocate_size);
    assert_true(block);
    // 计算真正需要的内存块的起始位置
    ptr = (char*)(((size_t)block + MALLOC_GUARD_SIZE + sizeof(*block_info) + MALLOC_ALIGNMENT) & ~(MALLOC_ALIGNMENT - 1));
    // 在内存块前后填充标志字符,初始化此内存块。
    memset(ptr - MALLOC_GUARD_SIZE, MALLOC_GUARD_PATTERN, MALLOC_GUARD_SIZE);
    memset(ptr + size, MALLOC_GUARD_PATTERN, MALLOC_GUARD_SIZE);
    memset(ptr, MALLOC_ALLOC_PATTERN, size);
    // 初始化MallocBlockInfo结构
    block_info = (MallocBlockInfo*)(ptr - (MALLOC_GUARD_SIZE + sizeof(*block_info)));
    set_source_location(&block_info->location, file, line);
    block_info->allocated_size = allocate_size;
    block_info->size = size;
    block_info->block = block;
    block_info->node.value = block_info;
    // 将内存块插入存储内存块的链表
    list_add(block_list, &block_info->node);
    return ptr;
}
#define malloc test_malloc

test_free()函数的代码实现

#undef free
void _test_free(void* const ptr, const char* file, const int line) {
    unsigned int i;
    char *block = (char*)ptr;
    MallocBlockInfo *block_info;
    _assert_true((int)ptr, "ptr", file, line);
    block_info = (MallocBlockInfo*)(block - (MALLOC_GUARD_SIZE + sizeof(*block_info)));
    // 内存越界检查
    {
        char *guards[2] = {block - MALLOC_GUARD_SIZE, block + block_info->size};
        for (i = 0; i < ARRAY_LENGTH(guards); i++) {
            unsigned int j;
            char * const guard = guards[i];
            for (j = 0; j < MALLOC_GUARD_SIZE; j++) {
                const char diff = guard[j] - MALLOC_GUARD_PATTERN;
                if (diff) {
                    print_error(
                        "Guard block of 0x%08x size=%d allocated by " SOURCE_LOCATION_FORMAT " at 0x%08x is corrupt\n",
                        (size_t)ptr, block_info->size,
                        block_info->location.file, block_info->location.line,
                        (size_t)&guard[j]);
                    _fail(file, line);
                }
            }
        }
    }
    // 从存储内存块的链表中移除
    list_remove(&block_info->node, NULL, NULL);
    block = block_info->block;
    memset(block, MALLOC_FREE_PATTERN, block_info->allocated_size);
    // 释放该内存块的空间
    free(block);
}
#define free test_free

will_return()函数的代码实现

void will_return(const char * const function_name, const char * const file, const int line, const void* const value, const int count) {
    SymbolValue * const return_value = malloc(sizeof(*return_value));
    assert_true(count > 0);
    return_value->value = value;
    set_source_location(&return_value->location, file, line);
    add_symbol_value(&global_function_result_map_head, &function_name, 1, return_value, count);
}

mock()函数的代码实现

void* mock(const char * const function, const char* const file, const int line) {
    void *result;
    // 调用get_symbol_value()获取对应的mock值
    const int rc = get_symbol_value(&global_function_result_map_head, &function, 1, &result);
    if (rc) {
        // 成功获取mock值
        SymbolValue * const symbol = result;
        void * const value = (void*)symbol->value;
        global_last_mock_value_location = symbol->location;
        if (rc == 1) {
            free(symbol);
        }
        return value;
    } else {
        // 链表中没有对应的mock值
        print_error("ERROR: " SOURCE_LOCATION_FORMAT " - Could not get value to mock function %s\n", file, line, function);
        if (source_location_is_set(&global_last_mock_value_location)) {
            print_error("Previously returned mock value was declared at " SOURCE_LOCATION_FORMAT "\n",
                        global_last_mock_value_location.file,
                        global_last_mock_value_location.line);
        } else {
            print_error("There were no previously returned mock values for this test.\n");
        }
        exit_test(1);
    }
    return NULL;
}

Mock Object的简单示例

int func() {
    return (int)mock();
}

void test(void **state) {
    will_return(func, 0);
}

expect_*()函数集内函数的声明

void expect_in_set(const char* const function, const char* const parameter, const char* const file, const int line, const void *values[], const size_t number_of_values, const int count);
void expect_not_in_set(const char* const function, const char* const parameter, const char* const file, const int line, const void *values[], const size_t number_of_values, const int count);

void expect_in_range(const char* const function, const char* const parameter, const char* const file, const int line, const int minimum, const int maximum, const int count);
void expect_not_in_range(const char* const function, const char* const parameter, const char* const file, const int line, const int minimum, const int maximum, const int count);
void expect_value(const char* const function, const char* const parameter, const char* const file, const int line, const void* const value, const int count);
void expect_not_value(const char* const function, const char* const parameter, const char* const file, const int line, const void* const value, const int count);
void expect_string(const char* const function, const char* const parameter, const char* const file, const int line, const char* string, const int count);
void expect_not_string(const char* const function, const char* const parameter, const char* const file, const int line, const char* string, const int count);
void expect_memory(const char* const function, const char* const parameter, const char* const file, const int line, const void* const memory, const size_t size, const int count);
void expect_not_memory(const char* const function, const char* const parameter, const char* const file, const int line, const void* const memory, const size_t size, const int count);
void expect_any(const char* const function, const char* const parameter, const char* const file, const int line, const int count);

check_expected()函数的代码实现

void check_expected(const char * const function_name, const char * const parameter_name, const char* file, const int line, const void* value) {
    void *result;// 存储参数检查事件
    const char* symbols[] = {function_name, parameter_name};
    // 获取设置的参数值,及参数检查事件
    const int rc = get_symbol_value(&global_function_parameter_map_head, symbols, 2, &result);
    if (!rc)
    {
        // 获取设置的参数值失败
        print_error("ERROR: " SOURCE_LOCATION_FORMAT " - Could not get value to check parameter %s of function %s\n", file, line,
                    parameter_name, function_name);
        if (source_location_is_set(&global_last_parameter_location)) {
            print_error("Previously declared parameter value was declared at " SOURCE_LOCATION_FORMAT "\n",
                        global_last_parameter_location.file,
                        global_last_parameter_location.line);
        } else {
            print_error("There were no previously declared parameter values for this test.\n");
        }
        exit_test(1);
    }
    // 获得设置的参数值成功
    CheckParameterEvent * const check = (CheckParameterEvent*)result;
    int check_succeeded;
    global_last_parameter_location = check->location;
    check_succeeded = check->check_value(value, check->check_value_data);
    if (rc == 1) {
        free(check);
    }
    if (!check_succeeded) {
        print_error("ERROR: Check of parameter %s, function %s failed\n"
                    "Expected parameter declared at "SOURCE_LOCATION_FORMAT "\n",
                    parameter_name, function_name,
                    global_last_parameter_location.file,
                    global_last_parameter_location.line);
        _fail(file, line);
    }
}

参数检查的简单示例

void func(type T) {
    check_expected(T)
    return ;
}

void test(void **state) {
    expect_value(func, T, 0);
}

数据计算函数集的代码实现

#include <assert.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if UNIT_TESTING
extern void mock_assert(const int result, const char* const expression, const char * const file, const int line);
#undef assert
#define assert(expression) mock_assert((int)(expression), #expression, __FILE__, __LINE__);

extern void* _test_malloc(const size_t size, const char* file, const int line);
extern void* _test_calloc(const size_t number_of_elements, const size_t size,  const char* file, const int line);
extern void _test_free(void* const ptr, const char* file, const int line);
#define malloc(size) _test_malloc(size, __FILE__, __LINE__)
#define calloc(num, size) _test_calloc(num, size, __FILE__, __LINE__)
#define free(ptr) _test_free(ptr, __FILE__, __LINE__)
#endif

int add(int a, int b);
int subtract(int a, int b);
extern int multiply(int a, int b);
int divide(int a, int b);
int pow2(int a);

int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    int * ret = malloc(sizeof(int));
    *ret = a - b;
    return *ret;
}

int divide(int a, int b) {
    assert(b);
    return a / b;
}

int pow2(int a) {
    return multiply(a, a);
}

数据计算函数集的测试代码

#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include "cmockery.h"
#include <stdio.h>

extern int add(int a, int b);
extern int subtract(int a, int b);
int multiply(int a, int b);
extern int divide(int a, int b);
extern int pow2(int a);

int multiply(int a, int b) {
    check_expected(a);
    check_expected(b);
    return (int)mock();
}

void test_add(void **state) {
    assert_int_equal(add(5, 5), 10);
    assert_int_equal(add(5, -5), 0);
}
void test_subtract(void **state) {
    assert_int_equal(subtract(3, 3), 0);
}
void test_divide(void **state) {
    assert_int_equal(divide(10, 2), 5);
    assert_int_equal(divide(2, 10), 0);
}
void test_divide_by_zero(void **state) {
    expect_assert_failure(divide(10, 0));
}
void test_pow2(void **state) {
    will_return(multiply, 9);
    expect_value(multiply, a, 3);
    expect_value(multiply, b, 3);
    assert_int_equal(pow2(3), 9);
}

int main(int argc, char* argv[]) {
    UnitTest tests[] = {
        test_case(test_add),
        test_case(test_subtract),
        test_case(test_divide),
        test_case(test_divide_by_zero),
        test_case(test_pow2),
    };
    return test_runner(tests);
}
⚠️ **GitHub.com Fallback** ⚠️