robotframework-pabot 5.2.0b1__tar.gz → 5.2.0rc2__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-5.2.0b1 → robotframework_pabot-5.2.0rc2}/PKG-INFO +30 -7
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/README.md +29 -6
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/pabot/ProcessManager.py +67 -28
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/pabot/__init__.py +1 -1
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/pabot/arguments.py +19 -1
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/pabot/pabot.py +495 -223
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/pabot/result_merger.py +13 -9
- robotframework_pabot-5.2.0rc2/src/pabot/writer.py +268 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/robotframework_pabot.egg-info/PKG-INFO +30 -7
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/robotframework_pabot.egg-info/SOURCES.txt +2 -3
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_missing_subprocess_output.py +22 -4
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_pabot.py +6 -6
- robotframework_pabot-5.2.0rc2/tests/test_writer_comprehensive.py +723 -0
- robotframework_pabot-5.2.0b1/src/pabot/skip_listener.py +0 -7
- robotframework_pabot-5.2.0b1/src/pabot/timeout_listener.py +0 -5
- robotframework_pabot-5.2.0b1/src/pabot/writer.py +0 -110
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/MANIFEST.in +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/pyproject.toml +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/setup.cfg +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/setup.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/pabot/SharedLibrary.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/pabot/clientwrapper.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/pabot/coordinatorwrapper.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/pabot/execution_items.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/pabot/pabotlib.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/pabot/py3/__init__.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/pabot/py3/client.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/pabot/py3/coordinator.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/pabot/py3/messages.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/pabot/py3/worker.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/pabot/robotremoteserver.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/pabot/workerwrapper.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/robotframework_pabot.egg-info/dependency_links.txt +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/robotframework_pabot.egg-info/entry_points.txt +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/robotframework_pabot.egg-info/requires.txt +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/src/robotframework_pabot.egg-info/top_level.txt +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_arguments_output.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_basic_arguments.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_depends.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_dynamic_ordering.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_functional.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_ordering.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_pabot_process_handling.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_pabot_timeout.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_pabotlib.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_pabotprerunmodifier.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_pabotsuitenames_io.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_prerunmodifier.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_resultmerger.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_robotremoteserver.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_run_empty_suite.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_stacktrace.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_suite_structure.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_testlevelsplit_include.py +0 -0
- {robotframework_pabot-5.2.0b1 → robotframework_pabot-5.2.0rc2}/tests/test_testlevelsplit_output_task_order.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: robotframework-pabot
|
|
3
|
-
Version: 5.2.
|
|
3
|
+
Version: 5.2.0rc2
|
|
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
|
|
@@ -118,10 +118,11 @@ pabot [--verbose|--testlevelsplit|--command .. --end-command|
|
|
|
118
118
|
--shard i/n|
|
|
119
119
|
--artifacts extensions|--artifactsinsubfolders|
|
|
120
120
|
--resourcefile file|--argumentfile[num] file|--suitesfrom file
|
|
121
|
-
--ordering
|
|
121
|
+
--ordering file [static|dynamic] [skip|run_all]|
|
|
122
122
|
--chunk|
|
|
123
123
|
--pabotprerunmodifier modifier|
|
|
124
124
|
--no-rebot|
|
|
125
|
+
--pabotconsole [verbose|dotted|quiet|none]|
|
|
125
126
|
--help|--version]
|
|
126
127
|
[robot options] [path ...]
|
|
127
128
|
```
|
|
@@ -193,7 +194,7 @@ Supports all [Robot Framework command line options](https://robotframework.org/r
|
|
|
193
194
|
|
|
194
195
|
**--resourcefile [FILEPATH]**
|
|
195
196
|
Indicator for a file that can contain shared variables for distributing resources. This needs to be used together with
|
|
196
|
-
pabotlib option. Resource file syntax is same as Windows ini files
|
|
197
|
+
pabotlib option. Resource file syntax is same as Windows ini files where a section is a shared set of variables.
|
|
197
198
|
|
|
198
199
|
**--argumentfile[INTEGER] [FILEPATH]**
|
|
199
200
|
Run same suites with multiple [argumentfile](http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#argument-files) options.
|
|
@@ -206,10 +207,10 @@ Supports all [Robot Framework command line options](https://robotframework.org/r
|
|
|
206
207
|
Optionally read suites from output.xml file. Failed suites will run first and longer running ones will be executed
|
|
207
208
|
before shorter ones.
|
|
208
209
|
|
|
209
|
-
**--ordering [
|
|
210
|
+
**--ordering [FILEPATH] [MODE] [FAILURE POLICY]**
|
|
210
211
|
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)]
|
|
212
|
+
- MODE (optional): [ static (default) | dynamic ]
|
|
213
|
+
- FAILURE POLICY (optional, only in dynamic mode): [ skip | run_all (default) ]
|
|
213
214
|
|
|
214
215
|
**--chunk**
|
|
215
216
|
Optionally chunk tests to PROCESSES number of robot runs. This can save time because all the suites will share the same
|
|
@@ -226,6 +227,28 @@ Supports all [Robot Framework command line options](https://robotframework.org/r
|
|
|
226
227
|
for scenarios where Rebot should be run later due to large log files, ensuring better memory and resource availability.
|
|
227
228
|
Subprocess results are stored in the pabot_results folder.
|
|
228
229
|
|
|
230
|
+
**--pabotconsole [MODE]**
|
|
231
|
+
The --pabotconsole option controls how much output is printed to the console.
|
|
232
|
+
Note that all Pabot’s own messages are always logged to pabot_manager.log, regardless of the selected console mode.
|
|
233
|
+
|
|
234
|
+
The available options are:
|
|
235
|
+
- verbose (default):
|
|
236
|
+
Prints all messages to the console, corresponding closely to what is written to the log file.
|
|
237
|
+
- dotted:
|
|
238
|
+
Prints important messages, warnings, and errors to the console, along with execution progress using the following notation:
|
|
239
|
+
|
|
240
|
+
- PASS = .
|
|
241
|
+
- FAIL = F
|
|
242
|
+
- SKIP = s
|
|
243
|
+
|
|
244
|
+
Note that each Robot Framework process is represented by a single character.
|
|
245
|
+
Depending on the execution parameters, individual tests may not have their own status character;
|
|
246
|
+
instead, the status may represent an entire suite or a group of tests.
|
|
247
|
+
- quiet:
|
|
248
|
+
Similar to dotted, but suppresses execution progress output.
|
|
249
|
+
- none:
|
|
250
|
+
Produces no console output at all.
|
|
251
|
+
|
|
229
252
|
**--help**
|
|
230
253
|
Print usage instructions.
|
|
231
254
|
|
|
@@ -491,7 +514,7 @@ pabot_results/
|
|
|
491
514
|
│ ├── robot_stdout.out
|
|
492
515
|
│ ├── robot_stderr.out
|
|
493
516
|
│ └── artifacts...
|
|
494
|
-
pabot_manager.log
|
|
517
|
+
└── pabot_manager.log # Pabot's own main log.
|
|
495
518
|
```
|
|
496
519
|
|
|
497
520
|
Each `PABOTQUEUEINDEX` folder contains as default:
|
|
@@ -95,10 +95,11 @@ pabot [--verbose|--testlevelsplit|--command .. --end-command|
|
|
|
95
95
|
--shard i/n|
|
|
96
96
|
--artifacts extensions|--artifactsinsubfolders|
|
|
97
97
|
--resourcefile file|--argumentfile[num] file|--suitesfrom file
|
|
98
|
-
--ordering
|
|
98
|
+
--ordering file [static|dynamic] [skip|run_all]|
|
|
99
99
|
--chunk|
|
|
100
100
|
--pabotprerunmodifier modifier|
|
|
101
101
|
--no-rebot|
|
|
102
|
+
--pabotconsole [verbose|dotted|quiet|none]|
|
|
102
103
|
--help|--version]
|
|
103
104
|
[robot options] [path ...]
|
|
104
105
|
```
|
|
@@ -170,7 +171,7 @@ Supports all [Robot Framework command line options](https://robotframework.org/r
|
|
|
170
171
|
|
|
171
172
|
**--resourcefile [FILEPATH]**
|
|
172
173
|
Indicator for a file that can contain shared variables for distributing resources. This needs to be used together with
|
|
173
|
-
pabotlib option. Resource file syntax is same as Windows ini files
|
|
174
|
+
pabotlib option. Resource file syntax is same as Windows ini files where a section is a shared set of variables.
|
|
174
175
|
|
|
175
176
|
**--argumentfile[INTEGER] [FILEPATH]**
|
|
176
177
|
Run same suites with multiple [argumentfile](http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#argument-files) options.
|
|
@@ -183,10 +184,10 @@ Supports all [Robot Framework command line options](https://robotframework.org/r
|
|
|
183
184
|
Optionally read suites from output.xml file. Failed suites will run first and longer running ones will be executed
|
|
184
185
|
before shorter ones.
|
|
185
186
|
|
|
186
|
-
**--ordering [
|
|
187
|
+
**--ordering [FILEPATH] [MODE] [FAILURE POLICY]**
|
|
187
188
|
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)
|
|
188
|
-
- MODE (optional): [static (default)|dynamic]
|
|
189
|
-
- FAILURE POLICY (optional, only in dynamic mode): [skip|run_all (default)]
|
|
189
|
+
- MODE (optional): [ static (default) | dynamic ]
|
|
190
|
+
- FAILURE POLICY (optional, only in dynamic mode): [ skip | run_all (default) ]
|
|
190
191
|
|
|
191
192
|
**--chunk**
|
|
192
193
|
Optionally chunk tests to PROCESSES number of robot runs. This can save time because all the suites will share the same
|
|
@@ -203,6 +204,28 @@ Supports all [Robot Framework command line options](https://robotframework.org/r
|
|
|
203
204
|
for scenarios where Rebot should be run later due to large log files, ensuring better memory and resource availability.
|
|
204
205
|
Subprocess results are stored in the pabot_results folder.
|
|
205
206
|
|
|
207
|
+
**--pabotconsole [MODE]**
|
|
208
|
+
The --pabotconsole option controls how much output is printed to the console.
|
|
209
|
+
Note that all Pabot’s own messages are always logged to pabot_manager.log, regardless of the selected console mode.
|
|
210
|
+
|
|
211
|
+
The available options are:
|
|
212
|
+
- verbose (default):
|
|
213
|
+
Prints all messages to the console, corresponding closely to what is written to the log file.
|
|
214
|
+
- dotted:
|
|
215
|
+
Prints important messages, warnings, and errors to the console, along with execution progress using the following notation:
|
|
216
|
+
|
|
217
|
+
- PASS = .
|
|
218
|
+
- FAIL = F
|
|
219
|
+
- SKIP = s
|
|
220
|
+
|
|
221
|
+
Note that each Robot Framework process is represented by a single character.
|
|
222
|
+
Depending on the execution parameters, individual tests may not have their own status character;
|
|
223
|
+
instead, the status may represent an entire suite or a group of tests.
|
|
224
|
+
- quiet:
|
|
225
|
+
Similar to dotted, but suppresses execution progress output.
|
|
226
|
+
- none:
|
|
227
|
+
Produces no console output at all.
|
|
228
|
+
|
|
206
229
|
**--help**
|
|
207
230
|
Print usage instructions.
|
|
208
231
|
|
|
@@ -468,7 +491,7 @@ pabot_results/
|
|
|
468
491
|
│ ├── robot_stdout.out
|
|
469
492
|
│ ├── robot_stderr.out
|
|
470
493
|
│ └── artifacts...
|
|
471
|
-
pabot_manager.log
|
|
494
|
+
└── pabot_manager.log # Pabot's own main log.
|
|
472
495
|
```
|
|
473
496
|
|
|
474
497
|
Each `PABOTQUEUEINDEX` folder contains as default:
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import sys
|
|
3
3
|
import time
|
|
4
|
-
import signal
|
|
5
4
|
import threading
|
|
6
5
|
import subprocess
|
|
7
6
|
import datetime
|
|
8
7
|
import queue
|
|
9
8
|
import locale
|
|
9
|
+
import signal
|
|
10
10
|
|
|
11
11
|
try:
|
|
12
12
|
import psutil
|
|
@@ -28,26 +28,14 @@ class ProcessManager:
|
|
|
28
28
|
self.processes = []
|
|
29
29
|
self.lock = threading.Lock()
|
|
30
30
|
self.writer = get_writer()
|
|
31
|
+
self.interrupted = False
|
|
32
|
+
# Note: Signal handling is done in pabot.py's main_program() to ensure
|
|
33
|
+
# PabotLib is shut down gracefully before process termination
|
|
34
|
+
# This ProcessManager will check the interrupted flag set by pabot.py's keyboard_interrupt()
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
signal.signal(signal.SIGINT, self._handle_sigint)
|
|
36
|
-
else:
|
|
37
|
-
self.writer.write(
|
|
38
|
-
"[ProcessManager] (test mode) signal handlers disabled (not in main thread)"
|
|
39
|
-
)
|
|
40
|
-
except Exception as e:
|
|
41
|
-
self.writer.write(f"[WARN] Could not register signal handler: {e}")
|
|
42
|
-
|
|
43
|
-
# -------------------------------
|
|
44
|
-
# SIGNAL HANDLING
|
|
45
|
-
# -------------------------------
|
|
46
|
-
|
|
47
|
-
def _handle_sigint(self, signum, frame):
|
|
48
|
-
self.writer.write("[ProcessManager] Ctrl+C detected — terminating all subprocesses", color=Color.RED)
|
|
49
|
-
self.terminate_all()
|
|
50
|
-
sys.exit(130)
|
|
36
|
+
def set_interrupted(self):
|
|
37
|
+
"""Called by pabot.py when CTRL+C is received."""
|
|
38
|
+
self.interrupted = True
|
|
51
39
|
|
|
52
40
|
# -------------------------------
|
|
53
41
|
# OUTPUT STREAM READERS
|
|
@@ -213,7 +201,7 @@ class ProcessManager:
|
|
|
213
201
|
|
|
214
202
|
self.writer.write(
|
|
215
203
|
f"[ProcessManager] Terminating process tree PID={process.pid}",
|
|
216
|
-
|
|
204
|
+
level='debug'
|
|
217
205
|
)
|
|
218
206
|
|
|
219
207
|
# PRIMARY: psutil (best reliability)
|
|
@@ -304,11 +292,13 @@ class ProcessManager:
|
|
|
304
292
|
if verbose:
|
|
305
293
|
self.writer.write(
|
|
306
294
|
f"{ts} [PID:{process.pid}] [{pool_id}] [ID:{item_index}] "
|
|
307
|
-
f"EXECUTING PARALLEL {item_name}:\n{' '.join(cmd)}"
|
|
295
|
+
f"EXECUTING PARALLEL {item_name}:\n{' '.join(cmd)}",
|
|
296
|
+
level='debug'
|
|
308
297
|
)
|
|
309
298
|
else:
|
|
310
299
|
self.writer.write(
|
|
311
|
-
f"{ts} [PID:{process.pid}] [{pool_id}] [ID:{item_index}] EXECUTING {item_name}"
|
|
300
|
+
f"{ts} [PID:{process.pid}] [{pool_id}] [ID:{item_index}] EXECUTING {item_name}",
|
|
301
|
+
level='debug'
|
|
312
302
|
)
|
|
313
303
|
|
|
314
304
|
# Start logging thread
|
|
@@ -327,28 +317,76 @@ class ProcessManager:
|
|
|
327
317
|
while rc is None:
|
|
328
318
|
rc = process.poll()
|
|
329
319
|
|
|
320
|
+
# INTERRUPT CHECK - terminate process gracefully when CTRL+C is pressed
|
|
321
|
+
if self.interrupted:
|
|
322
|
+
ts = datetime.datetime.now()
|
|
323
|
+
self.writer.write(
|
|
324
|
+
f"{ts} [PID:{process.pid}] [{pool_id}] [ID:{item_index}] "
|
|
325
|
+
f"Process {item_name} interrupted by user (Ctrl+C)",
|
|
326
|
+
color=Color.YELLOW, level='warning'
|
|
327
|
+
)
|
|
328
|
+
self._terminate_tree(process)
|
|
329
|
+
rc = -1
|
|
330
|
+
|
|
331
|
+
# Dryrun process to mark all tests as failed due to user interrupt
|
|
332
|
+
this_dir = os.path.dirname(os.path.abspath(__file__))
|
|
333
|
+
listener_path = os.path.join(this_dir, "listener", "interrupt_listener.py")
|
|
334
|
+
dry_run_env = env.copy() if env else os.environ.copy()
|
|
335
|
+
before, after = split_on_first(cmd, "-A")
|
|
336
|
+
dryrun_cmd = before + ["--dryrun", '--listener', listener_path, '-A'] + after
|
|
337
|
+
|
|
338
|
+
self.writer.write(
|
|
339
|
+
f"{ts} [PID:{process.pid}] [{pool_id}] [ID:{item_index}] "
|
|
340
|
+
f"Starting dry run to mark test as failed due to user interrupt: {' '.join(dryrun_cmd)}",
|
|
341
|
+
level='debug'
|
|
342
|
+
)
|
|
343
|
+
try:
|
|
344
|
+
subprocess.run(
|
|
345
|
+
dryrun_cmd,
|
|
346
|
+
env=dry_run_env,
|
|
347
|
+
stdout=subprocess.PIPE,
|
|
348
|
+
stderr=subprocess.PIPE,
|
|
349
|
+
timeout=3,
|
|
350
|
+
text=True,
|
|
351
|
+
)
|
|
352
|
+
except subprocess.TimeoutExpired as e:
|
|
353
|
+
self.writer.write(f"Dry-run timed out after 3s: {e}", level='debug')
|
|
354
|
+
break
|
|
355
|
+
|
|
330
356
|
# TIMEOUT CHECK
|
|
331
357
|
if timeout and (time.time() - start > timeout):
|
|
332
358
|
ts = datetime.datetime.now()
|
|
333
359
|
self.writer.write(
|
|
334
360
|
f"{ts} [PID:{process.pid}] [{pool_id}] [ID:{item_index}] "
|
|
335
|
-
f"Process {item_name} killed due to exceeding the maximum timeout of {timeout} seconds"
|
|
361
|
+
f"Process {item_name} killed due to exceeding the maximum timeout of {timeout} seconds",
|
|
362
|
+
color=Color.YELLOW, level='warning'
|
|
336
363
|
)
|
|
337
364
|
self._terminate_tree(process)
|
|
338
365
|
rc = -1
|
|
339
366
|
|
|
340
367
|
# Dryrun process to mark all tests as failed due to timeout
|
|
341
368
|
this_dir = os.path.dirname(os.path.abspath(__file__))
|
|
342
|
-
listener_path = os.path.join(this_dir, "timeout_listener.py")
|
|
369
|
+
listener_path = os.path.join(this_dir, "listener", "timeout_listener.py")
|
|
343
370
|
dry_run_env = env.copy() if env else os.environ.copy()
|
|
344
371
|
before, after = split_on_first(cmd, "-A")
|
|
345
372
|
dryrun_cmd = before + ["--dryrun", '--listener', listener_path, '-A'] + after
|
|
346
373
|
|
|
347
374
|
self.writer.write(
|
|
348
375
|
f"{ts} [PID:{process.pid}] [{pool_id}] [ID:{item_index}] "
|
|
349
|
-
f"Starting dry run to mark
|
|
376
|
+
f"Starting dry run to mark test as failed due to timeout: {' '.join(dryrun_cmd)}",
|
|
377
|
+
level='debug'
|
|
350
378
|
)
|
|
351
|
-
|
|
379
|
+
try:
|
|
380
|
+
subprocess.run(
|
|
381
|
+
dryrun_cmd,
|
|
382
|
+
env=dry_run_env,
|
|
383
|
+
stdout=subprocess.PIPE,
|
|
384
|
+
stderr=subprocess.PIPE,
|
|
385
|
+
timeout=3,
|
|
386
|
+
text=True,
|
|
387
|
+
)
|
|
388
|
+
except subprocess.TimeoutExpired as e:
|
|
389
|
+
self.writer.write(f"Dry-run timed out after 3s: {e}", level='debug')
|
|
352
390
|
|
|
353
391
|
break
|
|
354
392
|
|
|
@@ -357,7 +395,8 @@ class ProcessManager:
|
|
|
357
395
|
ts = datetime.datetime.now()
|
|
358
396
|
self.writer.write(
|
|
359
397
|
f"{ts} [PID:{process.pid}] [{pool_id}] [ID:{item_index}] still running "
|
|
360
|
-
f"{item_name} after {(counter * 0.1):.1f}s"
|
|
398
|
+
f"{item_name} after {(counter * 0.1):.1f}s",
|
|
399
|
+
level='debug'
|
|
361
400
|
)
|
|
362
401
|
ping_interval += 50
|
|
363
402
|
next_ping += ping_interval
|
|
@@ -174,6 +174,7 @@ def _parse_pabot_args(args): # type: (List[str]) -> Tuple[List[str], Dict[str,
|
|
|
174
174
|
"shardcount": 1,
|
|
175
175
|
"chunk": False,
|
|
176
176
|
"no-rebot": False,
|
|
177
|
+
"pabotconsole": "verbose",
|
|
177
178
|
}
|
|
178
179
|
|
|
179
180
|
# Arguments that are flags (boolean)
|
|
@@ -200,6 +201,7 @@ def _parse_pabot_args(args): # type: (List[str]) -> Tuple[List[str], Dict[str,
|
|
|
200
201
|
"suitesfrom": str,
|
|
201
202
|
"artifacts": _parse_artifacts,
|
|
202
203
|
"shard": _parse_shard,
|
|
204
|
+
"pabotconsole": str,
|
|
203
205
|
}
|
|
204
206
|
|
|
205
207
|
argumentfiles = []
|
|
@@ -234,7 +236,12 @@ def _parse_pabot_args(args): # type: (List[str]) -> Tuple[List[str], Dict[str,
|
|
|
234
236
|
if arg_name == "command":
|
|
235
237
|
try:
|
|
236
238
|
end_index = args.index("--end-command", i)
|
|
237
|
-
pabot_args["
|
|
239
|
+
pabot_args["use_user_command"] = True
|
|
240
|
+
cmd_lines = args[i + 1 : end_index]
|
|
241
|
+
cmd = []
|
|
242
|
+
for line in cmd_lines:
|
|
243
|
+
cmd.extend(line.split())
|
|
244
|
+
pabot_args["command"] = cmd
|
|
238
245
|
i = end_index + 1
|
|
239
246
|
continue
|
|
240
247
|
except ValueError:
|
|
@@ -284,6 +291,17 @@ def _parse_pabot_args(args): # type: (List[str]) -> Tuple[List[str], Dict[str,
|
|
|
284
291
|
# move index past ordering args only
|
|
285
292
|
i += 2 + i_mode_offset + i_failure_offset
|
|
286
293
|
continue
|
|
294
|
+
elif arg_name == "pabotconsole":
|
|
295
|
+
console_type = args[i + 1]
|
|
296
|
+
valid_types = ("verbose", "dotted", "quiet", "none")
|
|
297
|
+
if console_type not in valid_types:
|
|
298
|
+
raise DataError(
|
|
299
|
+
f"Invalid value for --pabotconsole: {console_type}. "
|
|
300
|
+
f"Valid values are: {', '.join(valid_types)}"
|
|
301
|
+
)
|
|
302
|
+
pabot_args["pabotconsole"] = console_type
|
|
303
|
+
i += 2
|
|
304
|
+
continue
|
|
287
305
|
else:
|
|
288
306
|
value = value_args[arg_name](args[i + 1])
|
|
289
307
|
if arg_name == "shard":
|