﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-ngaut</title><link>http://www.cppblog.com/ngaut/</link><description>asm/c/c++/......</description><language>zh-cn</language><lastBuildDate>Thu, 04 Dec 2008 23:02:27 GMT</lastBuildDate><pubDate>Thu, 04 Dec 2008 23:02:27 GMT</pubDate><ttl>60</ttl><item><title>[转]udpcast</title><link>http://www.cppblog.com/ngaut/archive/2008/10/27/65265.html</link><dc:creator>ngaut</dc:creator><author>ngaut</author><pubDate>Mon, 27 Oct 2008 15:39:00 GMT</pubDate><guid>http://www.cppblog.com/ngaut/archive/2008/10/27/65265.html</guid><wfw:comment>http://www.cppblog.com/ngaut/comments/65265.html</wfw:comment><comments>http://www.cppblog.com/ngaut/archive/2008/10/27/65265.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ngaut/comments/commentRss/65265.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ngaut/services/trackbacks/65265.html</trackback:ping><description><![CDATA[<pre>Guofu,<br>  Following is what I got from  Alain long time ago.  Hope this helps.<br>   <br>  Thanks,<br>  Sai<br>   <br>  *****************************************************************<br>  There are no format documents yet about the protocol. The protocol is<br>not based on an RFC, but is homegrown specifically for udpcast.<br><br>Updcast uses two UDP port numbers, 9000 and 9001.<br><br>The receiver listens on 9000 (portbase), the sender on 9001 <br>(portbase+1)<br><br>The protocol runs as follows:<br><br>1. When the sender starts up, it broadcast a CMD_HELLO message to the<br>local network broadcast address<br><br>2. When the receiver starts up, or whenever it receives a CMD_HELLO<br>message, it sends a CMD_CONNECT_REQ address. If the CMD_CONNECT_REQ is<br>sent at startup, it is broadcast; else it is sent to the server's<br>address (as deducted from the CMD_HELLO message).<br><br>This allows the rendez-vous to be established no matter whether the<br>client or the server first starts up. Additionnally, the server can be<br>set up to periodically send its CMD_HELLO message (interesting for<br>asynchronous mode, see below).<br><br><br>3. The server replies to each CMD_CONNECT_REQ with a CMD_CONNECT_REPLY<br>(unicasted to the client who sent the CMD_CONNECT_REQ). The connect<br>reply contains the client number that the server assigned to that<br>client (clNr), the block size (size of packed), a bitmask of<br>capabilities, and the multicast address to be used for the actual data<br>transfer transfer.<br><br>At this stage, server and client know about each other, and are ready<br>to start the transfer. For convenience, the transfer may either be<br>started at the server, or at any participating client.<br><br>4. If transfer start is initiated by a client, it sends the server a<br>CMD_GO message.<br><br>5. If the transfer start is initiated by the server (or, after<br>reception of the CMD_GO message from a client), the server starts<br>transfering data by sending CMD_DATA packets. The reception of the<br>first CMD_DATA packet is a signal to all clients that now the<br>rendez-vous phase is over, and that the transfer has started.<br><br>The data is subdivided into slices, which are themselves subdivided<br>into stripes (only in FEC mode), which are subdivided in network<br>packets, which are made up of bytes.<br><br>A CMD_DATA packet contains the slice number (sliceNo), the block<br>number within that slice (blockNo), and the total number of bytes in<br>the slice, and then the data itself.<br><br>After each slice has been transmitted, lost packets are handled.<br><br>In FEC mode, lost packets are recovered by the client by using the<br>error correction packets included in each slice.<br><br>A CMD_FEC packet contains the number of stripes in the slice, the<br>slice number, the block number, and the number of bytes.<br><br><br>In non-FEC mode, the server asks each client to acknowledge at the end<br>of the slice (CMD_REQACK). The CMD_REQACK contains the identifier of<br>the slice to be acknowleged (sliceNo), the number of bytes in that<br>slice (bytes), and a retransmission counter. The clients reply to the<br>CMD_REQACK either with a CMD_OK (if they received everything) or with<br>a CMD_RETRANSMIT (if packets were missed). Both the CMD_OK and<br>CMD_RETRANSMIT message contain the sliceNo. The CMD_RETRANSMIT message<br>contains also a bitmap of the missed packets, and the retransmit id.<br><br>In response the CMD_RETRANSMIT messages, the server will retransmit<br>packets that have been missed by at least one client, increments the<br>rxmit counter and then send another CMD_REQACK. The rxmit counter is<br>used to discard late CMD_RETRANSMIT messages: indeed, after a round of<br>retransmission, CMD_RETRANSMIT messages from the previous round should<br>be ignored, or else the server may resend packets that have been<br>received in this round.<br><br>Clients may leave a transmission by sending a CMD_DISCONNECT. Sending<br>the CMD_DISCONNECT is important, or else the server will needlessly<br>wait for the acknowledgments of these clients. However, if a client<br>crashes without sending a CMD_DISCONNECT, the server has a timeout to<br>detect this situation, and continue with the other clients ("The<br>client #n has been dropped by the server").<br><br>When all clients have received all packets (i.e. all clients have send<br>a CMD_OK for that slice), the sender moves on to the next slice, until<br>end of file is reached. The server signals end of transfer by sending<br>a slice of zero bytes.<br><br>Including slice size in every packet, and number of stripes in every<br>FEC packet may seem redundant. However, this is needed in order to<br>make the protocol robust in cases of packet loss: if the number of<br>bytes was only in the first or in the last packet, then the loss of<br>that packet would make it hard to recover, because not only the data<br>was lost, but also the meta-data needed to reconstruct that<br>slice. This is especially relevant in FEC mode.<br><br>FEC mode is intended for unidirectional (asynchronous mode). In this<br>mode, there are no acknowledgments, and no retransmissions. This is<br>intended for situations where no receiver-to-sender communication is<br>possible, or where the latency of such a communication would be<br>prohibitively high, such as multicast over satellite.<br><br>The server sends (one or several) CMD_HELLO which includes the<br>multicast address it intends to use, and then starts with the<br>data. Each slice not only contains the data, but also a configurable<br>number of redundant "error correction" packets.<br><br>FEC mode uses an algorithm based on Vandermonde matrices to<br>recalculate the contents of any lost packets. The algorithm is chosen<br>such that all k data packets may be restored as long as the receiver<br>has gotten at least k packets (be it data or FEC). For example, with<br>k-3 data packets, and 3 FEC packets, all k data packets may be<br>reconstructed. K is a parameter of the algorithm, and the higher the<br>value for k, the more computation intensive the algorithm<br>is. Moreover, values of k greater than 128 are not supported. For that<br>reason, each slice (which may be up to 1024 packets) is subdivided in<br>several stripes (of at most 128 packets), which are interleaved<br>(i.e. first comes 1st packet of 1st stripe, than 1st packet of 2nd<br>stripe, ..., then 1st packet of last stripe, than 2nd packet of 1st<br>stripe, etc.) That way a burst loss of packets (for instance, 6<br>packets in a row) won't overly impact one stripe but will rather be<br>spread out among several. Indeed, if udp-sender has been set up to<br>include l redundant packets per stripe, it must be avoided at all cost<br>that more than l packets are lost per stripe, or otherwise the loss in<br>uncrecoverable.<br><br>Additional complications in the protocol arise from the fact that a<br>first version of the protocol used the native byte ordering from Intel<br>processors, rather than use the network byte order. This made udpcast<br>unportable to non-PC architectures. This was changed two years ago;<br>however in order to stay compatible with older versions, the receiver<br>and sender are able to detect that packets with the "wrong" byte order<br>have been received, and are able to correct for that: if the message<br>code (CMD_*) doesn't make sense in network byte order, udpcast tries<br>to interpret it in Intel byte order, and if that matches a known code,<br>the packet is byte-swapped.<br><br><br>Regards,<br><br>Alain<br></pre><img src ="http://www.cppblog.com/ngaut/aggbug/65265.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ngaut/" target="_blank">ngaut</a> 2008-10-27 23:39 <a href="http://www.cppblog.com/ngaut/archive/2008/10/27/65265.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>小心vmware6.0和vs2008的冲突</title><link>http://www.cppblog.com/ngaut/archive/2008/10/22/64683.html</link><dc:creator>ngaut</dc:creator><author>ngaut</author><pubDate>Wed, 22 Oct 2008 02:35:00 GMT</pubDate><guid>http://www.cppblog.com/ngaut/archive/2008/10/22/64683.html</guid><wfw:comment>http://www.cppblog.com/ngaut/comments/64683.html</wfw:comment><comments>http://www.cppblog.com/ngaut/archive/2008/10/22/64683.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/ngaut/comments/commentRss/64683.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ngaut/services/trackbacks/64683.html</trackback:ping><description><![CDATA[如果安装vmware6.0后，vs2008打开工程后自动关闭，卸载vmware试试，偶被这个问题小郁闷了一把
<img src ="http://www.cppblog.com/ngaut/aggbug/64683.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ngaut/" target="_blank">ngaut</a> 2008-10-22 10:35 <a href="http://www.cppblog.com/ngaut/archive/2008/10/22/64683.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>asio工程向boost.asio转换注意事项</title><link>http://www.cppblog.com/ngaut/archive/2008/10/21/64602.html</link><dc:creator>ngaut</dc:creator><author>ngaut</author><pubDate>Tue, 21 Oct 2008 03:50:00 GMT</pubDate><guid>http://www.cppblog.com/ngaut/archive/2008/10/21/64602.html</guid><wfw:comment>http://www.cppblog.com/ngaut/comments/64602.html</wfw:comment><comments>http://www.cppblog.com/ngaut/archive/2008/10/21/64602.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ngaut/comments/commentRss/64602.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ngaut/services/trackbacks/64602.html</trackback:ping><description><![CDATA[<h3>What are the differences in the source code?</h3>
<p>— Asio is in a namespace called <tt>asio::</tt>, whereas Boost.Asio puts everything under <tt>boost::asio::</tt>.</p>
<p>— The main Asio header file is called <tt>asio.hpp</tt>. The corresponding header in Boost.Asio is <tt>boost/asio.hpp</tt>. All other headers are similarly changed.</p>
<p>— Any macros used by or defined in Asio are prefixed with <tt>ASIO_</tt>. In Boost.Asio they are prefixed with <tt>BOOST_ASIO_</tt>.</p>
<p>— Asio includes a class for launching threads, <tt>asio::thread</tt>. Boost.Asio does not include this class, to avoid overlap with the Boost.Thread library</p>
<p>— Boost.Asio uses the Boost.System library to provide support for error codes (<tt>boost::system::error_code</tt> and <tt>boost::system::system_error</tt>). Asio includes these under its own namespace (<tt>asio::error_code</tt> and <tt>asio::system_error</tt>). The Boost.System version of these classes currently supports better extensibility for user-defined error codes. 需要包含&lt;boost/system/system_error.hpp&gt; 头文件</p>
<p>— Asio is header-file-only and for most uses does not require linking against any Boost library. Boost.Asio always requires that you link against the Boost.System library, and also against Boost.Thread if you want to launch threads using <tt>boost::thread</tt>.需要包含&lt;boost/thread.hpp&gt; </p>
<img src ="http://www.cppblog.com/ngaut/aggbug/64602.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ngaut/" target="_blank">ngaut</a> 2008-10-21 11:50 <a href="http://www.cppblog.com/ngaut/archive/2008/10/21/64602.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>os开发资源</title><link>http://www.cppblog.com/ngaut/archive/2008/10/14/63986.html</link><dc:creator>ngaut</dc:creator><author>ngaut</author><pubDate>Tue, 14 Oct 2008 13:26:00 GMT</pubDate><guid>http://www.cppblog.com/ngaut/archive/2008/10/14/63986.html</guid><wfw:comment>http://www.cppblog.com/ngaut/comments/63986.html</wfw:comment><comments>http://www.cppblog.com/ngaut/archive/2008/10/14/63986.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ngaut/comments/commentRss/63986.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ngaut/services/trackbacks/63986.html</trackback:ping><description><![CDATA[http://wiki.osdev.org/Serial_ports<br><a href="http://wiki.zh-kernel.org/project/linux-acpi">http://wiki.zh-kernel.org/project/linux-acpi</a><br><a href="http://udpcast.linux.lu/mkimagedoc.html">http://udpcast.linux.lu/mkimagedoc.html</a><br><a href="http://linuxguy.org/docs/linuxdisk/">http://linuxguy.org/docs/linuxdisk/</a><br><br><br>build floppy linux<br>http://ljh.ee.nchu.edu.tw/~cch/program/fdlinux.html<br>http://hi.baidu.com/magicfrog/blog/index/5<br>
<img src ="http://www.cppblog.com/ngaut/aggbug/63986.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ngaut/" target="_blank">ngaut</a> 2008-10-14 21:26 <a href="http://www.cppblog.com/ngaut/archive/2008/10/14/63986.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>发一个招聘贴</title><link>http://www.cppblog.com/ngaut/archive/2008/09/19/62309.html</link><dc:creator>ngaut</dc:creator><author>ngaut</author><pubDate>Fri, 19 Sep 2008 14:06:00 GMT</pubDate><guid>http://www.cppblog.com/ngaut/archive/2008/09/19/62309.html</guid><wfw:comment>http://www.cppblog.com/ngaut/comments/62309.html</wfw:comment><comments>http://www.cppblog.com/ngaut/archive/2008/09/19/62309.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ngaut/comments/commentRss/62309.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ngaut/services/trackbacks/62309.html</trackback:ping><description><![CDATA[要求:<br>1. 熟悉汇编语言，c/c++，熟悉80x86体系架构，实模式，保护模式<br>2. 良好的数据结构与算法基础<br>3. 熟悉MFC或者STL<br>4. 工作地点在武汉<br>5. 学历不限，英语不限，人品良好，有团队精神<br>6. 特别优秀者，只需具备：人品良好，有团队精神<br><br>有以下经验优先：<br>1. os引导程序开发， os开发<br>2. bios开发经验<br>3. 熟悉Linux裁剪，Linux内核<br>4. 有编译器开发经验<br><br>tel:13554674976&nbsp; 刘先生<br><br> <img src ="http://www.cppblog.com/ngaut/aggbug/62309.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ngaut/" target="_blank">ngaut</a> 2008-09-19 22:06 <a href="http://www.cppblog.com/ngaut/archive/2008/09/19/62309.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]Bug管理的经验和实践[转载]</title><link>http://www.cppblog.com/ngaut/archive/2008/09/09/61354.html</link><dc:creator>ngaut</dc:creator><author>ngaut</author><pubDate>Tue, 09 Sep 2008 00:07:00 GMT</pubDate><guid>http://www.cppblog.com/ngaut/archive/2008/09/09/61354.html</guid><wfw:comment>http://www.cppblog.com/ngaut/comments/61354.html</wfw:comment><comments>http://www.cppblog.com/ngaut/archive/2008/09/09/61354.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ngaut/comments/commentRss/61354.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ngaut/services/trackbacks/61354.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: [转载]孟岩：刘振飞，你好。我知道你以前是方正出版印刷系统的核心开发人员，后来来到微软的Office开发组。我认识你的时候你还在微软工作，状态似乎不错。为什么后来又离开微软了呢？&nbsp;刘振飞：93年到96年，我在北大计算机研究所读研。96年毕业后，我留在所里继续从事方正核心产品世纪RIP --- PSPNT的研发、维护、升级（还有外围软件开发比如新女娲补字NewNW、PDF流程系...&nbsp;&nbsp;<a href='http://www.cppblog.com/ngaut/archive/2008/09/09/61354.html'>阅读全文</a><img src ="http://www.cppblog.com/ngaut/aggbug/61354.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ngaut/" target="_blank">ngaut</a> 2008-09-09 08:07 <a href="http://www.cppblog.com/ngaut/archive/2008/09/09/61354.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]Google C++ Testing Framework Primer</title><link>http://www.cppblog.com/ngaut/archive/2008/08/31/60514.html</link><dc:creator>ngaut</dc:creator><author>ngaut</author><pubDate>Sun, 31 Aug 2008 12:19:00 GMT</pubDate><guid>http://www.cppblog.com/ngaut/archive/2008/08/31/60514.html</guid><wfw:comment>http://www.cppblog.com/ngaut/comments/60514.html</wfw:comment><comments>http://www.cppblog.com/ngaut/archive/2008/08/31/60514.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ngaut/comments/commentRss/60514.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ngaut/services/trackbacks/60514.html</trackback:ping><description><![CDATA[<div id="blog_text" class="cnt">
<p><strong>Google C++ Testing Framework Primer</strong></p>
<p>翻译：<a  href="http://rayleex.spaces.live.com/blog/cns%21C32DFA3924AF2128%21218.entry"><font color="#669966">Ray Li </font></a>(<a  href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#114;&#97;&#121;&#46;&#108;&#101;&#101;&#120;&#64;&#103;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;"><font color="#669966">ray.leex@gmail.com</font></a>) <br>
修改日期：2008年7月6日<br>
原文参见：<a  href="http://code.google.com/p/googletest/wiki/GoogleTestPrimer"><font color="#669966">http://code.google.com/p/googletest/wiki/GoogleTestPrimer</font></a></p>
<p><strong>Introduction</strong><strong>：为什么需要</strong><strong>Google C++ </strong><strong>测试框架？</strong></p>
<p>Google C++ 测试框架帮助你更好地编写C++测试。</p>
<p>无论你是在Linux，Windows，还是Mac环境下工作，只要你编写C++代码，Google 测试框架都可以帮上忙。</p>
<p>那么，哪些因素才能构成一个好的测试？以及，Google C++ 测试框架怎样满足这些因素？我们相信：</p>
<ol>
    <li>测试应该是<em>独立</em>、<em>可重复</em>的。因为其他测试成功或失败而导致我们要对自己的测试进行debug是非常痛苦的。Google C++ 测试框架通过将每个测试在不同的对象中运行，使得测试分离开来。当一个测试失败时，Google C++ 测试框架允许你独立运行它以进行快速除错。</li>
    <li>测试应该能够被很好地<em>组织</em>，并反映被测代码的结构。Google C++ 测试框架将测试组织成测试案例，案例中的测试可以共享数据和程序分支。这样一种通用模式能够很容易辨识，使得我们的测试容易维护。当开发人员在项目之间转换，开始在一个新的代码基上开始工作时，这种一致性格外有用。</li>
    <li>测试应该是<em>可移植</em>、<em>可重用</em>的。开源社区有很多平台独立的代码，它们的测试也应该是平台独立的。除开一些特殊情况，Google C++ 测试框架运行在不同的操作系统上、与不同的编译器（gcc、icc、MSVC）搭配，Google C++ 测试框架的测试很容易与不同的配置一起工作。</li>
    <li>当测试失败时，应该提供尽可能多的、关于问题的<em>信息</em>。Google C++ 测试框架在第一个测试失败时不会停下来。相反，它只是将当前测试停止，然后继续接下来的测试。你也可以设置对一些非致命的错误进行报告，并接着进行当前的测试。这样，你就可以在一次&#8220;运行-编辑-编译&#8221;循环中检查到并修复多个bug。</li>
    <li>测试框架应该能将测试编写人员从一些环境维护的工作中解放出来，使他们能够集中精力于测试的<em>内容</em>。Google C++ 测试框架自动记录下所有定义好的测试，不需要用户通过列举来指明哪些测试需要运行。</li>
    <li>测试应该<em>快速</em>。使用Google C++ 测试框架，你可以重用多个测试的共享资源，一次性完成设置/解除设置，而不用使一个测试去依赖另一测试。</li>
</ol>
<p>因为Google C++ 测试框架基于著名的xUnit架构，如果你之前使用过JUnit或PyUnit的话，你将会感觉非常熟悉。如果你没有接触过这些测试框架，它也只会占用你大约10分钟的时间来学习基本概念和上手。所以，让我们开始吧！</p>
<p>Note：本文偶尔会用&#8220;Google Test&#8221;来代指&#8220;Google C++ 测试框架&#8221;。</p>
<p><strong>基本概念</strong></p>
<p>使用Google Test时，你是从编写<em>断言</em>开始的，而断言是一些检查条件是否为真的语句。一个断言的结果可能是成功、非致命失败，或者致命失败。如果一个致命失败出现，他会结束当前的函数；否则，程序继续正常运行。</p>
<p><em>测试</em>使用断言来验证被测代码的行为。如果一个测试崩溃或是出现一个失败的断言，那么，该测试<em>失败</em>；否则该测试<em>成功</em>。</p>
<p>一个测试案例（test case）包含了一个或多个测试。你应该将自己的测试分别归类到测试案例中，以反映被测代码的结构。当测试案例中的多个测试需要共享通用对象和子程序时，你可以把他们放到一个测试固件（<em>test fixture</em>）类中。</p>
<p>一个<em>测试程序</em>可以包含多个测试案例。</p>
<p>从编写单个的断言开始，到创建测试和测试案例，我们将会介绍怎样编写一个测试程序。</p>
<p><strong>断言</strong></p>
<p>Google Test中的断言是一些与函数调用相似的宏。要测试一个类或函数，我们需要对其行为做出断言。当一个断言失败时，Google
Test会在屏幕上输出该代码所在的源文件及其所在的位置行号，以及错误信息。也可以在编写断言时，提供一个自定义的错误信息，这个信息在失败时会被附加
在Google Test的错误信息之后。</p>
<p>断言常常成对出现，它们都测试同一个类或者函数，但对当前功能有着不同的效果。ASSERT_*版本的断言失败时会产生致命失败，并<strong>结束当前函数</strong>。EXPECT_*版本的断言产生非致命失败，而不会中止当前函数。通常更推荐使用EXPECT_*断言，因为它们运行一个测试中可以有不止一个的错误被报告出来。但如果在编写断言如果失败，就没有必要继续往下执行的测试时，你应该使用ASSERT_*断言。</p>
<p>因为失败的ASSERT_*断言会立刻从当前的函数返回，可能会跳过其后的一些的清洁代码，这样也许会导致空间泄漏。根据泄漏本身的特质，这种情况
也许值得修复，也可能不值得我们关心——所以，如果你得到断言错误的同时，还得到了一个堆检查的错误，记住上面我们所说的这一点。</p>
<p>要提供一个自定义的错误消息，只需要使用&lt;&lt;操作符，或一个&lt;&lt;操作符的序列，将其输入到框架定义的宏中。下面是一个例子：</p>
<p>Cpp代码</p>
<ol>
    <li>ASSERT_EQ(x.size(), y.size()) &lt;&lt; "Vectors x and y are of unequal length"; &nbsp;&nbsp;</li>
    <li>for (int i = 0; i &lt; x.size(); ++i) { &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; EXPECT_EQ(x[i], y[i]) &lt;&lt; "Vectors x and y differ at index " &lt;&lt; i; &nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
</ol>
<pre>ASSERT_EQ(x.size(), y.size()) &lt;&lt; "Vectors x and y are of unequal length";<br>for (int i = 0; i &lt; x.size(); ++i) {<br>  EXPECT_EQ(x[i], y[i]) &lt;&lt; "Vectors x and y differ at index " &lt;&lt; i;<br>}</pre>
<p>任何能够被输出到ostream中的信息都可以被输出到一个断言宏中——特别是C字符串和string对象。如果一个宽字符串
（wchar_t*，windows上UNICODE模式TCHAR*或std::wstring）被输出到一个断言中，在打印时它会被转换成UTF-8
编码。</p>
<p><strong>基本断言</strong></p>
<p>下面这些断言实现了基本的true/false条件测试。</p>
<p><strong>致命断言</strong><br>
<strong>非致命断言</strong><br>
<strong>验证条件</strong></p>
<p>ASSERT_TRUE(<em>condition</em>);<br>
EXPECT_TRUE(<em>condition</em>); <br>
<em>condition</em>为真</p>
<p>ASSERT_FALSE(<em>condition</em>); <br>
EXPECT_FALSE(<em>condition</em>); <br>
<em>condition</em> 为假</p>
<p>记住，当它们失败时，ASSERT_*产生一个致命失败并从当前函数返回，而EXCEPT_*产生一个非致命失败，允许函数继续运行。在两种情况下，一个断言失败都意味着它所包含的测试失败。</p>
<p>有效平台：Linux、Windows、Mac。</p>
<p><strong>二进制比较</strong></p>
<p>本节描述了比较两个值的一些断言。</p>
<p><strong>致命断言</strong><br>
<strong>非致命断言</strong><br>
<strong>验证条件</strong></p>
<p>ASSERT_EQ(<em>expected</em>, <em>actual</em>);<br>
EXPECT_EQ(<em>expected</em>, <em>actual</em>);<br>
<em>expected</em> == <em>actual</em></p>
<p>ASSERT_NE(<em>val1</em>, <em>val2</em>);<br>
EXPECT_NE(<em>val1</em>, <em>val2</em>);<br>
<em>val1</em> != <em>val2</em></p>
<p>ASSERT_LT(<em>val1</em>, <em>val2</em>);<br>
EXPECT_LT(<em>val1</em>, <em>val2</em>);<br>
<em>val1</em> &lt; <em>val2</em></p>
<p>ASSERT_LE(<em>val1</em>, <em>val2</em>);<br>
EXPECT_LE(<em>val1</em>, <em>val2</em>);<br>
<em>val1</em> &lt;= <em>val2</em></p>
<p>ASSERT_GT(<em>val1</em>, <em>val2</em>);<br>
EXPECT_GT(<em>val1</em>, <em>val2</em>);<br>
<em>val1</em> &gt; <em>val2</em></p>
<p>ASSERT_GE(<em>val1</em>, <em>val2</em>);<br>
EXPECT_GE(<em>val1</em>, <em>val2</em>);<br>
<em>val1</em> &gt;= <em>val2</em></p>
<p>在出现失败事件时，Google Test会将两个值（<em>Val1</em>和<em>Val2</em>）都打印出来。在ASSERT_EQ*和EXCEPT_EQ*断言（以及我们随后介绍类似的断言）中，你应该把你希望测试的表达式放在<em>actual</em>（实际值）的位置上，将其期望值放在<em>expected</em>（期望值）的位置上，因为Google Test的测试消息为这种惯例做了一些优化。</p>
<p>参数值必须是可通过断言的比较操作符进行比较的，否则你会得到一个编译错误。参数值还必须支持&lt;&lt;操作符来将值输入到ostream中。所有的C++内置类型都支持这一点。</p>
<p>这些断言可以用于用户自定义的型别，但你必须重载相应的比较操作符（如==、&lt;等）。如果定义有相应的操作符，推荐使用ASSERT_*()宏，因为它们不仅会输出比较的结果，还会输出两个比较对象。</p>
<p>参数表达式总是只被解析一次。因此，参数表达式有一定的副作用（side
effect，这里应该是指编译器不同，操作符解析顺序的不确定性）也是可以接受的。但是，同其他普通C/C++函数一样，参数表达式的解析顺序是不确定
的（如，一种编译器可以自由选择一种顺序来进行解析），而你的代码不应该依赖于某种特定的参数解析顺序。</p>
<p>ASSERT_EQ()对指针进行的是指针比较。即，如果被用在两个C字符串上，它会比较它们是否指向同样的内存地址，而不是它们所指向的字符串是
否有相同值。所以，如果你想对两个C字符串（例如，const
char*）进行值比较，请使用ASSERT_STREQ()宏，该宏会在后面介绍到。特别需要一提的是，要验证一个C字符串是否为空（NULL），使用
ASSERT_STREQ(NULL, c_string)。但是要比较两个string对象时，你应该使用ASSERT_EQ。</p>
<p>本节中介绍的宏都可以处理窄字符串对象和宽字符串对象（string和wstring）。</p>
<p>有效平台：Linux、Windows、Mac。</p>
<p><strong>字符串比较</strong></p>
<p>该组断言用于比较两个C字符串。如果你想要比较两个string对象，相应地使用EXPECT_EQ、EXPECT_NE等断言。</p>
<p><strong>致命断言</strong><br>
<strong>非致命断言</strong><br>
<strong>验证条件</strong></p>
<p>ASSERT_STREQ(<em>expected_str</em>, <em>actual_str</em>);<br>
EXPECT_STREQ(<em>expected_str</em>, <em>actual_str</em>);<br>
两个C字符串有相同的内容</p>
<p>ASSERT_STRNE(<em>str1</em>, <em>str2</em>);<br>
EXPECT_STRNE(<em>str1</em>, <em>str2</em>);<br>
两个C字符串有不同的内容</p>
<p>ASSERT_STRCASEEQ(<em>expected_str</em>, <em>actual_str</em>);<br>
EXPECT_STRCASEEQ(<em>expected_str</em>, <em>actual_str</em>);<br>
两个C字符串有相同的内容，忽略大小写</p>
<p>ASSERT_STRCASENE(<em>str1</em>, <em>str2</em>);<br>
EXPECT_STRCASENE(<em>str1</em>, <em>str2</em>);<br>
两个C字符串有不同的内容，忽略大小写</p>
<p>注意断言名称中出现的&#8220;CASE&#8221;意味着大小写被忽略了。</p>
<p>*STREQ*和*STRNE*也接受宽字符串（wchar_t*）。如果两个宽字符串比较失败，它们的值会做为UTF-8窄字符串被输出。</p>
<p>一个NULL空指针和一个空字符串会被认为是<em>不一样</em>的。</p>
<p>有效平台：Linux、Windows、Mac。</p>
<p>参见：更多的字符串比较的技巧（如子字符串、前缀和正则表达式匹配），请参见[Advanced Guide Advanced Google Test Guide]。</p>
<p><strong>简单的测试</strong></p>
<p>要创建一个测试：</p>
<ol>
    <li>使用TEST（）宏来定义和命名一个测试函数，它们是一些没有返回值的普通C++函数。</li>
    <li>在这个函数中，与你想要包含的其它任何有效C++代码一起，使用Google Test提供的各种断言来进行检查。</li>
    <li>测试的结果由其中的断言决定；如果测试中的任意断言失败（无论是致命还是非致命），或者测试崩溃，那么整个测试就失败了。否则，测试通过。 </li>
</ol>
<p>Cpp代码</p>
<ol>
    <li>TEST(test_case_name, test_name) { &nbsp;&nbsp;</li>
    <li>... test body ... &nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
</ol>
<pre>TEST(test_case_name, test_name) {<br>... test body ...<br>}</pre>
<p>TEST（）的参数是从概括到特殊的。<em>第一个</em>参数是测试案例的名称，<em>第二个</em>参数是测试案例中的测试的名称。记住，一个测试案例可以包含任意数量的独立测试。一个测试的<em>全称</em>包括了包含它的测试案例名称，及其独立的名称。不同测试案例中的独立测试可以有相同的名称。</p>
<p>举例来说，让我们看一个简单的整数函数：</p>
<p>Cpp代码</p>
<ol>
    <li>int Factorial(int n); // 返回n的阶乘</li>
</ol>
<pre>int Factorial(int n); // 返回n的阶乘</pre>
<p>这个函数的测试案例应该看起来像是：</p>
<p>Cpp代码</p>
<ol>
    <li>// 测试0的阶乘</li>
    <li>TEST(FactorialTest, HandlesZeroInput) { &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; EXPECT_EQ(1, Factorial(0)); &nbsp;&nbsp;</li>
    <li>} &nbsp;&nbsp;</li>
    <li>// 测试正数的阶乘</li>
    <li>TEST(FactorialTest, HandlesPositiveInput) { &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; EXPECT_EQ(1, Factorial(1)); &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; EXPECT_EQ(2, Factorial(2)); &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; EXPECT_EQ(6, Factorial(3)); &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; EXPECT_EQ(40320, Factorial(8)); &nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
</ol>
<pre>// 测试0的阶乘<br>TEST(FactorialTest, HandlesZeroInput) {<br>  EXPECT_EQ(1, Factorial(0));<br>}<br>// 测试正数的阶乘<br>TEST(FactorialTest, HandlesPositiveInput) {<br>  EXPECT_EQ(1, Factorial(1));<br>  EXPECT_EQ(2, Factorial(2));<br>  EXPECT_EQ(6, Factorial(3));<br>  EXPECT_EQ(40320, Factorial(8));<br>}</pre>
<p>Google
Test根据测试案例来分组收集测试结果，因此，逻辑相关的测试应该在同一测试案例中；换句话说，它们的TEST（）的第一个参数应该是一样的。在上面的
例子中，我们有两个测试，HandlesZeroInput和HandlesPostiveInput，它们都属于同一个测试案例
FactorialTest。</p>
<p>有效平台：Linux、Windows、Mac。</p>
<p><strong>测试固件（</strong><strong>Test Fixtures</strong><strong>，又做测试夹具、测试套件）：在多个测试中使用同样的数据配置</strong></p>
<p>当你发现自己编写了两个或多个测试来操作同样的数据，你可以采用一个<em>测试固件</em>。它让你可以在多个不同的测试中重用同样的对象配置。</p>
<p>要创建测试固件，只需：</p>
<ol>
    <li>创建一个类继承自testing::Test。将其中的成员声明为protected:或是public:，因为我们想要从子类中存取固件成员。</li>
    <li>在该类中声明你计划使用的任何对象。</li>
    <li>如果需要，编写一个默认构造函数或者SetUp()函数来为每个测试准备对象。常见错误包括将SetUp()拼写为Setup()（小写了u）——不要让它发生在你身上。</li>
    <li>如果需要，编写一个析构函数或者TearDown()函数来释放你在SetUp()函数中申请的资源。要知道什么时候应该使用构造函数/析构函数，什么时候又应该使用SetUp()/TearDown()函数，阅读我们的FAQ。</li>
    <li>如果需要，定义你的测试所需要共享的子程序。</li>
</ol>
<p>当我们要使用固件时，使用TEST_F()替换掉TEST()，它允许我们存取测试固件中的对象和子程序：</p>
<p>Cpp代码</p>
<ol>
    <li>TEST_F(test_case_name, test_name) { &nbsp;&nbsp;</li>
    <li>... test body ... &nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
</ol>
<pre>TEST_F(test_case_name, test_name) {<br>... test body ...<br>}</pre>
<p>与TEST()一样，第一个参数是测试案例的名称，但对TEST_F()来说，这个名称必须与测试固件类的名称一些。你可能已经猜到了：_F正是指固件。</p>
<p>不幸地是，C++宏系统并不允许我们创建一个单独的宏来处理两种类型的测试。使用错误的宏会导致编译期的错误。</p>
<p>而且，你必须在TEST_F()中使用它之前，定义好这个测试固件类。否则，你会得到编译器的报错：&#8220;virtual outside class declaration&#8221;。</p>
<p>对于TEST_F()中定义的每个测试，Google Test将会：</p>
<ol>
    <li>在运行时创建一个<em>全新</em>的测试固件</li>
    <li>马上通过SetUp()初始化它，</li>
    <li>运行测试</li>
    <li>调用TearDown()来进行清理工作</li>
    <li>删除测试固件。注意，同一测试案例中，不同的测试拥有不同的测试固件。Google Test在创建下一个测试固件前总是会对现有固件进行删除。Google Test不会对多个测试重用一个测试固件。测试对测试固件的改动并不会影响到其他测试。</li>
</ol>
<p>例如，让我们为一个名为Queue的FIFO队列类编写测试，该类的接口如下：</p>
<p>Cpp代码</p>
<ol>
    <li>template &lt;typename E&gt; // E为元素类型</li>
    <li>class Queue { &nbsp;&nbsp;</li>
    <li>public: &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; Queue(); &nbsp;&nbsp;</li>
    <li>void Enqueue(const E&amp; element); &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; E* Dequeue(); // 返回 NULL 如果队列为空.</li>
    <li>size_t size() const; &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; ... &nbsp;&nbsp;</li>
    <li>};&nbsp;&nbsp;</li>
</ol>
<pre>template &lt;typename E&gt; // E为元素类型<br>class Queue {<br>public:<br>  Queue();<br>  void Enqueue(const E&amp; element);<br>  E* Dequeue(); // 返回 NULL 如果队列为空.<br>  size_t size() const;<br>  ...<br>};</pre>
<p>首先，定义一个固件类。习惯上，你应该把它的名字定义为FooTest，这里的Foo是被测试的类。</p>
<p>Cpp代码</p>
<ol>
    <li>class QueueTest : public testing::Test { &nbsp;&nbsp;</li>
    <li>protected: &nbsp;&nbsp;</li>
    <li>virtual void SetUp() { &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp; q1_.Enqueue(1); &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp; q2_.Enqueue(2); &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp; q2_.Enqueue(3); &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; } &nbsp;&nbsp;</li>
    <li>// virtual void TearDown() {}</li>
    <li>&nbsp;&nbsp; Queue&lt;int&gt; q0_; &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; Queue&lt;int&gt; q1_; &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; Queue&lt;int&gt; q2_; &nbsp;&nbsp;</li>
    <li>};&nbsp;&nbsp;</li>
</ol>
<pre>class QueueTest : public testing::Test {<br>protected:<br>  virtual void SetUp() {<br>    q1_.Enqueue(1);<br>    q2_.Enqueue(2);<br>    q2_.Enqueue(3);<br>  }<br>  // virtual void TearDown() {}<br>  Queue&lt;int&gt; q0_;<br>  Queue&lt;int&gt; q1_;<br>  Queue&lt;int&gt; q2_;<br>};</pre>
<p>在这个案例中，我们不需要TearDown()，因为每个测试后除了析构函数外不需要进行其它的清理工作了。</p>
<p>接下来我们使用TEST_F()和这个固件来编写测试。</p>
<p>Cpp代码</p>
<ol>
    <li>TEST_F(QueueTest, IsEmptyInitially) { &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; EXPECT_EQ(0, q0_.size()); &nbsp;&nbsp;</li>
    <li>} &nbsp;&nbsp;</li>
    <li>TEST_F(QueueTest, DequeueWorks) { &nbsp;&nbsp;</li>
    <li>int* n = q0_.Dequeue(); &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; EXPECT_EQ(NULL, n); &nbsp;&nbsp;</li>
    <li> <br></li>
    <li>&nbsp;&nbsp; n = q1_.Dequeue(); &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; ASSERT_TRUE(n != NULL); &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; EXPECT_EQ(1, *n); &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; EXPECT_EQ(0, q1_.size()); &nbsp;&nbsp;</li>
    <li>delete n; &nbsp;&nbsp;</li>
    <li> <br></li>
    <li>&nbsp;&nbsp; n = q2_.Dequeue(); &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; ASSERT_TRUE(n != NULL); &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; EXPECT_EQ(2, *n); &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; EXPECT_EQ(1, q2_.size()); &nbsp;&nbsp;</li>
    <li>delete n; &nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
</ol>
<pre>TEST_F(QueueTest, IsEmptyInitially) {<br>  EXPECT_EQ(0, q0_.size());<br>}<br>TEST_F(QueueTest, DequeueWorks) {<br>  int* n = q0_.Dequeue();<br>  EXPECT_EQ(NULL, n);<br><br>  n = q1_.Dequeue();<br>  ASSERT_TRUE(n != NULL);<br>  EXPECT_EQ(1, *n);<br>  EXPECT_EQ(0, q1_.size());<br>  delete n;<br><br>  n = q2_.Dequeue();<br>  ASSERT_TRUE(n != NULL);<br>  EXPECT_EQ(2, *n);<br>  EXPECT_EQ(1, q2_.size());<br>  delete n;<br>}</pre>
<p>上面这段代码既使用了ASSERT_*断言，又使用了EXPECT_*断言。经验上讲，如果你想要断言失败后，测试能够继续进行以显示更多的错误
时，你应该使用EXPECT_*断言；使用ASSERT_*如果该断言失败后继续往下执行毫无意义。例如，Dequeue测试中的第二个断言是
ASSERT_TURE(n!= NULL)，因为我们随后会n指针解引用，如果n指针为空的话，会导致一个段错误。</p>
<p>当这些测试开始时，会发生如下情况：</p>
<ol>
    <li>Google Test创建一个QueueTest对象（我们把它叫做t1）。</li>
    <li>t1.SetUp()初始化t1。</li>
    <li>第一个测试（IsEmptyInitiallly）在t1上运行。</li>
    <li>测试完成后，t1.TearDown()进行一些清理工作。</li>
    <li>t1被析构。</li>
    <li>以上步骤在另一个QueueTest对象上重复进行，这回会运行DequeueWorks测试。</li>
</ol>
<p>有效平台：Linux、Windows、Mac。</p>
<p>注意：当一个测试对象被构造时，Google Test会自动地保存所有的Google Test变量标识，对象析构后进行恢复。</p>
<p><strong>调用测试</strong></p>
<p>TEST()和TEST_F()向Google Test隐式注册它们的测试。因此，与很多其他的C++测试框架不同，你不需要为了运行你定义的测试而将它们全部再列出来一次。</p>
<p>在定义好测试后，你可以通过RUN_ALL_TESTS()来运行它们，如果所有测试成功，该函数返回0，否则会返回1.注意RUN_ALL_TESTS()会运行你链接到的所有测试——它们可以来自不同的测试案例，甚至是来自不同的文件。</p>
<p>当被调用时，RUN_ALL_TESTS()宏会：</p>
<ol>
    <li>保存所有的Google Test标志。</li>
    <li>为一个侧测试创建测试固件对象。</li>
    <li>调用SetUp()初始化它。</li>
    <li>在固件对象上运行测试。</li>
    <li>调用TearDown()清理固件。</li>
    <li>删除固件。</li>
    <li>恢复所有Google Test标志的状态。</li>
    <li>重复上诉步骤，直到所有测试完成。</li>
</ol>
<p>此外，如果第二步时，测试固件的构造函数产生一个致命错误，继续执行3至5部显然没有必要，所以它们会被跳过。与之相似，如果第3部产生致命错误，第4部也会被跳过。</p>
<p>重要：你不能忽略掉RUN_ALL_TESTS()的返回值，否则gcc会报一个编译错误。这样设计的理由是自动化测试服务会根据测试退出返回码来
决定一个测试是否通过，而不是根据其stdout/stderr输出；因此你的main()函数必须返回RUN_ALL_TESTS()的值。</p>
<p>而且，你应该只调用RUN_ALL_TESTS()一次。多次调用该函数会与Google Test的一些高阶特性（如线程安全死亡测试thread-safe death tests）冲突，因而是不被支持的。</p>
<p>有效平台：Linux、Windows、Mac。</p>
<p><strong>编写</strong><strong>main()</strong><strong>函数</strong></p>
<p>你可以从下面这个样板开始:</p>
<p>Cpp代码</p>
<ol>
    <li>#include "this/package/foo.h"</li>
    <li>#include &lt;gtest/gtest.h&gt;</li>
    <li>namespace { &nbsp;&nbsp;</li>
    <li>// 测试Foo类的测试固件</li>
    <li>class FooTest : public testing::Test { &nbsp;&nbsp;</li>
    <li>protected: &nbsp;&nbsp;</li>
    <li>// You can remove any or all of the following functions if its body</li>
    <li>// is empty.</li>
    <li>&nbsp;&nbsp; FooTest() { &nbsp;&nbsp;</li>
    <li>// You can do set-up work for each test here.</li>
    <li>&nbsp;&nbsp; } &nbsp;&nbsp;</li>
    <li>virtual ~FooTest() { &nbsp;&nbsp;</li>
    <li>// You can do clean-up work that doesn't throw exceptions here.</li>
    <li>&nbsp;&nbsp; } &nbsp;&nbsp;</li>
    <li>// If the constructor and destructor are not enough for setting up</li>
    <li>// and cleaning up each test, you can define the following methods:</li>
    <li>virtual void SetUp() { &nbsp;&nbsp;</li>
    <li>// Code here will be called immediately after the constructor (right</li>
    <li>// before each test).</li>
    <li>&nbsp;&nbsp; } &nbsp;&nbsp;</li>
    <li>virtual void TearDown() { &nbsp;&nbsp;</li>
    <li>// Code here will be called immediately after each test (right</li>
    <li>// before the destructor).</li>
    <li>&nbsp;&nbsp; } &nbsp;&nbsp;</li>
    <li>// Objects declared here can be used by all tests in the test case for Foo.</li>
    <li>}; &nbsp;&nbsp;</li>
    <li> <br></li>
    <li>// Tests that the Foo::Bar() method does Abc.</li>
    <li>TEST_F(FooTest, MethodBarDoesAbc) { &nbsp;&nbsp;</li>
    <li>const string input_filepath = "this/package/testdata/myinputfile.dat"; &nbsp;&nbsp;</li>
    <li>const string output_filepath = "this/package/testdata/myoutputfile.dat"; &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; Foo f; &nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp; EXPECT_EQ(0, f.Bar(input_filepath, output_filepath)); &nbsp;&nbsp;</li>
    <li>} &nbsp;&nbsp;</li>
    <li> <br></li>
    <li>// Tests that Foo does Xyz.</li>
    <li>TEST_F(FooTest, DoesXyz) { &nbsp;&nbsp;</li>
    <li>// Exercises the Xyz feature of Foo.</li>
    <li>} &nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp; // namespace</li>
    <li> <br></li>
    <li>int main(int argc, char **argv) { &nbsp;&nbsp;</li>
    <li>testing::InitGoogleTest(&amp;argc, argv); &nbsp;&nbsp;</li>
    <li>return RUN_ALL_TESTS(); &nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
</ol>
<pre>#include "this/package/foo.h"<br>#include &lt;gtest/gtest.h&gt;<br>namespace {<br>// 测试Foo类的测试固件<br>class FooTest : public testing::Test {<br>protected:<br>  // You can remove any or all of the following functions if its body<br>  // is empty.<br>  FooTest() {<br>    // You can do set-up work for each test here.<br>  }<br>  virtual ~FooTest() {<br>    // You can do clean-up work that doesn't throw exceptions here.<br>  }<br>  // If the constructor and destructor are not enough for setting up<br>  // and cleaning up each test, you can define the following methods:<br>  virtual void SetUp() {<br>    // Code here will be called immediately after the constructor (right<br>    // before each test).<br>  }<br>  virtual void TearDown() {<br>    // Code here will be called immediately after each test (right<br>    // before the destructor).<br>  }<br>  // Objects declared here can be used by all tests in the test case for Foo.<br>};<br><br>// Tests that the Foo::Bar() method does Abc.<br>TEST_F(FooTest, MethodBarDoesAbc) {<br>  const string input_filepath = "this/package/testdata/myinputfile.dat";<br>  const string output_filepath = "this/package/testdata/myoutputfile.dat";<br>  Foo f;<br>  EXPECT_EQ(0, f.Bar(input_filepath, output_filepath));<br>}<br><br>// Tests that Foo does Xyz.<br>TEST_F(FooTest, DoesXyz) {<br>  // Exercises the Xyz feature of Foo.<br>}<br>}  // namespace<br><br>int main(int argc, char **argv) {<br>  testing::InitGoogleTest(&amp;argc, argv);<br>  return RUN_ALL_TESTS();<br>}</pre>
<p>testing::InitGoogleTest()函数负责解析命令行传入的Google
Test标志，并删除所有它可以处理的标志。这使得用户可以通过各种不同的标志控制一个测试程序的行为。关于这一点我们会在GTestAdvanced中
讲到。你必须在调用RUN_ALL_TESTS()之前调用该函数，否则就无法正确地初始化标示。</p>
<p>在Windows上InitGoogleTest()可以支持宽字符串，所以它也可以被用在以UNICODE模式编译的程序中。</p>
<p><strong>进阶阅读</strong></p>
<p>恭喜你！你已经学到了一些Google Test基础。你可以从编写和运行几个Google Test测试开始，再阅读一下<a  href="http://code.google.com/p/googletest/wiki/GoogleTestSamples"><font color="#669966">GoogleTestSamples</font></a>，或是继续研究<a  href="http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide"><font color="#669966">GoogleTestAdvancedGuide</font></a>，其中描述了很多更有用的Google Test特性。</p>
<p><strong>已知局限</strong></p>
<p>Google Test被设计为线程安全的。但是，我们还没有时间在各种平台上实现同步原语（synchronization
primitives）。因此，目前从两个线程同时使用Google
Test断言是不安全的。由于通常断言是在主线程中完成的，因此在大多数测试中这都不算问题。如果你愿意帮忙，你可以试着在gtest-port.h中实
现必要的同步原语。</p>
</div><img src ="http://www.cppblog.com/ngaut/aggbug/60514.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ngaut/" target="_blank">ngaut</a> 2008-08-31 20:19 <a href="http://www.cppblog.com/ngaut/archive/2008/08/31/60514.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>我也发一个超级简单的玩具级编译器，仅此说明一下编译的几个过程【开源】</title><link>http://www.cppblog.com/ngaut/archive/2008/08/10/58451.html</link><dc:creator>ngaut</dc:creator><author>ngaut</author><pubDate>Sun, 10 Aug 2008 08:56:00 GMT</pubDate><guid>http://www.cppblog.com/ngaut/archive/2008/08/10/58451.html</guid><wfw:comment>http://www.cppblog.com/ngaut/comments/58451.html</wfw:comment><comments>http://www.cppblog.com/ngaut/archive/2008/08/10/58451.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cppblog.com/ngaut/comments/commentRss/58451.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ngaut/services/trackbacks/58451.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/ngaut/archive/2008/08/10/58451.html'>阅读全文</a><img src ="http://www.cppblog.com/ngaut/aggbug/58451.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ngaut/" target="_blank">ngaut</a> 2008-08-10 16:56 <a href="http://www.cppblog.com/ngaut/archive/2008/08/10/58451.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>介绍一款国产开源C编译器</title><link>http://www.cppblog.com/ngaut/archive/2008/08/08/58346.html</link><dc:creator>ngaut</dc:creator><author>ngaut</author><pubDate>Fri, 08 Aug 2008 09:23:00 GMT</pubDate><guid>http://www.cppblog.com/ngaut/archive/2008/08/08/58346.html</guid><wfw:comment>http://www.cppblog.com/ngaut/comments/58346.html</wfw:comment><comments>http://www.cppblog.com/ngaut/archive/2008/08/08/58346.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/ngaut/comments/commentRss/58346.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ngaut/services/trackbacks/58346.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/ngaut/archive/2008/08/08/58346.html'>阅读全文</a><img src ="http://www.cppblog.com/ngaut/aggbug/58346.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ngaut/" target="_blank">ngaut</a> 2008-08-08 17:23 <a href="http://www.cppblog.com/ngaut/archive/2008/08/08/58346.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>最近写了个小工具，自动往代码里面加入垃圾代码</title><link>http://www.cppblog.com/ngaut/archive/2008/07/29/57385.html</link><dc:creator>ngaut</dc:creator><author>ngaut</author><pubDate>Mon, 28 Jul 2008 17:32:00 GMT</pubDate><guid>http://www.cppblog.com/ngaut/archive/2008/07/29/57385.html</guid><wfw:comment>http://www.cppblog.com/ngaut/comments/57385.html</wfw:comment><comments>http://www.cppblog.com/ngaut/archive/2008/07/29/57385.html#Feedback</comments><slash:comments>9</slash:comments><wfw:commentRss>http://www.cppblog.com/ngaut/comments/commentRss/57385.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ngaut/services/trackbacks/57385.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/ngaut/archive/2008/07/29/57385.html'>阅读全文</a><img src ="http://www.cppblog.com/ngaut/aggbug/57385.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ngaut/" target="_blank">ngaut</a> 2008-07-29 01:32 <a href="http://www.cppblog.com/ngaut/archive/2008/07/29/57385.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>