青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Just enjoy programming

2014年5月11日 #

skiplist

    最近開始看leveldb的代碼,周末看了下skiplist,詳細介紹可以看http://blog.xiaoheshang.info/?p=248?;驹砭褪强臻g換效率,多用幾個指針來換取查找插入效率。自己也嘗試寫了下,對比了下leveldb的代碼,內存控制沒有leveldb實現的好,leveldb申請的時候基本上都是以block為單位申請空間,這樣可以減少內存碎片,空間上每個節點就只會申請指定level個數的指針。以前一直記混淆 const char *p , char const *p 和char * const p的區別,前兩者代碼指向的內容不可變,后者指向的指針不可變。

posted @ 2014-05-11 23:03 周強 閱讀(401) | 評論 (0)編輯 收藏

2014年1月3日 #

2013

      時間好快,不知不覺就步入了2014年,回顧2013,有收獲也有遺憾,最大的收獲就是遇到了“她”,真的感謝“她”在我還一無所有的時候選擇了我,雖然現在的我還不夠優秀,但今后的日子我會盡自己最大的努力讓你幸福。每每打電話回家的時候總有點愧疚感,父母這么大年紀還在起早摸黑的干活,希望能早日讓他們停下來安詳晚年。2013遺憾的是技術積累不夠,總感覺自己很忙,但卻成長較慢,很多時候自己都認為存在的都是合理的,沒有多思考下為什么這樣設計,會不會有更好的設計實現。2014希望自己能在技術上有所積累,希望自己能靜下心來專注做好該做的事情,不能太著急,慢慢積累,或許10年能專注地做好一件事也已經很不錯了。

posted @ 2014-01-03 21:17 周強 閱讀(197) | 評論 (0)編輯 收藏

2013年11月5日 #

python functools wraps (轉)

When you use a decorator, you're replacing one function with another. In other words, if you have a decorator

  1. def logged(func):  
  2.     def with_logging(*args, **kwargs):  
  3.         print func.__name__ + " was called"  
  4.         return func(*args, **kwargs)  
  5.     return with_logging  

then when you say

  1. @logged  
  2. def f(x):  
  3.    """does some math"""  
  4.    return x + x * x  

it's exactly the same as saying

  1. def f(x):  
  2.     """does some math"""  
  3.     return x + x * x  
  4. f = logged(f)  

and your function f is replaced with the function with_logging. Unfortunately, this means that if you then say

  1. 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:

  1. from functools import wraps  
  2. def logged(func):  
  3.     @wraps(func)  
  4.     def with_logging(*args, **kwargs):  
  5.         print func.__name__ + " was called"  
  6.         return func(*args, **kwargs)  
  7.     return with_logging  
  8.  
  9. @logged  
  10. def f(x):  
  11.    """does some math"""  
  12.    return x + x * x  
  13.   
  14. print f.__name__  # prints 'f'  
  15. print f.__doc__   # prints 'does some math'  


    轉自http://blog.csdn.net/wanghai__/article/details/7078792

posted @ 2013-11-05 20:40 周強 閱讀(392) | 評論 (0)編輯 收藏

2013年1月8日 #

Oracle Redo Log 機制 小結(轉載)

     摘要: Oracle 的Redo 機制DB的一個重要機制,理解這個機制對DBA來說也是非常重要,之前的Blog里也林林散散的寫了一些,前些日子看老白日記里也有說明,所以結合老白日記里的內容,對oracle 的整個Redo log 機制重新整理一下。 一.Redo log 說明Oracle 的Online redo log 是為確保已經提交的事務不會丟失而建立的一個機制。 因為這種健全的機制,才能...  閱讀全文

posted @ 2013-01-08 19:20 周強 閱讀(4923) | 評論 (0)編輯 收藏

2012年12月26日 #

【操作系統】Linux性能監控——CPU、Memory、IO、Network(轉載)

     摘要: 【操作系統】Linux性能監控——CPU、Memory、IO、Networkby illidan(綜合了幾篇文章和自己的實踐) 一、CPU1、良好狀態指標CPU利用率:User Time <= 70%,System Time <= 35%,User Time + System Time <= 70%。上下文切換:與CPU利用率相關聯,如果CPU利...  閱讀全文

