robotframework-pabot 5.2.2
Parallel test runner for Robot Framework
License: Apache License, Version 2.0
Requires Python: >=3.6

# Pabot

[中文版](README_zh.md)

[![Version](https://img.shields.io/pypi/v/robotframework-pabot.svg)](https://pypi.python.org/pypi/robotframework-pabot)
[![Downloads](http://pepy.tech/badge/robotframework-pabot)](http://pepy.tech/project/robotframework-pabot)

<img src="https://raw.githubusercontent.com/mkorpela/pabot/master/pabot.png" width="100">

----

A parallel executor for [Robot Framework](http://www.robotframework.org) tests. With Pabot you can split one execution into multiple and save test execution time.

[![Pabot presentation at robocon.io 2018](http://img.youtube.com/vi/i0RV6SJSIn8/0.jpg)](https://youtu.be/i0RV6SJSIn8 "Pabot presentation at robocon.io 2018")

## Table of Contents

- [Installation](#installation)
- [Basic use](#basic-use)
- [Contact](#contact)
- [Contributing](#contributing-to-the-project)
- [Command-line options](#command-line-options)
- [PabotLib](#pabotlib)
- [Controlling execution order, mode and level of parallelism](#controlling-execution-order-mode-and-level-of-parallelism)
- [Programmatic use](#programmatic-use)
- [Global variables](#global-variables)
- [Output Files Generated by Pabot](#output-files-generated-by-pabot)
- [Artifacts Handling and Parallel Execution Notes](#artifacts-handling-and-parallel-execution-notes)

----

## Installation:

Pabot can be installed in the following ways:

### From PyPI

    pip install -U robotframework-pabot

### From source (standard install)

Clone this repository and run:

    python setup.py install

### From source (editable / development mode)

Clone this repository and run:

    pip install --editable .

### Note

Starting from **Pabot version 5.2.0**, the Pabot installation only requires
**Robot Framework** to function.

The previously used dependency **natsort** has been replaced with a pure
Python implementation.

In addition, **robotframework-stacktrace** has been moved to an **optional**
dependency. If it is installed, Pabot will continue to use it when running
sub-Robot processes to improve fail/error logging both in Pabot’s own log file
and on the command line.

You can install the optional stacktrace support with:

    pip install -U robotframework-pabot[stacktrace]

## Basic use

Split execution to suite files.

     pabot [path to tests]

Split execution on test level.

     pabot --testlevelsplit [path to tests]

Run same tests with two different configurations.

     pabot --argumentfile1 first.args --argumentfile2 second.args [path to tests]

For more complex cases please read onward.

## Contact

Join [Pabot Slack channel](https://robotframework.slack.com/messages/C7HKR2L6L) in Robot Framework slack.
[Get invite to Robot Framework slack](https://robotframework-slack-invite.herokuapp.com/).


## Contributing to the project

There are several ways you can help in improving this tool:

   - Report an issue or an improvement idea to the [issue tracker](https://github.com/mkorpela/pabot/issues)
   - Contribute by programming and making a pull request (easiest way is to work on an issue from the issue tracker)

Before contributing, please read our detailed contributing guidelines:

- [Contributing Guide](CONTRIBUTING.md)
- [Code of Conduct](CODE_OF_CONDUCT.md)
- [Security Policy](SECURITY.md)

## Command-line options
<!-- NOTE: 
The sections inside these docstring markers are also used in Pabot's --help output.
Currently, the following transformations are applied:
- Remove Markdown links but keep the text
- Remove ** and backticks `

If you modify this part, make sure the Markdown section still looks clean and readable in the --help output.  -->

<!-- START DOCSTRING -->
```
pabot [--verbose|--testlevelsplit|--command .. --end-command|
        --processes num|--no-pabotlib|--pabotlibhost host|--pabotlibport port|
        --processtimeout num|
        --shard i/n|
        --artifacts extensions|--artifactsinsubfolders|
        --resourcefile file|--argumentfile[num] file|--suitesfrom file
        --ordering file [static|dynamic] [skip|run_all]|
        --chunk|
        --pabotprerunmodifier modifier|
        --no-rebot|
        --pabotconsole [verbose|dotted|quiet|none]|
        --help|--version]
      [robot options] [path ...]
```

PabotLib remote server is started by default to enable locking and resource distribution between parallel test executions.

Supports all [Robot Framework command line options](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#command-line-options) and also following pabot options:

**--verbose**     
  More output from the parallel execution.

**--testlevelsplit**          
  Split execution on test level instead of default suite level. If .pabotsuitenames contains both tests and suites then
  this will only affect new suites and split only them. Leaving this flag out when both suites and tests in 
  .pabotsuitenames file will also only affect new suites and add them as suite files.

**--command [ACTUAL COMMANDS TO START ROBOT EXECUTOR] --end-command**    
  RF script for situations where robot is not used directly.

**--processes [NUMBER OF PROCESSES]**          
  How many parallel executors to use (default max of 2 and cpu count). Special option "all" will use as many processes as 
  there are executable suites or tests.

**--no-pabotlib**  
  Disable the PabotLib remote server if you don't need locking or resource distribution features.

**--pabotlibhost [HOSTNAME]**          
  Connect to an already running instance of the PabotLib remote server at the given host (disables the local PabotLib 
  server start). For example, to connect to a remote PabotLib server running on another machine:
  
      pabot --pabotlibhost 192.168.1.123 --pabotlibport 8271 tests/

  The remote server can also be started and executed separately from pabot instances:
  
      python -m pabot.pabotlib <path_to_resourcefile> <host> <port>
      python -m pabot.pabotlib resource.txt 192.168.1.123 8271
  
  This enables sharing a resource with multiple Robot Framework instances.

  Additional details:
  - The default value for --pabotlibhost is 127.0.0.1.
  - If you provide a hostname other than 127.0.0.1, the local PabotLib server startup is automatically disabled.

**--pabotlibport [PORT]**          
  Port number of the PabotLib remote server (default is 8270). See --pabotlibhost for more information.

  Behavior with port and host settings:
  - If you set the port value to 0 and --pabotlibhost is 127.0.0.1 (default), a free port on localhost will be assigned automatically.

**--processtimeout [TIMEOUT]**          
  Maximum time in seconds to wait for a process before killing it. If not set, there's no timeout.

**--shard [INDEX]/[TOTAL]**   
  Optionally split execution into smaller pieces. This can be used for distributing testing to multiple machines.
  
**--artifacts [FILE EXTENSIONS]**   
  List of file extensions (comma separated). Defines which files (screenshots, videos etc.) from separate reporting 
  directories would be copied and included in a final report. Possible links to copied files in RF log would be updated 
  (only relative paths supported). The default value is `png`.

  Examples:

     --artifacts png,mp4,txt

  The artifact naming conventions are described in the README.md section: [Output Files Generated by Pabot](#output-files-generated-by-pabot).

**--artifactsinsubfolders**   
  Copy artifacts located not only directly in the RF output dir, but also in it's sub-folders.

**--resourcefile [FILEPATH]**          
  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.

**--argumentfile[INTEGER] [FILEPATH]**          
  Run same suites with multiple [argumentfile](http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#argument-files) options.

  For example:

     --argumentfile1 arg1.txt --argumentfile2 arg2.txt

**--suitesfrom [FILEPATH TO OUTPUTXML]**          
  Optionally read suites from output.xml file. Failed suites will run first and longer running ones will be executed 
  before shorter ones.

**--ordering [FILEPATH] [MODE] [FAILURE POLICY]**   
  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)
  - MODE (optional): [ static (default) | dynamic ]
  - FAILURE POLICY (optional, only in dynamic mode): [ skip | run_all (default) ]

**--chunk**   
  Optionally chunk tests to PROCESSES number of robot runs. This can save time because all the suites will share the same 
  setups and teardowns. Note that chunking is skipped if an ordering file is provided. The ordering file may contain 
  significantly complex execution orders, in which case consolidation into chunks is not possible.

**--pabotprerunmodifier [PRERUNMODIFIER MODULE OR CLASS]**   
  Like Robot Framework's --prerunmodifier, but executed only once in the pabot's main process after all other 
  --prerunmodifiers. But unlike the regular --prerunmodifier command, --pabotprerunmodifier is not executed again in each 
  pabot subprocesses. Depending on the intended use, this may be desirable as well as more efficient. Can be used, for 
  example, to modify the list of tests to be performed.

**--no-rebot**    
  If specified, the tests will execute as usual, but Rebot will not be called to merge the logs. This option is designed 
  for scenarios where Rebot should be run later due to large log files, ensuring better memory and resource availability. 
  Subprocess results are stored in the pabot_results folder.

**--pabotconsole [MODE]**
  The --pabotconsole option controls how much output is printed to the console.
  Note that all Pabot’s own messages are always logged to pabot_manager.log, regardless of the selected console mode.

  The available options are:
  - verbose (default):
    Prints all messages to the console, corresponding closely to what is written to the log file.
  - dotted:
    Prints important messages, warnings, and errors to the console, along with execution progress using the following notation:

      - PASS = .
      - FAIL = F
      - SKIP = s
    
    Note that each Robot Framework process is represented by a single character. 
    Depending on the execution parameters, individual tests may not have their own status character; 
    instead, the status may represent an entire suite or a group of tests.
  - quiet:
    Similar to dotted, but suppresses execution progress output.
  - none:
    Produces no console output at all.

**--help**             
  Print usage instructions.
 
**--version**                
  Print version information.

**Example usages:**

     pabot test_directory
     pabot --exclude FOO directory_to_tests
     pabot --command java -jar robotframework.jar --end-command --include SMOKE tests
     pabot --processes 10 tests     
     pabot --pabotlibhost 192.168.1.123 --pabotlibport 8271 --processes 10 tests
     pabot --artifacts png,mp4,txt --artifactsinsubfolders directory_to_tests
     # To disable PabotLib:
     pabot --no-pabotlib tests

<!-- END DOCSTRING -->
### PabotLib

pabot.PabotLib provides keywords that will help communication and data sharing between the executor processes.
These can be helpful when you must ensure that only one of the processes uses some piece of data or operates on some part of the system under test at a time.

PabotLib Docs are located at https://pabot.org/PabotLib.html.

Note that PabotLib uses the XML-RPC protocol, which does not support all possible object types. 
These limitations are described in the Robot Framework documentation in chapter [Supported argument and return value types](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#supported-argument-and-return-value-types).

### PabotLib example:

test.robot

      *** Settings ***
      Library    pabot.PabotLib
      
      *** Test Case ***
      Testing PabotLib
        Acquire Lock   MyLock
        Log   This part is critical section
        Release Lock   MyLock
        ${valuesetname}=    Acquire Value Set  admin-server
        ${host}=   Get Value From Set   host
        ${username}=     Get Value From Set   username
        ${password}=     Get Value From Set   password
        Log   Do something with the values (for example access host with username and password)
        Release Value Set
        Log   After value set release others can obtain the variable values

valueset.dat

      [Server1]
      tags=admin-server
      HOST=123.123.123.123
      USERNAME=user1
      PASSWORD=password1
      
      [Server2]
      tags=server
      HOST=121.121.121.121
      USERNAME=user2
      PASSWORD=password2

      [Server3]
      tags=admin-server
      HOST=222.222.222.222
      USERNAME=user3
      PASSWORD=password4


pabot call using resources from valueset.dat

      pabot --pabotlib --resourcefile valueset.dat test.robot

### Controlling execution order, mode, and level of parallelism

.pabotsuitenames file contains the list of suites that will be executed.
This file is created during pabot execution if it does not already exist. It acts as a cache to speed up processing when re-executing the same tests.
The file can be manually edited partially, but a simpler and more controlled approach is to use:

```bash
--ordering <FILENAME> [static|dynamic] [skip|run_all]
```

- **FILENAME** – path to the ordering file.  
- **mode** – optional execution mode, either `static` (default) or `dynamic`.  
    - `static` executes suites in predefined stages.  
    - `dynamic` executes tests as soon as all their dependencies are satisfied, allowing more optimal parallel execution.  
- **failure_policy** – determines behavior when dependencies fail. Used only in dynamic mode. Optional:  
    - `skip` – dependent tests are skipped if a dependency fails.  
    - `run_all` – all tests run regardless of failures (default).  

The ordering file syntax is similar to `.pabotsuitenames` but does not include the first 4 hash rows used by pabot. The ordering file defines the **execution order and dependencies** of suites and tests.  
The actual selection of what to run must still be done using options like `--test`, `--suite`, `--include`, or `--exclude`.

#### Controlling execution order

There different possibilities to influence the execution:

  * The order of suites can be changed.
  * 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:

```
--suite Top Suite.Sub Suite
--suite Top Suite
```

  * If the base suite name is changing with robot option [```--name / -N```](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#setting-suite-name) you can use either the new or old full test path. For example:

```
--test New Suite Name.Sub Suite.Test 1
OR
--test Old Suite Name.Sub Suite.Test 1
```

  * You can add a line with text `#WAIT` to force executor to wait until all previous suites have been executed.
  * 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.
  * 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.
      * Ordering mode effect to execution:
        * **Dynamic mode** will schedule dependent tests as soon as all their dependencies are satisfied. Note that in dynamic mode `#WAIT` is ignored, but you can achieve same results with using only `#DEPENDS` keywords.
        *  **Static mode** preserves stage barriers and executes the next stage only after all tests in the previous stage finish.
      * 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.
  * An example could be:

```
--test robotTest.1 Scalar.Test With Environment Variables #DEPENDS robotTest.1 Scalar.Test with BuiltIn Variables of Robot Framework
--test robotTest.1 Scalar.Test with BuiltIn Variables of Robot Framework
--test robotTest.2 Lists.Test with Keywords and a list
#WAIT
--test robotTest.2 Lists.Test with a Keyword that accepts multiple arguments
{
--test robotTest.2 Lists.Test with some Collections keywords
--test robotTest.2 Lists.Test to access list entries
}
--test robotTest.3 Dictionary.Test that accesses Dictionaries
--test robotTest.3 Dictionary.Dictionaries for named arguments #DEPENDS robotTest.3 Dictionary.Test that accesses Dictionaries
--test robotTest.1 Scalar.Test Case With Variables #DEPENDS robotTest.3 Dictionary.Test that accesses Dictionaries
--test robotTest.1 Scalar.Test with Numbers #DEPENDS robotTest.1 Scalar.Test With Arguments and Return Values
--test robotTest.1 Scalar.Test Case with Return Values #DEPENDS robotTest.1 Scalar.Test with Numbers
--test robotTest.1 Scalar.Test With Arguments and Return Values
--test robotTest.3 Dictionary.Test with Dictionaries as Arguments
--test robotTest.3 Dictionary.Test with FOR loops and Dictionaries #DEPENDS robotTest.1 Scalar.Test Case with Return Values
```

  * By using the command `#SLEEP X`, where `X` is an integer in the range [0-3600] (in seconds), you can 
  define a startup delay for each subprocess. `#SLEEP` affects the next line unless the next line starts a 
  group with `{`, in which case the delay applies to the entire group. If the next line begins with `--test` 
  or `--suite`, the delay is applied to that specific item. Any other occurrences of `#SLEEP` are ignored. 
  Note that `#SLEEP` has no effect within a group, i.e., inside a subprocess.

The following example clarifies the behavior:

```sh
pabot --processes 2 --ordering order.txt data_1
```

where order.txt is:

```
#SLEEP 1
{
#SLEEP 2
--suite Data 1.suite A
#SLEEP 3
--suite Data 1.suite B
#SLEEP 4
}
#SLEEP 5
#SLEEP 6
--suite Data 1.suite C
#SLEEP 7
--suite Data 1.suite D
#SLEEP 8
```

Possible output could be:

```
2025-02-15 19:15:00.408321 [0] [ID:1] SLEEPING 6 SECONDS BEFORE STARTING Data 1.suite C
2025-02-15 19:15:00.408321 [1] [ID:0] SLEEPING 1 SECONDS BEFORE STARTING Group_Data 1.suite A_Data 1.suite B
2025-02-15 19:15:01.409389 [PID:52008] [1] [ID:0] EXECUTING Group_Data 1.suite A_Data 1.suite B
2025-02-15 19:15:06.409024 [PID:1528] [0] [ID:1] EXECUTING Data 1.suite C
2025-02-15 19:15:09.257564 [PID:52008] [1] [ID:0] PASSED Group_Data 1.suite A_Data 1.suite B in 7.8 seconds
2025-02-15 19:15:09.259067 [1] [ID:2] SLEEPING 7 SECONDS BEFORE STARTING Data 1.suite D
2025-02-15 19:15:09.647342 [PID:1528] [0] [ID:1] PASSED Data 1.suite C in 3.2 seconds
2025-02-15 19:15:16.260432 [PID:48156] [1] [ID:2] EXECUTING Data 1.suite D
2025-02-15 19:15:18.696420 [PID:48156] [1] [ID:2] PASSED Data 1.suite D in 2.4 seconds
```

### Programmatic use

Library offers an endpoint `main_program` that will not call `sys.exit`. This can help in developing your own python program around pabot.

```Python
import sys
from pabot.pabot import main_program

def amazing_new_program():
    print("Before calling pabot")
    exit_code = main_program(['tests'])
    print(f"After calling pabot (return code {exit_code})")
    sys.exit(exit_code)

```

### Global variables

Pabot will insert following global variables to Robot Framework namespace. These are here to enable PabotLib functionality and for custom listeners etc. to get some information on the overall execution of pabot.

      PABOTQUEUEINDEX - this contains a unique index number for the execution. Indexes start from 0.
      PABOTLIBURI - this contains the URI for the running PabotLib server
      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.
      PABOTNUMBEROFPROCESSES - max number of concurrent processes that pabot may use in execution.
      CALLER_ID - a universally unique identifier for this execution.


### Output Files Generated by Pabot

Pabot generates several output files and folders during execution, both for internal use and for analysis purposes.

#### Internal File: `.pabotsuitenames`

Pabot creates a `.pabotsuitenames` file in the working directory. This is an internal hash file used to speed up execution in certain scenarios.  
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.  
Therefore, it is **recommended** to maintain a separate file for the `--ordering` option if needed.

#### Output Directory Structure

In addition to the standard `log.html`, `report.html`, and `output.xml` files, the specified `--outputdir` will contain:

- A folder named `pabot_results`, and  
- All defined artifacts (default: `.png` files)  
- Optionally, artifacts from subfolders if `--artifactsinsubfolders` is used

Artifacts are **copied** into the output directory and renamed with the following structure:

```
TIMESTAMP-ARGUMENT_INDEX-PABOTQUEUEINDEX
```

If you use the special option `notimestamps` at the end of the `--artifacts` command, (For example:  `--artifacts png,txt,notimestamps`) the timestamp part will be omitted, and the name will be in the format: 

```
ARGUMENT_INDEX-PABOTQUEUEINDEX
```

- **TIMESTAMP** = Time of `pabot` command invocation (not the screenshot's actual timestamp), format: `YYYYmmdd_HHMMSS`
- **ARGUMENT_INDEX** = Optional index number, only used if `--argumentfileN` options are given
- **PABOTQUEUEINDEX** = Process queue index (see section [Global Variables](#global-variables))

#### `pabot_results` Folder Structure

The structure of the `pabot_results` folder is as follows:

```
pabot_results/
├── [N]/                     # Optional: N = argument file index (if --argumentfileN is used)
│   └── PABOTQUEUEINDEX/     # One per subprocess
│       ├── output.xml
│       ├── robot_argfile.txt
│       ├── robot_stdout.out
│       ├── robot_stderr.out
│       └── artifacts...
└── pabot_manager.log        # Pabot's own main log.
```

Each `PABOTQUEUEINDEX` folder contains as default:

- `robot_argfile.txt` – Arguments used in that subprocess
- `robot_stdout.out` and `robot_stderr.out` – Stdout and stderr of the subprocess
- `output.xml` – The partial output file to be merged later
- Artifacts – Screenshots or other files copied from subprocess folders

> **Note:** The entire `pabot_results` folder is considered temporary and will be **deleted/overwritten** on the next `pabot` run using the same `--outputdir`.


### Artifacts Handling and Parallel Execution Notes

Due to parallel execution, artifacts like screenshots should ideally be:

- Embedded directly into the XML using tools like [SeleniumLibrary](https://robotframework.org/SeleniumLibrary/SeleniumLibrary.html#Set%20Screenshot%20Directory) with the `EMBED` option  
  _Example:_  
  `Library  SeleniumLibrary  screenshot_root_directory=EMBED`
- Or saved to the subprocess’s working directory (usually default behavior), ensuring separation across processes

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.