??xml version="1.0" encoding="utf-8" standalone="yes"?>
]]>
]]>
[python] view plaincopy
- def logged(func):
- def with_logging(*args, **kwargs):
- print func.__name__ + " was called"
- return func(*args, **kwargs)
- return with_logging
then when you say
[python] view plaincopy
- @logged
- def f(x):
- """does some math"""
- return x + x * x
it's exactly the same as saying
[python] view plaincopy
- def f(x):
- """does some math"""
- return x + x * x
- f = logged(f)
and your function f is replaced with the function with_logging. Unfortunately, this means that if you then say
[python] view plaincopy
- print f.__name__
it will print with_logging
because that's the name of your new function. In fact, if you look at the docstring for f, it will be blank because with_logging has no docstring, and so the docstring you wrote won't be there anymore. Also, if you look at the pydoc result for that function, it won't be listed as taking one argument x
; instead it'll be listed as taking *args
and **kwargs
because that's what with_logging takes.
If using a decorator always meant losing this information about a function, it would be a serious problem. That's why we have functools.wraps
. This takes a function used in a decorator and adds the functionality of copying over the function name, docstring, arguments list, etc. And since wraps
is itself a decorator, the following code does the correct thing:
[python] view plaincopy
- from functools import wraps
- def logged(func):
- @wraps(func)
- def with_logging(*args, **kwargs):
- print func.__name__ + " was called"
- return func(*args, **kwargs)
- return with_logging
- @logged
- def f(x):
- """does some math"""
- return x + x * x
- print f.__name__ # prints 'f'
- print f.__doc__ # prints 'does some math'
转自http://blog.csdn.net/wanghai__/article/details/7078792
在通常情况下,׃n库都是通过使用附加选项 -fpic ?nbsp;-fPIC q行~译Q从目标代码产生位置无关的代码(Position Independent CodeQPICQ,使用 -shared选项目标代码放q共享目标库中。位|无关代码需要能够被加蝲C同进E的不同地址Qƈ且能得以正确的执行,故其代码要经q特别的~译处理Q位|无关代码(PICQ对帔R和函数入口地址的操作都是采用基于基寄存器(base registerQBASE+ 偏移量的相对地址的寻址方式。即使程序被装蝲到内存中的不同地址Q即 BASE g同,而偏U量是不变的Q所以程序仍然可以找到正的入口地址或者常量?/p>
然而,当应用程序链接了多个׃n库,如果在这些共享库中,存在相同作用域范围的同名静态成员变量或者同?( 非静?) 全局变量Q那么当E序讉K完静态成员变量或全局变量l束析构Ӟ׃某内存块?double free ?x)导?core dumpQ这是由?Linux ~译器的~陷造成的?/p>
该问题源于笔者所从事的开发项目:(x)IBM Tivoli Workload Scheduler (TWS) LoadLeveler?strong>LoadLeveler?nbsp;IBM在高性能计算Q?strong>High Performance ComputingQHPCQ领域的一ƾ作业调度Y件。它主要分ؓ(f)两个大的模块Q分别是调度模块QschedulerQ和资源理模块Qresource mangerQ?两个模块中分别含有关于配|管理功能的׃n库,׃某些配置理选项Z模块所共同采用Q所以两模块之间׃n了部分源文g代码Q其中包含有同名的类静态成员?/p>
可以通过以下单的模型q行描述Q?/p>
?1. 应用场景
对应的各模块代码片段如下图所C:(x)
其中Qtest.c 是主E序Q包含有两个头文Ӟ(x)api1.h ?api2.hQ头文g api1.h 包含头文?lib1/lib.h 和一功能函数 func_api1()Qapi2.h 包含头文?lib2/lib.h 和一功能函数 func_api2()Q目?lib1 ?lib2 下的源文件分别编译生成共享库 lib1.so ?lib2.so。同Ӟ头文?lib1/lib.h ?lib2/lib.h 链接到同一׃n文g lib.h。在文g lib.h 中定义有一静态成员变?#8220;static std::vector<int> vec_int”?/p>
功能函数 func_api1() ?func_api2() 的实现类|通过调用静态成员函数达到访问静态成员变?nbsp;vec_int的目的:(x)
void func_api1(int i) { printf("%s.\n", __FILE__); A::set(i); A::print(); return; } |
静态成员函?A::set() ?A::print() 的实现如下:(x)
void A::set(int num) { vec_int.clear(); for (int i = 0; i < num; i++) { vec_int.push_back(i); } return; } |
void A::print() { for (int i = 0; i < vec_int.size(); i++) { printf("vec_int[%d] = %d, addr: %p.\n", i, vec_int[i], &vec_int[i]); } printf("vec_int addr: %p.\n", &vec_int); return; } |
A::set() 寚w态成?nbsp;vec_intq行赋值操作,?A::print() 则打印其中的g当前的内存地址?/p>
如果两个׃n库是通过选项 -fpic?nbsp;-fPIC~译的话Q运行程?testQ输出如下:(x)
$ export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH $ g++ -g -o lib1.so -fPIC-rdynamic -shared lib1/lib.c $ g++ -g -o lib2.so -fPIC-rdynamic -shared lib2/lib.c $ g++ -g -o test -L./ -l1 -l2 test.c $ ./test api1.h. vec_int[0] = 0, addr: 0x9cbf028. vec_int[1] = 1, addr: 0x9cbf02c. vec_int[2] = 2, addr: 0x9cbf030. vec_int[3] = 3, addr: 0x9cbf034. vec_int addr: 0xe89228. *** glibc detected *** ./test: double free or corruption (fasttop): 0x09cbf028*** ======= Backtrace:========= /lib/libc.so.6[0x2b2b16] /lib/libc.so.6(cfree+0x90)[0x2b6030] /usr/lib/libstdc++.so.6(_ZdlPv+0x21)[0x5d1731] ./lib1.so(_ZN9__gnu_cxx13new_allocatorIiE10deallocateEPij+0x1d)[0xe88417] ./lib1.so(_ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPij+0x33)[0xe88451] ./lib1.so(_ZNSt12_Vector_baseIiSaIiEED2Ev+0x42)[0xe8849a] ./lib1.so(_ZNSt6vectorIiSaIiEED1Ev+0x60)[0xe8850c] ./lib2.so[0x961d6c] /lib/libc.so.6(__cxa_finalize+0xa9)[0x275c79] ./lib2.so[0x961c34] ./lib2.so[0x962d3c] /lib/ld-linux.so.2[0x23a7de] /lib/libc.so.6(exit+0xe9)[0x2759c9] /lib/libc.so.6(__libc_start_main+0xe4)[0x25fdf4] ./test(__gxx_personality_v0+0x45)[0x80484c1] ======= Memory map:======== ...... 00960000-00963000 r-xp 00000000 00:1b 7668734 ./lib2.so 00963000-00964000 rwxp 00003000 00:1b 7668734 ./lib2.so 00970000-00971000 r-xp 00970000 00:00 0 [vdso] 00e86000-00e89000 r-xp 00000000 00:1b 7668022 ./lib1.so 00e89000-00e8a000 rwxp 00003000 00:1b 7668022 ./lib1.so 08048000-08049000 r-xp 00000000 00:1b 7668748 ./test 08049000-0804a000 rw-p 00000000 00:1b 7668748 ./test 09cbf000-09ce0000 rw-p 09cbf000 00:00 0 [heap] ...... Abort(coredump) $ |
从程序的输出直观的看刎ͼcore 产生是由于堆内存区域Q?strong>09cbf000-09ce0000Q中起始地址?nbsp;0x09cbf028的内存区被释放了两次D的,该地址正式静态成员变?nbsp;vec_int的第一个元素的地址?/p>
Z么会(x)出现同一块内存区Q被释放两次的情形呢Q?/p>
我们知道Q静态成员变量与全局变量cMQ都采用了静态存储方式。对于加了选项 -fpic?nbsp;-fPIC的共享库Q这些变量的地址都存攑֜该共享库的全局偏移表(Global Offset TableQGOTQ中?/p>
通过 objdump或?nbsp;readelf命o(h)分析׃n?nbsp;lib1.soQ结果如下:(x)
$ objdump -x -R lib1.so lib1.so: file format elf32-i386 ...... Sections: Idx Name Size VMA LMA File off Algn 0 .gnu.hash 000001e8 000000d4 000000d4 000000d4 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA ...... 18 .dynamic 000000d8 0000301c 0000301c 0000301c 2**2 CONTENTS, ALLOC, LOAD, DATA 19 .got 00000014 000030f4 000030f4 000030f4 2**2 CONTENTS, ALLOC, LOAD, DATA 20 .got.plt 00000114 00003108 00003108 00003108 2**2 CONTENTS, ALLOC, LOAD, DATA ...... DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE ...... 000030f4 R_386_GLOB_DAT __gmon_start__ 000030f8 R_386_GLOB_DAT _Jv_RegisterClasses 000030fc R_386_GLOB_DAT _ZN1A7vec_intE 00003104 R_386_GLOB_DAT __cxa_finalize ...... |
$ objdump -x -R lib1.so lib1.so: file format elf32-i386 ...... Sections: Idx Name Size VMA LMA File off Algn 0 .gnu.hash 000001e8 000000d4 000000d4 000000d4 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA ...... 18 .dynamic 000000d8 0000301c 0000301c 0000301c 2**2 CONTENTS, ALLOC, LOAD, DATA 19 .got 00000014 000030f4 000030f4 000030f4 2**2 CONTENTS, ALLOC, LOAD, DATA 20 .got.plt 00000114 00003108 00003108 00003108 2**2 CONTENTS, ALLOC, LOAD, DATA ...... DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE ...... 000030f4 R_386_GLOB_DAT __gmon_start__ 000030f8 R_386_GLOB_DAT _Jv_RegisterClasses 000030fc R_386_GLOB_DAT _ZN1A7vec_intE 00003104 R_386_GLOB_DAT __cxa_finalize ...... |
从上面两个命令的输出l果中可以看出,׃n?nbsp;lib1.so?nbsp;GOTD늚起始内存地址?nbsp;000030f4Q大ؓ(f) 20 字节 (0x14)Q静态成员变?nbsp;vec_int在共享库 lib1.so中的起始偏移地址?nbsp;000030fc。显?dng)?strong>vec_int位于该共享库?nbsp;GOTD内?/p>
当应用程序同旉?nbsp;lib1.so?nbsp;lib2.soӞ同名静态成员变?nbsp;vec_int分别位于其共享库?nbsp;GOT区。当E序q行Ӟpȝ从符可中查扑ƈ装蝲构造一?nbsp;vec_int数据Q这点从E序q行的输出结果(清单 4Q的“Backtrace”部分可以看到Q只?nbsp;lib1.so中的静态成员变量被装蝲构造;同时Q通过内存映射Q?strong>Memory mapQ部分(清单 4Q,可以观察?nbsp;vec_int对象的地址 0xe89228正好处在为共享库 lib1.so分配的可d存区 00e89000-00e8a000中:(x)
00e89000-00e8a000 rwxp 00003000 00:1b 7668022 ./lib1.so |
然后Q当E序l束Ӟ却对该变量进行了两次析构操作Q通过 gdb分析 core 文gQ?/p>
清单 7. core 文g分析l果
$ gdb ./test core.28440 …… Core was generated by `./test'. Program terminated with signal 6, Aborted. #0 0x00970402 in __kernel_vsyscall () (gdb) (gdb) where #0 0x00970402 in __kernel_vsyscall () #1 0x00272d10 in raise () from /lib/libc.so.6 #2 0x00274621 in abort () from /lib/libc.so.6 #3 0x002aae5b in __libc_message () from /lib/libc.so.6 #4 0x002b2b16 in _int_free () from /lib/libc.so.6 #5 0x002b6030 in free () from /lib/libc.so.6 #6 0x005d1731 in operator delete () from /usr/lib/libstdc++.so.6 #7 0x00e88417 in __gnu_cxx::new_allocator<int>::deallocate (this=0xe89228, __p=0x9cbf028) at /usr/lib/gcc/i386-redhat-linux/.../ext/new_allocator.h:94 #8 0x00e88451 in std::_Vector_base<int, ... (this=0xe89228, __p=0x9cbf028, __n=4) at /usr/lib/gcc/.../include/c++/4.1.2/bits/stl_vector.h:133 #9 0x00e8849a in ~_Vector_base (this=0xe89228) at /usr/lib/gcc/.../include/c++/4.1.2/bits/stl_vector.h:119 #10 0x00e8850cin ~vector (this=0xe89228) at /usr/lib/gcc/.../stl_vector.h:272 #11 0x00961d6c in __tcf_0 () at lib2/lib.c:3 #12 0x00275c79 in __cxa_finalize () from /lib/libc.so.6 #13 0x00961c34 in __do_global_dtors_aux () from ./lib2.so #14 0x00962d3c in _fini () from ./lib2.so #15 0x0023a7de in _dl_fini () from /lib/ld-linux.so.2 #16 0x002759c9 in exit () from /lib/libc.so.6 #17 0x0025fdf4 in __libc_start_main () from /lib/libc.so.6 #18 0x080484c1 in _start () (gdb) |
从清?7 中可以看出,从 #14 开始,E序q行 lib2.so中的析构操作Q直?#11Q都q行?nbsp;lib2.so中,当进入 #10 Ӟq行变量析构Ӟ其地址?nbsp;0x00e8850cQ该地址中的对象是程序启动时由共享库 lib1.so装蝲构造出来的Q清?1Q:(x)
./lib1.so(_ZNSt6vectorIiSaIiEED1Ev+0x60)[0xe8850c] |
当程序结束时Q运行库 glibc(g)到׃n?nbsp;lib2.so析构了ƈ非由其构造的对象Q导致了 core dump?/p>
q种情况下,如果替换使用选项 -fpie?nbsp;-fPIEQ操作步骤与q行l果如下所C:(x)
$ export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH $ g++ -g -o lib1.so -fPIE-rdynamic -shared lib1/lib.c $ g++ -g -o lib2.so -fPIE-rdynamic -shared lib2/lib.c $ g++ -g -pie -o test -L./ -l1 -l2 test.c $ ./test api1.h. vec_int[0] = 0, addr: 0x80e3028. vec_int[1] = 1, addr: 0x80e302c. vec_int[2] = 2, addr: 0x80e3030. vec_int[3] = 3, addr: 0x80e3034. vec_int addr: 0x75e224. $ |
E序q行l果W合期望q正常结束?/p>
q是因ؓ(f)Q当使用选项 -fpie?nbsp;-fPIEӞ生成的共享库不会(x)为静态成员变量或全局变量?nbsp;GOT中创建对应的条目Q通过 objdump?strong>readelf命o(h)可以查看Q此处不再赘qͼ(j)Q从而避免了׃静态对?#8220;构造一ơ,析构两次”而对同一内存区域释放两次引v的程?core dump?/p>
选项 -fpie?nbsp;-fPIE?nbsp;-fpic?nbsp;-fPIC的用法很怼Q区别在于前者L生成的位置无关代码看作是属于程序本w,q直接链接进该可执行E序Q而非存入全局偏移?nbsp;GOT中;q样Q对于同名的静态或全局对象的访问,其构造与析构操作保持一一对应?/p>
通过使用选项 -fpie?nbsp;-fPIE代替 -fpic或?nbsp;-fPICQ得生成的׃n库不?x)?f)静态成员变量或全局变量?nbsp;GOT中创建对应的条目Q同时也避免了针对同名静态对?#8220;构造一ơ,析构两次”的不当操作?br />
转自Q?a >http://www.ibm.com/developerworks/cn/linux/l-cn-sdlstatic/
试是Y件开发过E中极其重要的一环,详尽周密的测试能够减Y件BUGQ提高Y件品质。测试包括单元测试、系l测试等。其中单元测试是指针对Y件功能单元所作的试Q这里的功能单元可以是一个类的属性或者方法,试的目的是看这些基本单元是否工作正常。由于单元测试的内容很基Q因此可以看作是试工作的第一环,该项工作一般由开发h员自行完成。如果条件允许,单元试代码的开发应与程序代码的开发同步进行?/p>
虽然不同E序的单元测试代码不相同,但测试代码的框架却非常相|于是便出C一些单元测试类库,CppUnit便是其中之一?/p>
CppUnit是XUnit中的一员,XUnit是一个大家族Q还包括JUnit和PythonUnit{。CppUnit单实用,学习(fn)和用v来都很方便,|上已有一些文章对其作介绍Q但本文更着重于讲解其中的基本概念和使用Ҏ(gu)Q以帮助初次接触CppUnit的h员快速入门?/p>
安装
目前QCppUnit的最新版本是1.10.2Q你可以从下面地址获取Q?/p>http://sourceforge.net/projects/cppunit
解压后,你可以看到CppUnit包含如下目录Q?/p>
configQ?配置文g contribQ?contributionQ其他h贡献的外围代?docQ?文档Q需要通过doxygen工具生成Q也可以直接从sourceforge站点上下载打包好的文?examplesQ示例代?includeQ?头文?libQ?存放~译好的?srcQ?源文Ӟ以及(qing)~译库的工程{?/pre>然后打开src目录下的CppUnitLibraries工程Q执行build/batch buildQ编译成功的话,生成的库文g被拯到l(f)ib目录下?/p>
你也可以Ҏ(gu)需要选择所需的项目进行编译,其中目cppunit为静态库Qcppunit_dll为动态库Q生成的库文件ؓ(f)Q?/p>
cppunit.libQ?静态库release?cppunitd.libQ?静态库debug?cppunit_dll.libQ?动态库release?cppunitd_dll.libQ动态库debug?/pre>要用CppUnitQ还得设|好头文件和库文件\径,以VC6ZQ选择Tools/Options/DirectoriesQ在Include files和Library files中分别添?CppUnitPath%/include?CppUnitPath%/libQ其?CppUnitPath%表示CppUnit所在\径?/p>
做好准备工作后,我们可以编写自q单元试代码了。需说明的是QCppUnit所用的动态运行期库均为多U程动态库Q因此你的单元测试程序也得用相应设|,否则?x)发生冲H?/p>
概念
在用之前,我们有必要认识一下CppUnit中的主要c,当然你也可以先看后面的例子,遇到问题再回q头来看q一节?/p>
CppUnit核心内容主要包括六个斚wQ?/p>
1. 试对象QTestQTestFixtureQ?..Q:(x)用于开发测试用例,以及(qing)Ҏ(gu)试用例进行组l管理?/p>
2. 试l果QTestResultQ:(x)处理试用例执行l果。TestResult与下面的TestListener采用的是观察者模式(Observer PatternQ?/p>
3. 试l果监听者(TestListenerQ:(x)TestListener作ؓ(f)TestResult的观察者,担Q实际的结果处理角艌Ӏ?/p>
4. l果输出QOutputterQ:(x)结果进行输出,可以制定不同的输出格式?/p>
5. 对象工厂QTestFactoryQ:(x)用于创徏试对象Q对试用例q行自动化管理?/p>
6. 试执行体(TestRunnerQ:(x)用于q行一个测试?/p>
以上各模块的主要cȝ承结构如下:(x)
Test TestFixture TestResult TestListener _______|_________ | | | | | TestSuccessListener TestComposite TestLeaf | | | |____________| TestResultCollector TestSuit | TestCase | TestCaller<Fixture> Outputter TestFactory TestRunner ____________________|_________________ | | | | TestFactoryRegistry CompilerOutputter TextOutputter XmlOutputter | TestSuiteFactory<TestCaseType>接下来再对其中一些关键类作以介绍?/p>
TestQ所有测试对象的基类?/p>
CppUnit采用?wi)Şl构来组l管理测试对象(cM于目录树(wi)Q,因此q里采用了组合设计模式(Composite PatternQ,Test的两个直接子cTestLeaf和TestComposite分别表示“试?#8221;中的叶节点和非叶节点Q其中TestComposite主要L(fng)l管理的作用Q就像目录树(wi)中的文g夹,而TestLeaf才是最l具有执行能力的试对象Q就像目录树(wi)中的文g?/p>
Test最重要的一个公共接口ؓ(f)Q?/p>
virtual void run(TestResult *result) = 0;其作用ؓ(f)执行试对象Q将l果提交lresult?/p>
在实际应用中Q我们一般不?x)直接用Test、TestComposite以及(qing)TestLeafQ除非我们要重新定制某些机制?/p>
TestFixtureQ用于维护一l测试用例的上下文环境?/p>
在实际应用中Q我们经怼(x)开发一l测试用例来Ҏ(gu)个类的接口加以测试,而这些测试用例很可能h相同的初始化和清理代码。ؓ(f)此,CppUnit引入TestFixture来实现这一机制?/p>
TestFixtureh以下两个接口Q分别用于处理测试环境的初始化与清理工作Q?/p>
virtual void setUp();
virtual void tearDown();TestCaseQ测试用例,从名字上可以看出来Q它便是单元试的执行对象?/p>
TestCase从Test和TestFixture多承而来Q通过把Test::run制定成模板函敎ͼTemplate MethodQ而将两个父类的操作融合在一Prun函数的伪定义如下Q?/p>
// 伪代?nbsp;
void TestCase::run(TestResult* result)
{
result->startTest(this); // 通知result试开?br /> if( result->protect(this, &TestCase::setUp) ) // 调用setUpQ初始化环境
result->protect(this, &TestCase::runTest); // 执行runTestQ即真正的测试代?br /> result->protect(this, &TestCase::tearDown); // 调用tearDownQ清理环?br /> result->endTest(this); // 通知result试l束
}q里要提到的是函数runTestQ它是TestCase定义的一个接口,原型如下Q?/p>
virtual void runTest();用户需从TestCasez出子cdƈ实现runTest以开发自己所需的测试用例?/p>
另外q要提到的就是TestResult的protectҎ(gu)Q其作用是对执行函数Q实际上是函数对象)(j)的错误信息(包括断言和异常等Q进行捕P从而实现对试l果的统计?/p>
TestSuitQ测试包Q按照树(wi)形结构管理测试用?/p>
TestSuit是TestComposite的一个实玎ͼ它采用vector来管理子试对象QTestQ,从而Ş成递归的树(wi)形结构?/p>
TestCallerQTestCase适配器(AdapterQ,它将成员函数转换成测试用?/p>
虽然我们可以从TestCasez自己的测试类Q但从TestCasecȝ定义可以看出Q它只能支持一个测试用例,q对于测试代码的l织和维护很不方便,其是那些有共同上下文环境的一l测试。ؓ(f)此,CppUnit提供了TestCaller以解册个问题?/p>
TestCaller是一个模板类Q它以实CTestFixture接口的类为模板参敎ͼ目标类中某个符合runTest原型的测试方法适配成TestCase的子cR?/p>
在实际应用中Q我们大多采用TestFixture和TestCaller相组合的方式Q具体例子参见后文?/p>
TestResult和TestListenerQ处理测试信息和l果
前面已经提到QTestResult和TestListener采用了观察者模式,TestResultl护一个注册表Q用于管理向其登记过的TestListenerQ当TestResult收到试对象QTestQ的试信息Ӟ再一一分发l它所辖的TestListener。这一设计有助于实现对同一试的多U处理方式?/p>
TestFactoryQ测试工?/p>
q是一个辅助类Q通过借助一pd宏定义让试用例的组l管理变得自动化。参见后面的例子?/p>
TestRunnerQ用于执行测试用?/p>
TestRunner待执行的测试对象管理v来,然后供用戯用。其接口为:(x)
virtual void addTest( Test *test ); virtual void run( TestResult &controller, const std::string &testPath = "" );q也是一个辅助类Q需注意的是Q通过addTestd到TestRunner中的试对象必须是通过new动态创建的Q用户不能删除这个对象,因ؓ(f)TestRunner自行管理测试对象的生命期?/p>
使用
先让我们看看一个简单的例子Q?/p>
#include <cppunit/TestCase.h>~译后运行,输出l果为:(x)
#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/TextOutputter.h>
// 定义试用例
class SimpleTest : public CppUnit::TestCase
{
public:
void runTest() // 重蝲试Ҏ(gu)
{
int i = 1;
CPPUNIT_ASSERT_EQUAL(0, i);
}
};
int main(int argc, char* argv[])
{
CppUnit::TestResult r;
CppUnit::TestResultCollector rc;
r.addListener(&rc); // 准备好结果收集器
SimpleTest t;
t.run(&r); // q行试用例
CppUnit::TextOutputter o(&rc, std::cout);
o.write(); // 结果输?br />
return 0;
}!!!FAILURES!!!
Test Results:
Run: 1 Failures: 1 Errors: 0
1) test: (F) line: 18 E:/CppUnitExamples/SimpleTest.cpp
equality assertion failed
- Expected: 1
- Actual : 0上面的例子很单,需说明的是CPPUNIT_ASSERT_EQUAL宏。CppUnit定义了一l宏用于(g)错误,CPPUNIT_ASSERT_EQUAL是其中之一Q当断言p|ӞCppUnit便会(x)错误信息报告给TestResult。这些宏定义的说明如下:(x)
CPPUNIT_ASSERT(condition)Q判断condition的值是否ؓ(f)真,如果为假则生成错误信息?/p>
CPPUNIT_ASSERT_MESSAGE(message, condition)Q与CPPUNIT_ASSERTcMQ但l果为假时报告messsage信息?/p>
CPPUNIT_FAIL(message)Q直接报告messsage错误信息?/p>
CPPUNIT_ASSERT_EQUAL(expected, actual)Q判断expected和actual的值是否相{,如果不等输出错误信息?/p>
CPPUNIT_ASSERT_EQUAL_MESSAGE(message, expected, actual)Q与CPPUNIT_ASSERT_EQUALcMQ但断言p|时输出message信息?/p>
CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, actual, delta)Q判断expected与actual的偏差是否小于deltaQ用于QҎ(gu)比较?/p>
CPPUNIT_ASSERT_THROW(expression, ExceptionType)Q判断执行表辑ּexpression后是否抛出ExceptionType异常?/p>
CPPUNIT_ASSERT_NO_THROW(expression)Q断a执行表达式expression后无异常抛出?/p>
接下来再看看TestFixture和TestCaller的组合用:(x)
#include <cppunit/TestCase.h>~译后运行结果ؓ(f)Q?/span>
#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/TextOutputter.h>
#include <cppunit/TestCaller.h>
#include <cppunit/TestRunner.h>
// 定义试c?br />class StringTest : public CppUnit::TestFixture
{
public:
void setUp() // 初始?br /> {
m_str1 = "Hello, world";
m_str2 = "Hi, cppunit";
}
void tearDown() // 清理
{
}
void testSwap() // 试Ҏ(gu)1
{
std::string str1 = m_str1;
std::string str2 = m_str2;
m_str1.swap(m_str2);
CPPUNIT_ASSERT(m_str1 == str2);
CPPUNIT_ASSERT(m_str2 == str1);
}
void testFind() // 试Ҏ(gu)2
{
int pos1 = m_str1.find(',');
int pos2 = m_str2.rfind(',');
CPPUNIT_ASSERT_EQUAL(5, pos1);
CPPUNIT_ASSERT_EQUAL(2, pos2);
}
protected:
std::string m_str1;
std::string m_str2;
};
int main(int argc, char* argv[])
{
CppUnit::TestResult r;
CppUnit::TestResultCollector rc;
r.addListener(&rc); // 准备好结果收集器
CppUnit::TestRunner runner; // 定义执行实体
runner.addTest(new CppUnit::TestCaller<StringTest>("testSwap", &StringTest::testSwap)); // 构徏试用例1
runner.addTest(new CppUnit::TestCaller<StringTest>("testFind", &StringTest::testFind)); // 构徏试用例2
runner.run(r); // q行试
CppUnit::TextOutputter o(&rc, std::cout);
o.write(); // 结果输?br />
return rc.wasSuccessful() ? 0 : -1;
}OK (2 tests)上面的代码从功能上讲没有什么问题,但编写v来太J琐了,为此Q我们可以借助CppUnit定义的一套辅助宏Q将试用例的定义和注册变得自动化。上面的代码攚w后如下Q?/p>
#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/TextOutputter.h>
#include <cppunit/TestRunner.h>
#include <cppunit/extensions/HelperMacros.h>
// 定义试c?br />class StringTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(StringTest); // 定义试?br /> CPPUNIT_TEST(testSwap); // d试用例1
CPPUNIT_TEST(testFind); // d试用例2
CPPUNIT_TEST_SUITE_END(); // l束试包定?br />
public:
void setUp() // 初始?br /> {
m_str1 = "Hello, world";
m_str2 = "Hi, cppunit";
}
void tearDown() // 清理
{
}
void testSwap() // 试Ҏ(gu)1
{
std::string str1 = m_str1;
std::string str2 = m_str2;
m_str1.swap(m_str2);
CPPUNIT_ASSERT(m_str1 == str2);
CPPUNIT_ASSERT(m_str2 == str1);
}
void testFind() // 试Ҏ(gu)2
{
int pos1 = m_str1.find(',');
int pos2 = m_str2.rfind(',');
CPPUNIT_ASSERT_EQUAL(5, pos1);
CPPUNIT_ASSERT_EQUAL(2, pos2);
}
protected:
std::string m_str1;
std::string m_str2;
};
CPPUNIT_TEST_SUITE_REGISTRATION(StringTest); // 自动注册试?br />
int main(int argc, char* argv[])
{
CppUnit::TestResult r;
CppUnit::TestResultCollector rc;
r.addListener(&rc); // 准备好结果收集器
CppUnit::TestRunner runner; // 定义执行实体
runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());
runner.run(r); // q行试
CppUnit::TextOutputter o(&rc, std::cout);
o.write(); // 结果输?br />
return rc.wasSuccessful() ? 0 : -1;
}CppUnit的简单介l就到此Q相信你已经了解了其中的基本概念Q也能够开发单元测试代码了?br />
转自Q?a >http://blog.csdn.net/freefalcon/article/details/753819
]]>