posted @ 2012-12-26 22:02 周強 閱讀(1389) | 評論 (0)編輯 收藏

2012年8月28日 #

top命令(轉載)

     摘要: TOP是一個動態顯示過程,即可以通過用戶按鍵來不斷刷新當前狀態.如果在前臺執行該命令,它將獨占前臺,直到用戶終止該程序為止.比較準確的說,top命令提供了實時的對系統處理器的狀態監視.它將顯示系統中CPU最“敏感”的任務列表.該命令可以按CPU使用.內存使用和執行時間對任務進行排序;而且該命令的很多特性都可以通過交互式命令或者在個人定制文件中進行設定.top - 12:38...  閱讀全文

posted @ 2012-08-28 20:42 周強 閱讀(396) | 評論 (0)編輯 收藏

2012年3月2日 #

技巧:多共享動態庫中同名對象重復析構問題的解決方法(轉載)

Linux 支持的共享程序庫(lib*.so)技術不僅能夠有效利用系統資源,而且還對程序設計帶來了很大的便利性、通用性等,因此被各種級別的應用系統廣泛采用。 動態鏈接的共享庫是在加載應用程序時被加載的,而且它與應用程序是在運行時綁定的:通過動態鏈接器,將動態共享庫映射進應用程序的可執行內存中(動態鏈接);在啟動應用程序時,動態裝載器將所需的共享目標庫映射到應用程序的內存(動態裝載)。

在通常情況下,共享庫都是通過使用附加選項 -fpic 或 -fPIC 進行編譯,從目標代碼產生位置無關的代碼(Position Independent Code,PIC),使用 -shared選項將目標代碼放進共享目標庫中。位置無關代碼需要能夠被加載到不同進程的不同地址,并且能得以正確的執行,故其代碼要經過特別的編譯處理:位置無關代碼(PIC)對常量和函數入口地址的操作都是采用基于基寄存器(base register)BASE+ 偏移量的相對地址的尋址方式。即使程序被裝載到內存中的不同地址,即 BASE 值不同,而偏移量是不變的,所以程序仍然可以找到正確的入口地址或者常量。

然而,當應用程序鏈接了多個共享庫,如果在這些共享庫中,存在相同作用域范圍的同名靜態成員變量或者同名 ( 非靜態 ) 全局變量,那么當程序訪問完靜態成員變量或全局變量結束析構時,由于某內存塊的 double free 會導致 core dump,這是由于 Linux 編譯器的缺陷造成的。

應用場景原型

該問題源于筆者所從事的開發項目:IBM Tivoli Workload Scheduler (TWS) LoadLeveler。LoadLeveler是 IBM在高性能計算(High Performance Computing,HPC)領域的一款作業調度軟件。它主要分為兩個大的模塊,分別是調度模塊(scheduler)和資源管理模塊(resource manger)。 兩個模塊中分別含有關于配置管理功能的共享庫,由于某些配置管理選項為兩模塊所共同采用,所以兩模塊之間共享了部分源文件代碼,其中包含有同名的類靜態成員。

可以通過以下簡單的模型進行描述:


圖 1. 應用場景
圖片示例 

對應的各模塊代碼片段如下圖所示:


圖 2. 應用場景模擬代碼
圖片示例 

其中,test.c 是主程序,包含有兩個頭文件:api1.h 與 api2.h;頭文件 api1.h 包含頭文件 lib1/lib.h 和一功能函數 func_api1(),api2.h 包含頭文件 lib2/lib.h 和一功能函數 func_api2();目錄 lib1 和 lib2 下的源文件分別編譯生成共享庫 lib1.so 和 lib2.so。同時,頭文件 lib1/lib.h 與 lib2/lib.h 鏈接到同一共享文件 lib.h。在文件 lib.h 中定義有一靜態成員變量“static std::vector<int> vec_int”。

回頁首

功能函數與各靜態成員函數代碼清單

