robotframework-pabot 4.0.5__tar.gz → 4.1.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 (44) hide show
  1. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/PKG-INFO +3 -2
  2. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/README.md +58 -47
  3. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/pabot/__init__.py +1 -1
  4. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/pabot/arguments.py +2 -12
  5. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/pabot/execution_items.py +19 -7
  6. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/pabot/pabot.py +70 -82
  7. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/pabot/result_merger.py +7 -2
  8. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/robotframework_pabot.egg-info/PKG-INFO +3 -2
  9. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/robotframework_pabot.egg-info/SOURCES.txt +1 -0
  10. robotframework_pabot-4.1.0/tests/test_depends.py +219 -0
  11. robotframework_pabot-4.1.0/tests/test_pabotprerunmodifier.py +212 -0
  12. robotframework_pabot-4.1.0/tests/test_resultmerger.py +126 -0
  13. robotframework_pabot-4.0.5/tests/test_depends.py +0 -145
  14. robotframework_pabot-4.0.5/tests/test_resultmerger.py +0 -73
  15. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/LICENSE.txt +0 -0
  16. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/MANIFEST.in +0 -0
  17. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/pyproject.toml +0 -0
  18. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/setup.cfg +0 -0
  19. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/setup.py +0 -0
  20. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/pabot/SharedLibrary.py +0 -0
  21. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/pabot/clientwrapper.py +0 -0
  22. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/pabot/coordinatorwrapper.py +0 -0
  23. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/pabot/pabotlib.py +0 -0
  24. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/pabot/py3/__init__.py +0 -0
  25. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/pabot/py3/client.py +0 -0
  26. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/pabot/py3/coordinator.py +0 -0
  27. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/pabot/py3/messages.py +0 -0
  28. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/pabot/py3/worker.py +0 -0
  29. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/pabot/robotremoteserver.py +0 -0
  30. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/pabot/workerwrapper.py +0 -0
  31. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/robotframework_pabot.egg-info/dependency_links.txt +0 -0
  32. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/robotframework_pabot.egg-info/entry_points.txt +0 -0
  33. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/robotframework_pabot.egg-info/requires.txt +0 -0
  34. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/src/robotframework_pabot.egg-info/top_level.txt +0 -0
  35. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/tests/test_arguments_output.py +0 -0
  36. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/tests/test_functional.py +0 -0
  37. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/tests/test_ordering.py +0 -0
  38. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/tests/test_pabot.py +0 -0
  39. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/tests/test_pabotlib.py +0 -0
  40. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/tests/test_pabotsuitenames_io.py +0 -0
  41. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/tests/test_prerunmodifier.py +0 -0
  42. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/tests/test_stacktrace.py +0 -0
  43. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/tests/test_testlevelsplit_include.py +0 -0
  44. {robotframework_pabot-4.0.5 → robotframework_pabot-4.1.0}/tests/test_testlevelsplit_output_task_order.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: robotframework-pabot
3
- Version: 4.0.5
3
+ Version: 4.1.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
@@ -20,5 +20,6 @@ License-File: LICENSE.txt
20
20
  Requires-Dist: robotframework>=3.2
21
21
  Requires-Dist: robotframework-stacktrace>=0.4.1
22
22
  Requires-Dist: natsort>=8.2.0
23
+ Dynamic: download-url
23
24
 
24
25
  A parallel executor for Robot Framework tests. With Pabot you can split one execution into multiple and save test execution time.
@@ -53,45 +53,45 @@ There are several ways you can help in improving this tool:
53
53
  - Contribute by programming and making a pull request (easiest way is to work on an issue from the issue tracker)
54
54
 
55
55
  ## Command-line options
56
-
57
- pabot [--verbose|--testlevelsplit|--command .. --end-command|
58
- --processes num|--no-pabotlib|--pabotlibhost host|--pabotlibport port|
59
- --processtimeout num|
60
- --shard i/n|
61
- --artifacts extensions|--artifactsinsubfolders|
62
- --resourcefile file|--argumentfile[num] file|--suitesfrom file]
63
- [robot options] [path ...]
56
+ <!-- START DOCSTRING -->
57
+ pabot [--verbose|--testlevelsplit|--command .. --end-command|
58
+ --processes num|--no-pabotlib|--pabotlibhost host|--pabotlibport port|
59
+ --processtimeout num|
60
+ --shard i/n|
61
+ --artifacts extensions|--artifactsinsubfolders|
62
+ --resourcefile file|--argumentfile[num] file|--suitesfrom file|--ordering file
63
+ --chunk
64
+ --pabotprerunmodifier modifier]
65
+ [robot options] [path ...]
66
+
67
+ PabotLib remote server is started by default to enable locking and resource distribution between parallel test executions.
64
68
 
