googletest运行机制

原文:http://man6.org/blog/%E5%BC%80%E6%BA%90%E5%BA%93/googletest.md

说明

googletest 版本:1.11.0

googletest CMakeLists.txt 修改

我们需要修改下 googletest 的CMakeLists.txt,来使得编译出来的库带有调试信息。

需要修改的位置如下


# googletest/CMakeLists.txt

cmake_minimum_required(VERSION2.8.12)
set(CMAKE_EXPORT_COMPILE_COMMANDSON)
# 添加这一行即可
add_compile_options(-g -O0)

TestSuite

gtest 中的 TestSuite 的概念表示一个测试组,同一个 TestSuite 下的所有测试都属于这个组,有了 TestSuite 这个概念,可以方便的对测试用例进行分类。

googletest 简单执行流程分析

#include"gtest/gtest.h"intadd(int a, int b){
  return a + b;
}

TEST(addSuite, test0) {
  EXPECT_EQ(add(1, 2), 3);
}

TEST(addSuite, test1) {
  EXPECT_EQ(add(4, 3), 6);
}

intmain(int argc, char** argv){
  testing::InitGoogleTest(&argc, argv);
  returnRUN_ALL_TESTS();
}

这个例子中,需要考虑几个问题

  1. TEST 宏是如何定义的,为什么写了 TEST 宏就可以自动执行测试?

  1. addSuite 这个 TestSuite 是怎么被管理的,test0 与 test1 这两个测试用例又是如何被 addSuite 管理的?

先看一下 TEST 宏的定义

#if !GTEST_DONT_DEFINE_TEST#define TEST(test_suite_name, test_name) GTEST_TEST(test_suite_name, test_name)#endif#define GTEST_TEST(test_suite_name, test_name)             \
  GTEST_TEST_(test_suite_name, test_name, ::testing::Test, \
              ::testing::internal::GetTestTypeId())// Helper macro for defining tests.#define GTEST_TEST_(test_suite_name, test_name, parent_class, parent_id)      \
  static_assert(sizeof(GTEST_STRINGIFY_(test_suite_name)) > 1,                \
                "test_suite_name must not be empty");                         \
  static_assert(sizeof(GTEST_STRINGIFY_(test_name)) > 1,                      \
                "test_name must not be empty");                               \
  class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)                    \
      : public parent_class {                                                 \
   public:                                                                    \
    GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() = default;           \
    ~GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() override = default; \
    GTEST_DISALLOW_COPY_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name,   \
                                                           test_name));       \
    GTEST_DISALLOW_MOVE_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name,   \
                                                           test_name));       \
                                                                              \
   private:                                                                   \
    void TestBody() override;                                                 \
    static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;     \
  };                                                                          \
                                                                              \
  ::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_suite_name,          \
                                                    test_name)::test_info_ =  \
      ::testing::internal::MakeAndRegisterTestInfo(                           \
          #test_suite_name, #test_name, nullptr, nullptr,                     \
          ::testing::internal::CodeLocation(__FILE__, __LINE__), (parent_id), \
          ::testing::internal::SuiteApiResolver<                              \
              parent_class>::GetSetUpCaseOrSuite(__FILE__, __LINE__),         \
          ::testing::internal::SuiteApiResolver<                              \
              parent_class>::GetTearDownCaseOrSuite(__FILE__, __LINE__),      \
          new ::testing::internal::TestFactoryImpl);                                  \
  void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody()

从上面这几个宏中可以看出,TEST 这个宏其实就是在定义了一个类。这个宏写的太难看了φ(* ̄0 ̄),还是使用 gcc 的命令展开一下这个宏。


gcc -E -P test.cpp -o macro_expand.cpp
// 展开后的相关代码intadd(int a, int b){
    return a + b;
}

static_assert(sizeof("addSuite") > 1, "test_suite_name must not be empty");
static_assert(sizeof("test0") > 1, "test_name must not be empty");
classaddSuite_test0_Test : public ::testing::Test
{
public:
    addSuite_test0_Test() = default;
    ~addSuite_test0_Test() override = default;
    addSuite_test0_Test(addSuite_test0_Test const &) = delete;
    addSuite_test0_Test &operator=(addSuite_test0_Test const &) = delete;
    addSuite_test0_Test(addSuite_test0_Test &&) noexcept = delete;
    addSuite_test0_Test &operator=(addSuite_test0_Test &&) noexcept = delete;

private:
    voidTestBody()override;
    static ::testing::TestInfo *const test_info_ __attribute__((unused));
};
::testing::TestInfo *const addSuite_test0_Test::test_info_ = 
    ::testing::internal::MakeAndRegisterTestInfo("addSuite", "test0", nullptr, nullptr,
                                                ::testing::internal::CodeLocation("mytest.cpp", 7), 
                                                (::testing::internal::GetTestTypeId()), 
                                                ::testing::internal::SuiteApiResolver<::testing::Test>::GetSetUpCaseOrSuite("mytest.cpp", 7), 
                                                ::testing::internal::SuiteApiResolver<::testing::Test>::GetTearDownCaseOrSuite("mytest.cpp", 7), 
                                                new ::testing::internal::TestFactoryImpl);