功能函數 func_api1() 與 func_api2() 的實現類似,通過調用靜態成員函數達到訪問靜態成員變量 vec_int的目的:


清單 1. 功能函數 func_api1(int)
          void func_api1(int i) {      printf("%s.\n", __FILE__);       A::set(i);      A::print();      return;   }      

靜態成員函數 A::set() 與 A::print() 的實現如下:


清單 2. 靜態成員函數 A::set(int)
          void A::set(int num) {      vec_int.clear();      for (int i = 0; i < num; i++) {          vec_int.push_back(i);      }      return;   }      


清單 3. 靜態成員函數 A::print()
          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() 對靜態成員 vec_int進行賦值操作,而 A::print() 則打印其中的值與當前項的內存地址。

回頁首

運行結果

如果兩個共享庫是通過選項 -fpic或 -fPIC編譯的話,運行程序 test,輸出如下:


清單 4. 選項 -fPIC 的測試結果
          $ 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 產生是由于堆內存區域(09cbf000-09ce0000)中起始地址為 0x09cbf028的內存區被釋放了兩次導致的,該地址正式靜態成員變量 vec_int的第一個元素的地址。

為什么會出現同一塊內存區,被釋放兩次的情形呢?

回頁首

原因分析

我們知道,靜態成員變量與全局變量類似,都采用了靜態存儲方式。對于加了選項 -fpic或 -fPIC的共享庫,這些變量的地址都存放在該共享庫的全局偏移表(Global Offset Table,GOT)中。

通過 objdump或者 readelf命令分析共享庫 lib1.so,結果如下:


清單 5. objdump 分析共享庫 lib1.so 的輸出
          $ 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   ......      


清單 6. readelf 分析共享庫 lib1.so 的輸出
          $ 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   ......      

從上面兩個命令的輸出結果中可以看出,共享庫 lib1.so中 GOT段的起始內存地址為 000030f4,大小為 20 字節 (0x14);靜態成員變量 vec_int在共享庫 lib1.so中的起始偏移地址為 000030fc。顯然,vec_int位于該共享庫的 GOT段內。

當應用程序同時鏈接 lib1.so和 lib2.so時,同名靜態成員變量 vec_int分別位于其共享庫的 GOT區。當程序運行時,系統從符號表中查找并裝載構造一份 vec_int數據,這點從程序運行的輸出結果(清單 4)的“Backtrace”部分可以看到:只有 lib1.so中的靜態成員變量被裝載構造;同時,通過內存映射(Memory map)部分(清單 4),可以觀察到 vec_int對象的地址 0xe89228正好處在為共享庫 lib1.so分配的可讀內存區 00e89000-00e8a000中:

        00e89000-00e8a000 rwxp 00003000 00:1b 7668022    ./lib1.so

然后,當程序結束時,卻對該變量進行了兩次析構操作,通過 gdb分析 core 文件:


清單 7. core 文件分析結果
          $ 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 開始,程序進行 lib2.so中的析構操作,直到 #11,都運行在 lib2.so中,當進入幀 #10 時,進行變量析構時,其地址為 0x00e8850c,該地址中的對象是程序啟動時由共享庫 lib1.so裝載構造出來的(清單 1):

        ./lib1.so(_ZNSt6vectorIiSaIiEED1Ev+0x60)[0xe8850c]

當程序結束時,運行庫 glibc檢測到共享庫 lib2.so析構了并非由其構造的對象,導致了 core dump。

這種情況下,如果替換使用選項 -fpie或 -fPIE,操作步驟與運行結果如下所示:


清單 8. 選項 -fPIE 的測試結果
          $ 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.   $      

程序運行結果符合期望并正常結束。

這是因為,當使用選項 -fpie或 -fPIE時,生成的共享庫不會為靜態成員變量或全局變量在 GOT中創建對應的條目(通過 objdumpreadelf命令可以查看,此處不再贅述),從而避免了由于靜態對象“構造一次,析構兩次”而對同一內存區域釋放兩次引起的程序 core dump。

