robotframework-pabot 4.1.1__tar.gz → 4.3.0__tar.gz

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.
Files changed (51) hide show
  1. {robotframework_pabot-4.1.1/src/robotframework_pabot.egg-info → robotframework_pabot-4.3.0}/PKG-INFO +71 -13
  2. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/README.md +68 -11
  3. robotframework_pabot-4.3.0/pyproject.toml +3 -0
  4. robotframework_pabot-4.3.0/setup.py +14 -0
  5. robotframework_pabot-4.3.0/src/pabot/__init__.py +10 -0
  6. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/src/pabot/arguments.py +72 -0
  7. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/src/pabot/execution_items.py +81 -1
  8. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/src/pabot/pabot.py +154 -69
  9. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0/src/robotframework_pabot.egg-info}/PKG-INFO +71 -13
  10. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/src/robotframework_pabot.egg-info/SOURCES.txt +3 -0
  11. robotframework_pabot-4.3.0/tests/test_arguments_output.py +260 -0
  12. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/tests/test_depends.py +75 -2
  13. robotframework_pabot-4.3.0/tests/test_missing_subprocess_output.py +134 -0
  14. robotframework_pabot-4.3.0/tests/test_ordering.py +444 -0
  15. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/tests/test_pabot.py +12 -3
  16. robotframework_pabot-4.3.0/tests/test_run_empty_suite.py +74 -0
  17. robotframework_pabot-4.3.0/tests/test_suite_structure.py +232 -0
  18. robotframework_pabot-4.1.1/pyproject.toml +0 -3
  19. robotframework_pabot-4.1.1/setup.py +0 -5
  20. robotframework_pabot-4.1.1/src/pabot/__init__.py +0 -5
  21. robotframework_pabot-4.1.1/tests/test_arguments_output.py +0 -67
  22. robotframework_pabot-4.1.1/tests/test_ordering.py +0 -194
  23. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/LICENSE.txt +0 -0
  24. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/MANIFEST.in +0 -0
  25. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/setup.cfg +0 -0
  26. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/src/pabot/SharedLibrary.py +0 -0
  27. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/src/pabot/clientwrapper.py +0 -0
  28. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/src/pabot/coordinatorwrapper.py +0 -0
  29. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/src/pabot/pabotlib.py +0 -0
  30. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/src/pabot/py3/__init__.py +0 -0
  31. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/src/pabot/py3/client.py +0 -0
  32. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/src/pabot/py3/coordinator.py +0 -0
  33. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/src/pabot/py3/messages.py +0 -0
  34. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/src/pabot/py3/worker.py +0 -0
  35. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/src/pabot/result_merger.py +0 -0
  36. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/src/pabot/robotremoteserver.py +0 -0
  37. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/src/pabot/workerwrapper.py +0 -0
  38. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/src/robotframework_pabot.egg-info/dependency_links.txt +0 -0
  39. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/src/robotframework_pabot.egg-info/entry_points.txt +0 -0
  40. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/src/robotframework_pabot.egg-info/requires.txt +0 -0
  41. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/src/robotframework_pabot.egg-info/top_level.txt +0 -0
  42. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/tests/test_basic_arguments.py +0 -0
  43. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/tests/test_functional.py +0 -0
  44. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/tests/test_pabotlib.py +0 -0
  45. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/tests/test_pabotprerunmodifier.py +0 -0
  46. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/tests/test_pabotsuitenames_io.py +0 -0
  47. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/tests/test_prerunmodifier.py +0 -0
  48. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/tests/test_resultmerger.py +0 -0
  49. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/tests/test_stacktrace.py +0 -0
  50. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/tests/test_testlevelsplit_include.py +0 -0
  51. {robotframework_pabot-4.1.1 → robotframework_pabot-4.3.0}/tests/test_testlevelsplit_output_task_order.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: robotframework-pabot
3
- Version: 4.1.1
3
+ Version: 4.3.0
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
@@ -22,6 +22,7 @@ Requires-Dist: robotframework>=3.2
22
22
  Requires-Dist: robotframework-stacktrace>=0.4.1
