死亡测试 ######################################## 前言 **************************************** “死亡测试”名字比较恐怖,这里的“死亡”指的的是程序的崩溃。通常在测试过程中,我们需要考虑各种各样的输入,有的输入可能直接导致程序崩溃,这时我们就需要检查程序是否按照预期的方式挂掉,这也就是所谓的“死亡测试”。gtest的死亡测试能做到在一个安全的环境下执行崩溃的测试案例,同时又对崩溃结果进行验证。 使用的宏 **************************************** .. list-table:: :header-rows: 1 * - 致命断言 - 非致命断言 - 解释 * - ASSERT_DEATH(statement, regex) - EXPECT_DEATH(statement, regex) - 语句会由于给定的错误崩溃 * - ASSERT_EXIT(statement, predicate, regex) - EXPECT_EXIT(statement, predicate, regex) - 语句使用给定的错误退出,其退出代码匹配predicate 由于有些异常只在Debug下抛出,因此还提供了 ``*_DEBUG_DEATH`` ,用来处理Debug和Realease下的不同。 \*_DEATH(statement, regex) **************************************** - statement是被测试的代码语句 - regex是一个正则表达式,用来匹配异常时在stderr中输出的内容 如下面的例子: .. code-block:: cpp void Foo(){ int *pInt = 0; *pInt = 42 ; } TEST(FooDeathTest, Demo){ EXPECT_DEATH(Foo(), ""); } .. important:: 编写死亡测试案例时,TEST的第一个参数,即testcase_name,请使用DeathTest后缀。原因是gtest会优先运行死亡测试案例,应该是为线程安全考虑。 \*_EXIT(statement, predicate, regex) **************************************** - statement是被测试的代码语句 - predicate 在这里必须是一个委托,接收int型参数,并返回bool。只有当返回值为true时,死亡测试案例才算通过。gtest提供了一些常用的predicate: .. code-block:: cpp testing::ExitedWithCode(exit_code) 如果程序正常退出并且退出码与exit_code相同则返回 true .. code-block:: cpp testing::KilledBySignal(signal_number) // Windows下不支持 如果程序被signal_number信号kill的话就返回true - regex是一个正则表达式,用来匹配异常时在stderr中输出的内容 .. note:: \*_DEATH其实是对\*_EXIT进行的一次包装,\*_DEATH的predicate判断进程是否以非0退出码退出或被一个信号杀死。 例: .. code-block:: cpp TEST(ExitDeathTest, Demo){ EXPECT_EXIT(_exit(1), testing::ExitedWithCode(1), ""); } \*_DEBUG_DEATH **************************************** 先来看定义: .. code-block:: cpp #ifdef NDEBUG #define EXPECT_DEBUG_DEATH(statement, regex) \ do { statement; } while (false) #define ASSERT_DEBUG_DEATH(statement, regex) \ do { statement; } while (false) #else #define EXPECT_DEBUG_DEATH(statement, regex) \ EXPECT_DEATH(statement, regex) #define ASSERT_DEBUG_DEATH(statement, regex) \ ASSERT_DEATH(statement, regex) #endif // NDEBUG for EXPECT_DEBUG_DEATH 可以看到:在Debug版和Release版本下, \*_DEBUG_DEATH的定义不一样。因为很多异常只会在Debug版本下抛出,而在Realease版本下不会抛出,所以针对Debug和Release分别做了不同的处理。看gtest里自带的例子就明白了: .. code-block:: cpp int DieInDebugElse12(int* sideeffect) { if (sideeffect) *sideeffect = 12; #ifndef NDEBUG GTEST_LOG_(FATAL, "debug death inside DieInDebugElse12()"); #endif // NDEBUG return 12; } TEST(TestCase, TestDieOr12WorksInDgbAndOpt) { int sideeffect = 0; // Only asserts in dbg. EXPECT_DEBUG_DEATH(DieInDebugElse12(&sideeffect), "death"); #ifdef NDEBUG // opt-mode has sideeffect visible. EXPECT_EQ(12, sideeffect); #else // dbg-mode no visible sideeffect. EXPECT_EQ(0, sideeffect); #endif } 关于正则表达式 **************************************** 在POSIX系统(Linux, Cygwin, 和 Mac)中,gtest的死亡测试中使用的是POSIX风格的正则表达式,想了解POSIX风格表达式可参考: - `POSIX extended regular expression `_ - `Wikipedia entry `_ 在Windows系统中,gtest的死亡测试中使用的是gtest自己实现的简单的正则表达式语法。 相比POSIX风格,gtest的简单正则表达式少了很多内容,比如 ("x|y"), ("(xy)"), ("[xy]") 和("x{5,7}")都不支持。 下面是简单正则表达式支持的一些内容: .. list-table:: :header-rows: 1 * - 表达式 - 解释 * - c - matches any literal character c * - \\d - matches any decimal digit * - \\D - matches any character that's not a decimal digit * - \\f - matches \f * - \\n - matches \n * - \\r - matches \r * - \\s - matches any ASCII whitespace, including \n * - \\S - matches any character that's not a whitespace * - \\t - matches \t * - \\v - matches \v * - \\w - matches any letter, _, or decimal digit * - \\W - matches any character that \\w doesn't match * - \\c - matches any literal character c, which must be a punctuation * - \. - matches any single character except \n * - A? - matches 0 or 1 occurrences of A * - A* - matches 0 or many occurrences of A * - A+ - matches 1 or many occurrences of A * - \^ - matches the beginning of a string (not that of each line) * - \$ - matches the end of a string (not that of each line) * - xy - matches x followed by y gtest定义两个宏,用来表示当前系统支持哪套正则表达式风格: - POSIX风格:GTEST_USES_POSIX_RE = 1 - Simple风格:GTEST_USES_SIMPLE_RE=1 死亡测试运行方式 **************************************** - fast方式(默认的方式) .. code-block:: cpp testing::FLAGS_gtest_death_test_style = "fast"; - threadsafe方式 .. code-block:: cpp testing::FLAGS_gtest_death_test_style = "threadsafe"; 你可以在 main() 里为所有的死亡测试设置测试形式,也可以为某次测试单独设置。Google Test会在每次测试之前保存这个标记并在测试完成后恢复,所以你不需要去管这部分工作 。如: .. code-block:: cpp TEST(MyDeathTest, TestOne) { testing::FLAGS_gtest_death_test_style = "threadsafe"; // This test is run in the "threadsafe" style: ASSERT_DEATH(ThisShouldDie(), ""); } TEST(MyDeathTest, TestTwo) { // This test is run in the "fast" style: ASSERT_DEATH(ThisShouldDie(), ""); } int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); testing::FLAGS_gtest_death_test_style = "fast"; return RUN_ALL_TESTS(); } .. important:: - 不要在死亡测试里释放内存。 - 在父进程里再次释放内存。 - 不要在程序中使用内存堆检查。 总结 **************************************** 关于死亡测试,gtest官方的文档已经很详细了,同时在源码中也有大量的示例。如想了解更多的请参考官方的文档,或是直接看gtest源码。 简单来说,通过\*_DEATH(statement, regex)和\*_EXIT(statement, predicate, regex),我们可以非常方便的编写导致崩溃的测试案例,并且在不影响其他案例执行的情况下,对崩溃案例的结果进行检查。