選項 -fpie和 -fPIE與 -fpic及 -fPIC的用法很相似,區別在于前者總是將生成的位置無關代碼看作是屬于程序本身,并直接鏈接進該可執行程序,而非存入全局偏移表 GOT中;這樣,對于同名的靜態或全局對象的訪問,其構造與析構操作將保持一一對應。

回頁首

結束語

通過使用選項 -fpie或 -fPIE代替 -fpic或者 -fPIC,使得生成的共享庫不會為靜態成員變量或全局變量在 GOT中創建對應的條目,同時也就避免了針對同名靜態對象“構造一次,析構兩次”的不當操作。

轉自:http://www.ibm.com/developerworks/cn/linux/l-cn-sdlstatic/ 





posted @ 2012-03-02 16:04 周強 閱讀(455) | 評論 (0)編輯 收藏

2012年3月1日 #

CppUnit快速入門(轉載)

簡介

測試是軟件開發過程中極其重要的一環,詳盡周密的測試能夠減少軟件BUG,提高軟件品質。測試包括單元測試、系統測試等。其中單元測試是指針對軟件功能單元所作的測試,這里的功能單元可以是一個類的屬性或者方法,測試的目的是看這些基本單元是否工作正常。由于單元測試的內容很基礎,因此可以看作是測試工作的第一環,該項工作一般由開發人員自行完成。如果條件允許,單元測試代碼的開發應與程序代碼的開發同步進行。

雖然不同程序的單元測試代碼不盡相同,但測試代碼的框架卻非常相似,于是便出現了一些單元測試類庫,CppUnit便是其中之一。

CppUnit是XUnit中的一員,XUnit是一個大家族,還包括JUnit和PythonUnit等。CppUnit簡單實用,學習和使用起來都很方便,網上已有一些文章對其作介紹,但本文更著重于講解其中的基本概念和使用方法,以幫助初次接觸CppUnit的人員快速入門。

安裝

目前,CppUnit的最新版本是1.10.2,你可以從下面地址獲?。?/p>http://sourceforge.net/projects/cppunit

解壓后,你可以看到CppUnit包含如下目錄:

config: 配置文件 contrib: contribution,其他人貢獻的外圍代碼 doc: 文檔,需要通過doxygen工具生成,也可以直接從sourceforge站點上下載打包好的文檔 examples:示例代碼 include: 頭文件 lib: 存放編譯好的庫 src: 源文件,以及編譯庫的工程等

然后打開src目錄下的CppUnitLibraries工程,執行build/batch build,編譯成功的話,生成的庫文件將被拷貝到lib目錄下。

你也可以根據需要選擇所需的項目進行編譯,其中項目cppunit為靜態庫,cppunit_dll為動態庫,生成的庫文件為:

cppunit.lib: 靜態庫release版 cppunitd.lib: 靜態庫debug版 cppunit_dll.lib: 動態庫release版 cppunitd_dll.lib:動態庫debug版

要使用CppUnit,還得設置好頭文件和庫文件路徑,以VC6為例,選擇Tools/Options/Directories,在Include files和Library files中分別添加%CppUnitPath%/include和%CppUnitPath%/lib,其中%CppUnitPath%表示CppUnit所在路徑。

做好準備工作后,我們就可以編寫自己的單元測試代碼了。需說明的是,CppUnit所用的動態運行期庫均為多線程動態庫,因此你的單元測試程序也得使用相應設置,否則會發生沖突。

概念

在使用之前,我們有必要認識一下CppUnit中的主要類,當然你也可以先看后面的例子,遇到問題再回過頭來看這一節。

CppUnit核心內容主要包括六個方面,

1. 測試對象(Test,TestFixture,...):用于開發測試用例,以及對測試用例進行組織管理。

2. 測試結果(TestResult):處理測試用例執行結果。TestResult與下面的TestListener采用的是觀察者模式(Observer Pattern)。

3. 測試結果監聽者(TestListener):TestListener作為TestResult的觀察者,擔任實際的結果處理角色。

4. 結果輸出(Outputter):將結果進行輸出,可以制定不同的輸出格式。

