RobotFramework学习系列(三)

  前两篇文章讲解了Suite的构造,文件到Robot的数据格式的转化。这篇文章继续讲解

  回到入口函数Robotframework().main()

  8行之前已经讲过了,8行为对suite的配置。9行为重点

 1     def main(self, datasources, **options):
 2         settings = RobotSettings(options)
 3         LOGGER.register_console_logger(**settings.console_logger_config)
 4         LOGGER.info('Settings:
%s' % unicode(settings))
 5         suite = TestSuiteBuilder(settings['SuiteNames'],
 6                                  settings['WarnOnSkipped'],
 7                                  settings['RunEmptySuite']).build(*datasources)
 8         suite.configure(**settings.suite_config)
 9         result = suite.run(settings)
10         LOGGER.info("Tests execution ended. Statistics:
%s"
11                     % result.suite.stat_message)
12         if settings.log or settings.report or settings.xunit:
13             writer = ResultWriter(settings.output if settings.log else result)
14             writer.write_results(settings.get_rebot_settings())
15         return result.return_code

  suite.run()是robot unningmodel.py下TestSuite的方法,具体构造已经在上篇文章中简单讲述了

 1     def run(self, settings=None, **options):
 2         with STOP_SIGNAL_MONITOR:
 3             IMPORTER.reset()
 4             pyloggingconf.initialize(settings['LogLevel'])
 5             init_global_variables(settings)
 6             output = Output(settings)
 7             runner = Runner(output, settings)
 8             self.visit(runner)
 9         output.close(runner.result)
10         return runner.result

   这里出现了with STOP_SIGNAL_MONITOR 这个也算是python中的魔法实现了。这篇文章先讲一下这块,with的原理如下

  1、紧跟with后面的语句被执行后后,对象的__enter__()方法被调用,这个方法的返回值将被赋值给as后面的变量
  2、当with后面的代码块全部被执行完之后,将调用前面返回对象的__exit__()方法

  示例代码和输出结果如下

 1 class Test:
 2     def __enter__(self):
 3         print "__enter__"
 4         return self
 5     def __exit__(self, *exc_info):
 6         print "__exit__"
 7     def output(self, message):
 8         print message
 9 with Test() as test:
10     test.output('test')
1 C:Python27python.exe E:/test/testwith.py
2 __enter__
3 test
4 __exit__

  返回来看STOP_SIGNAL_MONITOR这个类

 1 class _StopSignalMonitor(object):
 2 
 3     def __init__(self):
 4         self._signal_count = 0
 5         self._running_keyword = False
 6         self._orig_sigint = None
 7         self._orig_sigterm = None
 8 
 9     def __call__(self, signum, frame):
10         self._signal_count += 1
11         LOGGER.info('Received signal: %s.' % signum)
12         if self._signal_count > 1:
13             sys.__stderr__.write('Execution forcefully stopped.
')
14             raise SystemExit()
15         sys.__stderr__.write('Second signal will force exit.
')
16         if self._running_keyword and not sys.platform.startswith('java'):
17             self._stop_execution_gracefully()
18 
19     def _stop_execution_gracefully(self):
20         raise ExecutionFailed('Execution terminated by signal', exit=True)
21 
22     def start(self):
23         # TODO: Remove start() in favor of __enter__ in RF 2.9. Refactoring
24         # the whole signal handler at that point would be a good idea.
25         self.__enter__()
26 
27     def __enter__(self):
28         if signal:
29             self._orig_sigint = signal.getsignal(signal.SIGINT)
30             self._orig_sigterm = signal.getsignal(signal.SIGTERM)
31             for signum in signal.SIGINT, signal.SIGTERM:
32                 self._register_signal_handler(signum)
33         return self
34 
35     def __exit__(self, *exc_info):
36         if signal:
37             signal.signal(signal.SIGINT, self._orig_sigint)
38             signal.signal(signal.SIGTERM, self._orig_sigterm)
39 
40     def _register_signal_handler(self, signum):
41         try:
42             signal.signal(signum, self)
43         except (ValueError, IllegalArgumentException), err:
44             # ValueError occurs e.g. if Robot doesn't run on main thread.
45             # IllegalArgumentException is http://bugs.jython.org/issue1729
46             if currentThread().getName() == 'MainThread':
47                 self._warn_about_registeration_error(signum, err)
48 
49     def _warn_about_registeration_error(self, signum, err):
50         name, ctrlc = {signal.SIGINT: ('INT', 'or with Ctrl-C '),
51                        signal.SIGTERM: ('TERM', '')}[signum]
52         LOGGER.warn('Registering signal %s failed. Stopping execution '
53                     'gracefully with this signal %sis not possible. '
54                     'Original error was: %s' % (name, ctrlc, err))
55 
56     def start_running_keyword(self, in_teardown):
57         self._running_keyword = True
58         if self._signal_count and not in_teardown:
59             self._stop_execution_gracefully()
60 
61     def stop_running_keyword(self):
62         self._running_keyword = False

  程序开始运行时执行顺序为:

  1、__enter__():

    查看signal是否导入成功,导入成功,则获取一下执行signal.getsignal(),对signal.SIGINT和signal.SIGTERM,这两种信号,一个是Crtl+C触发,一个是进程自身要求中断引起,当程序初始运行时,getsignal返回的都是0

    调用_register_signal_handler 对两种信号进行注册,以备将来捕获。在注册时候,使用了signal.signal(signum, self)。请注意这块,signale的第二个参数为捕获到对应信号后的处理函数,此时把self传进去,也就是说处理函数为self()也就是_StopSignalMonito()这块,一会再讲

    此时由于信号只能在主线程设置,所以又判断了一下,给出了相应的告警

  2、退出

    分为两种,接收到SIGINIT or SIGTERM

    此时会调用_StopSignalMonito()进行信号处理,这块又用到Python的魔法,__call__()

    其中的逻辑是,接到一次信号,判断自身状态,如果测试在跑,则后续不再进行,执行teardown后正常退出;如果再次接到信号,直接调用系统的SystemExit()。也就是说在程序运行过程中,我们要中断测试,需要按两次Ctrl+C

    然后再执行__exit__(),此时又执行了signal.signal,传入的是Stop_SignalMonito的两个变量,在执行__enter__时被初始化,此时应该都是0,也就是singal.SIG_DFL,进程采用默认操作。退出with

    无故障,直接调用__exit__(),和上述接到中断信号调用__exit__()的流程是一样的

原文地址:https://www.cnblogs.com/sumoning/p/12525630.html