23
23
  Requires-Dist: natsort>=8.2.0
24
24
  Dynamic: download-url
25
+ Dynamic: license-file
25
26
 
26
27
  # Pabot
27
28
 
@@ -88,9 +89,10 @@ pabot [--verbose|--testlevelsplit|--command .. --end-command|
88
89
  --processtimeout num|
89
90
  --shard i/n|
90
91
  --artifacts extensions|--artifactsinsubfolders|
91
- --resourcefile file|--argumentfile[num] file|--suitesfrom file|--ordering file
92
- --chunk
93
- --pabotprerunmodifier modifier
92
+ --resourcefile file|--argumentfile[num] file|--suitesfrom file|--ordering file|
93
+ --chunk|
94
+ --pabotprerunmodifier modifier|
95
+ --no-rebot|
94
96
  --help|--version]
95
97
  [robot options] [path ...]
96
98
 
@@ -154,7 +156,7 @@ Supports all [Robot Framework command line options](https://robotframework.org/r
154
156
  Indicator for a file that can contain shared variables for distributing resources. This needs to be used together with
155
157
  pabotlib option. Resource file syntax is same as Windows ini files. Where a section is a shared set of variables.
156
158
 
157
- --argumentfile [INTEGER] [FILEPATH]
159
+ --argumentfile[INTEGER] [FILEPATH]
158
160
  Run same suites with multiple [argumentfile](http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#argument-files) options.
159
161
 
160
162
  For example:
@@ -178,11 +180,16 @@ Supports all [Robot Framework command line options](https://robotframework.org/r
178
180
  pabot subprocesses. Depending on the intended use, this may be desirable as well as more efficient. Can be used, for
179
181
  example, to modify the list of tests to be performed.
180
182
 
181
- --help
182
- Print usage instructions.
183
+ --no-rebot
184
+ If specified, the tests will execute as usual, but Rebot will not be called to merge the logs. This option is designed
185
+ for scenarios where Rebot should be run later due to large log files, ensuring better memory and resource availability.
186
+ Subprocess results are stored in the pabot_results folder.
187
+
188
+ --help
189
+ Print usage instructions.
183
190
 
184
- --version
185
- Print version information.
191
+ --version
192
+ Print version information.
186
193
 
187
194
  Example usages:
188
195
 
@@ -259,14 +266,18 @@ After this come the suite names.
259
266
 
260
267
  With ```--ordering FILENAME``` you can have a list that controls order also. The syntax is same as .pabotsuitenames file syntax but does not contain 4 hash rows that are present in .pabotsuitenames.
261
268
 
269
+ Note: The `--ordering` file is intended only for defining the execution order of suites and tests. The actual selection of what to run must still be done using options like `--test`, `--suite`, `--include`, or `--exclude`.
270
+
262
271
  There different possibilities to influence the execution:
263
272
 
264
273
  * The order of suites can be changed.
265
274
  * If a directory (or a directory structure) should be executed sequentially, add the directory suite name to a row as a ```--suite``` option.
266
275
  * If the base suite name is changing with robot option [```--name / -N```](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#setting-the-name) you can also give partial suite name without the base suite.
267
- * You can add a line with text `#WAIT` to force executor to wait until all previous suites have been executed.
268
- * You can group suites and tests together to same executor process by adding line `{` before the group and `}`after.
269
- * You can introduce dependencies using the word `#DEPENDS` after a test declaration. Can be used several times if it is necessary to refer to several different tests. Please take care that in case of circular dependencies an exception will be thrown. An example could be.
276
+ * You can add a line with text to force executor to wait until all previous suites have been executed.
277
+ * 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.
278
+ * 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. Please take care that in case of circular dependencies an exception will be thrown. 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 `{}` brackets.
279
+ * 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.
280
+ * An example could be:
270
281
 
271
282
  ```
272
283
  --test robotTest.1 Scalar.Test With Environment Variables #DEPENDS robotTest.1 Scalar.Test with BuiltIn Variables of Robot Framework
@@ -274,8 +285,10 @@ There different possibilities to influence the execution:
274
285
  --test robotTest.2 Lists.Test with Keywords and a list
275
286
  #WAIT
276
287
  --test robotTest.2 Lists.Test with a Keyword that accepts multiple arguments
288
+ {
277
289
  --test robotTest.2 Lists.Test with some Collections keywords
278
290
  --test robotTest.2 Lists.Test to access list entries
291
+ }
279
292
  --test robotTest.3 Dictionary.Test that accesses Dictionaries
280
293
  --test robotTest.3 Dictionary.Dictionaries for named arguments #DEPENDS robotTest.3 Dictionary.Test that accesses Dictionaries
281
294
  --test robotTest.1 Scalar.Test Case With Variables #DEPENDS robotTest.3 Dictionary.Test that accesses Dictionaries
@@ -284,6 +297,51 @@ There different possibilities to influence the execution:
284
297
  --test robotTest.1 Scalar.Test With Arguments and Return Values
285
298
  --test robotTest.3 Dictionary.Test with Dictionaries as Arguments
286
299
  --test robotTest.3 Dictionary.Test with FOR loops and Dictionaries #DEPENDS robotTest.1 Scalar.Test Case with Return Values
300
+ ```
301
+
302
+ * By using the command `#SLEEP X`, where `X` is an integer in the range [0-3600] (in seconds), you can
303
+ define a startup delay for each subprocess. `#SLEEP` affects the next line unless the next line starts a
304
+ group with `{`, in which case the delay applies to the entire group. If the next line begins with `--test`
305
+ or `--suite`, the delay is applied to that specific item. Any other occurrences of `#SLEEP` are ignored.
306
+ Note that `#SLEEP` has no effect within a group, i.e., inside a subprocess.
307
+
308
+ The following example clarifies the behavior:
309
+
310
+ ```sh
311
+ pabot --processes 2 --ordering order.txt data_1
312
+ ```
313
+
314
+ where order.txt is:
315
+
316
+ ```
317
+ #SLEEP 1
318
+ {
319
+ #SLEEP 2
320
+ --suite Data 1.suite A
321
+ #SLEEP 3
322
+ --suite Data 1.suite B
323
+ #SLEEP 4
324
+ }
325
+ #SLEEP 5
326
+ #SLEEP 6
327
+ --suite Data 1.suite C
328
+ #SLEEP 7
329
+ --suite Data 1.suite D
330
+ #SLEEP 8
331
+ ```
332
+
333
+ prints something like this:
334
+
335
+ ```
336
+ 2025-02-15 19:15:00.408321 [0] [ID:1] SLEEPING 6 SECONDS BEFORE STARTING Data 1.suite C
337
+ 2025-02-15 19:15:00.408321 [1] [ID:0] SLEEPING 1 SECONDS BEFORE STARTING Group_Data 1.suite A_Data 1.suite B
338
+ 2025-02-15 19:15:01.409389 [PID:52008] [1] [ID:0] EXECUTING Group_Data 1.suite A_Data 1.suite B
339
+ 2025-02-15 19:15:06.409024 [PID:1528] [0] [ID:1] EXECUTING Data 1.suite C
340
+ 2025-02-15 19:15:09.257564 [PID:52008] [1] [ID:0] PASSED Group_Data 1.suite A_Data 1.suite B in 7.8 seconds
341
+ 2025-02-15 19:15:09.259067 [1] [ID:2] SLEEPING 7 SECONDS BEFORE STARTING Data 1.suite D
342
+ 2025-02-15 19:15:09.647342 [PID:1528] [0] [ID:1] PASSED Data 1.suite C in 3.2 seconds
343
+ 2025-02-15 19:15:16.260432 [PID:48156] [1] [ID:2] EXECUTING Data 1.suite D
344
+ 2025-02-15 19:15:18.696420 [PID:48156] [1] [ID:2] PASSED Data 1.suite D in 2.4 seconds
287
345
  ```
288
346
 
289
347
  ### Programmatic use
@@ -63,9 +63,10 @@ pabot [--verbose|--testlevelsplit|--command .. --end-command|
63
63
  --processtimeout num|
64
64
  --shard i/n|
65
65
  --artifacts extensions|--artifactsinsubfolders|
66
- --resourcefile file|--argumentfile[num] file|--suitesfrom file|--ordering file
67
- --chunk
68
- --pabotprerunmodifier modifier
66
+ --resourcefile file|--argumentfile[num] file|--suitesfrom file|--ordering file|
67
+ --chunk|
68
+ --pabotprerunmodifier modifier|
69
+ --no-rebot|
69
70
  --help|--version]
70
71
  [robot options] [path ...]
71
72
 
@@ -129,7 +130,7 @@ Supports all [Robot Framework command line options](https://robotframework.org/r
129
130
  Indicator for a file that can contain shared variables for distributing resources. This needs to be used together with
130
131
  pabotlib option. Resource file syntax is same as Windows ini files. Where a section is a shared set of variables.
131
132
 
132
- --argumentfile [INTEGER] [FILEPATH]
133
+ --argumentfile[INTEGER] [FILEPATH]
133
134
  Run same suites with multiple [argumentfile](http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#argument-files) options.
134
135
 
135
136
  For example:
@@ -153,11 +154,16 @@ Supports all [Robot Framework command line options](https://robotframework.org/r
153
154
  pabot subprocesses. Depending on the intended use, this may be desirable as well as more efficient. Can be used, for
154
155
  example, to modify the list of tests to be performed.
155
156
 
156
- --help
157
- Print usage instructions.
157
+ --no-rebot
158
+ If specified, the tests will execute as usual, but Rebot will not be called to merge the logs. This option is designed
159
+ for scenarios where Rebot should be run later due to large log files, ensuring better memory and resource availability.
160
+ Subprocess results are stored in the pabot_results folder.
161
+
162
+ --help
163
+ Print usage instructions.
158
164
 
159
- --version
160
- Print version information.
165
+ --version
166
+ Print version information.
161
167
 
162
168
  Example usages:
163
169
 
@@ -234,14 +240,18 @@ After this come the suite names.
234
240
 
235
241
  With ```--ordering FILENAME``` you can have a list that controls order also. The syntax is same as .pabotsuitenames file syntax but does not contain 4 hash rows that are present in .pabotsuitenames.
236
242
 
243
+ Note: The `--ordering` file is intended only for defining the execution order of suites and tests. The actual selection of what to run must still be done using options like `--test`, `--suite`, `--include`, or `--exclude`.
244
+
237
245
  There different possibilities to influence the execution:
238
246
 
239
247
  * The order of suites can be changed.
240
248
  * If a directory (or a directory structure) should be executed sequentially, add the directory suite name to a row as a ```--suite``` option.
241
249
  * If the base suite name is changing with robot option [```--name / -N```](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#setting-the-name) you can also give partial suite name without the base suite.
242
- * You can add a line with text `#WAIT` to force executor to wait until all previous suites have been executed.
243
- * You can group suites and tests together to same executor process by adding line `{` before the group and `}`after.
244
- * You can introduce dependencies using the word `#DEPENDS` after a test declaration. Can be used several times if it is necessary to refer to several different tests. Please take care that in case of circular dependencies an exception will be thrown. An example could be.
250
+ * You can add a line with text to force executor to wait until all previous suites have been executed.
251
+ * 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.
252
+ * 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. Please take care that in case of circular dependencies an exception will be thrown. 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 `{}` brackets.
253
+ * 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.
254
+ * An example could be:
245
255
 
246
256
  ```
247
257
  --test robotTest.1 Scalar.Test With Environment Variables #DEPENDS robotTest.1 Scalar.Test with BuiltIn Variables of Robot Framework
@@ -249,8 +259,10 @@ There different possibilities to influence the execution:
249
259
  --test robotTest.2 Lists.Test with Keywords and a list
250
260
  #WAIT
251
261
  --test robotTest.2 Lists.Test with a Keyword that accepts multiple arguments
262
+ {
252
263
  --test robotTest.2 Lists.Test with some Collections keywords
253
264
  --test robotTest.2 Lists.Test to access list entries
265
+ }
254
266
  --test robotTest.3 Dictionary.Test that accesses Dictionaries
255
267
  --test robotTest.3 Dictionary.Dictionaries for named arguments #DEPENDS robotTest.3 Dictionary.Test that accesses Dictionaries
256
268
  --test robotTest.1 Scalar.Test Case With Variables #DEPENDS robotTest.3 Dictionary.Test that accesses Dictionaries
@@ -259,6 +271,51 @@ There different possibilities to influence the execution:
259
271
  --test robotTest.1 Scalar.Test With Arguments and Return Values
260
272
  --test robotTest.3 Dictionary.Test with Dictionaries as Arguments
261
273
  --test robotTest.3 Dictionary.Test with FOR loops and Dictionaries #DEPENDS robotTest.1 Scalar.Test Case with Return Values
274
+ ```
275
+
276
+ * By using the command `#SLEEP X`, where `X` is an integer in the range [0-3600] (in seconds), you can
277
+ define a startup delay for each subprocess. `#SLEEP` affects the next line unless the next line starts a
278
+ group with `{`, in which case the delay applies to the entire group. If the next line begins with `--test`
279
+ or `--suite`, the delay is applied to that specific item. Any other occurrences of `#SLEEP` are ignored.
280
+ Note that `#SLEEP` has no effect within a group, i.e., inside a subprocess.
281
+
282
+ The following example clarifies the behavior:
283
+
284
+ ```sh
285
+ pabot --processes 2 --ordering order.txt data_1
286
+ ```
287
+
288
+ where order.txt is:
289
+
290
+ ```
291
+ #SLEEP 1
292
+ {
293
+ #SLEEP 2
294
+ --suite Data 1.suite A
295
+ #SLEEP 3
296
+ --suite Data 1.suite B
297
+ #SLEEP 4
298
+ }
299
+ #SLEEP 5
300
+ #SLEEP 6
301
+ --suite Data 1.suite C
302
+ #SLEEP 7
303
+ --suite Data 1.suite D
304
+ #SLEEP 8
305
+ ```
306
+
307
+ prints something like this:
308
+
309
+ ```
310
+ 2025-02-15 19:15:00.408321 [0] [ID:1] SLEEPING 6 SECONDS BEFORE STARTING Data 1.suite C
311
+ 2025-02-15 19:15:00.408321 [1] [ID:0] SLEEPING 1 SECONDS BEFORE STARTING Group_Data 1.suite A_Data 1.suite B
312
+ 2025-02-15 19:15:01.409389 [PID:52008] [1] [ID:0] EXECUTING Group_Data 1.suite A_Data 1.suite B
313
+ 2025-02-15 19:15:06.409024 [PID:1528] [0] [ID:1] EXECUTING Data 1.suite C
314
+ 2025-02-15 19:15:09.257564 [PID:52008] [1] [ID:0] PASSED Group_Data 1.suite A_Data 1.suite B in 7.8 seconds
315
+ 2025-02-15 19:15:09.259067 [1] [ID:2] SLEEPING 7 SECONDS BEFORE STARTING Data 1.suite D
316
+ 2025-02-15 19:15:09.647342 [PID:1528] [0] [ID:1] PASSED Data 1.suite C in 3.2 seconds
317
+ 2025-02-15 19:15:16.260432 [PID:48156] [1] [ID:2] EXECUTING Data 1.suite D
318
+ 2025-02-15 19:15:18.696420 [PID:48156] [1] [ID:2] PASSED Data 1.suite D in 2.4 seconds
262
319
  ```
263
320
 
264
321
  ### Programmatic use
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42", "wheel"]
3
+ build-backend = "setuptools.build_meta"
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env python
2
+
3
+ from setuptools import setup
4
+ import os
5
+ import sys
6
+
7
+ # Add src to path so that version can be imported
8
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
9
+ from pabot import __version__
10
+
11
+ setup(
12
+ name="robotframework-pabot",
13
+ version=__version__,
14
+ )
@@ -0,0 +1,10 @@
1
+ from __future__ import absolute_import
2
+
3
+ # Avoid import errors during setup/build
4
+ try:
5
+ from .pabotlib import PabotLib
6
+ __all__ = ["PabotLib"]
7
+ except ImportError:
8
+ pass
9
+
10
+ __version__ = "4.3.0"
@@ -1,5 +1,9 @@
1
+ import atexit
1
2
  import multiprocessing
3
+ import os
4
+ import glob
2
5
  import re
6
+ import tempfile
3
7
  from typing import Dict, List, Optional, Tuple
4
8
 
5
9
  from robot import __version__ as ROBOT_VERSION
@@ -16,6 +20,7 @@ from .execution_items import (
16
20
  SuiteItem,
17
21
  TestItem,
18
22
  WaitItem,
23
+ SleepItem,
19
24
  )
20
25
 
21
26
  ARGSMATCHER = re.compile(r"--argumentfile(\d+)")
@@ -65,9 +70,72 @@ def parse_args(
65
70
  options_for_subprocesses["name"] = "Suites"
66
71
  opts = _delete_none_keys(options)
67
72
  opts_sub = _delete_none_keys(options_for_subprocesses)
73
+ _replace_arg_files(pabot_args, opts_sub)
68
74
  return opts, datasources, pabot_args, opts_sub
69
75
 
70
76
 
77
+ # remove options from argumentfile according to different scenarios.
78
+ # -t/--test/--task shall be removed if --testlevelsplit options exists
79
+ # -s/--suite shall be removed if --testlevelsplit options does not exist
80
+ def _replace_arg_files(pabot_args, opts_sub):
81
+ _cleanup_old_pabot_temp_files()
82
+ if not opts_sub.get('argumentfile') or not opts_sub['argumentfile']:
83
+ return
84
+ arg_file_list = opts_sub['argumentfile']
85
+ temp_file_list = []
86
+ test_level = pabot_args.get('testlevelsplit')
87
+
88
+ for arg_file_path in arg_file_list:
89
+ with open(arg_file_path, 'r') as arg_file:
90
+ arg_file_lines = arg_file.readlines()
91
+ if not arg_file_lines:
92
+ continue
93
+
94
+ fd, temp_path = tempfile.mkstemp(prefix="pabot_temp_", suffix=".txt")
95
+ with os.fdopen(fd, 'wb') as temp_file:
96
+ for line in arg_file_lines:
97
+ if test_level and _is_test_option(line):
98
+ continue
99
+ elif not test_level and _is_suite_option(line):
100
+ continue
101
+ temp_file.write(line.encode('utf-8'))
102
+
103
+ temp_file_list.append(temp_path)
104
+
105
+ opts_sub['argumentfile'] = temp_file_list
106
+ atexit.register(cleanup_temp_file, temp_file_list)
107
+
108
+
109
+ def _is_suite_option(line):
110
+ return line.startswith('-s ') or line.startswith('--suite ')
111
+
112
+
113
+ def _is_test_option(line):
114
+ return line.startswith('-t ') or line.startswith('--test ') or line.startswith('--task ')
115
+
116
+
117
+ # clean the temp argument files before exiting the pabot process
118
+ def cleanup_temp_file(temp_file_list):
119
+ for temp_file in temp_file_list:
120
+ if os.path.exists(temp_file):
121
+ try:
122
+ os.remove(temp_file)
123
+ except Exception:
124
+ pass
125
+
126
+
127
+ # Deletes all possible pabot_temp_ files from os temp directory
128
+ def _cleanup_old_pabot_temp_files():
129
+ temp_dir = tempfile.gettempdir()
130
+ pattern = os.path.join(temp_dir, "pabot_temp_*.txt")
131
+ old_files = glob.glob(pattern)
132
+ for file_path in old_files:
133
+ try:
134
+ os.remove(file_path)
135
+ except Exception:
136
+ pass
137
+
138
+
71
139
  def _parse_shard(arg):
72
140
  # type: (str) -> Tuple[int, int]
73
141
  parts = arg.split("/")
@@ -91,6 +159,7 @@ def _parse_pabot_args(args): # type: (List[str]) -> Tuple[List[str], Dict[str,
91
159
  "shardindex": 0,
92
160
  "shardcount": 1,
93
161
  "chunk": False,
162
+ "no-rebot": False,
94
163
  }
95
164
  # Explicitly define argument types for validation
96
165
  flag_args = {
@@ -100,6 +169,7 @@ def _parse_pabot_args(args): # type: (List[str]) -> Tuple[List[str], Dict[str,
100
169
  "pabotlib",
101
170
  "artifactsinsubfolders",
102
171
  "chunk",
172
+ "no-rebot"
103
173
  }
104
174
  value_args = {
105
175
  "hive": str,
@@ -217,6 +287,8 @@ def parse_execution_item_line(text): # type: (str) -> ExecutionItem
217
287
  if text.startswith("DYNAMICTEST"):
218
288
  suite, test = text[12:].split(" :: ")
219
289
  return DynamicTestItem(test, suite)
290
+ if text.startswith("#SLEEP "):
291
+ return SleepItem(text[7:])
220
292
  if text == "#WAIT":
221
293
  return WaitItem()
222
294
  if text == "{":
@@ -7,11 +7,46 @@ from robot.utils import PY2, is_unicode
7
7
 
8
8
  import re
9
9
 
10
+
11
+ def create_dependency_tree(items):
12
+ # type: (List[ExecutionItem]) -> List[List[ExecutionItem]]
13
+ independent_tests = list(filter(lambda item: not item.depends, items))
14
+ dependency_tree = [independent_tests]
15
+ dependent_tests = list(filter(lambda item: item.depends, items))
16
+ unknown_dependent_tests = dependent_tests
17
+ while len(unknown_dependent_tests) > 0:
18
+ run_in_this_stage, run_later = [], []
19
+ for d in unknown_dependent_tests:
20
+ stage_indexes = []
21
+ for i, stage in enumerate(dependency_tree):
22
+ for test in stage:
23
+ if test.name in d.depends:
24
+ stage_indexes.append(i)
25
+ # All #DEPENDS test are already run:
26
+ if len(stage_indexes) == len(d.depends):
27
+ run_in_this_stage.append(d)
28
+ else:
29
+ run_later.append(d)
30
+ unknown_dependent_tests = run_later
31
+ if len(run_in_this_stage) == 0:
32
+ text = "There are circular or unmet dependencies using #DEPENDS. Check this/these test(s): " + str(run_later)
33
+ raise DataError(text)
34
+ else:
35
+ dependency_tree.append(run_in_this_stage)
36
+ flattened_dependency_tree = sum(dependency_tree, [])
37
+ if len(flattened_dependency_tree) != len(items):
38
+ raise DataError(
39
+ "Invalid test configuration: Circular or unmet dependencies detected between test suites. Please check your #DEPENDS definitions."
40
+ )
41
+ return dependency_tree
42
+
43
+
10
44
  @total_ordering
11
45
  class ExecutionItem(object):
12
46
  isWait = False
13
47
  type = None # type: str
14
48
  name = None # type: str
49
+ sleep = 0 # type: int
15
50
 
16
51
  def top_name(self):
17
52
  # type: () -> str
@@ -29,6 +64,14 @@ class ExecutionItem(object):
29
64
  # type: () -> str
30
65
  return ""
31
66
 
67
+ def set_sleep(self, sleep_time):
68
+ # type: (int) -> None
69
+ self.sleep = sleep_time
70
+
71
+ def get_sleep(self):
72
+ # type: () -> int
73
+ return self.sleep
74
+
32
75
  def modify_options_for_executor(self, options):
33
76
  options[self.type] = self.name
34
77
 
@@ -69,7 +112,7 @@ class GroupItem(ExecutionItem):
69
112
  type = "group"
70
113
 
71
114
  def __init__(self):
72
- self.name = "Group_"
115
+ self.name = "Group"
73
116
  self._items = []
74
117
  self._element_type = None
75
118
 
@@ -82,11 +125,26 @@ class GroupItem(ExecutionItem):
82
125
  )
83
126
  if len(self._items) > 0:
84
127
  self.name += "_"
128
+ if self.get_sleep() < item.get_sleep():
129
+ self.set_sleep(item.get_sleep())
85
130
  self.name += item.name
86
131
  self._element_type = item.type
87
132
  self._items.append(item)
133
+
134
+ def change_items_order_by_depends(self):
135
+ ordered_name = "Group"
136
+ dependency_tree = create_dependency_tree(self._items)
137
+ ordered = [item for sublist in dependency_tree for item in sublist]
138
+ for item in ordered:
139
+ ordered_name += f"_{item.name}"
140
+ self.name = ordered_name
141
+ self._items = ordered
88
142
 
89
143
  def modify_options_for_executor(self, options):
144
+ # Since a GroupItem contains either tests or suites, options are cleared
145
+ # and only the Group's content is used to avoid duplicate execution.
146
+ options['suite'] = []
147
+ options['test'] = []
90
148
  for item in self._items:
91
149
  if item.type not in options:
92
150
  options[item.type] = []
@@ -120,6 +178,7 @@ class RunnableItem(ExecutionItem):
120
178
  if len(depends_indexes) == 0
121
179
  else line_name[0:depends_indexes[0]].strip()
122
180
  )
181
+ assert len(self.name) != 0, f"Suite or test name cannot be empty and then contain #DEPENDS like: {name}"
123
182
  self.depends = (
124
183
  self._split_dependencies(line_name, depends_indexes)
125
184
  if len(depends_indexes) != 0
@@ -197,6 +256,8 @@ class TestItem(RunnableItem):
197
256
  def modify_options_for_executor(self, options):
198
257
  if "rerunfailed" in options:
199
258
  del options["rerunfailed"]
259
+ if "rerunfailedsuites" in options:
260
+ del options["rerunfailedsuites"]
200
261
  name = self.name
201
262
  for char in ["[", "?", "*"]:
202
263
  name = name.replace(char, "[" + char + "]")
@@ -207,6 +268,8 @@ class TestItem(RunnableItem):
207
268
  def modify_options_for_executor(self, options):
208
269
  if "rerunfailed" in options:
209
270
  del options["rerunfailed"]
271
+ if "rerunfailedsuites" in options:
272
+ del options["rerunfailedsuites"]
210
273
 
211
274
  def difference(self, from_items):
212
275
  # type: (List[ExecutionItem]) -> List[ExecutionItem]
@@ -274,6 +337,23 @@ class WaitItem(ExecutionItem):
274
337
  return self.name
275
338
 
276
339
 
340
+ class SleepItem(ExecutionItem):
341
+ type = "sleep"
342
+
343
+ def __init__(self, time):
344
+ try:
345
+ assert 3600 >= int(time) >= 0 # 1 h max.
346
+ self.name = time
347
+ self.sleep = int(time)
348
+ except ValueError:
349
+ raise ValueError("#SLEEP value %s is not integer" % time)
350
+ except AssertionError:
351
+ raise ValueError("#SLEEP value %s is not in between 0 and 3600" % time)
352
+
353
+ def line(self):
354
+ return "#SLEEP " + self.name
355
+
356
+
277
357
  class GroupStartItem(ExecutionItem):
278
358
  type = "group"
279
359