5. 對象工廠(TestFactory):用于創建測試對象,對測試用例進行自動化管理。

6. 測試執行體(TestRunner):用于運行一個測試。

以上各模塊的主要類繼承結構如下:

Test TestFixture TestResult TestListener _______|_________ | | | | | TestSuccessListener TestComposite TestLeaf | | | |____________| TestResultCollector TestSuit | TestCase | TestCaller<Fixture> Outputter TestFactory TestRunner ____________________|_________________ | | | | TestFactoryRegistry CompilerOutputter TextOutputter XmlOutputter | TestSuiteFactory<TestCaseType>

接下來再對其中一些關鍵類作以介紹。

Test:所有測試對象的基類。

CppUnit采用樹形結構來組織管理測試對象(類似于目錄樹),因此這里采用了組合設計模式(Composite Pattern),Test的兩個直接子類TestLeaf和TestComposite分別表示“測試樹”中的葉節點和非葉節點,其中TestComposite主要起組織管理的作用,就像目錄樹中的文件夾,而TestLeaf才是最終具有執行能力的測試對象,就像目錄樹中的文件。

Test最重要的一個公共接口為:

virtual void run(TestResult *result) = 0;

其作用為執行測試對象,將結果提交給result。

在實際應用中,我們一般不會直接使用Test、TestComposite以及TestLeaf,除非我們要重新定制某些機制。

TestFixture:用于維護一組測試用例的上下文環境。

在實際應用中,我們經常會開發一組測試用例來對某個類的接口加以測試,而這些測試用例很可能具有相同的初始化和清理代碼。為此,CppUnit引入TestFixture來實現這一機制。

TestFixture具有以下兩個接口,分別用于處理測試環境的初始化與清理工作:

virtual void setUp(); 
virtual void tearDown(); 

TestCase:測試用例,從名字上就可以看出來,它便是單元測試的執行對象。

TestCase從Test和TestFixture多繼承而來,通過把Test::run制定成模板函數(Template Method)而將兩個父類的操作融合在一起,run函數的偽定義如下:

// 偽代碼 
void TestCase::run(TestResult* result)
{
    result->startTest(this); // 通知result測試開始
    if( result->protect(this, &TestCase::setUp) ) // 調用setUp,初始化環境
        result->protect(this, &TestCase::runTest); // 執行runTest,即真正的測試代碼
    result->protect(this, &TestCase::tearDown); // 調用tearDown,清理環境
    result->endTest(this); // 通知result測試結束
}

這里要提到的是函數runTest,它是TestCase定義的一個接口,原型如下:

virtual void runTest();

用戶需從TestCase派生出子類并實現runTest以開發自己所需的測試用例。

另外還要提到的就是TestResult的protect方法,其作用是對執行函數(實際上是函數對象)的錯誤信息(包括斷言和異常等)進行捕獲,從而實現對測試結果的統計。

TestSuit:測試包,按照樹形結構管理測試用例

TestSuit是TestComposite的一個實現,它采用vector來管理子測試對象(Test),從而形成遞歸的樹形結構。

TestCaller:TestCase適配器(Adapter),它將成員函數轉換成測試用例

雖然我們可以從TestCase派生自己的測試類,但從TestCase類的定義可以看出,它只能支持一個測試用例,這對于測試代碼的組織和維護很不方便,尤其是那些有共同上下文環境的一組測試。為此,CppUnit提供了TestCaller以解決這個問題。

TestCaller是一個模板類,它以實現了TestFixture接口的類為模板參數,將目標類中某個符合runTest原型的測試方法適配成TestCase的子類。

在實際應用中,我們大多采用TestFixture和TestCaller相組合的方式,具體例子參見后文。

TestResult和TestListener:處理測試信息和結果

前面已經提到,TestResult和TestListener采用了觀察者模式,TestResult維護一個注冊表,用于管理向其登記過的TestListener,當TestResult收到測試對象(Test)的測試信息時,再一一分發給它所管轄的TestListener。這一設計有助于實現對同一測試的多種處理方式。

TestFactory:測試工廠

