boost::test笔记(1) Boost.Test > Components > The Execution Monitor

22 篇文章 0 订阅
2 篇文章 0 订阅

The Execution Monitor  的作用顾名思义,就是运行监视器。

根据文档的描述,这个监视器的作用就是捕捉异常,然后报告。

使用方法文档已经比较详细了。

我感兴趣的,是它的实现。

文档给出的例子是这样的:

 

//  (C) Copyright Gennadiy Rozental 2003-2005.
//  Distributed under the Boost Software License, Version 1.0.
//  (See accompanying file LICENSE_1_0.txt or copy at 
//  http://www.boost.org/LICENSE_1_0.txt)

//  See http://www.boost.org/libs/test for the library home page.

#include <boost/test/execution_monitor.hpp>
#include <boost/test/utils/basic_cstring/io.hpp>

#include <iostream>

struct my_exception1
{
    explicit    my_exception1( int res_code ) : m_res_code( res_code ) {}

    int         m_res_code;
};

struct my_exception2
{
    explicit    my_exception2( int res_code ) : m_res_code( res_code ) {}

    int         m_res_code;
};

namespace {

class dangerous_call {
public:
    dangerous_call( int argc ) : m_argc( argc ) {}
    
    int operator()()
    {
        // here we perform some operation under monitoring that could throw my_exception
        if( m_argc < 2 )
            throw my_exception1( 23 );
        if( m_argc > 3 )
            throw my_exception2( 45 );
        else if( m_argc > 2 )
            throw "too many args";

        return 1;
    }

private:
    // Data members  
    int     m_argc;
};

void translate_my_exception1( my_exception1 const& ex )
{
    std::cout << "Caught my_exception1(" << ex.m_res_code << ")"<< std::endl;   
}

void translate_my_exception2( my_exception2 const& ex )
{
    std::cout << "Caught my_exception2(" << ex.m_res_code << ")"<< std::endl;   
}

} // local_namespace

int
main( int argc , char *[] )
{ 
    ::boost::execution_monitor ex_mon;

    ex_mon.register_exception_translator<my_exception1>( &translate_my_exception1 );
    ex_mon.register_exception_translator<my_exception2>( &translate_my_exception2 );

    try {
        ex_mon.execute( ::boost::unit_test::callback0<int>( dangerous_call( argc ) ) );
    }
    catch ( boost::execution_exception const& ex ) {
        std::cout << "Caught exception: " << ex.what() << std::endl;
    }

    return 0;
}

// EOF

可以看到,基本的流程就是实例化 execution_monitor , 注册自定义的异常处理函数,接着用 boost::execution_monitor::execute  执行测试对象。

 

先来分析一下   ex_mon.register_exception_translator<my_exception1>( &translate_my_exception1 ) ;

ex_mon 的类型是 execution_monitor  ,查看 execution_monitor   的实现,可以看到  m_custom_translators  成员,其类型为boost::scoped_ptr<detail::translate_exception_base> ,是一个智能指针。而在  register_exception_translator  这个成员函数中,只有这么一句话:

m_custom_translators.reset(
        new detail::translate_exception<Exception,ExceptionTranslator>( tr,m_custom_translators ) );

是对 m_custom_translators  的重新赋值。而赋值的内容,是 new  返回的  translate_exception<Exception,ExceptionTranslator>*

很明显,translate_exception<Exception,ExceptionTranslator>  必然派生自  detail::translate_exception_base  , 否则这个赋值无法成立。查看translate_exception<Exception,ExceptionTranslator>,果然如此。

 

而  translate_exception<Exception,ExceptionTranslator>  的构造中,为什么要传入  m_custom_translators  呢? 联想到  main  里面  register_exception_translator  的用法,可以推测,需要这个参数来建立一个异常处理列表(这个说法不太准确,总之就是类似消息映射,建立异常—函数映射表)。

 

translate_exception<Exception,ExceptionTranslator>   的构造函数是这样的:

translate_exception( ExceptionTranslator const& tr, base_ptr& next )
    : translate_exception_base( next ), m_translator( tr ) {}

参数 next   对应   m_custom_translators 。 

base_ptr  的定义:  typedef boost::scoped_ptr<translate_exception_base> base_ptr

 

接着看:

translate_exception_base( boost::scoped_ptr<translate_exception_base>& next )
    {
        next.swap( m_next );
    }

m_next  是  translate_exception_base  的成员:boost::scoped_ptr<translate_exception_base> m_next;

将  next  赋值给  m_next  ,  很典型的列表操作嘛。

 

