robotframework-pabot 3.0.1__tar.gz → 4.0.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.
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/PKG-INFO +4 -1
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/README.md +9 -5
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/__init__.py +1 -1
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/arguments.py +87 -95
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/pabot.py +10 -9
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/robotframework_pabot.egg-info/PKG-INFO +4 -1
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/robotframework_pabot.egg-info/SOURCES.txt +12 -1
- robotframework_pabot-4.0.0/tests/test_arguments_output.py +67 -0
- robotframework_pabot-4.0.0/tests/test_depends.py +133 -0
- robotframework_pabot-4.0.0/tests/test_functional.py +51 -0
- robotframework_pabot-4.0.0/tests/test_ordering.py +194 -0
- robotframework_pabot-4.0.0/tests/test_pabot.py +1396 -0
- robotframework_pabot-4.0.0/tests/test_pabotlib.py +315 -0
- robotframework_pabot-4.0.0/tests/test_pabotsuitenames_io.py +65 -0
- robotframework_pabot-4.0.0/tests/test_resultmerger.py +73 -0
- robotframework_pabot-4.0.0/tests/test_stacktrace.py +45 -0
- robotframework_pabot-4.0.0/tests/test_testlevelsplit_include.py +53 -0
- robotframework_pabot-4.0.0/tests/test_testlevelsplit_output_task_order.py +90 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/LICENSE.txt +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/MANIFEST.in +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/pyproject.toml +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/setup.cfg +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/setup.py +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/SharedLibrary.py +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/clientwrapper.py +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/coordinatorwrapper.py +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/execution_items.py +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/pabotlib.py +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/py3/__init__.py +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/py3/client.py +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/py3/coordinator.py +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/py3/messages.py +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/py3/worker.py +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/result_merger.py +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/robotremoteserver.py +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/workerwrapper.py +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/robotframework_pabot.egg-info/dependency_links.txt +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/robotframework_pabot.egg-info/entry_points.txt +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/robotframework_pabot.egg-info/requires.txt +0 -0
- {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/robotframework_pabot.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: robotframework-pabot
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.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
|
|
@@ -17,5 +17,8 @@ Classifier: Development Status :: 5 - Production/Stable
|
|
|
17
17
|
Classifier: Framework :: Robot Framework
|
|
18
18
|
Requires-Python: >=3.6
|
|
19
19
|
License-File: LICENSE.txt
|
|
20
|
+
Requires-Dist: robotframework>=3.2
|
|
21
|
+
Requires-Dist: robotframework-stacktrace>=0.4.1
|
|
22
|
+
Requires-Dist: natsort>=8.2.0
|
|
20
23
|
|
|
21
24
|
A parallel executor for Robot Framework tests. With Pabot you can split one execution into multiple and save test execution time.
|
|
@@ -55,14 +55,14 @@ There are several ways you can help in improving this tool:
|
|
|
55
55
|
## Command-line options
|
|
56
56
|
|
|
57
57
|
pabot [--verbose|--testlevelsplit|--command .. --end-command|
|
|
58
|
-
--processes num|--pabotlib|--pabotlibhost host|--pabotlibport port|
|
|
58
|
+
--processes num|--no-pabotlib|--pabotlibhost host|--pabotlibport port|
|
|
59
59
|
--processtimeout num|
|
|
60
60
|
--shard i/n|
|
|
61
61
|
--artifacts extensions|--artifactsinsubfolders|
|
|
62
62
|
--resourcefile file|--argumentfile[num] file|--suitesfrom file]
|
|
63
63
|
[robot options] [path ...]
|
|
64
64
|
|
|
65
|
-
Supports all [Robot Framework command line options](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#all-command-line-options) and also following
|
|
65
|
+
Supports all [Robot Framework command line options](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#all-command-line-options) and also following pabot options:
|
|
66
66
|
|
|
67
67
|
--verbose
|
|
68
68
|
more output from the parallel execution
|
|
@@ -83,8 +83,11 @@ Supports all [Robot Framework command line options](https://robotframework.org/r
|
|
|
83
83
|
Special option "all" will use as many processes as there are
|
|
84
84
|
executable suites or tests.
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
PabotLib remote server is started by default to enable locking and resource distribution
|
|
87
|
+
between parallel test executions.
|
|
88
|
+
|
|
89
|
+
--no-pabotlib
|
|
90
|
+
Disable the PabotLib remote server if you don't need locking or resource distribution features.
|
|
88
91
|
|
|
89
92
|
--pabotlibhost [HOSTNAME]
|
|
90
93
|
Host name of the PabotLib remote server (default is 127.0.0.1)
|
|
@@ -142,8 +145,9 @@ Example usages:
|
|
|
142
145
|
pabot --command java -jar robotframework.jar --end-command --include SMOKE tests
|
|
143
146
|
pabot --processes 10 tests
|
|
144
147
|
pabot --pabotlibhost 192.168.1.123 --pabotlibport 8271 --processes 10 tests
|
|
145
|
-
pabot --pabotlib --pabotlibhost 192.168.1.111 --pabotlibport 8272 --processes 10 tests
|
|
146
148
|
pabot --artifacts png,mp4,txt --artifactsinsubfolders directory_to_tests
|
|
149
|
+
# To disable PabotLib:
|
|
150
|
+
pabot --no-pabotlib tests
|
|
147
151
|
|
|
148
152
|
### PabotLib
|
|
149
153
|
|
|
@@ -80,7 +80,7 @@ def _parse_pabot_args(args): # type: (List[str]) -> Tuple[List[str], Dict[str,
|
|
|
80
80
|
"verbose": False,
|
|
81
81
|
"help": False,
|
|
82
82
|
"testlevelsplit": False,
|
|
83
|
-
"pabotlib":
|
|
83
|
+
"pabotlib": True,
|
|
84
84
|
"pabotlibhost": "127.0.0.1",
|
|
85
85
|
"pabotlibport": 8270,
|
|
86
86
|
"processes": _processes_count(),
|
|
@@ -91,108 +91,100 @@ def _parse_pabot_args(args): # type: (List[str]) -> Tuple[List[str], Dict[str,
|
|
|
91
91
|
"shardcount": 1,
|
|
92
92
|
"chunk": False,
|
|
93
93
|
}
|
|
94
|
+
# Explicitly define argument types for validation
|
|
95
|
+
flag_args = {
|
|
96
|
+
"verbose", "help", "testlevelsplit", "pabotlib",
|
|
97
|
+
"artifactsinsubfolders", "chunk"
|
|
98
|
+
}
|
|
99
|
+
value_args = {
|
|
100
|
+
"hive": str,
|
|
101
|
+
"processes": lambda x: int(x) if x != 'all' else None,
|
|
102
|
+
"resourcefile": str,
|
|
103
|
+
"pabotlibhost": str,
|
|
104
|
+
"pabotlibport": int,
|
|
105
|
+
"processtimeout": int,
|
|
106
|
+
"ordering": _parse_ordering,
|
|
107
|
+
"suitesfrom": str,
|
|
108
|
+
"artifacts": lambda x: x.split(","),
|
|
109
|
+
"shard": _parse_shard
|
|
110
|
+
}
|
|
111
|
+
|
|
94
112
|
argumentfiles = []
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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:]
|
|
113
|
+
remaining_args = []
|
|
114
|
+
i = 0
|
|
115
|
+
|
|
116
|
+
# Track conflicting options during parsing
|
|
117
|
+
saw_pabotlib_flag = False
|
|
118
|
+
saw_no_pabotlib = False
|
|
119
|
+
|
|
120
|
+
while i < len(args):
|
|
121
|
+
arg = args[i]
|
|
122
|
+
if not arg.startswith('--'):
|
|
123
|
+
remaining_args.append(arg)
|
|
124
|
+
i += 1
|
|
153
125
|
continue
|
|
154
|
-
|
|
155
|
-
|
|
126
|
+
|
|
127
|
+
arg_name = arg[2:] # Strip '--'
|
|
128
|
+
|
|
129
|
+
if arg_name == "no-pabotlib":
|
|
130
|
+
saw_no_pabotlib = True
|
|
131
|
+
pabot_args["pabotlib"] = False # Just set the main flag
|
|
156
132
|
args = args[1:]
|
|
157
133
|
continue
|
|
158
|
-
if
|
|
159
|
-
|
|
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
|
|
134
|
+
if arg_name == "pabotlib":
|
|
135
|
+
saw_pabotlib_flag = True
|
|
180
136
|
args = args[1:]
|
|
181
137
|
continue
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
138
|
+
|
|
139
|
+
# Special case for command
|
|
140
|
+
if arg_name == "command":
|
|
141
|
+
try:
|
|
142
|
+
end_index = args.index("--end-command", i)
|
|
143
|
+
pabot_args["command"] = args[i+1:end_index]
|
|
144
|
+
i = end_index + 1
|
|
145
|
+
continue
|
|
146
|
+
except ValueError:
|
|
147
|
+
raise DataError("--command requires matching --end-command")
|
|
148
|
+
|
|
149
|
+
# Handle flag arguments
|
|
150
|
+
if arg_name in flag_args:
|
|
151
|
+
pabot_args[arg_name] = True
|
|
152
|
+
i += 1
|
|
153
|
+
continue
|
|
154
|
+
|
|
155
|
+
# Handle value arguments
|
|
156
|
+
if arg_name in value_args:
|
|
157
|
+
if i + 1 >= len(args):
|
|
158
|
+
raise DataError(f"--{arg_name} requires a value")
|
|
159
|
+
try:
|
|
160
|
+
value = value_args[arg_name](args[i + 1])
|
|
161
|
+
if arg_name == "shard":
|
|
162
|
+
pabot_args["shardindex"], pabot_args["shardcount"] = value
|
|
163
|
+
else:
|
|
164
|
+
pabot_args[arg_name] = value
|
|
165
|
+
i += 2
|
|
166
|
+
continue
|
|
167
|
+
except (ValueError, TypeError) as e:
|
|
168
|
+
raise DataError(f"Invalid value for --{arg_name}: {args[i + 1]}")
|
|
169
|
+
|
|
170
|
+
# Handle argument files
|
|
171
|
+
match = ARGSMATCHER.match(arg)
|
|
187
172
|
if match:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
173
|
+
if i + 1 >= len(args):
|
|
174
|
+
raise DataError(f"{arg} requires a value")
|
|
175
|
+
argumentfiles.append((match.group(1), args[i + 1]))
|
|
176
|
+
i += 2
|
|
177
|
+
continue
|
|
178
|
+
|
|
179
|
+
# If we get here, it's a non-pabot argument
|
|
180
|
+
remaining_args.append(arg)
|
|
181
|
+
i += 1
|
|
182
|
+
|
|
183
|
+
if saw_pabotlib_flag and saw_no_pabotlib:
|
|
184
|
+
raise DataError("Cannot use both --pabotlib and --no-pabotlib options together")
|
|
185
|
+
|
|
194
186
|
pabot_args["argumentfiles"] = argumentfiles
|
|
195
|
-
return
|
|
187
|
+
return remaining_args, pabot_args
|
|
196
188
|
|
|
197
189
|
|
|
198
190
|
def _parse_ordering(filename): # type: (str) -> List[ExecutionItem]
|
|
@@ -1152,14 +1152,15 @@ def generate_suite_names_with_builder(outs_dir, datasources, options):
|
|
|
1152
1152
|
settings = RobotSettings(opts)
|
|
1153
1153
|
|
|
1154
1154
|
# Note: first argument (included_suites) is deprecated from RobotFramework 6.1
|
|
1155
|
-
if ROBOT_VERSION
|
|
1155
|
+
if ROBOT_VERSION >= "6.1":
|
|
1156
1156
|
builder = TestSuiteBuilder(
|
|
1157
|
-
settings
|
|
1157
|
+
included_extensions=settings.extension, rpa=settings.rpa, lang=opts.get("language")
|
|
1158
1158
|
)
|
|
1159
1159
|
else:
|
|
1160
1160
|
builder = TestSuiteBuilder(
|
|
1161
|
-
|
|
1162
|
-
)
|
|
1161
|
+
settings["SuiteNames"], settings.extension, rpa=settings.rpa
|
|
1162
|
+
)
|
|
1163
|
+
|
|
1163
1164
|
suite = builder.build(*datasources)
|
|
1164
1165
|
settings.rpa = builder.rpa
|
|
1165
1166
|
suite.configure(**settings.suite_config)
|
|
@@ -1775,7 +1776,7 @@ def _create_execution_items_for_dry_run(
|
|
|
1775
1776
|
chunk_size = (
|
|
1776
1777
|
round(len(items) / processes_count)
|
|
1777
1778
|
if len(items) > processes_count
|
|
1778
|
-
else
|
|
1779
|
+
else 1
|
|
1779
1780
|
)
|
|
1780
1781
|
chunked_items = list(_chunk_items(items, chunk_size))
|
|
1781
1782
|
_NUMBER_OF_ITEMS_TO_BE_EXECUTED += len(chunked_items)
|
|
@@ -2018,17 +2019,17 @@ def _verify_depends(suite_names):
|
|
|
2018
2019
|
)
|
|
2019
2020
|
)
|
|
2020
2021
|
if suites_with_depends != suites_with_found_dependencies:
|
|
2021
|
-
raise
|
|
2022
|
+
raise DataError("Invalid test configuration: Some test suites have dependencies (#DEPENDS) that cannot be found.")
|
|
2022
2023
|
suites_with_circular_dependencies = list(
|
|
2023
2024
|
filter(lambda suite: suite.depends == suite.name, suites_with_depends)
|
|
2024
2025
|
)
|
|
2025
2026
|
if suites_with_circular_dependencies:
|
|
2026
|
-
raise
|
|
2027
|
+
raise DataError("Invalid test configuration: Test suites cannot depend on themselves.")
|
|
2027
2028
|
grouped_suites = list(
|
|
2028
2029
|
filter(lambda suite: isinstance(suite, GroupItem), suite_names)
|
|
2029
2030
|
)
|
|
2030
2031
|
if grouped_suites and suites_with_depends:
|
|
2031
|
-
raise
|
|
2032
|
+
raise DataError("Invalid test configuration: Cannot use both #DEPENDS and grouped suites.")
|
|
2032
2033
|
|
|
2033
2034
|
|
|
2034
2035
|
def _group_by_depend(suite_names):
|
|
@@ -2056,7 +2057,7 @@ def _group_by_depend(suite_names):
|
|
|
2056
2057
|
dependency_tree += [dependent_on_last_stage]
|
|
2057
2058
|
flattened_dependency_tree = sum(dependency_tree, [])
|
|
2058
2059
|
if len(flattened_dependency_tree) != len(runnable_suites):
|
|
2059
|
-
raise
|
|
2060
|
+
raise DataError("Invalid test configuration: Circular or unmet dependencies detected between test suites. Please check your #DEPENDS definitions.")
|
|
2060
2061
|
return dependency_tree
|
|
2061
2062
|
|
|
2062
2063
|
|
{robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/robotframework_pabot.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: robotframework-pabot
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.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
|
|
@@ -17,5 +17,8 @@ Classifier: Development Status :: 5 - Production/Stable
|
|
|
17
17
|
Classifier: Framework :: Robot Framework
|
|
18
18
|
Requires-Python: >=3.6
|
|
19
19
|
License-File: LICENSE.txt
|
|
20
|
+
Requires-Dist: robotframework>=3.2
|
|
21
|
+
Requires-Dist: robotframework-stacktrace>=0.4.1
|
|
22
|
+
Requires-Dist: natsort>=8.2.0
|
|
20
23
|
|
|
21
24
|
A parallel executor for Robot Framework tests. With Pabot you can split one execution into multiple and save test execution time.
|
|
@@ -25,4 +25,15 @@ src/robotframework_pabot.egg-info/SOURCES.txt
|
|
|
25
25
|
src/robotframework_pabot.egg-info/dependency_links.txt
|
|
26
26
|
src/robotframework_pabot.egg-info/entry_points.txt
|
|
27
27
|
src/robotframework_pabot.egg-info/requires.txt
|
|
28
|
-
src/robotframework_pabot.egg-info/top_level.txt
|
|
28
|
+
src/robotframework_pabot.egg-info/top_level.txt
|
|
29
|
+
tests/test_arguments_output.py
|
|
30
|
+
tests/test_depends.py
|
|
31
|
+
tests/test_functional.py
|
|
32
|
+
tests/test_ordering.py
|
|
33
|
+
tests/test_pabot.py
|
|
34
|
+
tests/test_pabotlib.py
|
|
35
|
+
tests/test_pabotsuitenames_io.py
|
|
36
|
+
tests/test_resultmerger.py
|
|
37
|
+
tests/test_stacktrace.py
|
|
38
|
+
tests/test_testlevelsplit_include.py
|
|
39
|
+
tests/test_testlevelsplit_output_task_order.py
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import tempfile
|
|
3
|
+
import textwrap
|
|
4
|
+
import unittest
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PabotArgumentsOutputsTest(unittest.TestCase):
|
|
10
|
+
def setUp(self):
|
|
11
|
+
self.tmpdir = tempfile.mkdtemp()
|
|
12
|
+
|
|
13
|
+
def tearDown(self):
|
|
14
|
+
shutil.rmtree(self.tmpdir)
|
|
15
|
+
|
|
16
|
+
def _run_tests_with(self, testfile, arg1file, arg2file):
|
|
17
|
+
robot_file = open("{}/test.robot".format(self.tmpdir), "w")
|
|
18
|
+
robot_file.write(textwrap.dedent(testfile))
|
|
19
|
+
robot_file.close()
|
|
20
|
+
with open("{}/arg1.txt".format(self.tmpdir), "w") as f:
|
|
21
|
+
f.write(textwrap.dedent(arg1file))
|
|
22
|
+
with open("{}/arg2.txt".format(self.tmpdir), "w") as f:
|
|
23
|
+
f.write(textwrap.dedent(arg2file))
|
|
24
|
+
process = subprocess.Popen(
|
|
25
|
+
[
|
|
26
|
+
sys.executable,
|
|
27
|
+
"-m" "pabot.pabot",
|
|
28
|
+
"--processes",
|
|
29
|
+
"2",
|
|
30
|
+
"--argumentfile1",
|
|
31
|
+
"{}/arg1.txt".format(self.tmpdir),
|
|
32
|
+
"--argumentfile2",
|
|
33
|
+
"{}/arg2.txt".format(self.tmpdir),
|
|
34
|
+
"--outputdir",
|
|
35
|
+
self.tmpdir,
|
|
36
|
+
"--output",
|
|
37
|
+
"test.xml",
|
|
38
|
+
"{}/test.robot".format(self.tmpdir),
|
|
39
|
+
],
|
|
40
|
+
cwd=self.tmpdir,
|
|
41
|
+
stdout=subprocess.PIPE,
|
|
42
|
+
stderr=subprocess.PIPE,
|
|
43
|
+
)
|
|
44
|
+
return process.communicate(), process.returncode
|
|
45
|
+
|
|
46
|
+
def test_argumentfile_outputs(self):
|
|
47
|
+
(stdout, stderr), rc = self._run_tests_with(
|
|
48
|
+
"""
|
|
49
|
+
*** Test Cases ***
|
|
50
|
+
Test 1
|
|
51
|
+
Log ${VALUE}
|
|
52
|
+
Should Be True ${VALUE} == 2
|
|
53
|
+
""",
|
|
54
|
+
"""
|
|
55
|
+
--variable VALUE:1
|
|
56
|
+
""",
|
|
57
|
+
"""
|
|
58
|
+
--variable VALUE:2
|
|
59
|
+
""",
|
|
60
|
+
)
|
|
61
|
+
self.assertEqual(rc, 1)
|
|
62
|
+
if sys.version_info < (3, 0):
|
|
63
|
+
self.assertIn("PASSED", stdout, stderr)
|
|
64
|
+
self.assertIn("failed", stdout, stderr)
|
|
65
|
+
else:
|
|
66
|
+
self.assertIn(b"PASSED", stdout, stderr)
|
|
67
|
+
self.assertIn(b"failed", stdout, stderr)
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import sys
|
|
3
|
+
import textwrap
|
|
4
|
+
import shutil
|
|
5
|
+
import tempfile
|
|
6
|
+
import unittest
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _string_convert(byte_string):
|
|
10
|
+
legacy_python = sys.version_info < (3, 0)
|
|
11
|
+
return byte_string.decode() if legacy_python else byte_string
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DependsTest(unittest.TestCase):
|
|
15
|
+
test_file = """
|
|
16
|
+
*** Settings ***
|
|
17
|
+
Test Template Test1
|
|
18
|
+
*** Test Cases ***
|
|
19
|
+
The Test S1Test 01 1
|
|
20
|
+
The Test S1Test 02 1
|
|
21
|
+
The Test S1Test 03 1
|
|
22
|
+
The Test S1Test 04 1
|
|
23
|
+
The Test S1Test 05 1
|
|
24
|
+
The Test S1Test 06 1
|
|
25
|
+
The Test S1Test 07 1
|
|
26
|
+
The Test S1Test 08 1
|
|
27
|
+
The Test S1Test 09 1
|
|
28
|
+
The Test S1Test 10 1
|
|
29
|
+
The Test S1Test 11 1
|
|
30
|
+
The Test S1Test 12 1
|
|
31
|
+
*** Keywords ***
|
|
32
|
+
Test1
|
|
33
|
+
[Arguments] ${arg}
|
|
34
|
+
Log Test
|
|
35
|
+
"""
|
|
36
|
+
passed = _string_convert(b"PASSED")
|
|
37
|
+
failed = _string_convert(b"FAILED")
|
|
38
|
+
test_01 = _string_convert(b"S1Test 01")
|
|
39
|
+
test_02 = _string_convert(b"S1Test 02")
|
|
40
|
+
test_08 = _string_convert(b"S1Test 08")
|
|
41
|
+
|
|
42
|
+
def setUp(self):
|
|
43
|
+
self.tmpdir = tempfile.mkdtemp()
|
|
44
|
+
|
|
45
|
+
def tearDown(self):
|
|
46
|
+
shutil.rmtree(self.tmpdir)
|
|
47
|
+
|
|
48
|
+
def _run_tests_with(self, testfile, orderfile):
|
|
49
|
+
robot_file = open("{}/test.robot".format(self.tmpdir), "w")
|
|
50
|
+
robot_file.write(textwrap.dedent(testfile))
|
|
51
|
+
robot_file.close()
|
|
52
|
+
with open("{}/order.dat".format(self.tmpdir), "w") as f:
|
|
53
|
+
f.write(textwrap.dedent(orderfile))
|
|
54
|
+
process = subprocess.Popen(
|
|
55
|
+
[
|
|
56
|
+
sys.executable,
|
|
57
|
+
"-m" "pabot.pabot",
|
|
58
|
+
"--testlevelsplit",
|
|
59
|
+
"--ordering",
|
|
60
|
+
"{}/order.dat".format(self.tmpdir),
|
|
61
|
+
"{}/test.robot".format(self.tmpdir),
|
|
62
|
+
],
|
|
63
|
+
cwd=self.tmpdir,
|
|
64
|
+
stdout=subprocess.PIPE,
|
|
65
|
+
stderr=subprocess.PIPE,
|
|
66
|
+
)
|
|
67
|
+
return process.communicate()
|
|
68
|
+
|
|
69
|
+
def test_dependency_ok(self):
|
|
70
|
+
stdout, stderr = self._run_tests_with(
|
|
71
|
+
self.test_file,
|
|
72
|
+
"""
|
|
73
|
+
--test Test.The Test S1Test 01 #DEPENDS Test.The Test S1Test 02
|
|
74
|
+
--test Test.The Test S1Test 02 #DEPENDS Test.The Test S1Test 08
|
|
75
|
+
--test Test.The Test S1Test 08
|
|
76
|
+
""",
|
|
77
|
+
)
|
|
78
|
+
self.assertIn(self.passed, stdout, stderr)
|
|
79
|
+
self.assertNotIn(self.failed, stdout, stderr)
|
|
80
|
+
self.assertEqual(stdout.count(self.passed), 12)
|
|
81
|
+
test_01_index = stdout.find(self.test_01)
|
|
82
|
+
test_02_index = stdout.find(self.test_02)
|
|
83
|
+
test_08_index = stdout.find(self.test_08)
|
|
84
|
+
self.assertNotEqual(test_01_index, -1)
|
|
85
|
+
self.assertNotEqual(test_02_index, -1)
|
|
86
|
+
self.assertNotEqual(test_08_index, -1)
|
|
87
|
+
self.assertTrue(test_08_index < test_02_index)
|
|
88
|
+
self.assertTrue(test_02_index < test_01_index)
|
|
89
|
+
|
|
90
|
+
def test_circular_dependency(self):
|
|
91
|
+
stdout, stderr = self._run_tests_with(
|
|
92
|
+
self.test_file,
|
|
93
|
+
"""
|
|
94
|
+
--test Test.The Test S1Test 01 #DEPENDS Test.The Test S1Test 02
|
|
95
|
+
--test Test.The Test S1Test 02 #DEPENDS Test.The Test S1Test 01
|
|
96
|
+
--test Test.The Test S1Test 08
|
|
97
|
+
""",
|
|
98
|
+
)
|
|
99
|
+
self.assertIn(b"Invalid test configuration: Circular or unmet dependencies detected between test suites", stdout)
|
|
100
|
+
|
|
101
|
+
def test_unmet_dependency(self):
|
|
102
|
+
stdout, stderr = self._run_tests_with(
|
|
103
|
+
self.test_file,
|
|
104
|
+
"""
|
|
105
|
+
--test Test.The Test S1Test 01
|
|
106
|
+
--test Test.The Test S1Test 02 #DEPENDS Test.The Test S1Test 23
|
|
107
|
+
--test Test.The Test S1Test 08
|
|
108
|
+
""",
|
|
109
|
+
)
|
|
110
|
+
self.assertIn(b"Invalid test configuration: Circular or unmet dependencies detected between test suites. Please check your #DEPENDS definitions.", stdout)
|
|
111
|
+
|
|
112
|
+
def test_same_reference(self):
|
|
113
|
+
stdout, stderr = self._run_tests_with(
|
|
114
|
+
self.test_file,
|
|
115
|
+
"""
|
|
116
|
+
--test Test.The Test S1Test 01
|
|
117
|
+
--test Test.The Test S1Test 02 #DEPENDS Test.The Test S1Test 02
|
|
118
|
+
--test Test.The Test S1Test 08
|
|
119
|
+
""",
|
|
120
|
+
)
|
|
121
|
+
self.assertIn(b"Invalid test configuration: Circular or unmet dependencies detected between test suites. Please check your #DEPENDS definitions.", stdout)
|
|
122
|
+
|
|
123
|
+
def test_wait(self):
|
|
124
|
+
stdout, stderr = self._run_tests_with(
|
|
125
|
+
self.test_file,
|
|
126
|
+
"""
|
|
127
|
+
--test Test.The Test S1Test 01
|
|
128
|
+
--test Test.The Test S1Test 02 #DEPENDS Test.The Test S1Test 08
|
|
129
|
+
#WAIT
|
|
130
|
+
--test Test.The Test S1Test 08
|
|
131
|
+
""",
|
|
132
|
+
)
|
|
133
|
+
self.assertIn(b"Invalid test configuration: Circular or unmet dependencies detected between test suites", stdout)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import tempfile
|
|
4
|
+
import textwrap
|
|
5
|
+
import unittest
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
if os.name != "posix":
|
|
11
|
+
raise unittest.SkipTest("Only posix test")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PabotPassJsonUsingVariableOptionTests(unittest.TestCase):
|
|
15
|
+
def setUp(self):
|
|
16
|
+
self.tmpdir = tempfile.mkdtemp()
|
|
17
|
+
robot_file = open("{}/test.robot".format(self.tmpdir), "w")
|
|
18
|
+
robot_file.write(
|
|
19
|
+
textwrap.dedent(
|
|
20
|
+
"""
|
|
21
|
+
*** Test Cases ***
|
|
22
|
+
Test Passing Json With -v option
|
|
23
|
+
Should Be Equal ${custom_var} {"key": "value"}
|
|
24
|
+
"""
|
|
25
|
+
)
|
|
26
|
+
)
|
|
27
|
+
robot_file.close()
|
|
28
|
+
|
|
29
|
+
process = subprocess.Popen(
|
|
30
|
+
[
|
|
31
|
+
sys.executable,
|
|
32
|
+
"-m" "pabot.pabot",
|
|
33
|
+
"-v",
|
|
34
|
+
'custom_var:{"key": "value"}',
|
|
35
|
+
"{}/test.robot".format(self.tmpdir),
|
|
36
|
+
],
|
|
37
|
+
cwd=self.tmpdir,
|
|
38
|
+
stdout=subprocess.PIPE,
|
|
39
|
+
stderr=subprocess.PIPE,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
self.stdout, self.stderr = process.communicate()
|
|
43
|
+
|
|
44
|
+
def test_stdout_should_display_passed_test(self):
|
|
45
|
+
if sys.version_info < (3, 0):
|
|
46
|
+
self.assertIn("PASSED Test", self.stdout, self.stderr)
|
|
47
|
+
else:
|
|
48
|
+
self.assertIn(b"PASSED Test", self.stdout, self.stderr)
|
|
49
|
+
|
|
50
|
+
def tearDown(self):
|
|
51
|
+
shutil.rmtree(self.tmpdir)
|