美文网首页
GTEST source code learning

GTEST source code learning

作者: JiShi | 来源:发表于2017-05-22 09:37 被阅读0次

    5/17/2017 10:46:37 AM


    Google Test Source Code Learning

    <h2 id="example">Example</h2>

    <span id="jump_example">SRC</span>
    test case: BasicTest(测试用例), test:Negative(测试特例)

    // negative numbers
    TEST( BasicTest, Negative ) {
        EXPECT_EQ(1, -1);
        EXPECT_EQ(-1, -1);
    }
    
    // 0 numbers
    TEST( BasicTest, Zero ) {
        EXPECT_EQ(1, 0);
        EXPECT_EQ(0.0, 0);
    }
    
    // positive numbers
    TEST( BasicTest, Positive ) {
        EXPECT_EQ(1, 1);
        EXPECT_EQ(-1, 1);
    }
    
    // assert
    TEST( AssertTest, Zero ) {
        ASSERT_TRUE( 0==0 );
        ASSERT_TRUE( 1==0 );
    }
    
    TEST( AssertTest, HELLOWORLD ) {
        ASSERT_STREQ( "hello world", "hell0 world" );
        ASSERT_STREQ( "hello world", "hello world" );
    }
    
    TEST( AssertTest, Near ) {
        ASSERT_NEAR( -1.0f, -1.1f, 0.2f );
        ASSERT_NEAR( -2.0f, -2.1f, 0.01f );
    }
    

    <span id="jump_output">Output</span>

    [==========] Running 3 tests from 1 test case.
    [----------] Global test environment set-up.
    [----------] 3 tests from BasicTest
    [ RUN      ] BasicTest.Negative
    /home/meyan/workspace-gui-10/qtgui/gtest_learning/test_src.cpp:5: Failure
    Value of: -1
    Expected: 1
    [  FAILED  ] BasicTest.Negative (0 ms)
    [ RUN      ] BasicTest.Zero
    /home/meyan/workspace-gui-10/qtgui/gtest_learning/test_src.cpp:11: Failure
    Value of: 0
    Expected: 1
    [  FAILED  ] BasicTest.Zero (0 ms)
    [ RUN      ] BasicTest.Positive
    /home/meyan/workspace-gui-10/qtgui/gtest_learning/test_src.cpp:18: Failure
    Value of: 1
    Expected: -1
    [  FAILED  ] BasicTest.Positive (0 ms)
    [----------] 3 tests from BasicTest (0 ms total)
    
    [----------] 3 tests from AssertTest
    [ RUN      ] AssertTest.Zero
    /home/meyan/workspace-gui-10/qtgui/gtest_learning/test_src.cpp:23: Failure
    Value of: 1==0
    Actual: false
    Expected: true
    [  FAILED  ] AssertTest.Zero (0 ms)
    [ RUN      ] AssertTest.HELLOWORLD
    /home/meyan/workspace-gui-10/qtgui/gtest_learning/test_src.cpp:27: Failure
    Value of: "hell0 world"
    Expected: "hello world"
    [  FAILED  ] AssertTest.HELLOWORLD (1 ms)
    [ RUN      ] AssertTest.Near
    /home/meyan/workspace-gui-10/qtgui/gtest_learning/test_src.cpp:33: Failure
    The difference between -2.0f and -2.1f is 0.0999999, which exceeds 0.01f, where
    -2.0f evaluates to -2,
    -2.1f evaluates to -2.1, and
    0.01f evaluates to 0.01.
    [  FAILED  ] AssertTest.Near (0 ms)
    [----------] 3 tests from AssertTest (1 ms total)
    
    [----------] Global test environment tear-down
    [==========] 6 tests from 2 test cases ran. (1 ms total)
    [  PASSED  ] 0 tests.
    [  FAILED  ] 6 tests, listed below:
    [  FAILED  ] BasicTest.Negative
    [  FAILED  ] BasicTest.Zero
    [  FAILED  ] BasicTest.Positive
    [  FAILED  ] AssertTest.Zero
    [  FAILED  ] AssertTest.HELLOWORLD
    [  FAILED  ] AssertTest.Near
    

    Macro expansion

    g++ main.cpp -I/usr/local/**/gmock/1.4.0/include/ -E >> main.i
    g++ test_src.cpp -I/usr/local/**/gmock/1.4.0/include/ -E >> test_src.i
    

    Tips:

    诸如internal::HandleExceptionsInMethodIfSupported的函数都是为了支持跨平台测试, 这里除非显示调用,其余的这类函数或处理都将被忽略。

    Questions:

    • How to run test?
    • How to store test?
    • How to print result?

    <h2 id="1">1. 自动调度机制</h2>
    gtest main function

    #include <gtest/gtest.h>
    int main( int argc, char **argv )
    {
        testing::InitGoogleTest( &argc, argv ); // parameter, filter...
        return RUN_ALL_TESTS(); // run tests
    }
    

    <h3 id="1.1">1.1 RUN_ALL_TESTS</h3>

    Expand RUN_ALL_TESTS():

    return (::testing::UnitTest::GetInstance()->Run());
    

    class UnitTest, 注意到class UnitTestImpl:

    class UnitTest {
    public:
        static UnitTest* GetInstance();
        int Run() GTEST_MUST_USE_RESULT_;
        ...
    private:
        UnitTest::UnitTest() {
            impl_ = new internal::UnitTestImpl(this);
        }
        internal::UnitTestImpl* impl() { return impl_; }
        const internal::UnitTestImpl* impl() const { return impl_; }
        
        internal::UnitTestImpl* impl_;
        // We disallow copying UnitTest.
        GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTest);
        ...
    }
    

    For UnitTest::RUN(),可以看到实际上调用了internal::UnitTestImpl::RunAllTests:

    int UnitTest::Run() {
      return internal::HandleExceptionsInMethodIfSupported(
          impl(),
          &internal::UnitTestImpl::RunAllTests,
          "auxiliary test code (environments or event listeners)") ? 0 : 1;
    }
    

    For UnitTestImpl::RunAllTests(),实际调用了TestCase::Run(),也就是说UnitTestImpl中存了若干个TestCase(std::vector<TestCase> test_cases_):

    bool UnitTestImpl::RunAllTests() {
        for (int test_index = 0; test_index < total_test_case_count(); test_index++) {
          GetMutableTestCase(test_index)->Run();
        }
    }
    
    TestCase *UnitTestImpl::GetMutableTestCase(int i) {
        const int index = GetElementOr(test_case_indices_, i, -1);
        return index < 0 ? NULL : test_cases_[index];
    }
    

    For class TestCase, 实际调用了TestInfo::Run(),即在TestCase中存储了若干的TestInfo:

    void TestCase::Run() {
        for (int i = 0; i < total_test_count(); i++) {
        GetMutableTestInfo(i)->Run();
    }
    
    TestInfo* TestCase::GetMutableTestInfo(int i) {
        const int index = GetElementOr(test_indices_, i, -1);
        return index < 0 ? NULL : test_info_list_[index];
    }
    

    For class TestInfo,可以看到实际的test是由一个工厂类产生并进行测试:

    void TestInfo::Run() {
        // Creates the test object.
        Test* const test = internal::HandleExceptionsInMethodIfSupported(
            factory_, &internal::TestFactoryBase::CreateTest,
            "the test fixture's constructor");
        test->Run();
    }
    

    Test* const test is create by factory_. test is built in runtime.

    void Test::Run() {
        internal::HandleExceptionsInMethodIfSupported(
        this, &Test::TestBody, "the test body");
    }
    

    <span id="jump_flow">Call flow</span>


    What are TestCase, TestInfo?
    How to create test?
    Where and when builds these tests?

    <h3 id="1.2">1.2 How to add tests?</h3>
    source code

    无论是main还是source code, 都没有显式调用测试的位置,所以只能分析TEST宏。

    # define TEST(test_case_name, test_name) \ 
        GTEST_TEST(test_case_name, test_name)
    

    GTEST_TEST:

    #define GTEST_TEST(test_case_name, test_name)\
      GTEST_TEST_(test_case_name, test_name, \
                  ::testing::Test, ::testing::internal::GetTestTypeId())
    

    GTEST_TEST_:

    #define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)\
    class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\
     public:\
      GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}\
     private:\
      virtual void TestBody();\
      static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;\
      GTEST_DISALLOW_COPY_AND_ASSIGN_(\
          GTEST_TEST_CLASS_NAME_(test_case_name, test_name));\
    };\
    \
    ::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\
      ::test_info_ =\
        ::testing::internal::MakeAndRegisterTestInfo(\
            #test_case_name, #test_name, NULL, NULL, \
            ::testing::internal::CodeLocation(__FILE__, __LINE__), \
            (parent_id), \
            parent_class::SetUpTestCase, \
            parent_class::TearDownTestCase, \
            new ::testing::internal::TestFactoryImpl<\
                GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\
    void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
    

    For TEST( BasicTest, Negative ), macro expansion:

    class BasicTest_Negative_Test : public ::testing::Test { 
        public: 
            BasicTest_Negative_Test() {} 
        private: 
            virtual void TestBody(); 
    
            static ::testing::TestInfo* const test_info_; 
    
            BasicTest_Negative_Test(const BasicTest_Negative_Test &); 
            void operator=(const BasicTest_Negative_Test &);
     };
    
     ::testing::TestInfo* const BasicTest_Negative_Test ::test_info_ = 
                ::testing::internal::MakeAndRegisterTestInfo( 
                "BasicTest", "Negative", "", "",
                (::testing::internal::GetTestTypeId()),
                ::testing::Test::SetUpTestCase, ::testing::Test::TearDownTestCase, 
                new ::testing::internal::TestFactoryImpl< BasicTest_Negative_Test>);
    
      void BasicTest_Negative_Test::TestBody() {
           EXPECT_EQ(1, -1);
           EXPECT_EQ(-1, -1);
      }
    

    也就是说,EXPECT_EQ就是我们需要的实现。GTEST是通过TestBody()进行测试。那说明test_info就是保存我们测试特例的位置。MakeAndRegisterTestInfo:

    TestInfo* MakeAndRegisterTestInfo(  
        const char* test_case_name,  
        const char* name,  
        const char* type_param,  
        const char* value_param,  
        CodeLocation code_location,  
        TypeId fixture_class_id,  
        SetUpTestCaseFunc set_up_tc,  
        TearDownTestCaseFunc tear_down_tc,  
        TestFactoryBase* factory) {  
      TestInfo* const test_info =  
        new TestInfo(test_case_name, name, type_param, value_param,  
                     code_location, fixture_class_id, factory);  
        GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);  
        return test_info;  
    }
    

    TestFactoryImpl工厂类, 用于创建出BasicTest_Negative_Test用于测试:

    template <class TestClass>  
    class TestFactoryImpl : public TestFactoryBase {  
     public:  
      virtual Test* CreateTest() { return new TestClass; }  
    };
    

    GetUnitTestImpl()->AddTestInfo():

    void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc,
        Test::TearDownTestCaseFunc tear_down_tc,
        TestInfo* test_info) {
        GetTestCase(test_info->test_case_name(), test_info->type_param(), set_up_tc, tear_down_tc)->AddTestInfo(test_info);
      }
    

    GetTestCase, 从UnitTestImpl中拿到对应的TestCase:

    TestCase* UnitTestImpl::GetTestCase(const char* test_case_name,
                                    const char* type_param,
                                    Test::SetUpTestCaseFunc set_up_tc,
                                    Test::TearDownTestCaseFunc tear_down_tc) {
        // Can we find a TestCase with the given name?
        const std::vector<TestCase*>::const_iterator test_case =
         std::find_if(test_cases_.begin(), test_cases_.end(),
                   TestCaseNameIs(test_case_name));
    
        if (test_case != test_cases_.end())
            return *test_case;
    
        // No.  Let's create one.
        TestCase* const new_test_case =
            new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc);
    
        test_cases_.push_back(new_test_case);
        test_case_indices_.push_back(static_cast<int>(test_case_indices_.size()));
        return new_test_case;
    }
    

    AddTestInfo, 向TestCase添加TestInfo:

    void TestCase::AddTestInfo(TestInfo * test_info) {
        test_info_list_.push_back(test_info);
        test_indices_.push_back(static_cast<int>(test_indices_.size()));
    }
    

    现在TestCase, TestInfo, TestBody()已经拿到了, 但是test怎么跑起来?测试用例,测试特例哪时候注册?
    static ::testing::TestInfo* const test_info_, static变量是先于main()初始化的,所以在main()开始之前, UnitTest, UnitTestImpl, TestCase, TestInfo 都会被初始化, 这样保证了测试自动调度。call flow.

    <h2 id="2">2. 结果统计机制</h2>
    output

    • 有多少test_case
    • test_case有多少个test
    • test_case有多少个test成功
    • test_case有多少个test失败
    • 失败的原因、位置、期待与实际的结果

    这个我们只关注如何统计结果,调用将在Listener机制中解释。
    所有的统计方法都是调用UnitTest的方法实现, 实际上就是对UnitTestImpl接口的调用:

    class UnitTestImpl {
    // Gets the number of successful test cases.
      int successful_test_case_count() const;
    
      // Gets the number of failed test cases.
      int failed_test_case_count() const;
    
      // Gets the number of all test cases.
      int total_test_case_count() const;
    
      // Gets the number of all test cases that contain at least one test
      // that should run.
      int test_case_to_run_count() const;
    
      // Gets the number of successful tests.
      int successful_test_count() const;
      ....
    };
    

    只关注UnitTestImpl::failed_test_case_count():

    int UnitTestImpl::failed_test_case_count() const {
        return CountIf(test_cases_, TestCaseFailed);
    }
    

    CountIf模板:

    template <class Container, typename Predicate>
    inline int CountIf(const Container& c, Predicate predicate) {
      int count = 0;
      for (typename Container::const_iterator it = c.begin(); it != c.end(); ++it) {
         if (predicate(*it))
             ++count;
      }
      return count;
    }
    
    // instance
    inline int CountIf(const std::vector<TestCase>& c, TestCaseFailed predicate) {
        for ( std::vector<TestCase>::const_iterator it = c.begin(); it != c.end(); ++it) {
         if (predicate(*it))
             ++count;
      }
      return count;
    }
    

    TestCaseFailed():

    static bool TestCaseFailed(const TestCase* test_case) {
      return test_case->should_run() && test_case->Failed();
    }
    

    最终调用TestResult::Failed():

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

    现在的问题是TestResult哪里产生的? 将EXPECT_EQ展开:

    #define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \
      GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_)
    
    #define GTEST_NONFATAL_FAILURE_(message) \
      GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure)
    
    #define GTEST_MESSAGE_(message, result_type) \
      GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type)
    
    #define GTEST_MESSAGE_AT_(file, line, message, result_type) \
      ::testing::internal::AssertHelper(result_type, file, line, message) \
         = ::testing::Message()
    

    出现了class AssertHelper(A class that enables one to stream messages to assertion macros).

    // Message assignment, for assertion streaming support.
    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)
                  );
    }
    
    void UnitTest::AddTestPartResult(
        TestPartResult::Type result_type,
        const char* file_name,
        int line_number,
        const std::string& message,
        const std::string& os_stack_trace) GTEST_LOCK_EXCLUDED_(mutex_) {
        ...
        const TestPartResult result = TestPartResult(result_type, file_name, line_number, msg.GetString().c_str());
        impl_->GetTestPartResultReporterForCurrentThread()->ReportTestPartResult(result);
        ...
    }
    
    void DefaultGlobalTestPartResultReporter::ReportTestPartResult(
        const TestPartResult& result) {
        unit_test_->current_test_result()->AddTestPartResult(result);
        unit_test_->listeners()->repeater()->OnTestPartResult(result);
    }
    
    // Adds a test part result to the list.
    void TestResult::AddTestPartResult(const TestPartResult& test_part_result) {
        test_part_results_.push_back(test_part_result);
    }
    

    <h2 id="3">3. Listener机制</h2>
    <h3 id="3.1">3.1 Listener的执行</h3>
    首先关注UnitTestImpl的构造函数:

    listeners()->SetDefaultResultPrinter(new PrettyUnitTestResultPrinter);
    

    listerners()返回TestEventListeners,供开发者监听执行的过程。

    class TestEventListeners {
      // The actual list of listeners.
      internal::TestEventRepeater* repeater_;
      // Listener responsible for the standard result output.
      TestEventListener* default_result_printer_;
      // Listener responsible for the creation of the XML output file.
      TestEventListener* default_xml_generator_;
    }
    
    class TestEventRepeater : public TestEventListener {  
    public:  
        virtual void OnTestProgramStart(const UnitTest& unit_test);  
        virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration);  
        virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test);  
        virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test);  
        virtual void OnTestCaseStart(const TestCase& test_case);  
        virtual void OnTestStart(const TestInfo& test_info);  
        virtual void OnTestPartResult(const TestPartResult& result);  
        virtual void OnTestEnd(const TestInfo& test_info);  
        virtual void OnTestCaseEnd(const TestCase& test_case);  
        virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test);  
        virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test);  
        virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration);  
        virtual void OnTestProgramEnd(const UnitTest& unit_test);  
    
    private:
        std::vector<TestEventListener*> listeners_;
    };
    

    执行顺序也是start正序,end逆序。


    <h3 id="3.2">3.2 定制自己的Listener</h3>
    如果要自定义Listener,需要实现一个继承于testing::TestEventListener或者testing::EmptyTestEventListener的类,后者更简便一些。如果想了解更多的实现,可以分析class PrettyUnitTestResultPrinter. 我们这里仅对test进行定制:

    void SimplePrinter::OnTestStart( const testing::TestInfo &test_info ) {
        printf( ".....Test starting.....\n" );
        printf( "Test Case: %s\n", test_info.test_case_name() );
        printf( "Test Name: %s\n", test_info.name() );
    }
    
    void SimplePrinter::OnTestPartResult( const testing::TestPartResult &test_part_result ) {
        printf("%s in %s:%d\n%s\n",
           test_part_result.failed() ? "*** Failure" : "Success",
           test_part_result.file_name(),
           test_part_result.line_number(),
           test_part_result.summary());
    }
    
    void SimplePrinter::OnTestEnd( const testing::TestInfo &test_info ) {
        printf( ".....Test ending.....\n" );
    }
    

    Also:

    ::testing::TestEventListeners &listerns = ::testing::UnitTest::GetInstance()->listeners();
    delete listerns.Release( listerns.default_result_printer() ); // Release vector then delete
    listerns.Append( new SimplePrinter );
    

    对比之前的output和现在的output:

    .....Test starting.....
    Test Case: AssertTest
    Test Name: Near
    *** Failure in /home/meyan/workspace-gui-10/qtgui/gtest_learning/test_src.cpp:33
    The difference between -2.0f and -2.1f is 0.0999999, which exceeds 0.01f, where
    -2.0f evaluates to -2,
    -2.1f evaluates to -2.1, and
    0.01f evaluates to 0.01.
    .....Test ending.....
    

    <h2 id="4">4. Others</h2>
    <h3 id="4.1">4.1 Assertions</h3>
    example, output
    GTest中有两个系列:

    1. ASSERT_*
    2. EXPECT_*

    EXPECT_TRUEASSERT_TRUE为例:

    #define EXPECT_TRUE(condition) \  
      GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \  
                          GTEST_NONFATAL_FAILURE_)  
    
    #define ASSERT_TRUE(condition) \  
      GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \  
                          GTEST_FATAL_FAILURE_)  
    

    两者的区别就是在是出错时调用了GTEST_NONFATAL_FAILURE_还是GTEST_FATAL_FAILURE_, 一个返回另一个继续执行:

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

    <h3 id="4.2">4.2 Test Fixtures</h3>
    Test Fixtures建立一个固定/一直的环境状态以确保测试可重复并且按照预期方式运行, 并且保证在不同的test中数据恒定。

    TEST_F(TestFixtures, First) {  
        EXPECT_EQ(data, 0);  
        data =  1;  
        EXPECT_EQ(data, 1);  
    }  
    

    这里将TEST_FTEST同时展开可以发现二者的区别就是声明的测试特例类继承于不同的父类:

    #define TEST_F(test_fixture, test_name)\  
      GTEST_TEST_(test_fixture, test_name, test_fixture, \  
          ::testing::internal::GetTypeId<test_fixture>())  
    
    #define GTEST_TEST(test_case_name, test_name)\  
      GTEST_TEST_(test_case_name, test_name, \  
          ::testing::Test, ::testing::internal::GetTestTypeId())  
    

    即:

    class TestFixtures_First_Test : public TestFixtures {
    

    其他的预处理如:

    // test case
    class TestFixtures : public ::testing::Test {
        static void SetUpTestCase()
        static void TearDownTestCase()
    }
    
    // global
    class EnvironmentTest : public ::testing::Environment;
    

    <h3 id="4.3">4.3 private code test</h3>
    实际就是通过friend实现,侵入式的方法,谨慎使用:

    class Foo {
    public:
        Foo() {}
    
    private:
        int Zero() const { return 0; }
        // Declare the friend tests
        FRIEND_TEST( FRIEND_TEST_Test, TEST );
    };
    
    TEST( FRIEND_TEST_Test, TEST ) {
        EXPECT_EQ( 0, Foo().Zero() );
    }
    

    macro expansion:

    class Foo {
    public:
        Foo() {}
    
    private:
        int Zero() const { return 0; }
        friend class FRIEND_TEST_Test_TEST_Test;
    };

    相关文章

      网友评论

          本文标题:GTEST source code learning

          本文链接:https://www.haomeiwen.com/subject/bnlbxxtx.html