robotframework-pabot 5.0.0__py3-none-any.whl → 5.2.0b1__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/ProcessManager.py +376 -0
- pabot/__init__.py +1 -1
- pabot/arguments.py +80 -24
- pabot/pabot.py +392 -166
- pabot/pabotlib.py +1 -1
- pabot/result_merger.py +2 -2
- pabot/robotremoteserver.py +27 -7
- pabot/skip_listener.py +7 -0
- pabot/timeout_listener.py +5 -0
- pabot/writer.py +110 -0
- {robotframework_pabot-5.0.0.dist-info → robotframework_pabot-5.2.0b1.dist-info}/METADATA +93 -42
- robotframework_pabot-5.2.0b1.dist-info/RECORD +25 -0
- robotframework_pabot-5.0.0.dist-info/RECORD +0 -22
- robotframework_pabot-5.0.0.dist-info/licenses/LICENSE.txt +0 -202
- {robotframework_pabot-5.0.0.dist-info → robotframework_pabot-5.2.0b1.dist-info}/WHEEL +0 -0
- {robotframework_pabot-5.0.0.dist-info → robotframework_pabot-5.2.0b1.dist-info}/entry_points.txt +0 -0
- {robotframework_pabot-5.0.0.dist-info → robotframework_pabot-5.2.0b1.dist-info}/top_level.txt +0 -0
pabot/pabotlib.py
CHANGED
|
@@ -60,7 +60,7 @@ class _PabotLib(object):
|
|
|
60
60
|
self, resourcefile
|
|
61
61
|
): # type: (Optional[str]) -> Dict[str, Dict[str, Any]]
|
|
62
62
|
vals = {} # type: Dict[str, Dict[str, Any]]
|
|
63
|
-
if resourcefile
|
|
63
|
+
if not resourcefile:
|
|
64
64
|
return vals
|
|
65
65
|
conf = configparser.ConfigParser()
|
|
66
66
|
conf.read(resourcefile)
|
pabot/result_merger.py
CHANGED
|
@@ -201,13 +201,13 @@ def prefix(source, timestamp_id):
|
|
|
201
201
|
if not id:
|
|
202
202
|
return ""
|
|
203
203
|
if os.path.split(path_without_id)[1] == 'pabot_results':
|
|
204
|
-
return "-".join([timestamp_id, id])
|
|
204
|
+
return "-".join([str(p) for p in [timestamp_id, id] if p is not None])
|
|
205
205
|
else:
|
|
206
206
|
# --argumentfileN in use: (there should be one subdir level more)
|
|
207
207
|
_, index = os.path.split(path_without_id)
|
|
208
208
|
if not index:
|
|
209
209
|
return ""
|
|
210
|
-
return "-".join([timestamp_id, index, id])
|
|
210
|
+
return "-".join([str(p) for p in [timestamp_id, index, id] if p is not None])
|
|
211
211
|
except:
|
|
212
212
|
return ""
|
|
213
213
|
|
pabot/robotremoteserver.py
CHANGED
|
@@ -452,20 +452,28 @@ class StandardStreamInterceptor(object):
|
|
|
452
452
|
self.output = ""
|
|
453
453
|
self.origout = sys.stdout
|
|
454
454
|
self.origerr = sys.stderr
|
|
455
|
-
|
|
456
|
-
|
|
455
|
+
self.stdout_capture = StringIO()
|
|
456
|
+
self.stderr_capture = StringIO()
|
|
457
|
+
sys.stdout = self.stdout_capture
|
|
458
|
+
sys.stderr = self.stderr_capture
|
|
457
459
|
|
|
458
460
|
def __enter__(self):
|
|
459
461
|
return self
|
|
460
462
|
|
|
461
463
|
def __exit__(self, *exc_info):
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
464
|
+
# Safely get output from captured streams, handling case where streams were restored
|
|
465
|
+
stdout = self._safe_getvalue(sys.stdout, self.stdout_capture)
|
|
466
|
+
stderr = self._safe_getvalue(sys.stderr, self.stderr_capture)
|
|
467
|
+
|
|
468
|
+
# Close only our StringIO objects
|
|
469
|
+
for stream in [self.stdout_capture, self.stderr_capture]:
|
|
470
|
+
if hasattr(stream, 'close'):
|
|
471
|
+
stream.close()
|
|
472
|
+
|
|
473
|
+
# Restore original streams
|
|
465
474
|
sys.stdout = self.origout
|
|
466
475
|
sys.stderr = self.origerr
|
|
467
|
-
|
|
468
|
-
stream.close()
|
|
476
|
+
|
|
469
477
|
if stdout and stderr:
|
|
470
478
|
if not stderr.startswith(
|
|
471
479
|
("*TRACE*", "*DEBUG*", "*INFO*", "*HTML*", "*WARN*", "*ERROR*")
|
|
@@ -474,6 +482,18 @@ class StandardStreamInterceptor(object):
|
|
|
474
482
|
if not stdout.endswith("\n"):
|
|
475
483
|
stdout += "\n"
|
|
476
484
|
self.output = stdout + stderr
|
|
485
|
+
|
|
486
|
+
def _safe_getvalue(self, current_stream, original_capture):
|
|
487
|
+
"""Safely get value from stream, handling case where stream was restored."""
|
|
488
|
+
# If current stream is still our StringIO, get value from it
|
|
489
|
+
if hasattr(current_stream, 'getvalue') and current_stream is original_capture:
|
|
490
|
+
return current_stream.getvalue()
|
|
491
|
+
# If current stream was restored but our capture still has data
|
|
492
|
+
print("*WARN* Stream capture was interrupted", file=sys.stderr)
|
|
493
|
+
if hasattr(original_capture, 'getvalue'):
|
|
494
|
+
return original_capture.getvalue()
|
|
495
|
+
else:
|
|
496
|
+
return ""
|
|
477
497
|
|
|
478
498
|
|
|
479
499
|
class KeywordResult(object):
|
pabot/skip_listener.py
ADDED
pabot/writer.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
import queue
|
|
3
|
+
import sys
|
|
4
|
+
import os
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
class Color:
|
|
8
|
+
RED = "\033[91m"
|
|
9
|
+
GREEN = "\033[92m"
|
|
10
|
+
YELLOW = "\033[93m"
|
|
11
|
+
ENDC = "\033[0m"
|
|
12
|
+
SUPPORTED_OSES = {"posix"} # Only Unix terminals support ANSI colors
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MessageWriter:
|
|
16
|
+
def __init__(self, log_file=None):
|
|
17
|
+
self.queue = queue.Queue()
|
|
18
|
+
self.log_file = log_file
|
|
19
|
+
if log_file:
|
|
20
|
+
os.makedirs(os.path.dirname(log_file), exist_ok=True)
|
|
21
|
+
self._stop_event = threading.Event()
|
|
22
|
+
self.thread = threading.Thread(target=self._writer)
|
|
23
|
+
self.thread.daemon = True
|
|
24
|
+
self.thread.start()
|
|
25
|
+
|
|
26
|
+
def _is_output_coloring_supported(self):
|
|
27
|
+
return sys.stdout.isatty() and os.name in Color.SUPPORTED_OSES
|
|
28
|
+
|
|
29
|
+
def _wrap_with(self, color, message):
|
|
30
|
+
if self._is_output_coloring_supported() and color:
|
|
31
|
+
return f"{color}{message}{Color.ENDC}"
|
|
32
|
+
return message
|
|
33
|
+
|
|
34
|
+
def _writer(self):
|
|
35
|
+
while not self._stop_event.is_set():
|
|
36
|
+
try:
|
|
37
|
+
message, color = self.queue.get(timeout=0.1)
|
|
38
|
+
except queue.Empty:
|
|
39
|
+
continue
|
|
40
|
+
if message is None:
|
|
41
|
+
self.queue.task_done()
|
|
42
|
+
break
|
|
43
|
+
print(self._wrap_with(color, message))
|
|
44
|
+
sys.stdout.flush()
|
|
45
|
+
if self.log_file:
|
|
46
|
+
with open(self.log_file, "a", encoding="utf-8") as f:
|
|
47
|
+
f.write(message + "\n")
|
|
48
|
+
self.queue.task_done()
|
|
49
|
+
|
|
50
|
+
def write(self, message, color=None):
|
|
51
|
+
self.queue.put((f"{message}", color))
|
|
52
|
+
|
|
53
|
+
def flush(self, timeout=5):
|
|
54
|
+
"""
|
|
55
|
+
Wait until all queued messages have been written.
|
|
56
|
+
|
|
57
|
+
:param timeout: Optional timeout in seconds. If None, wait indefinitely.
|
|
58
|
+
:return: True if queue drained before timeout (or no timeout), False if timed out.
|
|
59
|
+
"""
|
|
60
|
+
start = time.time()
|
|
61
|
+
try:
|
|
62
|
+
# Loop until Queue reports no unfinished tasks
|
|
63
|
+
while True:
|
|
64
|
+
# If writer thread died, break to avoid infinite loop
|
|
65
|
+
if not self.thread.is_alive():
|
|
66
|
+
# Give one last moment for potential in-flight task_done()
|
|
67
|
+
time.sleep(0.01)
|
|
68
|
+
# If still unfinished, we can't do more
|
|
69
|
+
return getattr(self.queue, "unfinished_tasks", 0) == 0
|
|
70
|
+
|
|
71
|
+
unfinished = getattr(self.queue, "unfinished_tasks", None)
|
|
72
|
+
if unfinished is None:
|
|
73
|
+
# Fallback: call join once and return
|
|
74
|
+
try:
|
|
75
|
+
self.queue.join()
|
|
76
|
+
return True
|
|
77
|
+
except Exception:
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
if unfinished == 0:
|
|
81
|
+
return True
|
|
82
|
+
|
|
83
|
+
if timeout is not None and (time.time() - start) > timeout:
|
|
84
|
+
return False
|
|
85
|
+
|
|
86
|
+
time.sleep(0.05)
|
|
87
|
+
except KeyboardInterrupt:
|
|
88
|
+
# Allow tests/cli to interrupt flushing
|
|
89
|
+
return False
|
|
90
|
+
|
|
91
|
+
def stop(self):
|
|
92
|
+
"""
|
|
93
|
+
Gracefully stop the writer thread and flush remaining messages.
|
|
94
|
+
"""
|
|
95
|
+
self.flush()
|
|
96
|
+
self._stop_event.set()
|
|
97
|
+
self.queue.put((None, None)) # sentinel to break thread loop
|
|
98
|
+
self.thread.join(timeout=1.0)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
_writer_instance = None
|
|
102
|
+
|
|
103
|
+
def get_writer(log_dir=None):
|
|
104
|
+
global _writer_instance
|
|
105
|
+
if _writer_instance is None:
|
|
106
|
+
if log_dir:
|
|
107
|
+
os.makedirs(log_dir, exist_ok=True)
|
|
108
|
+
log_file = os.path.join(log_dir or ".", "pabot_manager.log")
|
|
109
|
+
_writer_instance = MessageWriter(log_file=log_file)
|
|
110
|
+
return _writer_instance
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: robotframework-pabot
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.2.0b1
|
|
4
4
|
Summary: Parallel test runner for Robot Framework
|
|
5
5
|
Home-page: https://pabot.org
|
|
6
6
|
Download-URL: https://pypi.python.org/pypi/robotframework-pabot
|
|
@@ -12,17 +12,14 @@ Classifier: Intended Audience :: Developers
|
|
|
12
12
|
Classifier: Natural Language :: English
|
|
13
13
|
Classifier: Programming Language :: Python :: 3
|
|
14
14
|
Classifier: Topic :: Software Development :: Testing
|
|
15
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
16
15
|
Classifier: Development Status :: 5 - Production/Stable
|
|
17
16
|
Classifier: Framework :: Robot Framework
|
|
18
17
|
Requires-Python: >=3.6
|
|
19
18
|
Description-Content-Type: text/markdown
|
|
20
|
-
License-File: LICENSE.txt
|
|
21
19
|
Requires-Dist: robotframework>=3.2
|
|
22
20
|
Requires-Dist: robotframework-stacktrace>=0.4.1
|
|
23
21
|
Requires-Dist: natsort>=8.2.0
|
|
24
22
|
Dynamic: download-url
|
|
25
|
-
Dynamic: license-file
|
|
26
23
|
|
|
27
24
|
# Pabot
|
|
28
25
|
|
|
@@ -47,7 +44,7 @@ A parallel executor for [Robot Framework](http://www.robotframework.org) tests.
|
|
|
47
44
|
- [Contributing](#contributing-to-the-project)
|
|
48
45
|
- [Command-line options](#command-line-options)
|
|
49
46
|
- [PabotLib](#pabotlib)
|
|
50
|
-
- [Controlling execution order](#controlling-execution-order-and-level-of-parallelism)
|
|
47
|
+
- [Controlling execution order, mode and level of parallelism](#controlling-execution-order-mode-and-level-of-parallelism)
|
|
51
48
|
- [Programmatic use](#programmatic-use)
|
|
52
49
|
- [Global variables](#global-variables)
|
|
53
50
|
- [Output Files Generated by Pabot](#output-files-generated-by-pabot)
|
|
@@ -98,65 +95,89 @@ There are several ways you can help in improving this tool:
|
|
|
98
95
|
- Report an issue or an improvement idea to the [issue tracker](https://github.com/mkorpela/pabot/issues)
|
|
99
96
|
- Contribute by programming and making a pull request (easiest way is to work on an issue from the issue tracker)
|
|
100
97
|
|
|
98
|
+
Before contributing, please read our detailed contributing guidelines:
|
|
99
|
+
|
|
100
|
+
- [Contributing Guide](CONTRIBUTING.md)
|
|
101
|
+
- [Code of Conduct](CODE_OF_CONDUCT.md)
|
|
102
|
+
- [Security Policy](SECURITY.md)
|
|
103
|
+
|
|
101
104
|
## Command-line options
|
|
105
|
+
<!-- NOTE:
|
|
106
|
+
The sections inside these docstring markers are also used in Pabot's --help output.
|
|
107
|
+
Currently, the following transformations are applied:
|
|
108
|
+
- Remove Markdown links but keep the text
|
|
109
|
+
- Remove ** and backticks `
|
|
110
|
+
|
|
111
|
+
If you modify this part, make sure the Markdown section still looks clean and readable in the --help output. -->
|
|
112
|
+
|
|
102
113
|
<!-- START DOCSTRING -->
|
|
114
|
+
```
|
|
103
115
|
pabot [--verbose|--testlevelsplit|--command .. --end-command|
|
|
104
116
|
--processes num|--no-pabotlib|--pabotlibhost host|--pabotlibport port|
|
|
105
117
|
--processtimeout num|
|
|
106
118
|
--shard i/n|
|
|
107
119
|
--artifacts extensions|--artifactsinsubfolders|
|
|
108
|
-
--resourcefile file|--argumentfile[num] file|--suitesfrom file
|
|
120
|
+
--resourcefile file|--argumentfile[num] file|--suitesfrom file
|
|
121
|
+
--ordering <FILENAME> [static|dynamic] [skip|run_all]|
|
|
109
122
|
--chunk|
|
|
110
123
|
--pabotprerunmodifier modifier|
|
|
111
124
|
--no-rebot|
|
|
112
125
|
--help|--version]
|
|
113
126
|
[robot options] [path ...]
|
|
127
|
+
```
|
|
114
128
|
|
|
115
129
|
PabotLib remote server is started by default to enable locking and resource distribution between parallel test executions.
|
|
116
130
|
|
|
117
|
-
Supports all [Robot Framework command line options](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#
|
|
131
|
+
Supports all [Robot Framework command line options](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#command-line-options) and also following pabot options:
|
|
118
132
|
|
|
119
|
-
|
|
133
|
+
**--verbose**
|
|
120
134
|
More output from the parallel execution.
|
|
121
135
|
|
|
122
|
-
|
|
136
|
+
**--testlevelsplit**
|
|
123
137
|
Split execution on test level instead of default suite level. If .pabotsuitenames contains both tests and suites then
|
|
124
138
|
this will only affect new suites and split only them. Leaving this flag out when both suites and tests in
|
|
125
139
|
.pabotsuitenames file will also only affect new suites and add them as suite files.
|
|
126
140
|
|
|
127
|
-
|
|
141
|
+
**--command [ACTUAL COMMANDS TO START ROBOT EXECUTOR] --end-command**
|
|
128
142
|
RF script for situations where robot is not used directly.
|
|
129
143
|
|
|
130
|
-
|
|
144
|
+
**--processes [NUMBER OF PROCESSES]**
|
|
131
145
|
How many parallel executors to use (default max of 2 and cpu count). Special option "all" will use as many processes as
|
|
132
146
|
there are executable suites or tests.
|
|
133
147
|
|
|
134
|
-
|
|
148
|
+
**--no-pabotlib**
|
|
135
149
|
Disable the PabotLib remote server if you don't need locking or resource distribution features.
|
|
136
150
|
|
|
137
|
-
|
|
151
|
+
**--pabotlibhost [HOSTNAME]**
|
|
138
152
|
Connect to an already running instance of the PabotLib remote server at the given host (disables the local PabotLib
|
|
139
153
|
server start). For example, to connect to a remote PabotLib server running on another machine:
|
|
140
154
|
|
|
141
155
|
pabot --pabotlibhost 192.168.1.123 --pabotlibport 8271 tests/
|
|
142
156
|
|
|
143
|
-
The remote server can be
|
|
157
|
+
The remote server can also be started and executed separately from pabot instances:
|
|
144
158
|
|
|
145
159
|
python -m pabot.pabotlib <path_to_resourcefile> <host> <port>
|
|
146
160
|
python -m pabot.pabotlib resource.txt 192.168.1.123 8271
|
|
147
161
|
|
|
148
162
|
This enables sharing a resource with multiple Robot Framework instances.
|
|
149
163
|
|
|
150
|
-
|
|
164
|
+
Additional details:
|
|
165
|
+
- The default value for --pabotlibhost is 127.0.0.1.
|
|
166
|
+
- If you provide a hostname other than 127.0.0.1, the local PabotLib server startup is automatically disabled.
|
|
167
|
+
|
|
168
|
+
**--pabotlibport [PORT]**
|
|
151
169
|
Port number of the PabotLib remote server (default is 8270). See --pabotlibhost for more information.
|
|
152
170
|
|
|
153
|
-
|
|
171
|
+
Behavior with port and host settings:
|
|
172
|
+
- If you set the port value to 0 and --pabotlibhost is 127.0.0.1 (default), a free port on localhost will be assigned automatically.
|
|
173
|
+
|
|
174
|
+
**--processtimeout [TIMEOUT]**
|
|
154
175
|
Maximum time in seconds to wait for a process before killing it. If not set, there's no timeout.
|
|
155
176
|
|
|
156
|
-
|
|
177
|
+
**--shard [INDEX]/[TOTAL]**
|
|
157
178
|
Optionally split execution into smaller pieces. This can be used for distributing testing to multiple machines.
|
|
158
179
|
|
|
159
|
-
|
|
180
|
+
**--artifacts [FILE EXTENSIONS]**
|
|
160
181
|
List of file extensions (comma separated). Defines which files (screenshots, videos etc.) from separate reporting
|
|
161
182
|
directories would be copied and included in a final report. Possible links to copied files in RF log would be updated
|
|
162
183
|
(only relative paths supported). The default value is `png`.
|
|
@@ -165,49 +186,53 @@ Supports all [Robot Framework command line options](https://robotframework.org/r
|
|
|
165
186
|
|
|
166
187
|
--artifacts png,mp4,txt
|
|
167
188
|
|
|
168
|
-
|
|
189
|
+
The artifact naming conventions are described in the README.md section: [Output Files Generated by Pabot](#output-files-generated-by-pabot).
|
|
190
|
+
|
|
191
|
+
**--artifactsinsubfolders**
|
|
169
192
|
Copy artifacts located not only directly in the RF output dir, but also in it's sub-folders.
|
|
170
193
|
|
|
171
|
-
|
|
194
|
+
**--resourcefile [FILEPATH]**
|
|
172
195
|
Indicator for a file that can contain shared variables for distributing resources. This needs to be used together with
|
|
173
196
|
pabotlib option. Resource file syntax is same as Windows ini files. Where a section is a shared set of variables.
|
|
174
197
|
|
|
175
|
-
|
|
198
|
+
**--argumentfile[INTEGER] [FILEPATH]**
|
|
176
199
|
Run same suites with multiple [argumentfile](http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#argument-files) options.
|
|
177
200
|
|
|
178
201
|
For example:
|
|
179
202
|
|
|
180
203
|
--argumentfile1 arg1.txt --argumentfile2 arg2.txt
|
|
181
204
|
|
|
182
|
-
|
|
205
|
+
**--suitesfrom [FILEPATH TO OUTPUTXML]**
|
|
183
206
|
Optionally read suites from output.xml file. Failed suites will run first and longer running ones will be executed
|
|
184
207
|
before shorter ones.
|
|
185
208
|
|
|
186
|
-
|
|
187
|
-
Optionally give execution order from a file.
|
|
209
|
+
**--ordering [FILE PATH] [MODE] [FAILURE POLICY]**
|
|
210
|
+
Optionally give execution order from a file. See README.md section: [Controlling execution order, mode and level of parallelism](#controlling-execution-order-mode-and-level-of-parallelism)
|
|
211
|
+
- MODE (optional): [static (default)|dynamic]
|
|
212
|
+
- FAILURE POLICY (optional, only in dynamic mode): [skip|run_all (default)]
|
|
188
213
|
|
|
189
|
-
|
|
214
|
+
**--chunk**
|
|
190
215
|
Optionally chunk tests to PROCESSES number of robot runs. This can save time because all the suites will share the same
|
|
191
216
|
setups and teardowns.
|
|
192
217
|
|
|
193
|
-
|
|
218
|
+
**--pabotprerunmodifier [PRERUNMODIFIER MODULE OR CLASS]**
|
|
194
219
|
Like Robot Framework's --prerunmodifier, but executed only once in the pabot's main process after all other
|
|
195
220
|
--prerunmodifiers. But unlike the regular --prerunmodifier command, --pabotprerunmodifier is not executed again in each
|
|
196
221
|
pabot subprocesses. Depending on the intended use, this may be desirable as well as more efficient. Can be used, for
|
|
197
222
|
example, to modify the list of tests to be performed.
|
|
198
223
|
|
|
199
|
-
|
|
224
|
+
**--no-rebot**
|
|
200
225
|
If specified, the tests will execute as usual, but Rebot will not be called to merge the logs. This option is designed
|
|
201
226
|
for scenarios where Rebot should be run later due to large log files, ensuring better memory and resource availability.
|
|
202
227
|
Subprocess results are stored in the pabot_results folder.
|
|
203
228
|
|
|
204
|
-
|
|
229
|
+
**--help**
|
|
205
230
|
Print usage instructions.
|
|
206
231
|
|
|
207
|
-
|
|
232
|
+
**--version**
|
|
208
233
|
Print version information.
|
|
209
234
|
|
|
210
|
-
Example usages
|
|
235
|
+
**Example usages:**
|
|
211
236
|
|
|
212
237
|
pabot test_directory
|
|
213
238
|
pabot --exclude FOO directory_to_tests
|
|
@@ -226,6 +251,9 @@ These can be helpful when you must ensure that only one of the processes uses so
|
|
|
226
251
|
|
|
227
252
|
PabotLib Docs are located at https://pabot.org/PabotLib.html.
|
|
228
253
|
|
|
254
|
+
Note that PabotLib uses the XML-RPC protocol, which does not support all possible object types.
|
|
255
|
+
These limitations are described in the Robot Framework documentation in chapter [Supported argument and return value types](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#supported-argument-and-return-value-types).
|
|
256
|
+
|
|
229
257
|
### PabotLib example:
|
|
230
258
|
|
|
231
259
|
test.robot
|
|
@@ -271,18 +299,28 @@ pabot call using resources from valueset.dat
|
|
|
271
299
|
|
|
272
300
|
pabot --pabotlib --resourcefile valueset.dat test.robot
|
|
273
301
|
|
|
274
|
-
### Controlling execution order and level of parallelism
|
|
302
|
+
### Controlling execution order, mode, and level of parallelism
|
|
275
303
|
|
|
276
304
|
.pabotsuitenames file contains the list of suites that will be executed.
|
|
277
|
-
|
|
278
|
-
The file
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
305
|
+
This file is created during pabot execution if it does not already exist. It acts as a cache to speed up processing when re-executing the same tests.
|
|
306
|
+
The file can be manually edited partially, but a simpler and more controlled approach is to use:
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
--ordering <FILENAME> [static|dynamic] [skip|run_all]
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
- **FILENAME** – path to the ordering file.
|
|
313
|
+
- **mode** – optional execution mode, either `static` (default) or `dynamic`.
|
|
314
|
+
- `static` executes suites in predefined stages.
|
|
315
|
+
- `dynamic` executes tests as soon as all their dependencies are satisfied, allowing more optimal parallel execution.
|
|
316
|
+
- **failure_policy** – determines behavior when dependencies fail. Used only in dynamic mode. Optional:
|
|
317
|
+
- `skip` – dependent tests are skipped if a dependency fails.
|
|
318
|
+
- `run_all` – all tests run regardless of failures (default).
|
|
282
319
|
|
|
283
|
-
|
|
320
|
+
The ordering file syntax is similar to `.pabotsuitenames` but does not include the first 4 hash rows used by pabot. The ordering file defines the **execution order and dependencies** of suites and tests.
|
|
321
|
+
The actual selection of what to run must still be done using options like `--test`, `--suite`, `--include`, or `--exclude`.
|
|
284
322
|
|
|
285
|
-
|
|
323
|
+
#### Controlling execution order
|
|
286
324
|
|
|
287
325
|
There different possibilities to influence the execution:
|
|
288
326
|
|
|
@@ -294,7 +332,7 @@ There different possibilities to influence the execution:
|
|
|
294
332
|
--suite Top Suite
|
|
295
333
|
```
|
|
296
334
|
|
|
297
|
-
* If the base suite name is changing with robot option [```--name / -N```](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#setting-
|
|
335
|
+
* If the base suite name is changing with robot option [```--name / -N```](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#setting-suite-name) you can use either the new or old full test path. For example:
|
|
298
336
|
|
|
299
337
|
```
|
|
300
338
|
--test New Suite Name.Sub Suite.Test 1
|
|
@@ -304,8 +342,14 @@ OR
|
|
|
304
342
|
|
|
305
343
|
* You can add a line with text `#WAIT` to force executor to wait until all previous suites have been executed.
|
|
306
344
|
* You can group suites and tests together to same executor process by adding line `{` before the group and `}` after. Note that `#WAIT` cannot be used inside a group.
|
|
307
|
-
* You can introduce dependencies using the word `#DEPENDS` after a test declaration. This keyword can be used several times if it is necessary to refer to several different tests.
|
|
308
|
-
|
|
345
|
+
* You can introduce dependencies using the word `#DEPENDS` after a test declaration. This keyword can be used several times if it is necessary to refer to several different tests.
|
|
346
|
+
* The ordering algorithm is designed to preserve the exact user-defined order as closely as possible. However, if a test's execution dependencies are not yet satisfied, the test is postponed and moved to the earliest possible stage where all its dependencies are fulfilled.
|
|
347
|
+
* Please take care that in case of circular dependencies an exception will be thrown.
|
|
348
|
+
* Note that each `#WAIT` splits suites into separate execution blocks, and it's not possible to define dependencies for suites or tests that are inside another `#WAIT` block or inside another `{}` braces.
|
|
349
|
+
* Ordering mode effect to execution:
|
|
350
|
+
* **Dynamic mode** will schedule dependent tests as soon as all their dependencies are satisfied. Note that in dynamic mode `#WAIT` is ignored, but you can achieve same results with using only `#DEPENDS` keywords.
|
|
351
|
+
* **Static mode** preserves stage barriers and executes the next stage only after all tests in the previous stage finish.
|
|
352
|
+
* Note: Within a group `{}`, neither execution order nor the `#DEPENDS` keyword currently works. This is due to limitations in Robot Framework, which is invoked within Pabot subprocesses. These limitations may be addressed in a future release of Robot Framework. For now, tests or suites within a group will be executed in the order Robot Framework discovers them — typically in alphabetical order.
|
|
309
353
|
* An example could be:
|
|
310
354
|
|
|
311
355
|
```
|
|
@@ -359,7 +403,7 @@ where order.txt is:
|
|
|
359
403
|
#SLEEP 8
|
|
360
404
|
```
|
|
361
405
|
|
|
362
|
-
|
|
406
|
+
Possible output could be:
|
|
363
407
|
|
|
364
408
|
```
|
|
365
409
|
2025-02-15 19:15:00.408321 [0] [ID:1] SLEEPING 6 SECONDS BEFORE STARTING Data 1.suite C
|
|
@@ -424,6 +468,12 @@ Artifacts are **copied** into the output directory and renamed with the followin
|
|
|
424
468
|
TIMESTAMP-ARGUMENT_INDEX-PABOTQUEUEINDEX
|
|
425
469
|
```
|
|
426
470
|
|
|
471
|
+
If you use the special option `notimestamps` at the end of the `--artifacts` command, (For example: `--artifacts png,txt,notimestamps`) the timestamp part will be omitted, and the name will be in the format:
|
|
472
|
+
|
|
473
|
+
```
|
|
474
|
+
ARGUMENT_INDEX-PABOTQUEUEINDEX
|
|
475
|
+
```
|
|
476
|
+
|
|
427
477
|
- **TIMESTAMP** = Time of `pabot` command invocation (not the screenshot's actual timestamp), format: `YYYYmmdd_HHMMSS`
|
|
428
478
|
- **ARGUMENT_INDEX** = Optional index number, only used if `--argumentfileN` options are given
|
|
429
479
|
- **PABOTQUEUEINDEX** = Process queue index (see section [Global Variables](#global-variables))
|
|
@@ -441,6 +491,7 @@ pabot_results/
|
|
|
441
491
|
│ ├── robot_stdout.out
|
|
442
492
|
│ ├── robot_stderr.out
|
|
443
493
|
│ └── artifacts...
|
|
494
|
+
pabot_manager.log # Pabot's own main log. Basically same than prints in console
|
|
444
495
|
```
|
|
445
496
|
|
|
446
497
|
Each `PABOTQUEUEINDEX` folder contains as default:
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
pabot/ProcessManager.py,sha256=w3dgtEKGhn4rj3nQ1EEQFAPeiIv6OF-KU6R3WZwQxPs,11739
|
|
2
|
+
pabot/SharedLibrary.py,sha256=mIipGs3ZhKYEakKprcbrMI4P_Un6qI8gE7086xpHaLY,2552
|
|
3
|
+
pabot/__init__.py,sha256=KuCXsD2BDgEFiQAxVRLN3SXkkyCULXtW06tTTqCEuRo,202
|
|
4
|
+
pabot/arguments.py,sha256=6MBXKnbDgWNdu7NnRqqd2_GNOs-u8QOxCz9wHhyfUVI,11404
|
|
5
|
+
pabot/clientwrapper.py,sha256=yz7battGs0exysnDeLDWJuzpb2Q-qSjitwxZMO2TlJw,231
|
|
6
|
+
pabot/coordinatorwrapper.py,sha256=nQQ7IowD6c246y8y9nsx0HZbt8vS2XODhPVDjm-lyi0,195
|
|
7
|
+
pabot/execution_items.py,sha256=zDVGW0AAeVbM-scC3Yui2TxvIPx1wYyFKHTPU2BkJkY,13329
|
|
8
|
+
pabot/pabot.py,sha256=hfMZdMYmeZEo3mNAVrvmuPU9vGhKpIBEVq5R8BI5ovE,87438
|
|
9
|
+
pabot/pabotlib.py,sha256=vHbqV7L7mIvDzXBh9UcdULrwhBHNn70EDXF_31MNFO4,22320
|
|
10
|
+
pabot/result_merger.py,sha256=g4mm-BhhMK57Z6j6dpvfL5El1g5onOtfV4RByNrO8g0,9744
|
|
11
|
+
pabot/robotremoteserver.py,sha256=BdeIni9Q4LJKVDBUlG2uJ9tiyAjrPXwU_YsPq1THWoo,23296
|
|
12
|
+
pabot/skip_listener.py,sha256=xv7wH-yB_nfc_AT1oZh3C0iu8hS67g5a28x8hwSrYsE,254
|
|
13
|
+
pabot/timeout_listener.py,sha256=twZFiJEyn9tAqI1K6JDBGaZrxRqVLFDVWvyxa6d8Lb0,160
|
|
14
|
+
pabot/workerwrapper.py,sha256=BdELUVDs5BmEkdNBcYTlnP22Cj0tUpZEunYQMAKyKWU,185
|
|
15
|
+
pabot/writer.py,sha256=_sRN_EqIhaMwPYmTkN4NzO2Mj6tggcaxgYkqJKatb_c,3639
|
|
16
|
+
pabot/py3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
pabot/py3/client.py,sha256=Od9L4vZ0sozMHq_W_ITQHBBt8kAej40DG58wnxmbHGM,1434
|
|
18
|
+
pabot/py3/coordinator.py,sha256=kBshCzA_1QX_f0WNk42QBJyDYSwSlNM-UEBxOReOj6E,2313
|
|
19
|
+
pabot/py3/messages.py,sha256=7mFr4_0x1JHm5sW8TvKq28Xs_JoeIGku2bX7AyO0kng,2557
|
|
20
|
+
pabot/py3/worker.py,sha256=5rfp4ZiW6gf8GRz6eC0-KUkfx847A91lVtRYpLAv2sg,1612
|
|
21
|
+
robotframework_pabot-5.2.0b1.dist-info/METADATA,sha256=_7b2wWKxeuFlEbRCz_sHwmrWQCwp85v-rl9HX-mScyo,23811
|
|
22
|
+
robotframework_pabot-5.2.0b1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
23
|
+
robotframework_pabot-5.2.0b1.dist-info/entry_points.txt,sha256=JpAIFADTeFOQWdwmn56KpAil8V3-41ZC5ICXCYm3Ng0,43
|
|
24
|
+
robotframework_pabot-5.2.0b1.dist-info/top_level.txt,sha256=t3OwfEAsSxyxrhjy_GCJYHKbV_X6AIsgeLhYeHvObG4,6
|
|
25
|
+
robotframework_pabot-5.2.0b1.dist-info/RECORD,,
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
pabot/SharedLibrary.py,sha256=mIipGs3ZhKYEakKprcbrMI4P_Un6qI8gE7086xpHaLY,2552
|
|
2
|
-
pabot/__init__.py,sha256=QKXV5e-W0g2tAcx-NzbFZscMZr1HtBUzDrpQDrURXBk,200
|
|
3
|
-
pabot/arguments.py,sha256=m38y8mXKJ5BHlxSrsEI0gXlkzR5hv88G9i-FL_BouQ4,9168
|
|
4
|
-
pabot/clientwrapper.py,sha256=yz7battGs0exysnDeLDWJuzpb2Q-qSjitwxZMO2TlJw,231
|
|
5
|
-
pabot/coordinatorwrapper.py,sha256=nQQ7IowD6c246y8y9nsx0HZbt8vS2XODhPVDjm-lyi0,195
|
|
6
|
-
pabot/execution_items.py,sha256=zDVGW0AAeVbM-scC3Yui2TxvIPx1wYyFKHTPU2BkJkY,13329
|
|
7
|
-
pabot/pabot.py,sha256=j_CtB8S8gZ5qBSlXKuv_7Mhj7i5RVHm7X6tg1l_Fp6o,79549
|
|
8
|
-
pabot/pabotlib.py,sha256=FRZKaKy1ybyRkE-0SpaCsUWzxZAzNNU5dAywSm1QoPk,22324
|
|
9
|
-
pabot/result_merger.py,sha256=ST2szeXoaD3ipwr_vhIUH2SihlxhoXiOQu2Zj2VowyA,9674
|
|
10
|
-
pabot/robotremoteserver.py,sha256=L3O2QRKSGSE4ux5M1ip5XJMaelqaxQWJxd9wLLdtpzM,22272
|
|
11
|
-
pabot/workerwrapper.py,sha256=BdELUVDs5BmEkdNBcYTlnP22Cj0tUpZEunYQMAKyKWU,185
|
|
12
|
-
pabot/py3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
-
pabot/py3/client.py,sha256=Od9L4vZ0sozMHq_W_ITQHBBt8kAej40DG58wnxmbHGM,1434
|
|
14
|
-
pabot/py3/coordinator.py,sha256=kBshCzA_1QX_f0WNk42QBJyDYSwSlNM-UEBxOReOj6E,2313
|
|
15
|
-
pabot/py3/messages.py,sha256=7mFr4_0x1JHm5sW8TvKq28Xs_JoeIGku2bX7AyO0kng,2557
|
|
16
|
-
pabot/py3/worker.py,sha256=5rfp4ZiW6gf8GRz6eC0-KUkfx847A91lVtRYpLAv2sg,1612
|
|
17
|
-
robotframework_pabot-5.0.0.dist-info/licenses/LICENSE.txt,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
|
18
|
-
robotframework_pabot-5.0.0.dist-info/METADATA,sha256=-vtvV7PlcmXw8p1RmrDuLGw0LNcC_jwzMVVlQvyDuPE,20880
|
|
19
|
-
robotframework_pabot-5.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
20
|
-
robotframework_pabot-5.0.0.dist-info/entry_points.txt,sha256=JpAIFADTeFOQWdwmn56KpAil8V3-41ZC5ICXCYm3Ng0,43
|
|
21
|
-
robotframework_pabot-5.0.0.dist-info/top_level.txt,sha256=t3OwfEAsSxyxrhjy_GCJYHKbV_X6AIsgeLhYeHvObG4,6
|
|
22
|
-
robotframework_pabot-5.0.0.dist-info/RECORD,,
|