robotframework-pabot 4.3.2__tar.gz → 5.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.
Files changed (46) hide show
  1. {robotframework_pabot-4.3.2/src/robotframework_pabot.egg-info → robotframework_pabot-5.0.0}/PKG-INFO +98 -5
  2. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/README.md +97 -4
  3. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/pabot/__init__.py +1 -1
  4. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/pabot/execution_items.py +67 -31
  5. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/pabot/pabot.py +174 -82
  6. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/pabot/result_merger.py +19 -5
  7. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0/src/robotframework_pabot.egg-info}/PKG-INFO +98 -5
  8. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/tests/test_depends.py +136 -3
  9. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/tests/test_ordering.py +90 -15
  10. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/tests/test_pabot.py +29 -24
  11. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/tests/test_resultmerger.py +19 -5
  12. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/LICENSE.txt +0 -0
  13. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/MANIFEST.in +0 -0
  14. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/pyproject.toml +0 -0
  15. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/setup.cfg +0 -0
  16. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/setup.py +0 -0
  17. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/pabot/SharedLibrary.py +0 -0
  18. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/pabot/arguments.py +0 -0
  19. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/pabot/clientwrapper.py +0 -0
  20. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/pabot/coordinatorwrapper.py +0 -0
  21. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/pabot/pabotlib.py +0 -0
  22. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/pabot/py3/__init__.py +0 -0
  23. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/pabot/py3/client.py +0 -0
  24. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/pabot/py3/coordinator.py +0 -0
  25. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/pabot/py3/messages.py +0 -0
  26. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/pabot/py3/worker.py +0 -0
  27. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/pabot/robotremoteserver.py +0 -0
  28. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/pabot/workerwrapper.py +0 -0
  29. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/robotframework_pabot.egg-info/SOURCES.txt +0 -0
  30. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/robotframework_pabot.egg-info/dependency_links.txt +0 -0
  31. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/robotframework_pabot.egg-info/entry_points.txt +0 -0
  32. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/robotframework_pabot.egg-info/requires.txt +0 -0
  33. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/src/robotframework_pabot.egg-info/top_level.txt +0 -0
  34. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/tests/test_arguments_output.py +0 -0
  35. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/tests/test_basic_arguments.py +0 -0
  36. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/tests/test_functional.py +0 -0
  37. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/tests/test_missing_subprocess_output.py +0 -0
  38. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/tests/test_pabotlib.py +0 -0
  39. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/tests/test_pabotprerunmodifier.py +0 -0
  40. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/tests/test_pabotsuitenames_io.py +0 -0
  41. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/tests/test_prerunmodifier.py +0 -0
  42. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/tests/test_run_empty_suite.py +0 -0
  43. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/tests/test_stacktrace.py +0 -0
  44. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/tests/test_suite_structure.py +0 -0
  45. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/tests/test_testlevelsplit_include.py +0 -0
  46. {robotframework_pabot-4.3.2 → robotframework_pabot-5.0.0}/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: 4.3.2
