Google Test(GTest)の使用方法とソースコード解析-結果統計メカニズム分析
9997 ワード
ソースコードを分析する前に、例を見てみましょう.「Google Test(GTest)の使用方法とソースコード解析-概要」の最後のインスタンスコードを基準に、最後の「ローカルテスト」の結果を修正するのはエラーです.(転載breaksoftwareからのcsdnブログを明記してください)はどれだけの試験用例があります 1 1つの試験例の中でどれだけの試験特例がありますか 1 1つのテスト例の中でどれだけのテスト特例が成功しましたか 1 1つの試験例の中でどれだけの試験特例が失敗したか 失敗の原因、位置、期待結果、実績
ソースコードでは、TestResultはAddTestPartResultメソッドのみを使用して「ローカルテスト」の結果を保存します.このメソッドを呼び出す場所は1つだけです
しかし、このような混乱した保存はなぜテスト結果の統計に影響しないのだろうか.TestResultのFailed関数を見てみましょう
ここで、結果統計の実現についてお話しします.
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]);
}
出力を観察し、次の結果からGTestを分析して統計を取りました.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: Failure
Expected: 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.ThirdElement
1 FAILED TEST
『Google Test(GTest)使用方法とソースコード解析-自動スケジューリングメカニズム解析』では、テスト用例オブジェクトポインタがクラスUnitTestImplに保存されることを分析した.// The vector of TestCases in their original order. It owns the
// elements in the vector.
std::vector<TestCase*> test_cases_;
の結果の統計も、このvector変数に対するものに違いない.実際には,コードに次のような関数を見つけ,関数注釈から,上の出力の結果に対応する統計を知ることができる.// 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関数は、条件を満たすテストケースの個数を返し、SumOverTestCaseList関数は、条件を満たすすべてのテストケースの個数を返します.その実現も非常に簡単で,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;
}
という書き方の利点は、関数呼び出し反復器の要素をカプセル化し、あちこちを遍歴する必要がないことです.次に、TestCaseFailedを例に、入力された関数ポインタに重点を置きます.// Returns true iff the test case failed.
static bool TestCaseFailed(const TestCase* test_case) {
return test_case->should_run() && test_case->Failed();
}
TestCasePassedとの違いはtest_Caseで呼び出されたFailed関数はPassed関数になります.TestCaseのPassed関数はFailed関数を逆にするだけなので、最終的にはFailedに呼び出され、実装を見てみましょう.bool Failed() const { return failed_test_count() > 0; }
int TestCase::failed_test_count() const {
return CountIf(test_info_list_, TestFailed);
}
TestCaseテストケースオブジェクトが最終的にまたはその下のテストケースオブジェクトポインタに対してTestFailedを1つずつ呼び出すかを確認 // Returns true iff test failed.
static bool TestFailed(const TestInfo* test_info) {
return test_info->should_run() && test_info->result()->Failed();
}
はこの層を通って伝達され、最終論理はTestResultのFailed関数に実行される.bool TestResult::Failed() const {
for (int i = 0; i < total_part_count(); ++i) {
if (GetTestPartResult(i).failed())
return true;
}
return false;
}
GetTestPartResultが取得したテスト特例の「ローカルテスト」の結果.たとえば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つの「ローカルテスト」(3、4、および5行)があります.それらの結果はTestResultに保存されています(実際にはすべての状況が保存されているわけではありませんが、後で分析します)// The vector of TestPartResults
std::vector<TestPartResult> test_part_results_;
データの統計論理を見ました.次に、ソースコードがtest_に結果をどのように埋め込むかに注目する必要があります.part_results_に表示されます.ソースコードでは、TestResultはAddTestPartResultメソッドのみを使用して「ローカルテスト」の結果を保存します.このメソッドを呼び出す場所は1つだけです
void DefaultGlobalTestPartResultReporter::ReportTestPartResult(
const TestPartResult& result) {
unit_test_->current_test_result()->AddTestPartResult(result);
unit_test_->listeners()->repeater()->OnTestPartResult(result);
}
その呼び出しロジックは最終的に次のロジックに帰属する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
}
ここでは、AssertHelperの付与記号に注目すればよい.しかし、私たちが想像していたほど簡単ではありません.GTestがここで実現するのに欠陥があると思います.どうしてそう言うのですか.コードを検索すると、クラスの付与子呼び出しは1つしかありません.#define GTEST_MESSAGE_AT_(file, line, message, result_type) \
::testing::internal::AssertHelper(result_type, file, line, message) \
= ::testing::Message()
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の呼び出しは1箇所のみで、エラー時のみです.GTEST_MESSAGE_の呼び出しは3カ所ある#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_EXPECT_などのエラー時に呼び出されます.EQは内部でこのように呼び出されます#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \
GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_)
でもGTEST_SUCCESS_の呼び出しは1箇所のみ// Generates a success with a generic message.
#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded")
GTEST_SUCCEEDは各判断のマクロには表示されません.例えばEXPECT_EQの実現は#define EXPECT_EQ(val1, val2) \
EXPECT_PRED_FORMAT2(::testing::internal:: \
EqHelper<GTEST_IS_NULL_LITERAL_(val1)>::Compare, \
val1, val2)
EXPECT_PRED_FORMAT 2マクロではエラーのみが処理されています.GTEST_を呼び出します.NONFATAL_FAILURE_——これにより、AssertHelperの付与子がトリガーされ、結果が「ローカルテスト」結果セットに保存されます.正しい場合は結果を「ローカルテスト」結果セットに保存しません!!しかし、TestResultがローカルテスト個数を計算する関数注釈は、すべての状況の結果を含んでいることを示しています.// 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());
}
だから、その注釈は間違っています!!エラーが発生した場合のみ「ローカルテスト」エラー結果を保存したり、GTEST_を人為的に呼び出したりします.SUCCEEDは「ローカルテスト」の正しい結果を保存しますが、その他の場合は保存しません.ずっとtest_part_results_保存されたデータは少し混乱していて、その意味を正確に表現していません.しかし、このような混乱した保存はなぜテスト結果の統計に影響しないのだろうか.TestResultのFailed関数を見てみましょう
bool TestResult::Failed() const {
for (int i = 0; i < total_part_count(); ++i) {
if (GetTestPartResult(i).failed())
return true;
}
return false;
}
GTESTを呼び出す人がいなかったらSUCCEEDが「ローカルテスト」の正しい結果を保存する場合、test_part_results_エラー結果のみが保存されています.エラーの結果がなければtotal_part_count関数は0を返します.Failed関数からfalseが返されます.エラーはありません.ここで、結果統計の実現についてお話しします.