回过头接着分析  translate_exception( ExceptionTranslator const& tr, base_ptr& next )
    : translate_exception_base( next ), m_translator( tr ) {} :

可以看到,参数 tr  被赋给了 m_translator  。结合  translate_exception_base::m_next   分析,  translate_exception<Exception,ExceptionTranslator>  果然是一个列表的节点,数据成员是  m_translator  ,节点指针是  m_next  。(感谢  scope_ptr  ,列表头节点析构,整个列表自动析构)

 

到这里,就可以整理一下这个“异常映射表”的建立过程了:

template<typename Exception, typename ExceptionTranslator>
void
execution_monitor::register_exception_translator( ExceptionTranslator const& tr, boost::type<Exception>*=0 )

这个函数带入了异常类型,异常处理函数类型,异常函数三个信息( boost::type<Exception>*   用于引发函数重载,但对于这个参数的必要性,我持怀疑态度,因为我用类似的模板函数,并不需要这样的参数来引发函数重载,我用的是g++),其中异常类型是用模板参数,显式带入的。

 

然后,利用带入的信息,new一个异常映射表的节点,并加入映射表中(都在   translate_exception 的构造函数内完成)。

随后  execution_monitor  m_custom_translators   记录表头节点。

这样,一个异常映射表就建立起来了。

 

那么,这个映射表又是如何捕捉不同的异常的呢?

 

查看  translate_exception_base  的定义,有这么一个纯虚函数:

virtual int operator()( unit_test::callback0<int> const& F ) = 0;

其参数的类型,一个回调对象,与  ex_mon.execute( ::boost::unit_test::callback0<int>( dangerous_call( argc ) )   是一致的。显然,excute  这个函数,肯定要调用到这个  operator()  来执行被测试对象,捕捉其异常。

 

而这个函数是纯虚的,其定义在派生类中必然出现。在  translate_exception<Exception,ExceptionTranslator>  中,定义如下:

int operator()( unit_test::callback0<int> const& F )
    {
        try {
           
return m_next ? (*m_next)( F ) : F();
        } catch( Exception const& e ) {
            m_translator( e );
            return boost::exit_exception_failure;
        }
    }

 

注意下划线部分,这是一个递归调用的写法。但是,  operate()  是虚函数,“递”的实际上并不是同一个函数。这就是精妙之处了,在函数递进的过程中,不同异常的捕捉也一层层地建立了,直到最后执行被测试对象。而异常是如何抛出的呢?是根据函数调用的顺序,一层层向外抛出,直到被捕获。由此, operator()  以递归的方式,保证抛出的异常能够遍历整个“异常映射表”。这实在是一种简洁得美丽的实现。

 

execution_monitor  最精华的,就是这个 exception_traslator  的实现了,其他的地方基本上就是标准异常处理和系统信号处理的内容了。

 

局限性:excution_monitor 只能测试没有参数的被测对象,不能像 boost::signal 那样,绑定任意参数的函数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Test ignored. java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test at org.springframework.util.Assert.state(Assert.java:76) at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.getOrFindConfigurationClasses(SpringBootTestContextBootstrapper.java:237) at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.processMergedContextConfiguration(SpringBootTestContextBootstrapper.java:152) at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildMergedContextConfiguration(AbstractTestContextBootstrapper.java:393) at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildDefaultMergedContextConfiguration(AbstractTestContextBootstrapper.java:309) at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildMergedContextConfiguration(AbstractTestContextBootstrapper.java:262) at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildTestContext(AbstractTestContextBootstrapper.java:107) at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.buildTestContext(SpringBootTestContextBootstrapper.java:102) at org.springframework.test.context.TestContextManager.<init>(TestContextManager.java:137) at org.springframework.test.context.TestContextManager.<init>(TestContextManager.java:122) at org.junit.jupiter.engine.execution.ExtensionValuesStore.lambda$getOrComputeIfAbsent$4(ExtensionValuesStore.java:86) at org.junit.jupiter.engine.execution.ExtensionValuesStore$MemoizingSupplier.computeValue(ExtensionValuesStore.java:223) at org.junit.jupiter.engine.execution.ExtensionValuesStore$MemoizingSupplier.get(ExtensionValuesStore.java:211) at org.junit.jupiter.engine.execution.ExtensionValuesStore$StoredValue.evaluate(ExtensionValuesStore.java:191) at org.junit.jupiter.engine.execution.ExtensionValuesStore$StoredValue.acces
07-15

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值