GTest源码剖析(六)——RUN_ALL_TESTS

2023-11-03

GTest源码剖析——RUN_ALL_TESTS

1 RUN_ALL_TESTS()源码分析

RUN_ALL_TESTS()之前是宏定义,现在改变为函数。其实际上仅仅是调用了UnitTest单例的Run()函数。

源码如下:
其实现为获取一个UnitTest单例,然后调用其Run()函数


int RUN_ALL_TESTS();

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

实际上,RUN_ALL_TESTS()的操作主要是,依次调用
1. UnitTest::Run()
2. UnitTest::Run()
3. UnitTestImpl::RunAllTests()
4. TestCase::Run()
5. TestCase::Run()
6. Test::Run()
7. Test::TestBody()

并在这些函数里设置相应的操作,以保证测试运行的正确性。

1.1 UnitTest::Run()

UnitTest::Run()主要设置了相关死亡测试的设置。真正的实现交给了RunAllTest()

  1. 如果忽略死亡测试和支持跨平台及异常处理的逻辑,其实现可以简化为:
int UnitTest::Run() 
{
  return internal::HandleExceptionsInMethodIfSupported(
      impl(),
      &internal::UnitTestImpl::RunAllTests,
      "auxiliary test code (environments or event listeners)") ? 0 : 1;
}
  1. 如果我们在剔除HandleExceptionsInMethodIfSupported对支持跨平台及异常处理的逻辑,其实现直接简化为:
int UnitTest::Run() 
{
  return impl->RunAllTests();
}
  1. 全部源码如下:

int UnitTest::Run() 
{
  const bool in_death_test_child_process = GTEST_FLAG(internal_run_death_test).length() > 0;

  const internal::ScopedPrematureExitFile premature_exit_file(
      in_death_test_child_process ?
      NULL : internal::posix::GetEnv("TEST_PREMATURE_EXIT_FILE"));

  impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions));

#if GTEST_HAS_SEH
  if (impl()->catch_exceptions() || in_death_test_child_process) 
  {
# if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT
    SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT |
                 SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
# endif  

# if (defined(_MSC_VER) || GTEST_OS_WINDOWS_MINGW) && !GTEST_OS_WINDOWS_MOBILE
    _set_error_mode(_OUT_TO_STDERR);
# endif

# if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE
    if (!GTEST_FLAG(break_on_failure))
      _set_abort_behavior(0x0, flags:_WRITE_ABORT_MSG | _CALL_REPORTFAULT); 
# endif
  }
#endif  // GTEST_HAS_SEH

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

1.2 HandleExceptionsInMethodIfSupported

HandleSehExceptionsInMethodIfSupported 主要用于捕获异常和跨平台处理。
这里可以看出GTest对于跨平台的支持和捕获异常的不遗余力。其实这么多代码主要是为了实现如下的功能:

Result HandleExceptionsInMethodIfSupported(T* object, Result (T::*method)(), const char* location) {
    return (object->*method)();
}

全部源码如下:


template <class T, typename Result>
Result HandleExceptionsInMethodIfSupported(T* object, Result (T::*method)(), const char* location) {
  if (internal::GetUnitTestImpl()->catch_exceptions()) 
  {
#if GTEST_HAS_EXCEPTIONS
    try 
    {
      return HandleSehExceptionsInMethodIfSupported(object, method, location);
    } 
    catch (const internal::GoogleTestFailureException&) 
    { 
      throw;
    } 
    catch (const std::exception& e) 
    { 
      internal::ReportFailureInUnknownLocation(
          TestPartResult::kFatalFailure,
          FormatCxxExceptionMessage(e.what(), location));
    } 
    catch (...) 
    {  
      internal::ReportFailureInUnknownLocation(
          TestPartResult::kFatalFailure,
          FormatCxxExceptionMessage(NULL, location));
    }
    return static_cast<Result>(0);
#else
    return HandleSehExceptionsInMethodIfSupported(object, method, location);
#endif  // GTEST_HAS_EXCEPTIONS
  } 
  else 
  {
    return (object->*method)();
  }
}

