robotframework-pabot 3.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pabot/SharedLibrary.py +62 -0
- pabot/__init__.py +4 -0
- pabot/arguments.py +236 -0
- pabot/clientwrapper.py +10 -0
- pabot/coordinatorwrapper.py +8 -0
- pabot/execution_items.py +320 -0
- pabot/pabot.py +2072 -0
- pabot/pabotlib.py +578 -0
- pabot/py3/__init__.py +0 -0
- pabot/py3/client.py +40 -0
- pabot/py3/coordinator.py +63 -0
- pabot/py3/messages.py +104 -0
- pabot/py3/worker.py +52 -0
- pabot/result_merger.py +272 -0
- pabot/robotremoteserver.py +632 -0
- pabot/workerwrapper.py +8 -0
- robotframework_pabot-3.1.0.dist-info/LICENSE.txt +202 -0
- robotframework_pabot-3.1.0.dist-info/METADATA +24 -0
- robotframework_pabot-3.1.0.dist-info/RECORD +22 -0
- robotframework_pabot-3.1.0.dist-info/WHEEL +5 -0
- robotframework_pabot-3.1.0.dist-info/entry_points.txt +2 -0
- robotframework_pabot-3.1.0.dist-info/top_level.txt +1 -0
pabot/SharedLibrary.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from __future__ import absolute_import
|
|
2
|
+
|
|
3
|
+
from robot import __version__ as ROBOT_VERSION
|
|
4
|
+
from robot.api import logger
|
|
5
|
+
from robot.libraries.BuiltIn import BuiltIn
|
|
6
|
+
from robot.libraries.Remote import Remote
|
|
7
|
+
from robot.running.testlibraries import TestLibrary
|
|
8
|
+
|
|
9
|
+
from .pabotlib import PABOT_QUEUE_INDEX
|
|
10
|
+
from .robotremoteserver import RemoteLibraryFactory
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SharedLibrary(object):
|
|
14
|
+
|
|
15
|
+
ROBOT_LIBRARY_SCOPE = "GLOBAL"
|
|
16
|
+
|
|
17
|
+
def __init__(self, name, args=None):
|
|
18
|
+
"""
|
|
19
|
+
Import a library so that the library instance is shared between executions.
|
|
20
|
+
[https://pabot.org/PabotLib.html?ref=log#import-shared-library|Open online docs.]
|
|
21
|
+
"""
|
|
22
|
+
# FIXME: RELATIVE IMPORTS WITH FILE NAME
|
|
23
|
+
self._remote = None
|
|
24
|
+
if BuiltIn().get_variable_value("${%s}" % PABOT_QUEUE_INDEX) is None:
|
|
25
|
+
logger.debug(
|
|
26
|
+
"Not currently running pabot. Importing library for this process."
|
|
27
|
+
)
|
|
28
|
+
self._lib = RemoteLibraryFactory(
|
|
29
|
+
TestLibrary.from_name(name, args=args, variables=None, create_keywords=True).instance
|
|
30
|
+
if ROBOT_VERSION >= "7.0"
|
|
31
|
+
else TestLibrary(name, args=args).get_instance()
|
|
32
|
+
)
|
|
33
|
+
return
|
|
34
|
+
uri = BuiltIn().get_variable_value("${PABOTLIBURI}")
|
|
35
|
+
logger.debug("PabotLib URI %r" % uri)
|
|
36
|
+
remotelib = Remote(uri) if uri else None
|
|
37
|
+
if remotelib:
|
|
38
|
+
try:
|
|
39
|
+
port = remotelib.run_keyword("import_shared_library", [name], {"args": args})
|
|
40
|
+
except RuntimeError:
|
|
41
|
+
logger.error("No connection - is pabot called with --pabotlib option?")
|
|
42
|
+
raise
|
|
43
|
+
self._remote = Remote("http://127.0.0.1:%s" % port)
|
|
44
|
+
logger.debug(
|
|
45
|
+
"Lib imported with name %s from http://127.0.0.1:%s" % (name, port)
|
|
46
|
+
)
|
|
47
|
+
else:
|
|
48
|
+
logger.error("No connection - is pabot called with --pabotlib option?")
|
|
49
|
+
raise AssertionError("No connection to pabotlib")
|
|
50
|
+
|
|
51
|
+
def get_keyword_names(self):
|
|
52
|
+
if self._remote:
|
|
53
|
+
return self._remote.get_keyword_names()
|
|
54
|
+
return self._lib.get_keyword_names()
|
|
55
|
+
|
|
56
|
+
def run_keyword(self, name, args, kwargs):
|
|
57
|
+
if self._remote:
|
|
58
|
+
return self._remote.run_keyword(name, args, kwargs)
|
|
59
|
+
result = self._lib.run_keyword(name, args, kwargs)
|
|
60
|
+
if result["status"] == "FAIL":
|
|
61
|
+
raise AssertionError(result["error"])
|
|
62
|
+
return result.get("return")
|
pabot/__init__.py
ADDED
pabot/arguments.py
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import multiprocessing
|
|
2
|
+
import re
|
|
3
|
+
from typing import Dict, List, Optional, Tuple
|
|
4
|
+
|
|
5
|
+
from robot import __version__ as ROBOT_VERSION
|
|
6
|
+
from robot.errors import DataError
|
|
7
|
+
from robot.run import USAGE
|
|
8
|
+
from robot.utils import ArgumentParser
|
|
9
|
+
|
|
10
|
+
from .execution_items import (
|
|
11
|
+
DynamicTestItem,
|
|
12
|
+
ExecutionItem,
|
|
13
|
+
GroupEndItem,
|
|
14
|
+
GroupStartItem,
|
|
15
|
+
IncludeItem,
|
|
16
|
+
SuiteItem,
|
|
17
|
+
TestItem,
|
|
18
|
+
WaitItem,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
ARGSMATCHER = re.compile(r"--argumentfile(\d+)")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _processes_count(): # type: () -> int
|
|
25
|
+
try:
|
|
26
|
+
return max(multiprocessing.cpu_count(), 2)
|
|
27
|
+
except NotImplementedError:
|
|
28
|
+
return 2
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _filter_argument_parser_options(**options):
|
|
32
|
+
# Note: auto_pythonpath is deprecated since RobotFramework 5.0, but only
|
|
33
|
+
# communicated to users from 6.1
|
|
34
|
+
if ROBOT_VERSION >= "5.0" and "auto_pythonpath" in options:
|
|
35
|
+
del options["auto_pythonpath"]
|
|
36
|
+
return options
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def parse_args(
|
|
40
|
+
args,
|
|
41
|
+
): # type: (List[str]) -> Tuple[Dict[str, object], List[str], Dict[str, object], Dict[str, object]]
|
|
42
|
+
args, pabot_args = _parse_pabot_args(args)
|
|
43
|
+
options, datasources = ArgumentParser(
|
|
44
|
+
USAGE,
|
|
45
|
+
**_filter_argument_parser_options(
|
|
46
|
+
auto_pythonpath=False,
|
|
47
|
+
auto_argumentfile=True,
|
|
48
|
+
env_options="ROBOT_OPTIONS",
|
|
49
|
+
),
|
|
50
|
+
).parse_args(args)
|
|
51
|
+
options_for_subprocesses, sources_without_argfile = ArgumentParser(
|
|
52
|
+
USAGE,
|
|
53
|
+
**_filter_argument_parser_options(
|
|
54
|
+
auto_pythonpath=False,
|
|
55
|
+
auto_argumentfile=False,
|
|
56
|
+
env_options="ROBOT_OPTIONS",
|
|
57
|
+
),
|
|
58
|
+
).parse_args(args)
|
|
59
|
+
if len(datasources) != len(sources_without_argfile):
|
|
60
|
+
raise DataError(
|
|
61
|
+
"Pabot does not support datasources in argumentfiles.\nPlease move datasources to commandline."
|
|
62
|
+
)
|
|
63
|
+
if len(datasources) > 1 and options["name"] is None:
|
|
64
|
+
options["name"] = "Suites"
|
|
65
|
+
options_for_subprocesses["name"] = "Suites"
|
|
66
|
+
opts = _delete_none_keys(options)
|
|
67
|
+
opts_sub = _delete_none_keys(options_for_subprocesses)
|
|
68
|
+
return opts, datasources, pabot_args, opts_sub
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _parse_shard(arg):
|
|
72
|
+
# type: (str) -> Tuple[int, int]
|
|
73
|
+
parts = arg.split("/")
|
|
74
|
+
return int(parts[0]), int(parts[1])
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _parse_pabot_args(args): # type: (List[str]) -> Tuple[List[str], Dict[str, object]]
|
|
78
|
+
pabot_args = {
|
|
79
|
+
"command": ["pybot" if ROBOT_VERSION < "3.1" else "robot"],
|
|
80
|
+
"verbose": False,
|
|
81
|
+
"help": False,
|
|
82
|
+
"testlevelsplit": False,
|
|
83
|
+
"pabotlib": False,
|
|
84
|
+
"pabotlibhost": "127.0.0.1",
|
|
85
|
+
"pabotlibport": 8270,
|
|
86
|
+
"processes": _processes_count(),
|
|
87
|
+
"processtimeout": None,
|
|
88
|
+
"artifacts": ["png"],
|
|
89
|
+
"artifactsinsubfolders": False,
|
|
90
|
+
"shardindex": 0,
|
|
91
|
+
"shardcount": 1,
|
|
92
|
+
"chunk": False,
|
|
93
|
+
}
|
|
94
|
+
argumentfiles = []
|
|
95
|
+
while args and (
|
|
96
|
+
args[0]
|
|
97
|
+
in [
|
|
98
|
+
"--" + param
|
|
99
|
+
for param in [
|
|
100
|
+
"hive",
|
|
101
|
+
"command",
|
|
102
|
+
"processes",
|
|
103
|
+
"verbose",
|
|
104
|
+
"resourcefile",
|
|
105
|
+
"testlevelsplit",
|
|
106
|
+
"pabotlib",
|
|
107
|
+
"pabotlibhost",
|
|
108
|
+
"pabotlibport",
|
|
109
|
+
"processtimeout",
|
|
110
|
+
"ordering",
|
|
111
|
+
"suitesfrom",
|
|
112
|
+
"artifacts",
|
|
113
|
+
"artifactsinsubfolders",
|
|
114
|
+
"help",
|
|
115
|
+
"shard",
|
|
116
|
+
"chunk",
|
|
117
|
+
]
|
|
118
|
+
]
|
|
119
|
+
or ARGSMATCHER.match(args[0])
|
|
120
|
+
):
|
|
121
|
+
if args[0] == "--hive":
|
|
122
|
+
pabot_args["hive"] = args[1]
|
|
123
|
+
args = args[2:]
|
|
124
|
+
continue
|
|
125
|
+
if args[0] == "--command":
|
|
126
|
+
end_index = args.index("--end-command")
|
|
127
|
+
pabot_args["command"] = args[1:end_index]
|
|
128
|
+
args = args[end_index + 1 :]
|
|
129
|
+
continue
|
|
130
|
+
if args[0] == "--processes":
|
|
131
|
+
pabot_args["processes"] = int(args[1]) if args[1] != 'all' else None
|
|
132
|
+
args = args[2:]
|
|
133
|
+
continue
|
|
134
|
+
if args[0] == "--verbose":
|
|
135
|
+
pabot_args["verbose"] = True
|
|
136
|
+
args = args[1:]
|
|
137
|
+
continue
|
|
138
|
+
if args[0] == "--chunk":
|
|
139
|
+
pabot_args["chunk"] = True
|
|
140
|
+
args = args[1:]
|
|
141
|
+
continue
|
|
142
|
+
if args[0] == "--resourcefile":
|
|
143
|
+
pabot_args["resourcefile"] = args[1]
|
|
144
|
+
args = args[2:]
|
|
145
|
+
continue
|
|
146
|
+
if args[0] == "--pabotlib":
|
|
147
|
+
pabot_args["pabotlib"] = True
|
|
148
|
+
args = args[1:]
|
|
149
|
+
continue
|
|
150
|
+
if args[0] == "--ordering":
|
|
151
|
+
pabot_args["ordering"] = _parse_ordering(args[1])
|
|
152
|
+
args = args[2:]
|
|
153
|
+
continue
|
|
154
|
+
if args[0] == "--testlevelsplit":
|
|
155
|
+
pabot_args["testlevelsplit"] = True
|
|
156
|
+
args = args[1:]
|
|
157
|
+
continue
|
|
158
|
+
if args[0] == "--pabotlibhost":
|
|
159
|
+
pabot_args["pabotlibhost"] = args[1]
|
|
160
|
+
args = args[2:]
|
|
161
|
+
continue
|
|
162
|
+
if args[0] == "--pabotlibport":
|
|
163
|
+
pabot_args["pabotlibport"] = int(args[1])
|
|
164
|
+
args = args[2:]
|
|
165
|
+
continue
|
|
166
|
+
if args[0] == "--processtimeout":
|
|
167
|
+
pabot_args["processtimeout"] = int(args[1])
|
|
168
|
+
args = args[2:]
|
|
169
|
+
continue
|
|
170
|
+
if args[0] == "--suitesfrom":
|
|
171
|
+
pabot_args["suitesfrom"] = args[1]
|
|
172
|
+
args = args[2:]
|
|
173
|
+
continue
|
|
174
|
+
if args[0] == "--artifacts":
|
|
175
|
+
pabot_args["artifacts"] = args[1].split(",")
|
|
176
|
+
args = args[2:]
|
|
177
|
+
continue
|
|
178
|
+
if args[0] == "--artifactsinsubfolders":
|
|
179
|
+
pabot_args["artifactsinsubfolders"] = True
|
|
180
|
+
args = args[1:]
|
|
181
|
+
continue
|
|
182
|
+
if args[0] == "--shard":
|
|
183
|
+
pabot_args["shardindex"], pabot_args["shardcount"] = _parse_shard(args[1])
|
|
184
|
+
args = args[2:]
|
|
185
|
+
continue
|
|
186
|
+
match = ARGSMATCHER.match(args[0])
|
|
187
|
+
if match:
|
|
188
|
+
argumentfiles += [(match.group(1), args[1])]
|
|
189
|
+
args = args[2:]
|
|
190
|
+
continue
|
|
191
|
+
if args and args[0] == "--help":
|
|
192
|
+
pabot_args["help"] = True
|
|
193
|
+
args = args[1:]
|
|
194
|
+
pabot_args["argumentfiles"] = argumentfiles
|
|
195
|
+
return args, pabot_args
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _parse_ordering(filename): # type: (str) -> List[ExecutionItem]
|
|
199
|
+
try:
|
|
200
|
+
with open(filename, "r") as orderingfile:
|
|
201
|
+
return [
|
|
202
|
+
parse_execution_item_line(line.strip())
|
|
203
|
+
for line in orderingfile.readlines()
|
|
204
|
+
]
|
|
205
|
+
except:
|
|
206
|
+
raise DataError("Error parsing ordering file '%s'" % filename)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _delete_none_keys(d): # type: (Dict[str, Optional[object]]) -> Dict[str, object]
|
|
210
|
+
keys = set()
|
|
211
|
+
for k in d:
|
|
212
|
+
if d[k] is None:
|
|
213
|
+
keys.add(k)
|
|
214
|
+
for k in keys:
|
|
215
|
+
del d[k]
|
|
216
|
+
return d
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def parse_execution_item_line(text): # type: (str) -> ExecutionItem
|
|
220
|
+
if text.startswith("--suite "):
|
|
221
|
+
return SuiteItem(text[8:])
|
|
222
|
+
if text.startswith("--test "):
|
|
223
|
+
return TestItem(text[7:])
|
|
224
|
+
if text.startswith("--include "):
|
|
225
|
+
return IncludeItem(text[10:])
|
|
226
|
+
if text.startswith("DYNAMICTEST"):
|
|
227
|
+
suite, test = text[12:].split(" :: ")
|
|
228
|
+
return DynamicTestItem(test, suite)
|
|
229
|
+
if text == "#WAIT":
|
|
230
|
+
return WaitItem()
|
|
231
|
+
if text == "{":
|
|
232
|
+
return GroupStartItem()
|
|
233
|
+
if text == "}":
|
|
234
|
+
return GroupEndItem()
|
|
235
|
+
# Assume old suite name
|
|
236
|
+
return SuiteItem(text)
|
pabot/clientwrapper.py
ADDED
pabot/execution_items.py
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
from functools import total_ordering
|
|
2
|
+
from typing import Dict, List, Optional, Tuple, Union
|
|
3
|
+
|
|
4
|
+
from robot import __version__ as ROBOT_VERSION
|
|
5
|
+
from robot.errors import DataError
|
|
6
|
+
from robot.utils import PY2, is_unicode
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@total_ordering
|
|
10
|
+
class ExecutionItem(object):
|
|
11
|
+
|
|
12
|
+
isWait = False
|
|
13
|
+
type = None # type: str
|
|
14
|
+
name = None # type: str
|
|
15
|
+
|
|
16
|
+
def top_name(self):
|
|
17
|
+
# type: () -> str
|
|
18
|
+
return self.name.split(".")[0]
|
|
19
|
+
|
|
20
|
+
def contains(self, other):
|
|
21
|
+
# type: (ExecutionItem) -> bool
|
|
22
|
+
return False
|
|
23
|
+
|
|
24
|
+
def difference(self, from_items):
|
|
25
|
+
# type: (List[ExecutionItem]) -> List[ExecutionItem]
|
|
26
|
+
return []
|
|
27
|
+
|
|
28
|
+
def line(self):
|
|
29
|
+
# type: () -> str
|
|
30
|
+
return ""
|
|
31
|
+
|
|
32
|
+
def modify_options_for_executor(self, options):
|
|
33
|
+
options[self.type] = self.name
|
|
34
|
+
|
|
35
|
+
def __eq__(self, other):
|
|
36
|
+
if isinstance(other, ExecutionItem):
|
|
37
|
+
return (self.name, self.type) == (other.name, other.type)
|
|
38
|
+
return NotImplemented
|
|
39
|
+
|
|
40
|
+
def __ne__(self, other):
|
|
41
|
+
return not (self == other)
|
|
42
|
+
|
|
43
|
+
def __lt__(self, other):
|
|
44
|
+
return (self.name, self.type) < (other.name, other.type)
|
|
45
|
+
|
|
46
|
+
def __hash__(self):
|
|
47
|
+
return hash(self.name) | hash(self.type)
|
|
48
|
+
|
|
49
|
+
def __repr__(self):
|
|
50
|
+
return "<" + self.type + ":" + self.name + ">"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class HivedItem(ExecutionItem):
|
|
54
|
+
|
|
55
|
+
type = "hived"
|
|
56
|
+
|
|
57
|
+
def __init__(self, item, hive):
|
|
58
|
+
self._item = item
|
|
59
|
+
self._hive = hive
|
|
60
|
+
|
|
61
|
+
def modify_options_for_executor(self, options):
|
|
62
|
+
self._item.modify_options_for_executor(options)
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def name(self):
|
|
66
|
+
return self._item.name
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class GroupItem(ExecutionItem):
|
|
70
|
+
|
|
71
|
+
type = "group"
|
|
72
|
+
|
|
73
|
+
def __init__(self):
|
|
74
|
+
self.name = "Group_"
|
|
75
|
+
self._items = []
|
|
76
|
+
self._element_type = None
|
|
77
|
+
|
|
78
|
+
def add(self, item):
|
|
79
|
+
if item.isWait:
|
|
80
|
+
raise DataError("[EXCEPTION] Ordering : Group can not contain #WAIT")
|
|
81
|
+
if self._element_type and self._element_type != item.type:
|
|
82
|
+
raise DataError(
|
|
83
|
+
"[EXCEPTION] Ordering : Group can contain only test or suite elements. Not bouth"
|
|
84
|
+
)
|
|
85
|
+
if len(self._items) > 0:
|
|
86
|
+
self.name += "_"
|
|
87
|
+
self.name += item.name
|
|
88
|
+
self._element_type = item.type
|
|
89
|
+
self._items.append(item)
|
|
90
|
+
|
|
91
|
+
def modify_options_for_executor(self, options):
|
|
92
|
+
for item in self._items:
|
|
93
|
+
if item.type not in options:
|
|
94
|
+
options[item.type] = []
|
|
95
|
+
opts = {}
|
|
96
|
+
item.modify_options_for_executor(opts)
|
|
97
|
+
options[item.type].append(opts[item.type])
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class RunnableItem(ExecutionItem):
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
depends = None # type: str
|
|
104
|
+
depends_keyword = "#DEPENDS"
|
|
105
|
+
|
|
106
|
+
def set_name_and_depends(self, name):
|
|
107
|
+
line_name = name.encode("utf-8") if PY2 and is_unicode(name) else name
|
|
108
|
+
depends_begin_index = line_name.find(self.depends_keyword)
|
|
109
|
+
self.name = (
|
|
110
|
+
line_name
|
|
111
|
+
if depends_begin_index == -1
|
|
112
|
+
else line_name[0:depends_begin_index].strip()
|
|
113
|
+
)
|
|
114
|
+
self.depends = (
|
|
115
|
+
line_name[depends_begin_index + len(self.depends_keyword) :].strip()
|
|
116
|
+
if depends_begin_index != -1
|
|
117
|
+
else None
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
def line(self):
|
|
121
|
+
# type: () -> str
|
|
122
|
+
line_without_depends = "--" + self.type + " " + self.name
|
|
123
|
+
return (
|
|
124
|
+
line_without_depends + " " + self.depends_keyword + " " + self.depends
|
|
125
|
+
if self.depends
|
|
126
|
+
else line_without_depends
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class SuiteItem(RunnableItem):
|
|
131
|
+
|
|
132
|
+
type = "suite"
|
|
133
|
+
|
|
134
|
+
def __init__(self, name, tests=None, suites=None, dynamictests=None):
|
|
135
|
+
# type: (str, Optional[List[str]], Optional[List[str]], Optional[List[str]]) -> None
|
|
136
|
+
assert (PY2 and isinstance(name, basestring)) or isinstance(name, str)
|
|
137
|
+
self.set_name_and_depends(name)
|
|
138
|
+
testslist = [
|
|
139
|
+
TestItem(t) for t in tests or []
|
|
140
|
+
] # type: List[Union[TestItem, DynamicTestItem]]
|
|
141
|
+
dynamictestslist = [
|
|
142
|
+
DynamicTestItem(t, self.name) for t in dynamictests or []
|
|
143
|
+
] # type: List[Union[TestItem, DynamicTestItem]]
|
|
144
|
+
self.tests = testslist + dynamictestslist
|
|
145
|
+
self.suites = [SuiteItem(s) for s in suites or []]
|
|
146
|
+
|
|
147
|
+
def difference(self, from_items):
|
|
148
|
+
# type: (List[ExecutionItem]) -> List[ExecutionItem]
|
|
149
|
+
if self.tests:
|
|
150
|
+
return [t for t in self.tests if t not in from_items]
|
|
151
|
+
if self.suites:
|
|
152
|
+
return [s for s in self.suites if s not in from_items]
|
|
153
|
+
return []
|
|
154
|
+
|
|
155
|
+
def contains(self, other):
|
|
156
|
+
# type: (ExecutionItem) -> bool
|
|
157
|
+
if self == other:
|
|
158
|
+
return True
|
|
159
|
+
return other.name.startswith(self.name + ".")
|
|
160
|
+
|
|
161
|
+
def __eq__(self, other):
|
|
162
|
+
if not isinstance(other, SuiteItem):
|
|
163
|
+
return False
|
|
164
|
+
if self.name == other.name:
|
|
165
|
+
return True
|
|
166
|
+
if other.name.endswith('.'+self.name):
|
|
167
|
+
return True
|
|
168
|
+
if self.name.endswith('.'+other.name):
|
|
169
|
+
return True
|
|
170
|
+
return False
|
|
171
|
+
|
|
172
|
+
def __hash__(self):
|
|
173
|
+
return RunnableItem.__hash__(self)
|
|
174
|
+
|
|
175
|
+
def tags(self):
|
|
176
|
+
# TODO Make this happen
|
|
177
|
+
return []
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class TestItem(RunnableItem):
|
|
181
|
+
|
|
182
|
+
type = "test"
|
|
183
|
+
|
|
184
|
+
def __init__(self, name):
|
|
185
|
+
# type: (str) -> None
|
|
186
|
+
self.set_name_and_depends(name)
|
|
187
|
+
|
|
188
|
+
if ROBOT_VERSION >= "3.1":
|
|
189
|
+
|
|
190
|
+
def modify_options_for_executor(self, options):
|
|
191
|
+
if "rerunfailed" in options:
|
|
192
|
+
del options["rerunfailed"]
|
|
193
|
+
name = self.name
|
|
194
|
+
for char in ["[", "?", "*"]:
|
|
195
|
+
name = name.replace(char, "[" + char + "]")
|
|
196
|
+
options[self.type] = name
|
|
197
|
+
|
|
198
|
+
else:
|
|
199
|
+
|
|
200
|
+
def modify_options_for_executor(self, options):
|
|
201
|
+
if "rerunfailed" in options:
|
|
202
|
+
del options["rerunfailed"]
|
|
203
|
+
|
|
204
|
+
def difference(self, from_items):
|
|
205
|
+
# type: (List[ExecutionItem]) -> List[ExecutionItem]
|
|
206
|
+
return []
|
|
207
|
+
|
|
208
|
+
def contains(self, other):
|
|
209
|
+
# type: (ExecutionItem) -> bool
|
|
210
|
+
return self == other
|
|
211
|
+
|
|
212
|
+
def tags(self):
|
|
213
|
+
# TODO Make this happen
|
|
214
|
+
return []
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class DynamicSuiteItem(SuiteItem):
|
|
218
|
+
type = "dynamicsuite"
|
|
219
|
+
|
|
220
|
+
def __init__(self, name, variables):
|
|
221
|
+
SuiteItem.__init__(self, name)
|
|
222
|
+
self._variables = variables
|
|
223
|
+
|
|
224
|
+
def modify_options_for_executor(self, options):
|
|
225
|
+
variables = options.get("variable", [])[:]
|
|
226
|
+
variables.extend(self._variables)
|
|
227
|
+
options["variable"] = variables
|
|
228
|
+
options["suite"] = self.name
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class DynamicTestItem(ExecutionItem):
|
|
232
|
+
|
|
233
|
+
type = "dynamictest"
|
|
234
|
+
|
|
235
|
+
def __init__(self, name, suite):
|
|
236
|
+
# type: (str, str) -> None
|
|
237
|
+
self.name = name.encode("utf-8") if PY2 and is_unicode(name) else name
|
|
238
|
+
self.suite = suite # type:str
|
|
239
|
+
|
|
240
|
+
def line(self):
|
|
241
|
+
return "DYNAMICTEST %s :: %s" % (self.suite, self.name)
|
|
242
|
+
|
|
243
|
+
def modify_options_for_executor(self, options):
|
|
244
|
+
options["suite"] = self.suite
|
|
245
|
+
variables = options.get("variable", [])[:]
|
|
246
|
+
variables.append("DYNAMICTEST:" + self.name)
|
|
247
|
+
options["variable"] = variables
|
|
248
|
+
|
|
249
|
+
def difference(self, from_items):
|
|
250
|
+
return []
|
|
251
|
+
|
|
252
|
+
def contains(self, other):
|
|
253
|
+
return self == other
|
|
254
|
+
|
|
255
|
+
def tags(self):
|
|
256
|
+
# TODO Make this happen
|
|
257
|
+
return []
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
class WaitItem(ExecutionItem):
|
|
261
|
+
|
|
262
|
+
type = "wait"
|
|
263
|
+
isWait = True
|
|
264
|
+
|
|
265
|
+
def __init__(self):
|
|
266
|
+
self.name = "#WAIT"
|
|
267
|
+
|
|
268
|
+
def line(self):
|
|
269
|
+
return self.name
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class GroupStartItem(ExecutionItem):
|
|
273
|
+
|
|
274
|
+
type = "group"
|
|
275
|
+
|
|
276
|
+
def __init__(self):
|
|
277
|
+
self.name = "#START"
|
|
278
|
+
|
|
279
|
+
def line(self):
|
|
280
|
+
return "{"
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class GroupEndItem(ExecutionItem):
|
|
284
|
+
|
|
285
|
+
type = "group"
|
|
286
|
+
|
|
287
|
+
def __init__(self):
|
|
288
|
+
self.name = "#END"
|
|
289
|
+
|
|
290
|
+
def line(self):
|
|
291
|
+
return "}"
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
class IncludeItem(ExecutionItem):
|
|
295
|
+
|
|
296
|
+
type = "include"
|
|
297
|
+
|
|
298
|
+
def __init__(self, tag):
|
|
299
|
+
self.name = tag
|
|
300
|
+
|
|
301
|
+
def line(self):
|
|
302
|
+
return "--include " + self.name
|
|
303
|
+
|
|
304
|
+
def contains(self, other):
|
|
305
|
+
return self.name in other.tags()
|
|
306
|
+
|
|
307
|
+
def tags(self):
|
|
308
|
+
return [self.name]
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
class SuiteItems(ExecutionItem):
|
|
312
|
+
|
|
313
|
+
type = "suite"
|
|
314
|
+
|
|
315
|
+
def __init__(self, suites):
|
|
316
|
+
self.suites = suites
|
|
317
|
+
self.name = " ".join([suite.name for suite in suites])
|
|
318
|
+
|
|
319
|
+
def modify_options_for_executor(self, options):
|
|
320
|
+
options["suite"] = [suite.name for suite in self.suites]
|