日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

Google Test(GTest)使用方法和源码解析——结果统计机制分析

發(fā)布時間:2023/11/27 生活经验 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Google Test(GTest)使用方法和源码解析——结果统计机制分析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

? ? ? ? 在分析源碼之前,我們先看一個例子。以《Google Test(GTest)使用方法和源碼解析——概況?》一文中最后一個實例代碼為基準(zhǔn),修改最后一個“局部測試”結(jié)果為錯誤。(轉(zhuǎn)載請指明出于breaksoftware的csdn博客)

class ListTest : public testing::Test {protected:virtual void SetUp() {_m_list[0] = 11;_m_list[1] = 12;_m_list[2] = 13;}int _m_list[3];
};
TEST_F(ListTest, FirstElement) {EXPECT_EQ(11, _m_list[0]);
}TEST_F(ListTest, SecondElement) {EXPECT_EQ(12, _m_list[1]);
}TEST_F(ListTest, ThirdElement) {EXPECT_EQ(0, _m_list[2]);
}

? ? ? ? 然后我們觀察其輸出,從下面的結(jié)果我們可以分析出GTest幫我們統(tǒng)計了:

  • 有多少測試用例
  • 一個測試用例中有多少測試特例
  • 一個測試用例中有多少測試特例成功
  • 一個測試用例中有多少測試特例失敗
  • 失敗的原因、位置、期待結(jié)果、實際結(jié)果
Running main() from gtest_main.cc
[==========] Running 3 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 3 tests from ListTest
[ RUN      ] ListTest.FirstElement
[       OK ] ListTest.FirstElement (0 ms)
[ RUN      ] ListTest.SecondElement
[       OK ] ListTest.SecondElement (0 ms)
[ RUN      ] ListTest.ThirdElement
../samples/sample11_unittest.cc:86: FailureExpected: 0
To be equal to: _m_list[2]Which is: 13
[  FAILED  ] ListTest.ThirdElement (0 ms)
[----------] 3 tests from ListTest (0 ms total)[----------] Global test environment tear-down
[==========] 3 tests from 1 test case ran. (0 ms total)
[  PASSED  ] 2 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] ListTest.ThirdElement1 FAILED TEST

? ? ? ? 在《Google Test(GTest)使用方法和源碼解析——自動調(diào)度機制分析》一文中,我們分析了,測試用例對象指針將保存在類UnitTestImpl中

// The vector of TestCases in their original order.  It owns the
// elements in the vector.std::vector<TestCase*> test_cases_;

? ? ? ? 那么結(jié)果的統(tǒng)計,肯定也是針對這個vector變量的。實際也是如此,我們在代碼中找到如下函數(shù),從函數(shù)注釋,我們就可以知道其對應(yīng)于上面輸出中那個結(jié)果的統(tǒng)計

// Gets the number of successful test cases.
int UnitTestImpl::successful_test_case_count() const {return CountIf(test_cases_, TestCasePassed);
}// Gets the number of failed test cases.
int UnitTestImpl::failed_test_case_count() const {return CountIf(test_cases_, TestCaseFailed);
}// Gets the number of all test cases.
int UnitTestImpl::total_test_case_count() const {return static_cast<int>(test_cases_.size());
}// Gets the number of all test cases that contain at least one test
// that should run.
int UnitTestImpl::test_case_to_run_count() const {return CountIf(test_cases_, ShouldRunTestCase);
}// Gets the number of successful tests.
int UnitTestImpl::successful_test_count() const {return SumOverTestCaseList(test_cases_, &TestCase::successful_test_count);
}// Gets the number of failed tests.
int UnitTestImpl::failed_test_count() const {return SumOverTestCaseList(test_cases_, &TestCase::failed_test_count);
}// Gets the number of disabled tests that will be reported in the XML report.
int UnitTestImpl::reportable_disabled_test_count() const {return SumOverTestCaseList(test_cases_,&TestCase::reportable_disabled_test_count);
}// Gets the number of disabled tests.
int UnitTestImpl::disabled_test_count() const {return SumOverTestCaseList(test_cases_, &TestCase::disabled_test_count);
}// Gets the number of tests to be printed in the XML report.
int UnitTestImpl::reportable_test_count() const {return SumOverTestCaseList(test_cases_, &TestCase::reportable_test_count);
}// Gets the number of all tests.
int UnitTestImpl::total_test_count() const {return SumOverTestCaseList(test_cases_, &TestCase::total_test_count);
}// Gets the number of tests that should run.
int UnitTestImpl::test_to_run_count() const {return SumOverTestCaseList(test_cases_, &TestCase::test_to_run_count);
}