這是一個輔助類,通過借助一系列宏定義讓測試用例的組織管理變得自動化。參見后面的例子。

TestRunner:用于執行測試用例

TestRunner將待執行的測試對象管理起來,然后供用戶調用。其接口為:

virtual void addTest( Test *test ); virtual void run( TestResult &controller, const std::string &testPath = "" );

這也是一個輔助類,需注意的是,通過addTest添加到TestRunner中的測試對象必須是通過new動態創建的,用戶不能刪除這個對象,因為TestRunner將自行管理測試對象的生命期。

使用

先讓我們看看一個簡單的例子:

#include <cppunit/TestCase.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/TextOutputter.h>

// 定義測試用例
class SimpleTest : public CppUnit::TestCase
{
public:
    void runTest() // 重載測試方法
    {
        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); // 運行測試用例

    CppUnit::TextOutputter o(&rc, std::cout);
    o.write(); // 將結果輸出

    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定義了一組宏用于檢測錯誤,CPPUNIT_ASSERT_EQUAL是其中之一,當斷言失敗時,CppUnit便會將錯誤信息報告給TestResult。這些宏定義的說明如下:

CPPUNIT_ASSERT(condition):判斷condition的值是否為真,如果為假則生成錯誤信息。

CPPUNIT_ASSERT_MESSAGE(message, condition):與CPPUNIT_ASSERT類似,但結果為假時報告messsage信息。

CPPUNIT_FAIL(message):直接報告messsage錯誤信息。

CPPUNIT_ASSERT_EQUAL(expected, actual):判斷expected和actual的值是否相等,如果不等輸出錯誤信息。

CPPUNIT_ASSERT_EQUAL_MESSAGE(message, expected, actual):與CPPUNIT_ASSERT_EQUAL類似,但斷言失敗時輸出message信息。

CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, actual, delta):判斷expected與actual的偏差是否小于delta,用于浮點數比較。

CPPUNIT_ASSERT_THROW(expression, ExceptionType):判斷執行表達式expression后是否拋出ExceptionType異常。

CPPUNIT_ASSERT_NO_THROW(expression):斷言執行表達式expression后無異常拋出。

接下來再看看TestFixture和TestCaller的組合使用:

#include <cppunit/TestCase.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/TextOutputter.h>
#include <cppunit/TestCaller.h>
#include <cppunit/TestRunner.h>

// 定義測試類
class StringTest : public CppUnit::TestFixture
{
public:
    void setUp() // 初始化
    {
        m_str1 = "Hello, world";
        m_str2 = "Hi, cppunit";
    }

    void tearDown() // 清理
    {
    }

    void testSwap() // 測試方法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() // 測試方法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); // 運行測試

    CppUnit::TextOutputter o(&rc, std::cout);
    o.write(); // 將結果輸出

    return rc.wasSuccessful() ? 0 : -1;
}
編譯后運行結果為:
OK (2 tests)

上面的代碼從功能上講沒有什么問題,但編寫起來太繁瑣了,為此,我們可以借助CppUnit定義的一套輔助宏,將測試用例的定義和注冊變得自動化。上面的代碼改造后如下:

#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/TextOutputter.h>
#include <cppunit/TestRunner.h>
#include <cppunit/extensions/HelperMacros.h>


// 定義測試類
class StringTest : public CppUnit::TestFixture
{
    CPPUNIT_TEST_SUITE(StringTest);  // 定義測試包
    CPPUNIT_TEST(testSwap);  // 添加測試用例1
    CPPUNIT_TEST(testFind);  // 添加測試用例2
    CPPUNIT_TEST_SUITE_END();  // 結束測試包定義
    
public:
    void setUp() // 初始化
    {
        m_str1 = "Hello, world";
        m_str2 = "Hi, cppunit";
    }

    void tearDown() // 清理
    {
    }

    void testSwap() // 測試方法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() // 測試方法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); // 自動注冊測試包

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); // 運行測試

    CppUnit::TextOutputter o(&rc, std::cout);
    o.write(); // 將結果輸出

    return rc.wasSuccessful() ? 0 : -1;
}