//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( GetExceptionCode())) 
  {
    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
}

1.3 UnitTestImpl::RunAllTests()


bool UnitTestImpl::RunAllTests() 
{
  // Makes sure InitGoogleTest() was called.
  if (!GTestIsInitialized()) 
  {
    printf("%s",
           "\nThis test program did NOT call ::testing::InitGoogleTest "
           "before calling RUN_ALL_TESTS().  Please fix it.\n");
    return false;
  }

  // Do not run any test if the --help flag was specified.
  if (g_help_flag)
    return true;

  // Repeats the call to the post-flag parsing initialization in case the
  // user didn't call InitGoogleTest.
  PostFlagParsingInit();

  // Even if sharding is not on, test runners may want to use the
  // GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding
  // protocol.
  internal::WriteToShardStatusFileIfNeeded();

  //执行死亡测试
  bool in_subprocess_for_death_test = false;
#if GTEST_HAS_DEATH_TEST
  in_subprocess_for_death_test = (internal_run_death_test_flag_.get() != NULL);
# if defined(GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_)
  if (in_subprocess_for_death_test) 
  {
    GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_();
  }
# endif
#endif  // GTEST_HAS_DEATH_TEST

    //应该是检查environments相关的设置,可以暂不关心。
  const bool should_shard = ShouldShard(kTestTotalShards, kTestShardIndex,
                                        in_subprocess_for_death_test);

    //匹配Filter,对所有的测试用例进行匹配,确定是否需要运行该测试用例
  const bool has_tests_to_run = FilterTests(should_shard
                                              ? HONOR_SHARDING_PROTOCOL
                                              : IGNORE_SHARDING_PROTOCOL) > 0;

  // Lists the tests and exits if the --gtest_list_tests flag was specified.
  if (GTEST_FLAG(list_tests)) 
  {
    ListTestsMatchingFilter();
    return true;
  }

  //设置随机运行测试用例
  random_seed_ = GTEST_FLAG(shuffle) ? GetRandomSeedFromFlag(GTEST_FLAG(random_seed)) : 0;

  // True iff at least one test has failed.
  bool failed = false;

  //获取事件监听器,如果用户订阅了某事件,则在适当的时间点上报该事件,让用户进行额外的操作
  TestEventListener* repeater = listeners()->repeater();

  start_timestamp_ = GetTimeInMillis();

  //上报事件OnTestProgramStart
  repeater->OnTestProgramStart(*parent_);

  const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat);

  const bool forever = repeat < 0;
  for (int i = 0; forever || i != repeat; i++) 
  {
    //清除所有非AddHoc的测试结果,为后续运行做准备。
    ClearNonAdHocTestResult();

    const TimeInMillis start = GetTimeInMillis();

    // Shuffles test cases and tests if requested.
    if (has_tests_to_run && GTEST_FLAG(shuffle)) 
    {
      random()->Reseed(random_seed_);
      ShuffleTests();
    }

    // Tells the unit test event listeners that the tests are about to start.
    repeater->OnTestIterationStart(*parent_, i);

    // Runs each test case 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 during global
      // set-up.
      if (!Test::HasFatalFailure()) 
      {
        //逐个运行TestCase,即最终运行所有的TestBody()
        for (int test_index = 0; test_index < total_test_case_count(); test_index++) 
        {
          GetMutableTestCase(test_index)->Run();
        }
      }

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

    elapsed_time_ = GetTimeInMillis() - start;

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

    // Gets the result and clears it.
    if (!Passed()) 
    {
      failed = true;
    }

    UnshuffleTests();

    if (GTEST_FLAG(shuffle)) 
    {
      // Picks a new random seed for each iteration.
      random_seed_ = GetNextRandomSeed(random_seed_);
    }
  }

  //上报事件OnTestProgramEnd
  repeater->OnTestProgramEnd(*parent_);

  return !failed;
}