? ? ? ??CountIf函數(shù)返回符合條件的測試用例個數(shù),SumOverTestCaseList函數(shù)返回符合條件的所有測試特例的個數(shù)。其實現(xiàn)也非常簡單,我們以CountIf為例

template <class Container, typename Predicate>
inline int CountIf(const Container& c, Predicate predicate) {// Implemented as an explicit loop since std::count_if() in libCstd on// Solaris has a non-standard signature.int count = 0;for (typename Container::const_iterator it = c.begin(); it != c.end(); ++it) {if (predicate(*it))++count;}return count;
}

? ? ? ? 這種寫法的一個好處就是我們封裝了函數(shù)調(diào)用迭代器中元素,從而不用到處都是遍歷。然后我們將重點放在傳入的函數(shù)指針,以TestCaseFailed為例

// Returns true iff the test case failed.
static bool TestCaseFailed(const TestCase* test_case) {return test_case->should_run() && test_case->Failed();
}

? ? ? ? 它和TestCasePassed區(qū)別就是將test_case調(diào)用的Failed函數(shù)變成Passed函數(shù)。而TestCase的Passed函數(shù)只是對Failed函數(shù)取反,所以最終還是調(diào)用到Failed中,我們看下其實現(xiàn)

bool Failed() const { return failed_test_count() > 0; }
int TestCase::failed_test_count() const {return CountIf(test_info_list_, TestFailed);
}

? ? ? ? 可見TestCase測試用例對象最終還是要對其下的測試特例對象指針逐個調(diào)用TestFailed

  // Returns true iff test failed.static bool TestFailed(const TestInfo* test_info) {return test_info->should_run() && test_info->result()->Failed();}

? ? ? ? 經(jīng)過這層傳遞,最終邏輯運行到TestResult的Failed函數(shù)中

bool TestResult::Failed() const {for (int i = 0; i < total_part_count(); ++i) {if (GetTestPartResult(i).failed())return true;}return false;
}

? ? ? ? GetTestPartResult獲取的一個測試特例中“局部測試”的結(jié)果。比如

TEST(IsPrimeTest, Negative) {// This test belongs to the IsPrimeTest test case.EXPECT_FALSE(IsPrime(-1));EXPECT_FALSE(IsPrime(-2));EXPECT_FALSE(IsPrime(INT_MIN));
}

? ? ? ? 這個測試特例中有三個“局部測試”(3、4和5行)。它們的結(jié)果保存在TestResult的(實際上并不是所有情況都保存,我們將在之后分析)

// The vector of TestPartResultsstd::vector<TestPartResult> test_part_results_;

? ? ? ? 現(xiàn)在我們看到了數(shù)據(jù)的統(tǒng)計邏輯,接下來我們需要關(guān)注源碼是如何將結(jié)果填充到test_part_results_中的。

? ? ? ? 在源碼中,TestResult只提供了AddTestPartResult方法用于保存“局部測試”結(jié)果。而調(diào)用該方法的地方只有一處

void DefaultGlobalTestPartResultReporter::ReportTestPartResult(const TestPartResult& result) {unit_test_->current_test_result()->AddTestPartResult(result);unit_test_->listeners()->repeater()->OnTestPartResult(result);
}

? ? ? ? 其調(diào)用邏輯最終會歸于如下邏輯

