c语言实现一个单元测试框架(Unit Test Framework)代码
2012-03-23 22:56
585 查看
csdn lidp 转载注明出处
此单元测试框架为我在google code上的开源项目spider-tool的一部分,
关于spider-tool,欢迎访问google code.
https://spider-tool.googlecode.com
test_engine.h
test_engine.c
测试 代码:
此单元测试框架为我在google code上的开源项目spider-tool的一部分,
关于spider-tool,欢迎访问google code.
https://spider-tool.googlecode.com
test_engine.h
/* * Spider -- An open source C language toolkit. * * Copyright (C) 2011 , Inc. * * lidp <openser@yeah.net> * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. */ /*! * \brief Unit Test Framework * */ #ifndef _TEST_ENGINE_H #define _TEST_ENGINE_H #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif #define SPD_TEST_FRAMEWORK #ifdef SPD_TEST_FRAMEWORK #define SPD_TEST_INIT(name) static enum spd_test_result name(struct spd_test_record *record, enum spd_test_cmd type, struct spd_test *test) #define SPD_TEST_REGISTER(name) spd_test_register(name) #define SPD_TEST_UNREGISTER(name) spd_test_unregister(name) #define SPD_TEST_REPORT(name, category,file) spd_test_report(name, category,file) #define SPD_TEST_RUN(name, category) spd_test_run(name, category) #else #define SPD_TEST_INIT(name) #define SPD_TEST_REGISTER(name) #define SPD_TEST_UNREGISTER(name) #define SPD_TEST_REPORT(name, category,file) #define SPD_TEST_RUN(name, category) #endif enum spd_test_result { TEST_RESULT_NOT_RUN, TEST_RESULT_PASS, TEST_RESULT_FAILED, }; enum spd_test_cmd { SPD_TEST_CMD_INIT, SPD_TEST_CMD_RUN, }; struct spd_test_record { const char *name; /* unique name of this test */ const char *category; /* test in this category can be run */ const char *description; /* a description of test */ }; struct spd_test; typedef enum spd_test_result (spd_test_cb_t)(struct spd_test_record *record, enum spd_test_cmd type, struct spd_test* test); /* register a test record */ int spd_test_register(spd_test_cb_t *cb); /* unresigster a test record */ int spd_test_unregister(spd_test_cb_t *cb); /* * run test framework in three mode : * name : run given name test case, may be NULL * category : run a class of test of given category , may be NULL * if both name and category is NULL , will run all test case. * */ int spd_test_run(const char *name, const char *category); /*! *\brief report test result in three mode ,by name, by category or all. may write result to textfile. */ int spd_test_report(const char *name, const char *category, const char *textfile); int __spd_test_update_state(const char *file, const char *func, int line , struct spd_test *test, const char *fmt, ...); #define spd_test_update_state(test, fmt,...) __spd_test_update_state(__FILE__, __PRETTY_FUNCTION__, __LINE__, (test), (fmt), ## __VA_ARGS__) #if defined(__cplusplus) || defined(c_plusplus) } #endif #endif
test_engine.c
/* * Spider -- An open source C language toolkit. * * Copyright (C) 2011 , Inc. * * lidp <openser@yeah.net> * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. */ #include "test_engine.h" #include "logger.h" #include "str.h" #include "linkedlist.h" #include "times.h" /* stand for a single test record */ struct spd_test { struct spd_test_record info; /* store test info */ enum spd_test_result result; /* test result */ struct spd_str *status_str; /* test status in string */ unsigned int howlong; /* time this test cost */ spd_test_cb_t *callback; /* real func perform test work */ SPD_LIST_ENTRY(spd_test) list; }; struct test_resultstr{ enum spd_test_result result; const char* state; } ; static const struct test_resultstr test_result2str [] = { {TEST_RESULT_NOT_RUN, "NOT RUN"}, {TEST_RESULT_PASS, "PASS"}, {TEST_RESULT_FAILED, "FAILED"}, }; /* time mode */ enum test_type { TEST_NAME = 0, TEST_CATEGORY, TEST_ALL, }; /*! global structure containing both total and last test execution results */ static struct spd_test_execute_results { unsigned int total_tests; /*!< total number of tests, regardless if they have been executed or not */ unsigned int total_passed; /*!< total number of executed tests passed */ unsigned int total_failed; /*!< total number of executed tests failed */ unsigned int total_time; /*!< total time of all executed tests */ unsigned int last_passed; /*!< number of passed tests during last execution */ unsigned int last_failed; /*!< number of failed tests during last execution */ unsigned int last_time; /*!< total time of the last test execution */ } last_results; static SPD_LIST_HEAD_STATIC(spd_test_list, spd_test); static struct spd_test* spd_test_create(spd_test_cb_t*callback); static struct spd_test* spd_test_free(struct spd_test *test); static int spd_test_insert(struct spd_test *test); static struct spd_test* spd_test_remove(spd_test_cb_t* cb); static int spd_test_category_cmp(const char *cat1, const char *cat2); int __spd_test_update_state(const char * file, const char * func, int line, struct spd_test * test, const char * fmt,...) { struct spd_str *buf = NULL; va_list ap; if(!(buf = spd_str_create(128))) { return -1; } va_start(ap, fmt); spd_str_set_va(&buf, 0, fmt, ap); va_end(ap); spd_str_append(&test->status_str,0, "[%s:%s:%d]: %s", file, func, line,spd_str_buffer(buf)); spd_safe_free(buf); return 0; } int spd_test_register(spd_test_cb_t *cb) { struct spd_test *test; if(!cb) { spd_log(LOG_WARNING, "No callback be specified\n"); return -1; } if(!(test = spd_test_create(cb))) { spd_log(LOG_WARNING, "failed to create test case\n"); return -1; } if(spd_test_insert(test)) { spd_log(LOG_WARNING, "failed to insert test \n"); spd_test_free(test); return -1; } return -1; } int spd_test_unregister(spd_test_cb_t *cb) { struct spd_test *test; if(!cb) { spd_log(LOG_WARNING, "No callback be specified\n"); return -1; } if(!(test = spd_test_remove(cb))) { return -1; } spd_test_free(test); return 0; } static struct spd_test *spd_test_free(struct spd_test* test) { if(!test) { return NULL; } spd_safe_free(test->status_str); spd_safe_free(test); return NULL; } static struct spd_test *spd_test_create(spd_test_cb_t * callback) { struct spd_test *test = NULL; if(!callback || !(test = spd_calloc(1, sizeof(struct spd_test)))) { return NULL; } test->callback = callback; test->callback(&test->info, SPD_TEST_CMD_INIT, test); if(spd_strlen_zero(test->info.category)) { spd_log(LOG_WARNING, "No category set, cannot register. \n"); return spd_test_free(test); } if(spd_strlen_zero(test->info.name)) { spd_log(LOG_WARNING, "No name set , cannot register. \n"); return spd_test_free(test); } if(test->info.category[0] != '/' || test->info.category[strlen(test->info.category) - 1] != '/') { spd_log(LOG_WARNING, "category %s must start with / and end whith /.\n", test->info.category); } if(spd_strlen_zero(test->info.description)) { spd_log(LOG_WARNING, "NO desc set , cannot register.\n"); return spd_test_free(test); } if(!(test->status_str = spd_str_create(128))) { return spd_test_free(test); } return test; } static int spd_test_insert(struct spd_test *test) { SPD_LIST_LOCK(&spd_test_list); SPD_LIST_INSERT_SORTALPHA(&spd_test_list, test,list,info.category); SPD_LIST_UNLOCK(&spd_test_list); return 0; } static struct spd_test* spd_test_remove(spd_test_cb_t *cb) { struct spd_test *cur = NULL; SPD_LIST_LOCK(&spd_test_list); SPD_LIST_TRAVERSE_SAFE_BEGIN(&spd_test_list, cur, list) { if(cur->callback == cb) { SPD_LIST_REMOVE_CURRENT(&spd_test_list, list); break; } } SPD_LIST_TRAVERSE_SAFE_END; SPD_LIST_UNLOCK(&spd_test_list); return cur; } static int spd_test_category_cmp(const char *cat1, const char *cat2) { int len1 = 0; int len2 = 0; if (!cat1 || !cat2) { return -1; } len1 = strlen(cat1); len2 = strlen(cat2); if (len2 > len1) { return -1; } return strncmp(cat1, cat2, len2) ? 1 : 0; } static int run_one(struct spd_test *test) { struct timeval start = spd_tvnow(); spd_str_reset(test->status_str); test->result = test->callback(&test->info, SPD_TEST_CMD_RUN, test); test->howlong = spd_tvdiff_ms(spd_tvnow(), start); return 0; } int spd_test_run(const char * name, const char * category) { enum test_type mode = TEST_ALL; struct spd_test *test = NULL; int ret = 0; int run = 0; if(!spd_strlen_zero(category)) { if(spd_strlen_zero(name)) { mode = TEST_NAME; } else { mode = TEST_CATEGORY; } } SPD_LIST_LOCK(&spd_test_list); memset(&last_results, 0, sizeof(last_results)); SPD_LIST_TRAVERSE(&spd_test_list, test, list) { run = 0; switch(mode) { case TEST_CATEGORY: if(!spd_test_category_cmp(test->info.category, category)) { run = 1; } break; case TEST_NAME: if(!spd_test_category_cmp(test->info.category, category) && !strcmp(test->info.name, name)) { run = 1; } break; case TEST_ALL: run = 1; break; } if(run) { spd_log(LOG_DEBUG, "START Unite Test %s - %s \n",test->info.category, test->info.name); run_one(test); last_results.last_time += test->howlong; if(test->result == TEST_RESULT_PASS) { last_results.last_passed++; } else if(test->result = TEST_RESULT_FAILED) { last_results.last_failed++; } } last_results.total_time += test->howlong; last_results.total_tests++; if(test->result != TEST_RESULT_NOT_RUN) { if(test->result == TEST_RESULT_PASS) { last_results.total_passed++; } else { last_results.total_failed++; } } } ret = last_results.total_passed + last_results.total_failed; if(!(last_results.last_passed + last_results.last_failed)) { spd_log(LOG_WARNING, "No Test Found.\n "); return ret; } else { spd_log(LOG_NOTICE, "Number of Tests: %d\n", (last_results.last_passed + last_results.last_failed)); spd_log(LOG_NOTICE, "Number of Tests Executed: %d\n",last_results.total_tests); spd_log(LOG_NOTICE, "Passed Tests: %d\n", last_results.last_passed); spd_log(LOG_NOTICE, "Failed Tests: %d\n",last_results.last_failed); spd_log(LOG_NOTICE, "Total Execution Time: %d\n", last_results.total_time); } SPD_LIST_UNLOCK(&spd_test_list); return ret; } static void test_report_one(FILE *txtfile, const struct spd_test *test) { if(!txtfile || !test) { return; } fprintf(txtfile, "\nName: %s\n", test->info.name); fprintf(txtfile, "Category: %s\n", test->info.category); fprintf(txtfile, "Description: %s\n", test->info.description); fprintf(txtfile, "Result: %s\n", test_result2str[test->result].state); if(test->result != TEST_RESULT_NOT_RUN) { fprintf(txtfile, "Time: %d\n",test->howlong); } if(test->result == TEST_RESULT_FAILED) { fprintf(txtfile, "Error Description: %s\n", spd_str_buffer(test->status_str)); } } int spd_test_report(const char * name, const char * category, const char * textfile) { enum test_type mode = TEST_ALL; FILE *ftext; struct spd_test *test = NULL; if(!spd_strlen_zero(category)) { if(spd_strlen_zero(name)) { mode = TEST_NAME; } else { mode = TEST_CATEGORY; } } if(!spd_strlen_zero(textfile)) { if(!(ftext = fopen(textfile, "w"))) { spd_log(LOG_ERROR, "cannot open textfile : %s\n", textfile); return -1; } } SPD_LIST_LOCK(&spd_test_list); if(ftext) { fprintf(ftext,"Number of Tests: %d\n", last_results.total_tests); fprintf(ftext,"Number of Tests Executed: %d\n",(last_results.total_passed + last_results.total_failed)); fprintf(ftext,"Passed Tests: %d\n", last_results.total_passed); fprintf(ftext,"Failed Tests: %d\n", last_results.total_failed); fprintf(ftext,"Total Execution Time: %d\n", last_results.total_time); } SPD_LIST_TRAVERSE(&spd_test_list, test, list) { switch(mode) { case TEST_CATEGORY: if(!spd_test_category_cmp(test->info.category, category)) { test_report_one(ftext, test); } break; case TEST_NAME: if(!spd_test_category_cmp(test->info.category, category) && !strcmp(test->info.name, name)) { test_report_one(ftext, test); } break; case TEST_ALL: test_report_one(ftext, test); break; } } SPD_LIST_UNLOCK(&spd_test_list); if(ftext) fclose(ftext); return 0; }
测试 代码:
enum { FAMILY = 0, KEY = 1, VALUE = 2, }; SPD_TEST_INIT(test_db) { int res = TEST_RESULT_PASS; int i; char buf[sizeof(large_name)] = {0,}; const char *inputs[][3] = { {"family", "key", "value"}, {"dbtest", "a", "b"}, {"dbtest", "a", "a"}, {"dbtest", "b", "a"}, {"dbtest", "b", "b"}, }; switch(type) { case SPD_TEST_CMD_INIT: record->name = "test_db"; record->category = "/spider/db/"; record->description = "spd db get|put|del unit test"; return TEST_RESULT_NOT_RUN; case SPD_TEST_CMD_RUN: break; } for(i = 0; i < ARRAY_LEN(inputs); i++) { if(spd_db_put(inputs[i][FAMILY], inputs[i][KEY], inputs[i][VALUE])) { spd_log(LOG_ERROR, "test failed in db put %s : %s : %s : \n", inputs[i][FAMILY], inputs[i][KEY], inputs[i][VALUE]); spd_test_update_state(test, "test failed in db put %s : %s : %s : \n", inputs[i][FAMILY], inputs[i][KEY], inputs[i][VALUE]); res = TEST_RESULT_FAILED; } if(spd_db_get(inputs[i][FAMILY], inputs[i][KEY], buf, sizeof(buf))) { spd_log(LOG_ERROR, "test failed in db put %s : %s : %s : \n", inputs[i][FAMILY], inputs[i][KEY], inputs[i][VALUE]); spd_test_update_state(test, "test failed in db put %s : %s : %s : \n", inputs[i][FAMILY], inputs[i][KEY], inputs[i][VALUE]); res = TEST_RESULT_FAILED; } else if (strcasecmp(inputs[i][VALUE], buf)) { spd_log(LOG_ERROR, "test failed in db get , this is not match value,expect %s but %s \n", buf,inputs[i][VALUE]); spd_test_update_state(test, "test failed in db get , this is not match value,expect %s but %s \n", buf,inputs[i][VALUE]); res = TEST_RESULT_FAILED; } else { //spd_log(LOG_NOTICE, "get success %s %s %s \n", inputs[i][FAMILY], inputs[i][KEY], buf); } if(spd_db_del(inputs[i][FAMILY], inputs[i][KEY])) { spd_log(LOG_ERROR, "test failed in db del %s : %s : \n", inputs[i][FAMILY], inputs[i][KEY]); spd_test_update_state(test, "test failed in db get , this is not match value,expect %s but %s \n", buf,inputs[i][VALUE]); res = TEST_RESULT_FAILED; } } return res; } int test_spddb() { SPD_TEST_REGISTER(test_db); SPD_TEST_RUN("test_db",NULL); SPD_TEST_REPORT("test_db", NULL, "/tmp/spddb_test"); SPD_TEST_UNREGISTER(test_db); return 0; }
相关文章推荐
- c语言实现一个单元测试框架(Unit Test Framework)
- Python 单元测试框架 unittest 实现传参
- python调用HTMLTestRunner+unittest实现一次执行多个测试类,并生成与每个测试类对应的测试报告,并不像某些人写的每次只执行一个测试类,具体看代码,附上整个project代码
- unittest单元测试框架实现参数化
- 三行代码实现C语言单元测试框架
- 一个通用的单元测试框架的思考和设计08-实现篇-在testcase代码中执行sql语句
- 三行代码实现C语言单元测试框架
- 一个通用的单元测试框架的思考和设计08-实现篇-在testcase代码中执行sql语句
- (1)写一个程序,用于分析一个字符串中各个单词出现的频率,并将单词和它出现的频率输出显示。(单词之间用空格隔开,如“Hello World My First Unit Test”); (2)编写单元测试进行测试; (3)用ElcEmma查看代码覆盖率,要求覆盖率达到100%。
- C++ Unit Test Framework(单元测试框架)
- Python单元测试框架unittest简介
- python单元测试框架unittest简介
- 用Visual studio2005的单元测试框架实现一个简单的计算器
- Python单元测试框架 — unittest详解
- 利用Python中unittest实现简单的单元测试实例详解
- 推荐:一个写的相当好的介绍C++单元测试框架Google Test (gtest) 教程
- 一个通用的单元测试框架的思考和设计03-实现篇-核心类源码
- unittest-Python单元测试框架
- 使用 Visual Studio 2005 Team System 进行单元测试并生成用于 Unit Test Framework 的源代码
- selenium一个完整的unittest测试框架格式(单线程,非测试报告)