3
+ Version: 5.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
@@ -39,6 +39,22 @@ A parallel executor for [Robot Framework](http://www.robotframework.org) tests.
39
39
 
40
40
  [![Pabot presentation at robocon.io 2018](http://img.youtube.com/vi/i0RV6SJSIn8/0.jpg)](https://youtu.be/i0RV6SJSIn8 "Pabot presentation at robocon.io 2018")
41
41
 
42
+ ## Table of Contents
43
+
44
+ - [Installation](#installation)
45
+ - [Basic use](#basic-use)
46
+ - [Contact](#contact)
47
+ - [Contributing](#contributing-to-the-project)
48
+ - [Command-line options](#command-line-options)
49
+ - [PabotLib](#pabotlib)
50
+ - [Controlling execution order](#controlling-execution-order-and-level-of-parallelism)
51
+ - [Programmatic use](#programmatic-use)
52
+ - [Global variables](#global-variables)
53
+ - [Output Files Generated by Pabot](#output-files-generated-by-pabot)
54
+ - [Artifacts Handling and Parallel Execution Notes](#artifacts-handling-and-parallel-execution-notes)
55
+
56
+ ----
57
+
42
58
  ## Installation:
43
59
 
44
60
  From PyPi:
@@ -271,11 +287,24 @@ Note: The `--ordering` file is intended only for defining the execution order of
271
287
  There different possibilities to influence the execution:
272
288
 
273
289
  * The order of suites can be changed.
274
- * If a directory (or a directory structure) should be executed sequentially, add the directory suite name to a row as a ```--suite``` option.
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.
290
+ * If a directory (or a directory structure) should be executed sequentially, add the directory suite name to a row as a ```--suite``` option. This usage is also supported when `--testlevelsplit` is enabled. As an alternative to using `--suite` options, you can also group tests into sequential batches using `{}` braces. (See below for details.) Note that if multiple `--suite` options are used, they must not reference the same test case. This means you cannot specify both parent and child suite names at the same time. For instance:
291
+
292
+ ```
293
+ --suite Top Suite.Sub Suite
294
+ --suite Top Suite
295
+ ```
296
+
297
+ * If the base suite name is changing with robot option [```--name / -N```](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#setting-the-name) you can use either the new or old full test path. For example:
298
+
299
+ ```
300
+ --test New Suite Name.Sub Suite.Test 1
301
+ OR
302
+ --test Old Suite Name.Sub Suite.Test 1
303
+ ```
304
+
276
305
  * You can add a line with text `#WAIT` to force executor to wait until all previous suites have been executed.
277
306
  * 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.
307
+ * You can introduce dependencies using the word `#DEPENDS` after a test declaration. This keyword can be used several times if it is necessary to refer to several different tests. The ordering algorithm is designed to preserve the exact user-defined order as closely as possible. However, if a test's execution dependencies are not yet satisfied, the test is postponed and moved to the earliest possible stage where all its dependencies are fulfilled. 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 `{}` braces.
279
308
  * 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
309
  * An example could be:
281
310
 
@@ -369,4 +398,68 @@ Pabot will insert following global variables to Robot Framework namespace. These
369
398
  PABOTEXECUTIONPOOLID - this contains the pool id (an integer) for the current Robot Framework executor. This is helpful for example when visualizing the execution flow from your own listener.
370
399
  PABOTNUMBEROFPROCESSES - max number of concurrent processes that pabot may use in execution.
371
400
  CALLER_ID - a universally unique identifier for this execution.
372
-
401
+
402
+
403
+ ### Output Files Generated by Pabot
404
+
405
+ Pabot generates several output files and folders during execution, both for internal use and for analysis purposes.
406
+
407
+ #### Internal File: `.pabotsuitenames`
408
+
409
+ Pabot creates a `.pabotsuitenames` file in the working directory. This is an internal hash file used to speed up execution in certain scenarios.
410
+ This file can also be used as a base for the `--ordering` file as described earlier. Although technically it can be modified, it will be overwritten during the next execution.
411
+ Therefore, it is **recommended** to maintain a separate file for the `--ordering` option if needed.
412
+
413
+ #### Output Directory Structure
414
+
415
+ In addition to the standard `log.html`, `report.html`, and `output.xml` files, the specified `--outputdir` will contain:
416
+
417
+ - A folder named `pabot_results`, and
418
+ - All defined artifacts (default: `.png` files)
419
+ - Optionally, artifacts from subfolders if `--artifactsinsubfolders` is used
420
+
421
+ Artifacts are **copied** into the output directory and renamed with the following structure:
422
+
423
+ ```
424
+ TIMESTAMP-ARGUMENT_INDEX-PABOTQUEUEINDEX
425
+ ```
426
+
427
+ - **TIMESTAMP** = Time of `pabot` command invocation (not the screenshot's actual timestamp), format: `YYYYmmdd_HHMMSS`
428
+ - **ARGUMENT_INDEX** = Optional index number, only used if `--argumentfileN` options are given
429
+ - **PABOTQUEUEINDEX** = Process queue index (see section [Global Variables](#global-variables))
430
+
431
+ #### `pabot_results` Folder Structure
432
+
433
+ The structure of the `pabot_results` folder is as follows:
434
+
435
+ ```
436
+ pabot_results/
437
+ ├── [N]/ # Optional: N = argument file index (if --argumentfileN is used)
438
+ │ └── PABOTQUEUEINDEX/ # One per subprocess
439
+ │ ├── output.xml
440
+ │ ├── robot_argfile.txt
441
+ │ ├── robot_stdout.out
442
+ │ ├── robot_stderr.out
443
+ │ └── artifacts...
444
+ ```
445
+
446
+ Each `PABOTQUEUEINDEX` folder contains as default:
447
+
448
+ - `robot_argfile.txt` – Arguments used in that subprocess
449
+ - `robot_stdout.out` and `robot_stderr.out` – Stdout and stderr of the subprocess
450
+ - `output.xml` – The partial output file to be merged later
451
+ - Artifacts – Screenshots or other files copied from subprocess folders
452
+
453
+ > **Note:** The entire `pabot_results` folder is considered temporary and will be **deleted/overwritten** on the next `pabot` run using the same `--outputdir`.
454
+
455
+
456
+ ### Artifacts Handling and Parallel Execution Notes
457
+
458
+ Due to parallel execution, artifacts like screenshots should ideally be:
459
+
460
+ - Embedded directly into the XML using tools like [SeleniumLibrary](https://robotframework.org/SeleniumLibrary/SeleniumLibrary.html#Set%20Screenshot%20Directory) with the `EMBED` option
461
+ _Example:_
462
+ `Library SeleniumLibrary screenshot_root_directory=EMBED`
463
+ - Or saved to the subprocess’s working directory (usually default behavior), ensuring separation across processes
464
+
465
+ If you manually specify a shared screenshot directory in your test code, **all processes will write to it concurrently**, which may cause issues such as overwriting or missing files if screenshots are taken simultaneously.
@@ -13,6 +13,22 @@ A parallel executor for [Robot Framework](http://www.robotframework.org) tests.
13
13
 
14
14
  [![Pabot presentation at robocon.io 2018](http://img.youtube.com/vi/i0RV6SJSIn8/0.jpg)](https://youtu.be/i0RV6SJSIn8 "Pabot presentation at robocon.io 2018")
15
15
 
16
+ ## Table of Contents
17
+
18
+ - [Installation](#installation)
19
+ - [Basic use](#basic-use)
20
+ - [Contact](#contact)
21
+ - [Contributing](#contributing-to-the-project)
22
+ - [Command-line options](#command-line-options)
23
+ - [PabotLib](#pabotlib)
24
+ - [Controlling execution order](#controlling-execution-order-and-level-of-parallelism)
25
+ - [Programmatic use](#programmatic-use)
26
+ - [Global variables](#global-variables)
27
+ - [Output Files Generated by Pabot](#output-files-generated-by-pabot)
28
+ - [Artifacts Handling and Parallel Execution Notes](#artifacts-handling-and-parallel-execution-notes)
29
+
30
+ ----
31
+
16
32
  ## Installation:
17
33
 
18
34
  From PyPi:
@@ -245,11 +261,24 @@ Note: The `--ordering` file is intended only for defining the execution order of
245
261
  There different possibilities to influence the execution:
246
262
 
247
263
  * The order of suites can be changed.
248
- * If a directory (or a directory structure) should be executed sequentially, add the directory suite name to a row as a ```--suite``` option.
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.
264
+ * If a directory (or a directory structure) should be executed sequentially, add the directory suite name to a row as a ```--suite``` option. This usage is also supported when `--testlevelsplit` is enabled. As an alternative to using `--suite` options, you can also group tests into sequential batches using `{}` braces. (See below for details.) Note that if multiple `--suite` options are used, they must not reference the same test case. This means you cannot specify both parent and child suite names at the same time. For instance:
265
+
266
+ ```
267
+ --suite Top Suite.Sub Suite
268
+ --suite Top Suite
269
+ ```
270
+
271
+ * If the base suite name is changing with robot option [```--name / -N```](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#setting-the-name) you can use either the new or old full test path. For example:
272
+
273
+ ```
274
+ --test New Suite Name.Sub Suite.Test 1
275
+ OR
276
+ --test Old Suite Name.Sub Suite.Test 1
277
+ ```
278
+
250
279
  * You can add a line with text `#WAIT` to force executor to wait until all previous suites have been executed.
251
280
  * 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.
281
+ * 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. The ordering algorithm is designed to preserve the exact user-defined order as closely as possible. However, if a test's execution dependencies are not yet satisfied, the test is postponed and moved to the earliest possible stage where all its dependencies are fulfilled. 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 `{}` braces.
253
282
  * 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
283
  * An example could be:
255
284
 
@@ -343,4 +372,68 @@ Pabot will insert following global variables to Robot Framework namespace. These
343
372
  PABOTEXECUTIONPOOLID - this contains the pool id (an integer) for the current Robot Framework executor. This is helpful for example when visualizing the execution flow from your own listener.
344
373
  PABOTNUMBEROFPROCESSES - max number of concurrent processes that pabot may use in execution.
345
374
  CALLER_ID - a universally unique identifier for this execution.
346
-
375
+
376
+
377
+ ### Output Files Generated by Pabot
378
+
379
+ Pabot generates several output files and folders during execution, both for internal use and for analysis purposes.
380
+
381
+ #### Internal File: `.pabotsuitenames`
382
+
383
+ Pabot creates a `.pabotsuitenames` file in the working directory. This is an internal hash file used to speed up execution in certain scenarios.
384
+ This file can also be used as a base for the `--ordering` file as described earlier. Although technically it can be modified, it will be overwritten during the next execution.
385
+ Therefore, it is **recommended** to maintain a separate file for the `--ordering` option if needed.
386
+
387
+ #### Output Directory Structure
388
+
389
+ In addition to the standard `log.html`, `report.html`, and `output.xml` files, the specified `--outputdir` will contain:
390
+
391
+ - A folder named `pabot_results`, and
392
+ - All defined artifacts (default: `.png` files)
393
+ - Optionally, artifacts from subfolders if `--artifactsinsubfolders` is used
394
+
395
+ Artifacts are **copied** into the output directory and renamed with the following structure:
396
+
397
+ ```
398
+ TIMESTAMP-ARGUMENT_INDEX-PABOTQUEUEINDEX
399
+ ```
400
+
401
+ - **TIMESTAMP** = Time of `pabot` command invocation (not the screenshot's actual timestamp), format: `YYYYmmdd_HHMMSS`
402
+ - **ARGUMENT_INDEX** = Optional index number, only used if `--argumentfileN` options are given
403
+ - **PABOTQUEUEINDEX** = Process queue index (see section [Global Variables](#global-variables))
404
+
405
+ #### `pabot_results` Folder Structure
406
+
407
+ The structure of the `pabot_results` folder is as follows:
408
+
409
+ ```
410
+ pabot_results/
411
+ ├── [N]/ # Optional: N = argument file index (if --argumentfileN is used)
412
+ │ └── PABOTQUEUEINDEX/ # One per subprocess
413
+ │ ├── output.xml
414
+ │ ├── robot_argfile.txt
415
+ │ ├── robot_stdout.out
416
+ │ ├── robot_stderr.out
417
+ │ └── artifacts...
418
+ ```
419
+
420
+ Each `PABOTQUEUEINDEX` folder contains as default:
421
+
422
+ - `robot_argfile.txt` – Arguments used in that subprocess
423
+ - `robot_stdout.out` and `robot_stderr.out` – Stdout and stderr of the subprocess
424
+ - `output.xml` – The partial output file to be merged later
425
+ - Artifacts – Screenshots or other files copied from subprocess folders
426
+
427
+ > **Note:** The entire `pabot_results` folder is considered temporary and will be **deleted/overwritten** on the next `pabot` run using the same `--outputdir`.
428
+
429
+
430
+ ### Artifacts Handling and Parallel Execution Notes
431
+
432
+ Due to parallel execution, artifacts like screenshots should ideally be:
433
+
434
+ - Embedded directly into the XML using tools like [SeleniumLibrary](https://robotframework.org/SeleniumLibrary/SeleniumLibrary.html#Set%20Screenshot%20Directory) with the `EMBED` option
435
+ _Example:_
436
+ `Library SeleniumLibrary screenshot_root_directory=EMBED`
437
+ - Or saved to the subprocess’s working directory (usually default behavior), ensuring separation across processes
438
+
439
+ If you manually specify a shared screenshot directory in your test code, **all processes will write to it concurrently**, which may cause issues such as overwriting or missing files if screenshots are taken simultaneously.
@@ -7,4 +7,4 @@ try:
7
7
  except ImportError:
8
8
  pass
9
9
 
10
- __version__ = "4.3.2"
10
+ __version__ = "5.0.0"
@@ -1,5 +1,5 @@
1
1
  from functools import total_ordering
2
- from typing import Dict, List, Optional, Tuple, Union
2
+ from typing import Dict, List, Optional, Tuple, Union, Set
3
3
 
4
4
  from robot import __version__ as ROBOT_VERSION
5
5
  from robot.errors import DataError
@@ -8,36 +8,68 @@ from robot.utils import PY2, is_unicode
8
8
  import re
9
9
 
10
10
 
11
- def create_dependency_tree(items):
11
+ def create_dependency_tree(items):
12
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)
13
+ dependency_tree = [] # type: List[List[ExecutionItem]]
14
+ scheduled = set() # type: Set[str]
15
+ name_to_item = {item.name: item for item in items} # type: Dict[str, ExecutionItem]
16
+
17
+ while items:
18
+ stage = [] #type: List[ExecutionItem]
19
+ stage_names = set() # type: Set[str]
20
+
21
+ for item in items:
22
+ if all(dep in scheduled for dep in item.depends):
23
+ stage.append(item)
24
+ stage_names.add(item.name)
28
25
  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
- )
26
+ break # Preserve input order
27
+
28
+ if not stage:
29
+ # Try to find any schedulable item even if it's out of order
30
+ for item in items:
31
+ if all(dep in scheduled for dep in item.depends):
32
+ stage = [item]
33
+ stage_names = {item.name}
34
+ break
35
+
36
+ if not stage:
37
+ # Prepare a detailed error message
38
+ unscheduled_items = [item.name for item in items]
39
+ unsatisfied_deps = {
40
+ item.name: [d for d in item.depends if d not in scheduled and d not in name_to_item]
41
+ for item in items
42
+ }
43
+ potential_cycles = {
44
+ item.name: [d for d in item.depends if d in unscheduled_items]
45
+ for item in items if item.depends
46
+ }
47
+
48
+ message = ["Invalid test configuration:"]
49
+
50
+ message_unsatisfied = []
51
+ for item, deps in unsatisfied_deps.items():
52
+ if deps:
53
+ message_unsatisfied.append(f" - {item} depends on missing: {', '.join(deps)}")
54
+ if message_unsatisfied:
55
+ message.append(" Unsatisfied dependencies:")
56
+ message.extend(message_unsatisfied)
57
+ message.append(" For these tests, check that there is not #WAIT between them and that they are not inside different groups { }")
58
+
59
+ message_cycles = []
60
+ for item, deps in potential_cycles.items():
61
+ if deps:
62
+ message_cycles.append(f" - {item} <-> {', '.join(deps)}")
63
+ if message_cycles:
64
+ message.append(" Possible circular dependencies:")
65
+ message.extend(message_cycles)
66
+
67
+ raise DataError("\n".join(message))
68
+
69
+ dependency_tree.append(stage)
70
+ scheduled.update(stage_names)
71
+ items = [item for item in items if item.name not in stage_names]
72
+
41
73
  return dependency_tree
42
74
 
43
75
 
@@ -47,6 +79,7 @@ class ExecutionItem(object):
47
79
  type = None # type: str
48
80
  name = None # type: str
49
81
  sleep = 0 # type: int
82
+ depends = [] # type: List[str] # Note that depends is used by RunnableItems.
50
83
 
51
84
  def top_name(self):
52
85
  # type: () -> str
@@ -156,7 +189,6 @@ class GroupItem(ExecutionItem):
156
189
  class RunnableItem(ExecutionItem):
157
190
  pass
158
191
 
159
- depends = None # type: List[str]
160
192
  depends_keyword = "#DEPENDS"
161
193
 
162
194
  def _split_dependencies(self, line_name, depends_indexes):
@@ -182,7 +214,7 @@ class RunnableItem(ExecutionItem):
182
214
  self.depends = (
183
215
  self._split_dependencies(line_name, depends_indexes)
184
216
  if len(depends_indexes) != 0
185
- else None
217
+ else []
186
218
  )
187
219
 
188
220
  def line(self):
@@ -243,6 +275,10 @@ class SuiteItem(RunnableItem):
243
275
  # TODO Make this happen
244
276
  return []
245
277
 
278
+ def modify_options_for_executor(self, options):
279
+ if not(options.get("runemptysuite") and options.get("suite")):
280
+ options[self.type] = self.name
281
+
246
282
 
247
283
  class TestItem(RunnableItem):
248
284
  type = "test"