voidaddSuite_test0_Test::TestBody(){
    switch (0)
    case0:
    default:
        if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("add(1, 2)", "3", add(1, 2), 3)))
            ;
        else
            ::testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure, "mytest.cpp", 8, gtest_ar.failure_message()) = ::testing::Message();
}
intmain(int argc, char **argv){
    testing::InitGoogleTest(&argc, argv);
    returnRUN_ALL_TESTS();
}

可以看到,代码中调用了 MakeAndRegisterTestInfo 函数,该函数的功能是创建一个 TestInfo 对象并注册。

TestInfo* MakeAndRegisterTestInfo(
    constchar* test_suite_name, constchar* name, constchar* type_param,
    constchar* value_param, CodeLocation code_location,
    TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc,
    TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory){
  TestInfo* const test_info =
      newTestInfo(test_suite_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;
}

看一下这个函数将 TestInfo 注册到了哪里?

voidAddTestInfo(internal::SetUpTestSuiteFunc set_up_tc,
                internal::TearDownTestSuiteFunc tear_down_tc,
                TestInfo* test_info){
#if GTEST_HAS_DEATH_TEST// In order to support thread-safe death tests, we need to// remember the original working directory when the test program// was first invoked.  We cannot do this in RUN_ALL_TESTS(), as// the user may have changed the current directory before calling// RUN_ALL_TESTS().  Therefore we capture the current directory in// AddTestInfo(), which is called to register a TEST or TEST_F// before main() is reached.if (original_working_dir_.IsEmpty()) {
    original_working_dir_.Set(FilePath::GetCurrentDir());
    GTEST_CHECK_(!original_working_dir_.IsEmpty())
        << "Failed to get the current working directory.";
}
#endif// GTEST_HAS_DEATH_TEST// 这里去获取对应的 TestSuite 对象,然后将 TestCase 注册到 TestSuiteGetTestSuite(test_info->test_suite_name(), test_info->type_param(),
                set_up_tc, tear_down_tc)
    ->AddTestInfo(test_info);
}

// 获取指定了名字的 TestSuite 对象, 如果不存在则创建一个TestSuite* UnitTestImpl::GetTestSuite(
    constchar* test_suite_name, constchar* type_param,
    internal::SetUpTestSuiteFunc set_up_tc,
    internal::TearDownTestSuiteFunc tear_down_tc){
  // Can we find a TestSuite with the given name?constauto test_suite =
      std::find_if(test_suites_.rbegin(), test_suites_.rend(),
                   TestSuiteNameIs(test_suite_name));

  if (test_suite != test_suites_.rend()) return *test_suite;

  // No.  Let's create one.auto* const new_test_suite =
      newTestSuite(test_suite_name, type_param, set_up_tc, tear_down_tc);

  // Is this a death test suite?if (internal::UnitTestOptions::MatchesFilter(test_suite_name,
                                               kDeathTestSuiteFilter)) {
    // Yes.  Inserts the test suite after the last death test suite// defined so far.  This only works when the test suites haven't// been shuffled.  Otherwise we may end up running a death test// after a non-death test.
    ++last_death_test_suite_;
    // 这里说明了 TestSuite 是被 UnitTestImpl::test_suites_ 这个 vector 管理的
    test_suites_.insert(test_suites_.begin() + last_death_test_suite_,
                        new_test_suite);
  } else {
    // No.  Appends to the end of the list.
    test_suites_.push_back(new_test_suite);
  }

  test_suite_indices_.push_back(static_cast(test_suite_indices_.size()));
  return new_test_suite;
}

// Adds a test to this test suite.  Will delete the test upon// destruction of the TestSuite object.// 将 TestInfo(TestCase) 插入到 TestSuite// 这里也可以看出 test0 与 test1 这两个测试用例是如何被 `addSuite` 管理的voidTestSuite::AddTestInfo(TestInfo* test_info){
  test_info_list_.push_back(test_info);
  test_indices_.push_back(static_cast(test_indices_.size()));
}

注册的所有 TestCase 最终都是被 main 函数中的 RUN_ALL_TESTS 函数调用的,下面看一下具体的调用路径。