1.5 TestCase::Run()


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

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

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

  //上报事件OnTestCaseStart
  repeater->OnTestCaseStart(*this);
  impl->os_stack_trace_getter()->UponLeavingGTest();

  //for TEST_F
  internal::HandleExceptionsInMethodIfSupported(
      this, &TestCase::RunSetUpTestCase, "SetUpTestCase()");

  const internal::TimeInMillis start = internal::GetTimeInMillis();

  //逐个运行TestCase里的Test,即最终运行所有的TestBody()
  for (int i = 0; i < total_test_count(); i++) 
  {
    GetMutableTestInfo(i)->Run();
  }
  elapsed_time_ = internal::GetTimeInMillis() - start;

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

  //for TEST_F
  internal::HandleExceptionsInMethodIfSupported(
      this, &TestCase::RunTearDownTestCase, "TearDownTestCase()");

  repeater->OnTestCaseEnd(*this);
  impl->set_current_test_case(NULL);
}

1.6 TestCase::Run()


void TestInfo::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);

  const TimeInMillis start = internal::GetTimeInMillis();

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

  // Creates the test object.
  // 此处确保所有的TEST_P里调用GetParam()的正确性
  Test* const test = internal::HandleExceptionsInMethodIfSupported(
      factory_, &internal::TestFactoryBase::CreateTest,
      "the test fixture's constructor");

  // Runs the test only if the test object was created and its
  // constructor didn't generate a fatal failure.
  if ((test != NULL) && !Test::HasFatalFailure()) 
  {
    // This doesn't throw as all user code that can throw are wrapped into
    // exception handling code.
    test->Run();
  }

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

  result_.set_elapsed_time(internal::GetTimeInMillis() - start);

  // 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(NULL);
}

1.7 Test::Run()


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

  internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
  impl->os_stack_trace_getter()->UponLeavingGTest();

  //for TEST_F
  internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()");

  // We will run the test only if SetUp() was successful.
  if (!HasFatalFailure()) 
  {
    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();

  // for TEST_F
  internal::HandleExceptionsInMethodIfSupported(
      this, &Test::TearDown, "TearDown()");
}

2 参考

github: googletest


ZhaiPillar
2017-09-17

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

GTest源码剖析(六)——RUN_ALL_TESTS 的相关文章

