0x00GTest源码之调用过程初探

gtest总观

  1. 对于测试用例名和测试特例名,不能有下划线(_)。因为GTest源码中需要使用下划线把它们连接成一个独立的类名
// Expands to the name of the class that implements the given test.
#define GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \
    test_case_name##_##test_name##_Test
  1. GTest有自动统计结果自动格式化输出结果自动调度执行等特性.不同测试用例不相互影响、相同测试用例不同测试特例不相互影响。我们称之为独立性。除了独立性,也不失灵活性——一个测试测试特例中可以通过不同宏(ASSERT_*类宏会影响之后执行,EXPECT_*类宏不会)控制是否影响之后的执行
    GTest框架具有选择性测试的特征可以让我们通过在程序参数控制执行哪个测试用例,比如我们希望只执行Factorial测试,就可以这样调用程序.
./sample1_unittest --gtest_filter=Factorial*
  1. 预处理
    我们测试时,往往要构造复杂的数据。如果我们在每个测试特例中都要构造一遍数据,将是非常繁琐和不美观的。GTest提供了一种提前构建数据的方式。我们以如下代码为例
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(13, _m_list[2]);
}

我们让ListTest类继承于GTest提供的基类testing::Test,并重载SetUp方法。这样我们每次执行ListTest的一个测试特例时,SetUp方法都会执行一次,从而将数据准备完毕。这样我们只要在一个类中构建好数据就行了。这儿需要注意一下TEST_F宏,它的第一参数要求是类名,即ListTes。不像TEST宏的第一个参数我们可以随便命名。

TEST宏实现

TEST位于gtest.h line2311

#define GTEST_TEST(test_case_name, test_name)\
  GTEST_TEST_(test_case_name, test_name, \
              ::testing::Test, ::testing::internal::GetTestTypeId())
 
// Define this macro to 1 to omit the definition of TEST(), which
// is a generic name and clashes with some other libraries.
#if !GTEST_DONT_DEFINE_TEST
# define TEST(test_case_name, test_name) GTEST_TEST(test_case_name, test_name)

GTEST_TEST_位于gtest-internal.h line1402

#define GTEST_TEST_(test_suit_name, test_name, parent_class, paremt_id)\
class GTEST_TEST_CLASS_NAME_(test_suit_name, test_name)\
    : public parent_class{ \
public:
    GTEST_TEST_CLASS_NAME_(test_suit_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_suit_name, test_name));
};


::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_suit_name, test_name)::test_info_ = 
    ::testing::internal::MakeAndRegisterTestInfo(
    #test_suit_name, #test_name, nullptr, nullptr,
    ::testing::internal::CodeLocation(__FILE__, __LINE__), (parent_id),
    ::testing::internal::SuitApiResolver<
        parent_class>::GetSetUpCaseOrSuite(__FILE, __LINE__),
    ::testing::internal::SuitApiResolver<
        parent_class>::GetSetDownCaseOrSuite(__FILE, __LINE__),
    new ::testing::internal::TestFactoryImpl<GTEST_TEST_CLASS_NAME_(
    test_suite_name, test_name)>);
void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody()
 

test_info_ 初始化