void AssertHelper::operator=(const Message& message) const {UnitTest::GetInstance()->AddTestPartResult(data_->type, data_->file, data_->line,AppendUserMessage(data_->message, message),UnitTest::GetInstance()->impl()->CurrentOsStackTraceExceptTop(1)// Skips the stack frame for this function itself.);  // NOLINT
}

? ? ? ? 到此,我們只要關(guān)注于AssertHelper的賦值符就行了。但是事情并不像我們想象的那么簡單,甚至我認為GTest在這兒實現(xiàn)有個缺陷。為什么這么說呢?我們搜索完代碼,發(fā)現(xiàn)該類的賦值符調(diào)用只有一處

#define GTEST_MESSAGE_AT_(file, line, message, result_type) \::testing::internal::AssertHelper(result_type, file, line, message) \= ::testing::Message()

? ? ? ? 調(diào)用GTEST_MESSAGE_AT_的地方只有

// Generates a nonfatal failure at the given source file location with
// a generic message.
#define ADD_FAILURE_AT(file, line) \GTEST_MESSAGE_AT_(file, line, "Failed", \::testing::TestPartResult::kNonFatalFailure)

? ? ? ? 和

#define GTEST_MESSAGE_(message, result_type) \GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type)

? ? ? ? 對ADD_FAILURE_AT的調(diào)用只有一處,且只是在出錯時。而對GTEST_MESSAGE_的調(diào)用則有三處

#define GTEST_FATAL_FAILURE_(message) \return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure)#define GTEST_NONFATAL_FAILURE_(message) \GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure)#define GTEST_SUCCESS_(message) \GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess)

? ? ? ??GTEST_FATAL_FAILURE_和GTEST_NONFATAL_FAILURE_都將在出錯時被調(diào)用,如EXPECT_EQ在內(nèi)部是這么調(diào)用的

#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_)

? ? ? ? 但是對GTEST_SUCCESS_的調(diào)用只有一處

// Generates a success with a generic message.
#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded")

? ? ? ??GTEST_SUCCEED并不會出現(xiàn)在每個判斷的宏中。比如EXPECT_EQ的實現(xiàn)是

#define EXPECT_EQ(val1, val2) \EXPECT_PRED_FORMAT2(::testing::internal:: \EqHelper<GTEST_IS_NULL_LITERAL_(val1)>::Compare, \val1, val2)

? ? ? ??EXPECT_PRED_FORMAT2宏中只處理了出錯的情況——調(diào)用GTEST_NONFATAL_FAILURE_——從而觸發(fā)AssertHelper的賦值符——將結(jié)果保存到“局部測試”結(jié)果集合中。而正確的情況下并不會保存結(jié)果到“局部測試”結(jié)果集中!!但是TestResult計算局部測試個數(shù)的函數(shù)注釋說明它包含了所有情況的結(jié)果

// Gets the number of all test parts.  This is the sum of the number
// of successful test parts and the number of failed test parts.
int TestResult::total_part_count() const {return static_cast<int>(test_part_results_.size());
}

? ? ? ? 所以,它的注釋是錯誤的!!只有出錯的情況會保存“局部測試”錯誤結(jié)果,或者人為調(diào)用GTEST_SUCCEED保存“局部測試”正確結(jié)果,而其他情況不保存。我一直覺得test_part_results_保存的數(shù)據(jù)有點混亂,沒有準(zhǔn)確的表達其意義。

? ? ? ? 但是這種混亂的保存為什么不會影響測試結(jié)果統(tǒng)計呢?我們再看下TestResult的Failed函數(shù)

bool TestResult::Failed() const {for (int i = 0; i < total_part_count(); ++i) {if (GetTestPartResult(i).failed())return true;}return false;
}

? ? ? ? 當(dāng)我們沒有人為調(diào)用GTEST_SUCCEED保存“局部測試”正確結(jié)果時,test_part_results_只保存了錯誤結(jié)果。如果沒有錯誤結(jié)果,total_part_count函數(shù)返回0。而從Failed函數(shù)返回false,即沒有出錯。
? ? ? ? 到此,我們將結(jié)果統(tǒng)計的實現(xiàn)講完了。

總結(jié)

以上是生活随笔為你收集整理的Google Test(GTest)使用方法和源码解析——结果统计机制分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。