CppUnit的簡單介紹就到此,相信你已經了解了其中的基本概念,也能夠開發單元測試代碼了。

轉自:http://blog.csdn.net/freefalcon/article/details/753819

posted @ 2012-03-01 20:25 周強 閱讀(266) | 評論 (0)編輯 收藏

2011年12月25日 #

分布式

     發現已經很久沒寫博客了,主要前段時間實在太忙了,實習,找工作,畢業答辯,經歷了很多事情,現在終于空閑下來了。從明天開始要研究下分布式系統了,計劃設計實現個分布式數據庫。

posted @ 2011-12-25 23:49 周強 閱讀(217) | 評論 (0)編輯 收藏

2011年8月13日 #

江湖

    這兩天放假,又看了下笑傲江湖??吹叫Π两星蠛蛣⒄L雖然隸屬不同派別,因為音律成為知己,合奏出一曲《笑傲江湖》。人生中又能碰到幾個能真正談心的知己呢。人生或許就像江湖,總會遇到很多人,很多事,總會遇到很多挫折,低谷。人生短短幾十載,應該豁達些,沒必要去想那些煩心事,這世界牛人很多,我注定也是個小人物,我能做的也就是盡量做好自己能做的事。
   

posted @ 2011-08-13 23:45 周強 閱讀(409) | 評論 (5)編輯 收藏