inline int RUN_ALL_TESTS(){
  return ::testing::UnitTest::GetInstance()->Run();
}

// Runs all tests in this UnitTest object and prints the result.// Returns 0 if successful, or 1 otherwise. We don't protect this under mutex_, as we only support calling it// from the main thread.intUnitTest::Run(){
  constbool in_death_test_child_process =
      internal::GTEST_FLAG(internal_run_death_test).length() > 0;

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

// Runs all tests in this UnitTest object, prints the result, and// returns true if all tests are successful.  If any exception is// thrown during a test, the test is considered to be failed, but the// rest of the tests will still be run. When parameterized tests are enabled, it expands and registers// parameterized tests first in RegisterParameterizedTests().// All other functions called from RunAllTests() may safely assume that// parameterized tests are ready to be counted and run.boolUnitTestImpl::RunAllTests(){
  for (int i = 0; gtest_repeat_forever || i != repeat; i++) {
    // Runs each test suite if there is at least one test to run.if (has_tests_to_run) {
      // Sets up all environments beforehand.
      repeater->OnEnvironmentsSetUpStart(*parent_);
      ForEach(environments_, SetUpEnvironment);
      repeater->OnEnvironmentsSetUpEnd(*parent_);

      // Runs the tests only if there was no fatal failure or skip triggered// during global set-up.if (Test::IsSkipped()) {
          // ... ...
      } elseif (!Test::HasFatalFailure()) {
        for (int test_index = 0; test_index < total_test_suite_count();
             test_index++) {
          // 该处调用运行一个 TestSuiteGetMutableSuiteCase(test_index)->Run();
          if (GTEST_FLAG(fail_fast) &&
              GetMutableSuiteCase(test_index)->Failed()) {
            for (int j = test_index + 1; j < total_test_suite_count(); j++) {
              GetMutableSuiteCase(j)->Skip();
            }
            break;
          }
        }
      } elseif (Test::HasFatalFailure()) {
          // ... ...
      }

      // Tears down all environments in reverse order afterwards.
      repeater->OnEnvironmentsTearDownStart(*parent_);
      std::for_each(environments_.rbegin(), environments_.rend(),
                    TearDownEnvironment);
      repeater->OnEnvironmentsTearDownEnd(*parent_);
    }

    // Tells the unit test event listener that the tests have just finished.
    repeater->OnTestIterationEnd(*parent_, i);
  }
}

// Runs every test in this TestSuite.voidTestSuite::Run(){
  if (!should_run_) return;

  internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
  impl->set_current_test_suite(this);

  TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater();

  // Call both legacy and the new API
  repeater->OnTestSuiteStart(*this);
//  Legacy API is deprecated but still available#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
  repeater->OnTestCaseStart(*this);
#endif//  GTEST_REMOVE_LEGACY_TEST_CASEAPI_

  impl->os_stack_trace_getter()->UponLeavingGTest();
  internal::HandleExceptionsInMethodIfSupported(
      this, &TestSuite::RunSetUpTestSuite, "SetUpTestSuite()");

  start_timestamp_ = internal::GetTimeInMillis();
  internal::Timer timer;
  for (int i = 0; i < total_test_count(); i++) {
    // 执行 TestSuite 中的某个 TestCaseGetMutableTestInfo(i)->Run();
    if (GTEST_FLAG(fail_fast) && GetMutableTestInfo(i)->result()->Failed()) {
      for (int j = i + 1; j < total_test_count(); j++) {
        GetMutableTestInfo(j)->Skip();
      }
      break;
    }
  }

  // Call both legacy and the new API
  repeater->OnTestSuiteEnd(*this);
//  Legacy API is deprecated but still available#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
  repeater->OnTestCaseEnd(*this);
#endif//  GTEST_REMOVE_LEGACY_TEST_CASEAPI_

  impl->set_current_test_suite(nullptr);
}