// Creates a new TestInfo object and registers it with Google Test;
// returns the created object.
//
// Arguments:
//
//   test_suite_name:   name of the test suite
//   name:             name of the test
//   type_param:       the name of the test's type parameter, or NULL if
//                     this is not a typed or a type-parameterized test.
//   value_param:      text representation of the test's value parameter,
//                     or NULL if this is not a value-parameterized test.
//   code_location:    code location where the test is defined
//   fixture_class_id: ID of the test fixture class
//   set_up_tc:        pointer to the function that sets up the test suite
//   tear_down_tc:     pointer to the function that tears down the test suite
//   factory:          pointer to the factory that creates a test object.
//                     The newly created TestInfo instance will assume
//                     ownership of the factory object.
TestInfo* MakeAndRegisterTestInfo(
    const char* test_suite_name, const char* name, const char* type_param,
    const char* value_param, CodeLocation code_location,
    TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc,
    TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory) {
  TestInfo* const test_info =
      new TestInfo(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;
}


// 1. CodeLocation
struct CodeLocation {
  CodeLocation(const std::string& a_file, int a_line)
    : file(a_file)
    , line(a_line)
  {
      
  }
    
    std::string file;
    int line;
}


// 2. ::testing::internal::GetTestTypeId()
typedef const void* TypeId;
template<typename T>
class TypeIdHelper
{
public:
    static bool dummy_;
};

template<typename T>
bool TypeIdHelper<T>::dummy_ = false;

// TEST_F调用
template<typename T>
TypeId GetTypeId(){
    return &(TypeIdHelper<T>::dummy_);
}

// TEST
TypeId GetTypeId(){
    return GetTypeId<Test>();
}


// 3. SetUpTestSuiteFunc set_up_tc
// 4. TearDownTestSuiteFunc tear_down_tc

//  Helper to identify which setup function for TestCase / TestSuite to call.
//  Only one function is allowed, either TestCase or TestSute but not both.

// Utility functions to help SuiteApiResolver
using SetUpTearDownSuiteFuncType = void (*)();

inline SetUpTearDownSuiteFuncType GetNotDefaultOrNull(
    SetUpTearDownSuiteFuncType a, SetUpTearDownSuiteFuncType def) {
  return a == def ? nullptr : a;
}

template <typename T>
//  Note that SuiteApiResolver inherits from T because
//  SetUpTestSuite()/TearDownTestSuite() could be protected. Ths way
//  SuiteApiResolver can access them.
struct SuiteApiResolver : T {
  // testing::Test is only forward declared at this point. So we make it a
  // dependend class for the compiler to be OK with it.
  using Test =
      typename std::conditional<sizeof(T) != 0, ::testing::Test, void>::type;

  static SetUpTearDownSuiteFuncType GetSetUpCaseOrSuite(const char* filename,
                                                        int line_num) {
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
    SetUpTearDownSuiteFuncType test_case_fp =
        GetNotDefaultOrNull(&T::SetUpTestCase, &Test::SetUpTestCase);
    SetUpTearDownSuiteFuncType test_suite_fp =
        GetNotDefaultOrNull(&T::SetUpTestSuite, &Test::SetUpTestSuite);

    GTEST_CHECK_(!test_case_fp || !test_suite_fp)
        << "Test can not provide both SetUpTestSuite and SetUpTestCase, please "
           "make sure there is only one present at "
        << filename << ":" << line_num;

    return test_case_fp != nullptr ? test_case_fp : test_suite_fp;
#else
    (void)(filename);
    (void)(line_num);
    return &T::SetUpTestSuite;
#endif
  }

  static SetUpTearDownSuiteFuncType GetTearDownCaseOrSuite(const char* filename,
                                                           int line_num) {
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
    SetUpTearDownSuiteFuncType test_case_fp =
        GetNotDefaultOrNull(&T::TearDownTestCase, &Test::TearDownTestCase);
    SetUpTearDownSuiteFuncType test_suite_fp =
        GetNotDefaultOrNull(&T::TearDownTestSuite, &Test::TearDownTestSuite);

    GTEST_CHECK_(!test_case_fp || !test_suite_fp)
        << "Test can not provide both TearDownTestSuite and TearDownTestCase,"
           " please make sure there is only one present at"
        << filename << ":" << line_num;

    return test_case_fp != nullptr ? test_case_fp : test_suite_fp;
#else
    (void)(filename);
    (void)(line_num);
    return &T::TearDownTestSuite;
#endif
  }
};

// 5. TestFactoryBase* factory
// new ::testing::internal::TestFactoryImpl<GTEST_TEST_CLASS_NAME_(
//  test_suite_name, test_name)>

class TestFactoryBase {
 public:
  virtual ~TestFactoryBase() {}

  // Creates a test instance to run. The instance is both created and destroyed
  // within TestInfoImpl::Run()
  virtual Test* CreateTest() = 0;

 protected:
  TestFactoryBase() {}

 private:
  GTEST_DISALLOW_COPY_AND_ASSIGN_(TestFactoryBase);
};

// This class provides implementation of TeastFactoryBase interface.
// It is used in TEST and TEST_F macros.
template <class TestClass>
class TestFactoryImpl : public TestFactoryBase {
 public:
  Test* CreateTest() override { return new TestClass; }
};

// trick
#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type) \
 type(type const &) = delete; \
 GTEST_DISALLOW_ASSIGN_(type)
 
#define GTEST_DISALLOW_ASSIGN_(type) \
 void operator=(type const &) = delete
 
// 6. GetUnitTestImpl
inline UnitTestImpl* GetUnitTestImpl()
{
    return UnitTest::GetInstance()->impl();
}

//gtest-internal-inl.h
void AddTestInfo(internal::SetUpTestSuiteFunc set_up_tc,
                   internal::TearDownTestSuiteFunc tear_down_tc,
                   TestInfo* test_info) {
    if (original_working_dir_.IsEmpty()) {
      original_working_dir_.Set(FilePath::GetCurrentDir());
      GTEST_CHECK_(!original_working_dir_.IsEmpty())
          << "Failed to get the current working directory.";
    }

    GetTestSuite(test_info->test_suite_name(), test_info->type_param(),
                 set_up_tc, tear_down_tc)
        ->AddTestInfo(test_info);
}

// 在没有找到测试实例对象指针的情况下,新建了一个TestCase测试用例对象,并将其指针保存到了test_suites_中
TestSuite* UnitTestImpl::GetTestSuite(
    const char* test_suite_name, const char* type_param,
    internal::SetUpTestSuiteFunc set_up_tc,
    internal::TearDownTestSuiteFunc tear_down_tc) {
  // Can we find a TestSuite with the given name?
  const auto 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 =
      new TestSuite(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_;
    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<int>(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.
void TestSuite::AddTestInfo(TestInfo* test_info) {
  test_info_list_.push_back(test_info);
  test_indices_.push_back(static_cast<int>(test_indices_.size()));
}

测试用例调度机制实现

// gtest_main.cc
GTEST_API_ int main(int argc, char **argv) {
  printf("Running main() from gtest_main.cc\n");
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}


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

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

template <class T, typename Result>
Result HandleExceptionsInMethodIfSupported(
    T* object, Result (T::*method)(), const char* location) {

    // ...
    return HandleSehExceptionsInMethodIfSupported(object, method, location);
}

// --> HandleSehExceptionsInMethodIfSupported
template <class T, typename Result>
Result HandleSehExceptionsInMethodIfSupported(
    T* object, Result (T::*method)(), const char* location) {
#if GTEST_HAS_SEH
  __try {
    return (object->*method)();
  } __except (internal::UnitTestOptions::GTestShouldProcessSEH(  // NOLINT
      GetExceptionCode())) {
    // We create the exception message on the heap because VC++ prohibits
    // creation of objects with destructors on stack in functions using __try
    // (see error C2712).
    std::string* exception_message = FormatSehExceptionMessage(
        GetExceptionCode(), location);
    internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure,
                                             *exception_message);
    delete exception_message;
    return static_cast<Result>(0);
  }
#else
  (void)location;
  return (object->*method)();
#endif  // GTEST_HAS_SEH
}

// impl() ->  &internal::UnitTestImpl::RunAllTests
{
    // 核心代码,gtest.cc
    for (int test_index = 0; test_index < total_test_suite_count(); test_index++) {
        GetMutableSuiteCase(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;
        }
    }
}

// Gets the i-th test suite among all the test suites. i can range from 0 to
// total_test_suite_count() - 1. If i is not in that range, returns NULL.
// file: gtest-internal-inl.h
TestSuite* GetMutableSuiteCase(int i) {
const int index = GetElementOr(test_suite_indices_, i, -1);
return index < 0 ? nullptr : test_suites_[static_cast<size_t>(index)];
}

// TestSuite
for (int i = 0; i < total_test_count(), i++)
{
    GetMutableTestInfo(i)->Run();
}

TestInfo* GetMutableTestInfo(int i) {
const int index = GetElementOr(test_indices_, i, -1);
return index < 0 ? nullptr : test_info_list_[static_cast<size_t>(index)];
}

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

// Test的Run方法
void Test::Run()
{
    // ...
    if (!HasFatalFailure() && !IsSkipped())
    {
        //..
        internal::HandleExceptionsInMethodIfSupported(
          this, &Test::TesBody, "the test body");
    }
}
// 所以最后调用的还是Test类的Run方法,而Test类的Run方法实际上只是调用了测试用例特例类重载了的TestBody方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Erice_s

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值