65
69
  Supports all [Robot Framework command line options](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#all-command-line-options) and also following pabot options:
66
70
 
67
71
  --verbose
68
- more output from the parallel execution
72
+ More output from the parallel execution.
69
73
 
70
74
  --testlevelsplit
71
- Split execution on test level instead of default suite level.
72
- If .pabotsuitenames contains both tests and suites then this
73
- will only affect new suites and split only them.
74
- Leaving this flag out when both suites and tests in
75
- .pabotsuitenames file will also only affect new suites and
76
- add them as suite files.
75
+ Split execution on test level instead of default suite level. If .pabotsuitenames contains both tests and suites then
76
+ this will only affect new suites and split only them. Leaving this flag out when both suites and tests in
77
+ .pabotsuitenames file will also only affect new suites and add them as suite files.
77
78
 
78
79
  --command [ACTUAL COMMANDS TO START ROBOT EXECUTOR] --end-command
79
- RF script for situations where robot is not used directly
80
-
81
- --processes [NUMBER OF PROCESSES]
82
- How many parallel executors to use (default max of 2 and cpu count).
83
- Special option "all" will use as many processes as there are
84
- executable suites or tests.
80
+ RF script for situations where robot is not used directly.
85
81
 
86
- PabotLib remote server is started by default to enable locking and resource distribution
87
- between parallel test executions.
82
+ --processes [NUMBER OF PROCESSES]
83
+ How many parallel executors to use (default max of 2 and cpu count). Special option "all" will use as many processes as
84
+ there are executable suites or tests.
88
85
 
89
- --no-pabotlib
86
+ --no-pabotlib
90
87
  Disable the PabotLib remote server if you don't need locking or resource distribution features.
91
88
 
92
- --pabotlibhost [HOSTNAME]
93
- Connect to an already running instance of the PabotLib remote server at the given host
94
- (disables the local PabotLib server start).
89
+ --pabotlibhost [HOSTNAME]
90
+ Connect to an already running instance of the PabotLib remote server at the given host (disables the local PabotLib
91
+ server start). For example, to connect to a remote PabotLib server running on another machine:
92
+
93
+ pabot --pabotlibhost 192.168.1.123 --pabotlibport 8271 tests/
94
+
95
95
  The remote server can be also started and executed separately from pabot instances:
96
96
 
97
97
  python -m pabot.pabotlib <path_to_resourcefile> <host> <port>
@@ -99,21 +99,20 @@ between parallel test executions.
99
99
 
100
100
  This enables sharing a resource with multiple Robot Framework instances.
101
101
 
102
- --pabotlibport [PORT]
103
- Port number of the PabotLib remote server (default is 8270)
104
- See --pabotlibhost for more information
102
+ --pabotlibport [PORT]
103
+ Port number of the PabotLib remote server (default is 8270). See --pabotlibhost for more information.
105
104
 
106
- --processtimeout [TIMEOUT]
105
+ --processtimeout [TIMEOUT]
107
106
  Maximum time in seconds to wait for a process before killing it. If not set, there's no timeout.
108
107
 
109
- --resourcefile [FILEPATH]
110
- Indicator for a file that can contain shared variables for distributing resources. This needs to be used together with pabotlib option. Resource file syntax is same as Windows ini files. Where a section is a shared set of variables.
108
+ --shard [INDEX]/[TOTAL]
109
+ Optionally split execution into smaller pieces. This can be used for distributing testing to multiple machines.
111
110
 
112
111
  --artifacts [FILE EXTENSIONS]
113
- List of file extensions (comma separated).
114
- Defines which files (screenshots, videos etc.) from separate reporting directories would be copied and included in a final report.
115
- Possible links to copied files in RF log would be updated (only relative paths supported).
116
- The default value is `png`.
112
+ List of file extensions (comma separated). Defines which files (screenshots, videos etc.) from separate reporting
113
+ directories would be copied and included in a final report. Possible links to copied files in RF log would be updated
114
+ (only relative paths supported). The default value is `png`.
115
+
117
116
  Examples:
118
117
 
119
118
  --artifacts png,mp4,txt
@@ -121,22 +120,33 @@ between parallel test executions.
121
120
  --artifactsinsubfolders
122
121
  Copy artifacts located not only directly in the RF output dir, but also in it's sub-folders.
123
122
 
124
- --argumentfile[INTEGER] [FILEPATH]
123
+ --resourcefile [FILEPATH]
124
+ Indicator for a file that can contain shared variables for distributing resources. This needs to be used together with
125
+ pabotlib option. Resource file syntax is same as Windows ini files. Where a section is a shared set of variables.
126
+
127
+ --argumentfile [INTEGER] [FILEPATH]
125
128
  Run same suites with multiple [argumentfile](http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#argument-files) options.
129
+
126
130
  For example:
127
131
 
128
132
  --argumentfile1 arg1.txt --argumentfile2 arg2.txt
129
133
 
130
- --suitesfrom [FILEPATH TO OUTPUTXML]
131
- Optionally read suites from output.xml file. Failed suites will run
132
- first and longer running ones will be executed before shorter ones.
134
+ --suitesfrom [FILEPATH TO OUTPUTXML]
135
+ Optionally read suites from output.xml file. Failed suites will run first and longer running ones will be executed
136
+ before shorter ones.
137
+
138
+ --ordering [FILE PATH]
139
+ Optionally give execution order from a file.
133
140
 
134
- --shard [INDEX]/[TOTAL]
135
- Optionally split execution into smaller pieces. This can
136
- be used for distributing testing to multiple machines.
141
+ --chunk
142
+ Optionally chunk tests to PROCESSES number of robot runs. This can save time because all the suites will share the same
143
+ setups and teardowns.
137
144
 
138
- --chunk
139
- Optionally chunk tests to PROCESSES number of robot runs. This can save time because all the suites will share the same setups and teardowns.
145
+ --pabotprerunmodifier [PRERUNMODIFIER MODULE OR CLASS]
146
+ Like Robot Framework's --prerunmodifier, but executed only once in the pabot's main process after all other
147
+ --prerunmodifiers. But unlike the regular --prerunmodifier command, --pabotprerunmodifier is not executed again in each
148
+ pabot subprocesses. Depending on the intended use, this may be desirable as well as more efficient. Can be used, for
149
+ example, to modify the list of tests to be performed.
140
150
 
141
151
  Example usages:
142
152
 
@@ -149,6 +159,7 @@ Example usages:
149
159
  # To disable PabotLib:
150
160
  pabot --no-pabotlib tests
151
161
 
162
+ <!-- END DOCSTRING -->
152
163
  ### PabotLib
153
164
 
154
165
  pabot.PabotLib provides keywords that will help communication and data sharing between the executor processes.
@@ -219,7 +230,7 @@ There different possibilities to influence the execution:
219
230
  * 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.
220
231
  * You can add a line with text `#WAIT` to force executor to wait until all previous suites have been executed.
221
232
  * You can group suites and tests together to same executor process by adding line `{` before the group and `}`after.
222
- * You can introduce dependencies using the word `#DEPENDS` after a test declaration. Please take care that in case of circular dependencies an exception will be thrown. An example could be.
233
+ * 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.
223
234
 
224
235
  ```
225
236
  --test robotTest.1 Scalar.Test With Environment Variables #DEPENDS robotTest.1 Scalar.Test with BuiltIn Variables of Robot Framework
@@ -2,4 +2,4 @@ from __future__ import absolute_import
2
2
 
3
3
  from .pabotlib import PabotLib
4
4
 
5
- __version__ = "4.0.5"
5
+ __version__ = "4.1.0"
@@ -106,8 +106,9 @@ def _parse_pabot_args(args): # type: (List[str]) -> Tuple[List[str], Dict[str,
106
106
  "resourcefile": str,
107
107
  "pabotlibhost": str,
108
108
  "pabotlibport": int,
109
+ "pabotprerunmodifier": str,
109
110
  "processtimeout": int,
110
- "ordering": _parse_ordering,
111
+ "ordering": str,
111
112
  "suitesfrom": str,
112
113
  "artifacts": lambda x: x.split(","),
113
114
  "shard": _parse_shard,
@@ -195,17 +196,6 @@ def _parse_pabot_args(args): # type: (List[str]) -> Tuple[List[str], Dict[str,
195
196
  return remaining_args, pabot_args
196
197
 
197
198
 
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
199
  def _delete_none_keys(d): # type: (Dict[str, Optional[object]]) -> Dict[str, object]
210
200
  keys = set()
211
201
  for k in d:
@@ -5,6 +5,7 @@ from robot import __version__ as ROBOT_VERSION
5
5
  from robot.errors import DataError
6
6
  from robot.utils import PY2, is_unicode
7
7
 
8
+ import re
8
9
 
9
10
  @total_ordering
10
11
  class ExecutionItem(object):
@@ -97,20 +98,31 @@ class GroupItem(ExecutionItem):
97
98
  class RunnableItem(ExecutionItem):
98
99
  pass
99
100
 
100
- depends = None # type: str
101
+ depends = None # type: List[str]
101
102
  depends_keyword = "#DEPENDS"
102
103
 
104
+ def _split_dependencies(self, line_name, depends_indexes):
105
+ depends_lst = [] if len(depends_indexes) < 2 else [line_name[i + len(self.depends_keyword) : j].strip() for i, j in zip(depends_indexes, depends_indexes[1:])]
106
+ depends_lst.append(line_name[depends_indexes[-1] + len(self.depends_keyword) : ].strip())
107
+ return depends_lst
108
+
109
+ def _merge_dependencies(self, line_start):
110
+ output_line = line_start
111
+ for d in self.depends:
112
+ output_line = output_line + " " + self.depends_keyword + " " + d
113
+ return output_line
114
+
103
115
  def set_name_and_depends(self, name):
104
116
  line_name = name.encode("utf-8") if PY2 and is_unicode(name) else name
105
- depends_begin_index = line_name.find(self.depends_keyword)
117
+ depends_indexes = [d.start() for d in re.finditer(self.depends_keyword, line_name)]
106
118
  self.name = (
107
119
  line_name
108
- if depends_begin_index == -1
109
- else line_name[0:depends_begin_index].strip()
120
+ if len(depends_indexes) == 0
121
+ else line_name[0:depends_indexes[0]].strip()
110
122
  )
111
123
  self.depends = (
112
- line_name[depends_begin_index + len(self.depends_keyword) :].strip()
113
- if depends_begin_index != -1
124
+ self._split_dependencies(line_name, depends_indexes)
125
+ if len(depends_indexes) != 0
114
126
  else None
115
127
  )
116
128
 
@@ -118,7 +130,7 @@ class RunnableItem(ExecutionItem):
118
130
  # type: () -> str
119
131
  line_without_depends = "--" + self.type + " " + self.name
120
132
  return (
121
- line_without_depends + " " + self.depends_keyword + " " + self.depends
133
+ self._merge_dependencies(line_without_depends)
122
134
  if self.depends
123
135
  else line_without_depends
124
136
  )
@@ -15,73 +15,12 @@
15
15
  # limitations under the License.
16
16
  #
17
17
  # partly based on work by Nokia Solutions and Networks Oyj
18
+
19
+ # Help documentation from README.md:
18
20
  """A parallel executor for Robot Framework test cases.
19
21
  Version [PABOT_VERSION]
20
22
 
21
- Supports all Robot Framework command line options and also following
22
- options (these must be before normal RF options):
23
-
24
- --verbose
25
- more output
26
-
27
- --command [ACTUAL COMMANDS TO START ROBOT EXECUTOR] --end-command
28
- RF script for situations where pybot is not used directly
29
-
30
- --processes [NUMBER OF PROCESSES]
31
- How many parallel executors to use (default max of 2 and cpu count).
32
- Special option "all" will use as many processes as there are
33
- executable suites or tests.
34
-
35
- --testlevelsplit
36
- Split execution on test level instead of default suite level.
37
- If .pabotsuitenames contains both tests and suites then this
38
- will only affect new suites and split only them.
39
- Leaving this flag out when both suites and tests in
40
- .pabotsuitenames file will also only affect new suites and
41
- add them as suite files.
42
-
43
- --resourcefile [FILEPATH]
44
- Indicator for a file that can contain shared variables for
45
- distributing resources.
46
-
47
- --no-pabotlib
48
- Disable the PabotLib remote server if you don't need locking or resource distribution features.
49
- PabotLib remote server is started by default.
50
-
51
- --pabotlibhost [HOSTNAME]
52
- Connect to an already running instance of the PabotLib remote server at the given host
53
- (disables the local PabotLib server start). For example, to connect to a
54
- remote PabotLib server running on another machine:
55
-
56
- pabot --pabotlibhost 192.168.1.123 --pabotlibport 8271 tests/
57
-
58
- The remote PabotLib server can be started separately using:
59
- python -m pabot.pabotlib <path_to_resourcefile> <host> <port>
60
- python -m pabot.pabotlib resource.txt 192.168.1.123 8271
61
-
62
- --pabotlibport [PORT]
63
- Port number of the PabotLib remote server (default is 8270)
64
-
65
- --processtimeout [TIMEOUT]
66
- Maximum time in seconds to wait for a process before killing it. If not set, there's no timeout.
67
-
68
- --ordering [FILE PATH]
69
- Optionally give execution order from a file.
70
-
71
- --suitesfrom [FILEPATH TO OUTPUTXML]
72
- Optionally read suites from output.xml file. Failed suites will run
73
- first and longer running ones will be executed before shorter ones.
74
-
75
- --argumentfile[INTEGER] [FILEPATH]
76
- Run same suite with multiple argumentfile options.
77
- For example "--argumentfile1 arg1.txt --argumentfile2 arg2.txt".
78
-
79
- --shard [SHARD]/[SHARD COUNT]
80
- Optionally split execution into smaller pieces. This can
81
- be used for distributing testing to multiple machines.
82
-
83
- --chunk
84
- Optionally chunk tests to PROCESSES number of robot runs.
23
+ PLACEHOLDER_README.MD
85
24
 
86
25
  Copyright 2022 Mikko Korpela - Apache 2 License
87
26
  """
@@ -184,6 +123,27 @@ _ROBOT_EXTENSIONS = [
184
123
  _ALL_ELAPSED = [] # type: List[Union[int, float]]
185
124
 
186
125
 
126
+ def extract_section(filename, start_marker, end_marker):
127
+ with open(filename, "r", encoding="utf-8") as f:
128
+ lines = f.readlines()
129
+
130
+ inside_section = False
131
+ extracted_lines = []
132
+
133
+ for line in lines:
134
+ if start_marker in line:
135
+ inside_section = True
136
+ continue
137
+ if end_marker in line:
138
+ inside_section = False
139
+ break
140
+ if inside_section:
141
+ # Add line from README.md without [] and (https: address)
142
+ extracted_lines.append(re.sub(r'\[([^\]]+)\]\(https?://[^\)]+\)', r'\1', line))
143
+
144
+ return "".join(extracted_lines).strip()
145
+
146
+
187
147
  class Color:
188
148
  SUPPORTED_OSES = ["posix"]
189
149
 
@@ -880,6 +840,8 @@ def solve_shard_suites(suite_names, pabot_args):
880
840
 
881
841
 
882
842
  def solve_suite_names(outs_dir, datasources, options, pabot_args):
843
+ if pabot_args.get("pabotprerunmodifier"):
844
+ options['prerunmodifier'].append(pabot_args['pabotprerunmodifier'])
883
845
  h = Hashes(
884
846
  dirs=get_hash_of_dirs(datasources),
885
847
  cmd=get_hash_of_command(options, pabot_args),
@@ -915,7 +877,7 @@ def solve_suite_names(outs_dir, datasources, options, pabot_args):
915
877
  for l in lines[4:]
916
878
  )
917
879
  execution_item_lines = [parse_execution_item_line(l) for l in lines[4:]]
918
- if corrupted or h != file_h or file_hash != hash_of_file:
880
+ if corrupted or h != file_h or file_hash != hash_of_file or pabot_args.get("pabotprerunmodifier"):
919
881
  return _regenerate(
920
882
  file_h,
921
883
  h,
@@ -1949,7 +1911,11 @@ def main_program(args):
1949
1911
  _start_message_writer()
1950
1912
  options, datasources, pabot_args, opts_for_run = parse_args(args)
1951
1913
  if pabot_args["help"]:
1952
- print(__doc__.replace("[PABOT_VERSION]", PABOT_VERSION))
1914
+ help_print = __doc__.replace(
1915
+ "PLACEHOLDER_README.MD",
1916
+ extract_section("README.md", "<!-- START DOCSTRING -->", "<!-- END DOCSTRING -->")
1917
+ )
1918
+ print(help_print.replace("[PABOT_VERSION]", PABOT_VERSION))
1953
1919
  return 0
1954
1920
  if len(datasources) == 0:
1955
1921
  print("[ " + _wrap_with(Color.RED, "ERROR") + " ]: No datasources given.")
@@ -1988,7 +1954,8 @@ def main_program(args):
1988
1954
  )
1989
1955
  return result_code if not _ABNORMAL_EXIT_HAPPENED else 252
1990
1956
  except Information as i:
1991
- print(__doc__.replace("[PABOT_VERSION]", PABOT_VERSION))
1957
+ version_print = __doc__.replace("\nPLACEHOLDER_README.MD\n", "")
1958
+ print(version_print.replace("[PABOT_VERSION]", PABOT_VERSION))
1992
1959
  print(i.message)
1993
1960
  except DataError as err:
1994
1961
  print(err.message)
@@ -2010,10 +1977,24 @@ def main_program(args):
2010
1977
  _stop_message_writer()
2011
1978
 
2012
1979
 
1980
+ def _parse_ordering(filename): # type: (str) -> List[ExecutionItem]
1981
+ try:
1982
+ with open(filename, "r") as orderingfile:
1983
+ return [
1984
+ parse_execution_item_line(line.strip())
1985
+ for line in orderingfile.readlines()
1986
+ ]
1987
+ except FileNotFoundError:
1988
+ raise DataError("Error: File '%s' not found." % filename)
1989
+ except:
1990
+ raise DataError("Error parsing ordering file '%s'" % filename)
1991
+
1992
+
2013
1993
  def _group_suites(outs_dir, datasources, options, pabot_args):
2014
1994
  suite_names = solve_suite_names(outs_dir, datasources, options, pabot_args)
2015
1995
  _verify_depends(suite_names)
2016
- ordered_suites = _preserve_order(suite_names, pabot_args.get("ordering"))
1996
+ ordering_arg = _parse_ordering(pabot_args.get("ordering")) if (pabot_args.get("ordering")) is not None else None
1997
+ ordered_suites = _preserve_order(suite_names, ordering_arg)
2017
1998
  shard_suites = solve_shard_suites(ordered_suites, pabot_args)
2018
1999
  grouped_suites = (
2019
2000
  _chunked_suite_names(shard_suites, pabot_args["processes"])
@@ -2083,20 +2064,27 @@ def _group_by_depend(suite_names):
2083
2064
  return [suite_names]
2084
2065
  independent_tests = list(filter(lambda suite: not suite.depends, runnable_suites))
2085
2066
  dependency_tree = [independent_tests]
2086
- while True:
2087
- dependent_tests = list(filter(lambda suite: suite.depends, runnable_suites))
2088
- dependent_on_last_stage = list(
2089
- filter(
2090
- lambda suite: any(
2091
- test_in_tier_before.name == suite.depends
2092
- for test_in_tier_before in dependency_tree[-1]
2093
- ),
2094
- dependent_tests,
2095
- )
2096
- )
2097
- if not dependent_on_last_stage:
2098
- break
2099
- dependency_tree += [dependent_on_last_stage]
2067
+ dependent_tests = list(filter(lambda suite: suite.depends, runnable_suites))
2068
+ unknown_dependent_tests = dependent_tests
2069
+ while len(unknown_dependent_tests) > 0:
2070
+ run_in_this_stage, run_later = [], []
2071
+ for d in unknown_dependent_tests:
2072
+ stage_indexes = []
2073
+ for i, stage in enumerate(dependency_tree):
2074
+ for test in stage:
2075
+ if test.name in d.depends:
2076
+ stage_indexes.append(i)
2077
+ # All #DEPENDS test are already run:
2078
+ if len(stage_indexes) == len(d.depends):
2079
+ run_in_this_stage.append(d)
2080
+ else:
2081
+ run_later.append(d)
2082
+ unknown_dependent_tests = run_later
2083
+ if len(run_in_this_stage) == 0:
2084
+ text = "There are circular or unmet dependencies using #DEPENDS. Check this/these test(s): " + str(run_later)
2085
+ raise DataError(text)
2086
+ else:
2087
+ dependency_tree.append(run_in_this_stage)
2100
2088
  flattened_dependency_tree = sum(dependency_tree, [])
2101
2089
  if len(flattened_dependency_tree) != len(runnable_suites):
2102
2090
  raise DataError(
@@ -35,7 +35,7 @@ from robot.model import SuiteVisitor
35
35
 
36
36
 
37
37
  class ResultMerger(SuiteVisitor):
38
- def __init__(self, result, tests_root_name, out_dir, copied_artifacts):
38
+ def __init__(self, result, tests_root_name, out_dir, copied_artifacts, legacy_output):
39
39
  self.root = result.suite
40
40
  self.errors = result.errors
41
41
  self.current = None
@@ -43,6 +43,7 @@ class ResultMerger(SuiteVisitor):
43
43
  self._tests_root_name = tests_root_name
44
44
  self._prefix = ""
45
45
  self._out_dir = out_dir
46
+ self.legacy_output = legacy_output
46
47
 
47
48
  self._patterns = []
48
49
  regexp_template = (
@@ -150,6 +151,8 @@ class ResultMerger(SuiteVisitor):
150
151
 
151
152
  def merge_time(self, suite):
152
153
  cur = self.current
154
+ if ROBOT_VERSION >= "7.0" and not self.legacy_output:
155
+ cur.elapsed_time = None
153
156
  cur.endtime = max([cur.endtime, suite.endtime])
154
157
  cur.starttime = min([cur.starttime, suite.starttime])
155
158
 
@@ -222,13 +225,14 @@ def merge_groups(
222
225
  invalid_xml_callback,
223
226
  out_dir,
224
227
  copied_artifacts,
228
+ legacy_output
225
229
  ):
226
230
  merged = []
227
231
  for group in group_by_root(
228
232
  results, critical_tags, non_critical_tags, invalid_xml_callback
229
233
  ).values():
230
234
  base = group[0]
231
- merger = ResultMerger(base, tests_root_name, out_dir, copied_artifacts)
235
+ merger = ResultMerger(base, tests_root_name, out_dir, copied_artifacts, legacy_output)
232
236
  for out in group:
233
237
  merger.merge(out)
234
238
  merged.append(base)
@@ -259,6 +263,7 @@ def merge(
259
263
  invalid_xml_callback,
260
264
  settings.output_directory,
261
265
  copied_artifacts,
266
+ rebot_options.get('legacyoutput')
262
267
  )
263
268
  if len(merged) == 1:
264
269
  if not merged[0].suite.doc:
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: robotframework-pabot
3
- Version: 4.0.5
3
+ Version: 4.1.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
@@ -20,5 +20,6 @@ License-File: LICENSE.txt
20
20
  Requires-Dist: robotframework>=3.2
21
21
  Requires-Dist: robotframework-stacktrace>=0.4.1
22
22
  Requires-Dist: natsort>=8.2.0
23
+ Dynamic: download-url
23
24
 
24
25
  A parallel executor for Robot Framework tests. With Pabot you can split one execution into multiple and save test execution time.
@@ -32,6 +32,7 @@ tests/test_functional.py
32
32
  tests/test_ordering.py
33
33
  tests/test_pabot.py
34
34
  tests/test_pabotlib.py
35
+ tests/test_pabotprerunmodifier.py
35
36
  tests/test_pabotsuitenames_io.py
36
37
  tests/test_prerunmodifier.py
37
38
  tests/test_resultmerger.py