僅列出標題  下一頁
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美日韩国产不卡| 欧美在线视频在线播放完整版免费观看| 亚洲电影网站| 久久久久久久久久码影片| 一本大道久久精品懂色aⅴ| 亚洲国产裸拍裸体视频在线观看乱了中文 | 亚洲欧美精品在线观看| 一区二区日韩欧美| 国产婷婷色综合av蜜臀av | 狂野欧美激情性xxxx欧美| 麻豆久久婷婷| 国产视频一区在线观看一区免费| 亚洲免费观看| 欧美高清在线一区| 亚洲性人人天天夜夜摸| 亚洲国产精品一区在线观看不卡| 欧美韩日亚洲| 亚洲夜间福利| 久久综合伊人77777尤物| 欧美成人精品一区| 亚洲欧美国产视频| 欧美1区视频| 久久婷婷国产综合尤物精品| 欧美暴力喷水在线| 夜夜爽av福利精品导航 | 免费精品视频| 欧美一区二区成人| 欧美成人精品不卡视频在线观看| 久久久亚洲国产美女国产盗摄| 午夜精品久久久久影视| 免费在线观看精品| 免费观看成人鲁鲁鲁鲁鲁视频| 国产日韩亚洲欧美综合| 亚洲视频一区在线| 亚洲宅男天堂在线观看无病毒| 夜夜嗨av一区二区三区网站四季av | 亚洲精品色婷婷福利天堂| 欧美日韩亚洲系列| 久久亚洲欧美国产精品乐播| 欧美高清不卡在线| 国产女人水真多18毛片18精品视频| 欧美国产成人精品| 一区二区三区在线看| 香蕉视频成人在线观看| 久久夜精品va视频免费观看| 久久成人这里只有精品| 136国产福利精品导航网址| 欧美怡红院视频| 国产精品亚洲美女av网站| 亚洲欧美在线磁力| 亚洲美女少妇无套啪啪呻吟| 女人香蕉久久**毛片精品| 激情丁香综合| 亚洲高清在线播放| 欧美国产一区二区| 亚洲欧洲美洲综合色网| 在线看片一区| 欧美成人午夜剧场免费观看| 欧美精品首页| 国产最新精品精品你懂的| 亚洲国产一区在线观看| 亚洲综合色在线| 国产精品毛片a∨一区二区三区|国| 亚洲人精品午夜在线观看| 欧美一区二视频在线免费观看| 国产精品久久999| 亚洲国产精品成人综合色在线婷婷 | 国产日韩在线看| 蜜桃av噜噜一区二区三区| 欧美另类亚洲| 激情欧美一区二区三区在线观看 | 嫩模写真一区二区三区三州| 午夜精品一区二区三区在线| 午夜免费日韩视频| 性做久久久久久久免费看| 日韩一级在线观看| 99热这里只有成人精品国产| 国产精品免费看| 麻豆乱码国产一区二区三区| 亚洲精品免费电影| 欧美影视一区| 中日韩高清电影网| 免费观看亚洲视频大全| 在线视频免费在线观看一区二区| 中文欧美在线视频| 亚洲免费在线播放| 亚洲国产精品黑人久久久 | 欧美午夜三级| 久久久久久噜噜噜久久久精品| 欧美成人午夜激情| 亚洲欧美999| 麻豆精品网站| 欧美一区二区大片| 欧美午夜片欧美片在线观看| 欧美国产激情二区三区| 欧美福利电影在线观看| 久久久久久一区二区三区| 欧美日韩免费一区二区三区| 免费不卡在线观看| 国产精品一区一区| 日韩亚洲综合在线| 亚洲日本aⅴ片在线观看香蕉| 欧美一区在线视频| 亚洲影视综合| 亚洲午夜小视频| 99天天综合性| 久久影院午夜片一区| 午夜综合激情| 国产日韩欧美在线视频观看| 久久精品99| 欧美日一区二区在线观看 | 一本色道久久加勒比精品| 亚洲精华国产欧美| 久久深夜福利免费观看| 久久免费国产| 韩国女主播一区二区三区| 亚洲在线一区| 亚洲制服av| 欧美日韩综合视频| 亚洲精品国产精品国自产在线| 国产在线观看精品一区二区三区| 亚洲在线播放电影| 亚洲每日在线| 欧美成人一区二区三区| 在线日韩中文| 免费成人性网站| 久久婷婷国产综合国色天香| 国产亚洲欧洲997久久综合| 欧美一级片一区| 美女视频黄 久久| 亚洲黄色成人久久久| 亚洲永久网站| 久久亚洲综合| 亚洲黄色影院| 欧美日韩一区在线观看视频| 一区二区三区视频在线观看 | 日韩一区二区免费看| 欧美成人蜜桃| 国产欧美va欧美va香蕉在| 亚洲欧美日韩精品久久奇米色影视| 国产精品私拍pans大尺度在线| 亚洲视频一区二区在线观看| 久久嫩草精品久久久久| 国产日韩欧美不卡| 免费久久精品视频| 乱码第一页成人| 亚洲在线国产日韩欧美| 红杏aⅴ成人免费视频| 美女国产精品| 亚洲一区日韩在线| 久久男女视频| 午夜精品国产更新| 在线免费不卡视频| 国产欧美91| 欧美国产精品中文字幕| 欧美一级精品大片| 亚洲欧洲日本国产| 免费看黄裸体一级大秀欧美| 99re66热这里只有精品4| 狠狠色狠狠色综合| 欧美视频在线免费看| 蜜桃视频一区| 亚洲精品在线视频观看| 欧美不卡高清| 久热这里只精品99re8久| 亚洲午夜精品久久久久久app| 亚洲乱码精品一二三四区日韩在线 | 欧美国产丝袜视频| 国产色产综合色产在线视频| 麻豆成人小视频| 一区二区三区久久久| 亚洲国产精品综合| 久久大逼视频| 在线天堂一区av电影| 亚洲国产一区在线观看| 久久综合伊人77777麻豆| 久久久久久夜精品精品免费| 亚洲乱码精品一二三四区日韩在线 | 一区二区电影免费观看| 很黄很黄激情成人| 国内精品久久久久伊人av| 亚洲小视频在线| 亚洲美女91| 日韩小视频在线观看| 男人的天堂亚洲在线| 欧美成人三级在线| 久久成人免费网| 久久久亚洲人| 欧美在线一二三区| 久久久av网站| 亚洲欧洲综合另类| 亚洲乱码一区二区| 中日韩美女免费视频网站在线观看| 亚洲精品久久在线| 国产精品一区二区a| 国产精品二区在线观看| 国产日产亚洲精品系列| 国产精品久线观看视频| 国产美女搞久久| 好看的av在线不卡观看|