随机推荐

  • 挺详细的一篇Cadence学习笔记

    目录 下载方法及连接 更新补丁后快捷方式失效怎么办 cadence软件介绍 Cadence Product Choices 新建工程 如何不打印右下角的信息框 原理图库 复合元器件 元器件逆时针放置引脚 添加元件库 元器件编号重新排列 页面
  • 对于msvcr120.dll丢失的问题,分享几种解决方法

    msvcr120 dll的作用是提供一系列的运行时函数和功能 以便应用程序能够正常运行 这些函数和功能包括内存管理 异常处理 输入输出操作 数学运算等 在没有这个库文件的情况下 应用程序可能无法正常启动或执行特定的功能 甚至会出现错误提示
  • JavaScript常用的定时器

    1 定时器 setTimeout setInterval clearInterval clearTimeout 1 1setTimeout 定时器 window setTimeout 调用函数 延迟的毫秒数 该定时器在定时器到期后执行调用函
  • 又被薪资倒挂了。。。

    十月中旬 一年一度的秋招接近尾声了 各家公司开始陆续开奖 公布今年校招各个档位的薪资水平 互联网的确有些卷 不仅体现在工作时长和强度上 就连每年应届生的薪资 也在同行的不断加码下 水涨船高 不过 薪资的卷 是令校招生喜闻乐见的事情 尽管 这
  • 深入理解 TCP 拥塞控制

    随着网络技术的飞速发展 越来越多的工作依赖网络完成 基于互联网的实时通信系统的质量和实时性也很大程度也依赖于网络质量 然而 在Internet的TCP IP体系结构中 拥塞的发生是其固有的属性 网络拥塞是指用户对网络资源 包括链路带宽 存储
  • python自动化办公——读取PPT写入word表格

    Python自动化办公 读取PPT内容写入word表格 文章目录 Python自动化办公 读取PPT内容写入word表格 一 需求分析 二 导入依赖 三 代码 四 结果及总结 一 需求分析 由于我们知识图谱课程需要将课堂小组汇报的PPT总结
  • Scala与Java混编译:java日志不打印的问题

    1 背景 我本地测试 大部分代码是scla开发 少部分是java代码 然后本地测试都是正确的 19 09 04 20 01 32 INFO TopoSparkSubmitter 加载Spark默认配置文件 Some etc spark2 c
  • 二进制简单计算

    二进制简单计算 1 24 35 值 用二进制补码方式进行计算 24的补码 00011000 35的原码 10100011 35的反码 11011100 35的补码 11011101 24 35的值 00011000 11011101 111
  • R语言中if语句使用方法之超详细教程

    在R语言中 if属于一种分支结构 即根据某个条件执行相关的语句 R中的if语句与else配合主要有3种结构 单个if语句 if cond expr 其它语句 即当括弧中的cond条件为TRUE时 则执行表达式expr 否则跳过后执行其后的语
  • 复习git的使用

    文章目录 复习git的使用 基础 提交文件 查看 回退 撤销修改 分支 创建 切换 tag 其他命令 HEAD 指针 的理解 复习git的使用 最近公司的老旧项目要由svn转到git git 命令大都忘记了 这里复习总结一下 基础 查看本地
  • unity虚拟相机cinemachine 之ScriptingExample源码解读轻松理解其作用

    我从demo里面找到了脚本的源码 运行的效果 是5秒切换到这个cube立方体 又5秒切换到另外一个 cylinder public class ScriptingExample MonoBehaviour CinemachineVirtua
  • 【TVM 学习资料】使用 Python 接口(AutoTVM)编译和优化模型

    本篇文章译自英文文档 Compiling and Optimizing a Model with the Python Interface AutoTVM 作者是 Chris Hoge 更多 TVM 中文文档可访问 TVM 中文站 TVMC
  • 14.navigator.userAgent属性检查浏览器类型

    如何使用navigator userAgent属性检查浏览器类型 navigator userAgent属性是什么 是个只读的字符串 声明浏览器用于HTTP请求的用户代理头的值 如何检查 let a navigator userAgent
  • SQLyog中文乱码的解决方案(中文显示成问号)

    问题描述 在SQLyog中键入的中文都变成了 如下图所示 解决方案 找到乱码的字段 右击然后选择 管理字段 在弹出的页面里点击 隐藏语言选项 取消隐藏 然后就可以看到Charset列 如下图所示 更改Charset列 选择utf8 之后点击
  • ld: warning: object file (/path/WYDemo.framework/WYDemo(WYSingleton.o)) was built for newer iOS vers...

    1 出现场景 1 在制作 WYDemo framework 工程中的 Development target 为 11 2 2 在使用 WYDemo framework 工程中的 Development target 为 8 0 2 解决方案
  • Scrach基本概念与操作

    基本概念 一个程序最初的触发是由事件 黄色积木 负责的 例如点击播放事件 按下空格事件 当接收到消息等 程序由舞台和角色组成 舞台和角色都可以有多个 Scratch本身提供了许多舞台和角色的素材 可直接使用 每个角色都有自己的脚本代码 由各
  • Using join buffer (Batched Key Access)

    2019独角兽企业重金招聘Python工程师标准 gt gt gt Using join buffer Batched Key Access 表连接算法 Batched Key Access BKA 原理 MySQL 5 6版本提供了很多性
  • 利用循环输出文字

    首先设置一个循环的函数 var arr 1 var i 0 function xh i arr var arr 1 var i 0 if i gt arr false else document write 人类的本质是复读机 xh if
  • Windows 安装yolo v4时 Cmake无法检测到CUDA的问题

    最近因为装yolov4真的是头发掉了一大把 好不容易避开了众多坑之后 结果Cmake检测不到CUDA了 具体的安装步骤参照了以下文章 https blog csdn net shuaijieer article details 106150
  • GTest源码剖析(六)——RUN_ALL_TESTS

    GTest源码剖析 RUN ALL TESTS GTest源码剖析RUN ALL TESTS RUN ALL TESTS源码分析 1 UnitTestRun 2 HandleExceptionsInMethodIfSupported 3 U