前两篇文章讲解了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:\n%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:\n%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\running\model.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:\Python27\python.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.\n')
14 raise SystemExit()
15 sys.__stderr__.write('Second signal will force exit.\n')
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__()的流程是一样的
软件测试技术交流群:1125760266
网友评论