forked from robotframework/robotframework
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvisitor.py
More file actions
163 lines (129 loc) · 6.35 KB
/
Copy pathvisitor.py
File metadata and controls
163 lines (129 loc) · 6.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# Copyright 2008-2015 Nokia Networks
# Copyright 2016- Robot Framework Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Interface to ease traversing through a test suite structure.
Visitors make it easy to modify test suite structures or to collect information
from them. They work both with the :mod:`executable model <robot.running.model>`
and the :mod:`result model <robot.result.model>`, but the objects passed to
the visitor methods are slightly different depending on the model they are
used with. The main differences are that on the execution side keywords do
not have child keywords nor messages, and that only the result objects have
status related attributes like :attr:`status` and :attr:`starttime`.
This module contains :class:`SuiteVisitor` that implements the core logic to
visit a test suite structure, and the :mod:`~robot.result` package contains
:class:`~robot.result.visitor.ResultVisitor` that supports visiting the whole
test execution result structure. Both of these visitors should be imported
via the :mod:`robot.api` package when used by external code.
Visitor algorithm
-----------------
All suite, test, keyword and message objects have a :meth:`visit` method that
accepts a visitor instance. These methods will then call the correct visitor
method :meth:`~SuiteVisitor.visit_suite`, :meth:`~SuiteVisitor.visit_test`,
:meth:`~SuiteVisitor.visit_keyword` or :meth:`~SuiteVisitor.visit_message`,
depending on the instance where the :meth:`visit` method exists.
The recommended and definitely easiest way to implement a visitor is extending
the :class:`SuiteVisitor` base class. The default implementation of its
:meth:`visit_x` methods take care of traversing child elements of the object
:obj:`x` recursively. A :meth:`visit_x` method first calls a corresponding
:meth:`start_x` method (e.g. :meth:`visit_suite` calls :meth:`start_suite`),
then calls :meth:`visit` for all child objects of the :obj:`x` object, and
finally calls the corresponding :meth:`end_x` method. The default
implementations of :meth:`start_x` and :meth:`end_x` do nothing.
Visitors extending the :class:`SuiteVisitor` can stop visiting at a certain
level either by overriding suitable :meth:`visit_x` method or by returning
an explicit ``False`` from any :meth:`start_x` method.
Examples
--------
The following example visitor modifies the test suite structure it visits.
It could be used, for example, with Robot Framework's ``--prerunmodifier``
option to modify test data before execution.
.. literalinclude:: /../../doc/api/code_examples/SelectEveryXthTest.py
For more examples it is possible to look at the source code of visitors used
internally by Robot Framework itself. Some good examples are
:class:`~robot.model.tagsetter.TagSetter` and
:mod:`keyword removers <robot.result.keywordremover>`.
"""
class SuiteVisitor(object):
"""Abstract class to ease traversing through the test suite structure.
See the :mod:`module level <robot.model.visitor>` documentation for more
information and an example.
"""
def visit_suite(self, suite):
"""Implements traversing through the suite and its direct children.
Can be overridden to allow modifying the passed in ``suite`` without
calling :func:`start_suite` or :func:`end_suite` nor visiting child
suites, tests or keywords (setup and teardown) at all.
"""
if self.start_suite(suite) is not False:
suite.keywords.visit(self)
suite.suites.visit(self)
suite.tests.visit(self)
self.end_suite(suite)
def start_suite(self, suite):
"""Called when suite starts. Default implementation does nothing.
Can return explicit ``False`` to stop visiting.
"""
pass
def end_suite(self, suite):
"""Called when suite ends. Default implementation does nothing."""
pass
def visit_test(self, test):
"""Implements traversing through the test and its keywords.
Can be overridden to allow modifying the passed in ``test`` without
calling :func:`start_test` or :func:`end_test` nor visiting keywords.
"""
if self.start_test(test) is not False:
test.keywords.visit(self)
self.end_test(test)
def start_test(self, test):
"""Called when test starts. Default implementation does nothing.
Can return explicit ``False`` to stop visiting.
"""
pass
def end_test(self, test):
"""Called when test ends. Default implementation does nothing."""
pass
def visit_keyword(self, kw):
"""Implements traversing through the keyword and its child keywords.
Can be overridden to allow modifying the passed in ``kw`` without
calling :func:`start_keyword` or :func:`end_keyword` nor visiting
child keywords.
"""
if self.start_keyword(kw) is not False:
kw.keywords.visit(self)
kw.messages.visit(self)
self.end_keyword(kw)
def start_keyword(self, keyword):
"""Called when keyword starts. Default implementation does nothing.
Can return explicit ``False`` to stop visiting.
"""
pass
def end_keyword(self, keyword):
"""Called when keyword ends. Default implementation does nothing."""
pass
def visit_message(self, msg):
"""Implements visiting the message.
Can be overridden to allow modifying the passed in ``msg`` without
calling :func:`start_message` or :func:`end_message`.
"""
if self.start_message(msg) is not False:
self.end_message(msg)
def start_message(self, msg):
"""Called when message starts. Default implementation does nothing.
Can return explicit ``False`` to stop visiting.
"""
pass
def end_message(self, msg):
"""Called when message ends. Default implementation does nothing."""
pass