// Creates the test object, runs it, records its result, and then// deletes it.voidTestInfo::Run(){
  if (!should_run_) return;

  // Tells UnitTest where to store test result.
  internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
  impl->set_current_test_info(this);

  TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater();

  // Notifies the unit test event listeners that a test is about to start.
  repeater->OnTestStart(*this);

  result_.set_start_timestamp(internal::GetTimeInMillis());
  internal::Timer timer;

  impl->os_stack_trace_getter()->UponLeavingGTest();

  // Creates the test object.
  Test* const test = internal::HandleExceptionsInMethodIfSupported(
      factory_, &internal::TestFactoryBase::CreateTest,
      "the test fixture's constructor");

  // Runs the test if the constructor didn't generate a fatal failure or invoke// GTEST_SKIP().// Note that the object will not be nullif (!Test::HasFatalFailure() && !Test::IsSkipped()) {
    // This doesn't throw as all user code that can throw are wrapped into// exception handling code.// 此处最终执行一个 TestCase
    test->Run();
  }

  if (test != nullptr) {
    // Deletes the test object.
    impl->os_stack_trace_getter()->UponLeavingGTest();
    internal::HandleExceptionsInMethodIfSupported(
        test, &Test::DeleteSelf_, "the test fixture's destructor");
  }

  result_.set_elapsed_time(timer.Elapsed());

  // Notifies the unit test event listener that a test has just finished.
  repeater->OnTestEnd(*this);

  // Tells UnitTest to stop associating assertion results to this// test.
  impl->set_current_test_info(nullptr);
}


// Runs the test and updates the test result.voidTest::Run(){
  if (!HasSameFixtureClass()) return;

  internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
  impl->os_stack_trace_getter()->UponLeavingGTest();
  internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()");
  // We will run the test only if SetUp() was successful and didn't call// GTEST_SKIP().// 这里调用的 &Test::TestBody 就是上面 TEST 宏生成的 addSuite_test0_Test::TestBody 函数// 可以使用 gdb 单步调试验证一下if (!HasFatalFailure() && !IsSkipped()) {
    impl->os_stack_trace_getter()->UponLeavingGTest();
    internal::HandleExceptionsInMethodIfSupported(
        this, &Test::TestBody, "the test body");
  }

  // However, we want to clean up as much as possible.  Hence we will// always call TearDown(), even if SetUp() or the test body has// failed.
  impl->os_stack_trace_getter()->UponLeavingGTest();
  internal::HandleExceptionsInMethodIfSupported(
      this, &Test::TearDown, "TearDown()");
}
#  gdb 打印堆栈来验证一下
(gdb) bt
#0  addSuite_test0_Test::TestBody (this=0x809cbe0) at /home/lzh/github/googletest/mytest/mytest.cpp:7#1  0x00000000080482ec in testing::internal::HandleSehExceptionsInMethodIfSupported (object=0x809cbe0, method=&virtual testing::Test::TestBody(),
    location=0x805f80b "the test body") at /home/lzh/github/googletest/googletest/src/gtest.cc:2607
#2  0x0000000008040001 in testing::internal::HandleExceptionsInMethodIfSupported (object=0x809cbe0, method=&virtual testing::Test::TestBody(),
    location=0x805f80b "the test body") at /home/lzh/github/googletest/googletest/src/gtest.cc:2643
#3  0x0000000008014664 in testing::Test::Run (this=0x809cbe0) at /home/lzh/github/googletest/googletest/src/gtest.cc:2682#4  0x00000000080150a6 in testing::TestInfo::Run (this=0x809bf90) at /home/lzh/github/googletest/googletest/src/gtest.cc:2861#5  0x00000000080159a3 in testing::TestSuite::Run (this=0x809c430) at /home/lzh/github/googletest/googletest/src/gtest.cc:3015#6  0x0000000008025335 in testing::internal::UnitTestImpl::RunAllTests (this=0x809c0f0) at /home/lzh/github/googletest/googletest/src/gtest.cc:5855#7  0x000000000804981a in testing::internal::HandleSehExceptionsInMethodIfSupported (object=0x809c0f0,
    method=(bool (testing::internal::UnitTestImpl::*)(testing::internal::UnitTestImpl * const)) 0x8024f16 ,
    location=0x80602f8 "auxiliary test code (environments or event listeners)") at /home/lzh/github/googletest/googletest/src/gtest.cc:2607
#8  0x000000000804123f in testing::internal::HandleExceptionsInMethodIfSupported (object=0x809c0f0,
    method=(bool (testing::internal::UnitTestImpl::*)(testing::internal::UnitTestImpl * const)) 0x8024f16 ,
    location=0x80602f8 "auxiliary test code (environments or event listeners)") at /home/lzh/github/googletest/googletest/src/gtest.cc:2643
#9  0x0000000008023a9a in testing::UnitTest::Run (this=0x8089500 ) at /home/lzh/github/googletest/googletest/src/gtest.cc:5438#10 0x000000000800ac33 in RUN_ALL_TESTS () at /home/lzh/github/googletest/mytest/../googletest/include/gtest/gtest.h:2490#11 main (argc=, argv=) at /home/lzh/github/googletest/mytest/mytest.cpp:17
(gdb) c

googletest 的简单的执行流程就是上面这样子啦。

你可能感兴趣的:(c++)