// 个人自制的C++测试管理类
// 代码(2018年版)结构如下:
1 TestBase: 测试对象的基类:
针对具体的测试对象,只要重载TEST_MAIN接口即可。
2 TestManager:测试对象的管理类:
实现对测试对象的添加,运行和删除操作。
针对具体的测试对象,实际上仅需要使用Add_<***>()即可。
3 一个调用示例:
// 具体代码如下:
TestBase类,仅一个头文件hpp,没有其他文件。
// TestBase.hpp
// 测试单元对象的基类
// Mylaf
// 2018-01-18 2032
//
#ifndef _ME_TESTBASE_HEADER_
#define _ME_TESTBASE_HEADER_
#if (defined _WIN32 || defined WINCE || defined __CYGWIN__)
// Windows
# include <io.h>
# include <direct.h>
//# if _DEBUG
//# pragma comment(lib, "mylaf.271603091qqd.lib")
//# else //!_DEBUG
//# pragma comment(lib, "mylaf.271603091qq.lib")
//# endif //_DEBUG
# pragma warning(disable:4996)
//int _mkdir(const char *dirname);
# define __mkdir(path,mode) _mkdir((path))
//int _access(const char *path, int mode);
# define __access _access
#define LIB_EXPORT
#if defined LIB_EXPORT
#define ME_C_API __declspec(dllexport)
#define ME_F_API extern "C" __declspec(dllexport)
#else // !LIB_EXPORT
#define ME_C_API __declspec(dllimport)
#define ME_F_API extern "C" __declspec(dllimport)
#endif // LIB_EXPORT
#elif (defined __GNUC__ && __GNUC__ >= 4)
# include <sys/stat.h>
# include <sys/types.h>
# include <unistd.h>
//int mkdir(const char *path, mode_t mode);
# define __mkdir(path,mode) mkdir((path),(mode))
//int access(const char *pathname, int mode)
# define __access access
#if defined LIB_EXPORT
#define ME_C_API
#define ME_F_API
#else // !LIB_EXPORT
#define ME_C_API
#define ME_F_API
#endif // LIB_EXPORT
#endif //
#include <iostream>
#include <fstream>
class ME_C_API TestBase {
public:
TestBase() {}
virtual ~TestBase() {}
public:
// @name:TEST_MAIN
// @brief:本测试文件的入口
// @note:请在此函数体内添加您的单元测试
// //@param[i]:argc 参数个数
// //@param[i]:argv 参数列表
// @warning:请不要修改此函数声明
//
virtual int TEST_MAIN(/*int argc, char* argv[]*/) {
std::cout << std::endl << "------------------------------";
std::cout << std::endl << __FUNCTION__ << " [" << __FILE__ << "] >>>" << std::endl;
// TODO: 请在下面的位置添加您的单元测试调用
// 调用多文件测试
//MultiTest();
// 调用单文件测试
//SingleTest();
// 调用单元测试0001
Test0001();
// 调用其他测试
// ...
std::cout << std::endl << "<<<" << __FUNCTION__ << " [" << __FILE__ << "] ";
std::cout << std::endl << "------------------------------" << std::endl;
return 0;
}
private:
// 多个文件测试
// path 是 文件夹image和文件 image.namelist.txt的父目录
int MultiTest(const char* path) {
if (path == NULL) {
std::cout << "PATH is NULL." << std::endl;
return -1;
}
// 路径是否存在
if (__access(path, 0) != 0) {
std::cout << "PATH NOT found." << std::endl;
return -2;
}
// 路径下面是否存在文件夹image和文件 image.namelist.txt
char sz[260] = { 0 };
sprintf(sz, "%s/image", path);
if (__access(path, 0) != 0) {
std::cout << "PATH/image NOT found." << std::endl;
return -3;
}
sprintf(sz, "%s/image.namelist.txt", path);
if (__access(path, 0) != 0) {
std::cout << "PATH/image.namelist.txt NOT found." << std::endl;
return -4;
}
// 读取image.namelist.txt内容
std::ifstream fin(sz, std::ios::binary | std::ios::in);
if (fin.fail()) {
std::cout << "Read file is failed!" << std::endl;
return -5;
}
char file[260];
while (!fin.eof()) {
memset(sz, 0, 260);
fin.getline(sz, 256, '\n');
// 跳过注释行
if (sz[0] == '#') {
continue;
}
// 处理Windows系统的\r\n
if (sz[strlen(sz) - 1] == '\r') {
sz[strlen(sz) - 1] = 0;
}
// 获取完整路径
memset(file, 0, 260);
sprintf(file, "%s/image/%s", path, sz);
// 检测完整路径是否存在
if (__access(file, 0) != 0) {
std::cout << file << " NOT found." << std::endl;
continue;
}
// TODO:调用单元测试并传递所需的参数
Test0001();
}
// 关闭文件
fin.close();
return 0;
}
// 单个文件测试
int SingleTest(const char* file) {
if (file == NULL) {
std::cout << "FILE is NULL." << std::endl;
return -1;
}
// 判断路径是否存在
if (__access(file, 0) != 0) {
std::cout << "PATH NOT found." << std::endl;
return -2;
}
// TODO:调用单元测试并传递所需的参数
Test0001();
return 0;
}
private:
// 单元测试001
virtual void Test0001() {
std::cout << __FUNCTION__ << ">>>" << std::endl;
}
// 自行定义其他单元测试:格式无限。
// 记得自行添加到 TEST_MAIN 或 MultiTest 或 SingleTest。
};
#endif // _ME_TESTBASE_HEADER_
TestManager类:TestManager.h和TestManager.cpp两个文件。
// TestManager.h
// 测试单元对象管理类
// Mylaf
// 2018-01-18 2036
//
// 使用示例:
// 获取管理器的全局实例
// TestManager* mgr = TestManager::getInstance();
//
// // TODO: 请在此处添加您的单元测试对象
// mgr->Add_<TestBase>();
//
// // 运行
// mgr->Run();
//
// // 释放资源
// mgr->Delete();
//
#ifndef _ME_TESTMANAGER_HEADER_
#define _ME_TESTMANAGER_HEADER_
#include "TestBase.hpp"
#include <vector>
#include <type_traits>
#include <mutex>
class ME_C_API TestManager {
private:
static TestManager* instance;
std::vector<TestBase*> vec_testptr;
std::mutex mtx;
private:
TestManager() {}
virtual ~TestManager() { Delete(-1); }
public:
// 单例模式:获取全局的实例对象指针
static TestManager* getInstance();
public:
// 添加测试单元对象类:必须继承于TestBase,否则无效。
// 返回位置(位置可用于Delete)
template<typename _Ty>
int Add_(void);
// 运行测试
virtual void Run(void);
// 删除指定位置的测试单元
// 当pos >= 0时,只删除pos位置上的测试单元,如果位置
virtual void Delete(int pos = -1);
};
// 添加测试单元对象类:必须继承于TestBase,否则无效。
// 返回位置(位置可用于Delete)
template<typename _Ty>
inline int TestManager::Add_(void) {
mtx.lock();
int pos = -1;
if (std::is_same<TestBase, _Ty>::value
|| std::is_base_of<TestBase, _Ty>::value) {
//
TestBase* ptr = new _Ty();
if (ptr != nullptr) {
// 查找存放的位置
pos = vec_testptr.size() - 1;
for (; pos > -1; --pos) {
if (vec_testptr[pos] == nullptr) {
// 修改
vec_testptr[pos] = ptr;
break;
}
}
//
if (pos < 0) {
// 如果当前vec里面没有空位,则追加到vec尾巴
pos = vec_testptr.size();
vec_testptr.push_back(ptr);
}
}
}
//std::cout << "Add: is_base_of(TestBase,***)" << std::endl;
mtx.unlock();
return pos;
}
#endif // _ME_TESTMANAGER_HEADER_
// TestManager.cpp
// 测试单元对象管理类
// Mylaf
// 2018-01-18 2036
//
#include "TestManager.h"
TestManager* TestManager::instance = nullptr;
// 单例模式:获取全局的实例对象指针
TestManager * TestManager::getInstance() {
if (instance == nullptr) {
instance = new TestManager();
}
return instance;
}
// 运行测试
void TestManager::Run(void) {
mtx.lock();
for (int i = 0; i < vec_testptr.size(); ++i)
{
if (vec_testptr[i] == nullptr) {
continue;
}
// 调用各自的测试入口
vec_testptr[i]->TEST_MAIN();
}
mtx.unlock();
}
// 删除指定位置的测试单元
// 当pos >= 0时,只删除pos位置上的测试单元,如果位置
void TestManager::Delete(int pos) {
mtx.lock();
if (pos < 0) {
// 删除从abs(pos)-1位置开始的所有测试单元
for (int i = -pos - 1; i < vec_testptr.size(); ++i) {
delete vec_testptr[i];
vec_testptr[i] = nullptr;
}
}
else {
// 删除指定位置的所有测试单元
if (pos > -1 && pos < vec_testptr.size()) {
delete vec_testptr[pos];
vec_testptr[pos] = nullptr;
}
}
mtx.unlock();
}
调用方法实例:仅一个文件main.cpp。
// main.cpp
// 测试管理调用示例
// Mylaf
// 2018-01-18 2040
//
#include "TestManager.h"
int main() {
//
TestManager* mgr = TestManager::getInstance();
// 添加测试类TestBase
mgr->Add_<TestBase>();
// TODO:在此处添加您的测试类
// 添加测试类的示例如上【添加测试类TestBase】
// 运行测试类
mgr->Run();
// 删除所有测试类
mgr->Delete();
//
return 0;
}
// 运行效果
// Mylaf
// 厦门
//